Skip to content

Add SpecializingFactorizations.jl package extension#1005

Merged
ChrisRackauckas merged 5 commits into
SciML:mainfrom
ChrisRackauckas-Claude:add-specializingfactorizations-ext
Jun 10, 2026
Merged

Add SpecializingFactorizations.jl package extension#1005
ChrisRackauckas merged 5 commits into
SciML:mainfrom
ChrisRackauckas-Claude:add-specializingfactorizations-ext

Conversation

@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Please ignore until reviewed by @ChrisRackauckas.

Adds an opt-in package extension wrapping SpecializingFactorizations.jl as two LinearSolve algorithms. This is a weakdep extension only — no hard dependency and no change to any default algorithm.

What SpecializingFactorizations.jl provides

It exposes two type-stable, single-workspace dense factorizations that detect the property a matrix actually has and dispatch to the specialized solve, all behind one concrete workspace type (the structure/rank choice is a runtime enum field, not a Julia type, so the pipeline infers and the warm path is allocation-free):

  • specializinglu / SpecializedLU — a square solver that cheaply scans A for structure (diagonal, bidiagonal, tridiagonal, banded, triangular, symmetric PD, symmetric/Hermitian indefinite) and uses the matching specialized factorization instead of a general O(n^3) LU.
  • specializingqr / SpecializedQR — a rectangular / rank-deficient least-squares solver. Column-pivoted rank-revealing QR (geqp3) that returns the least-squares / minimum-norm solution for any shape including singular/rank-deficient, never throwing.

Wrapped algorithms

  • SpecializedLUFactorization — wraps specializinglu; for square systems.
  • SpecializedQRFactorization — wraps specializingqr; for rectangular / rank-deficient least-squares.

Both wrapped because they cover complementary problem classes (square structured solves vs. rectangular/rank-deficient least-squares) and both expose the ldiv! + in-place re-factor (specializinglu! / specializingqr!) interface the LinearCache warm-solve path wants.

Implementation

  • src/extension_algs.jl: two AbstractDenseFactorization structs with docstrings + examples; exported from src/LinearSolve.jl.
  • ext/LinearSolveSpecializingFactorizationsExt.jl: init_cacheval builds the factorization; SciMLBase.solve! re-factors in place on cache.isfresh (reusing the workspace via specializing*!), guards issuccess, and returns build_linear_solution with the appropriate ReturnCode.
  • Project.toml: SpecializingFactorizations added to [weakdeps], [extensions], [compat] ("0.1"), [extras], and the test target. No SparseArrays trigger — it operates on dense matrices only.
  • test/specializing_factorizations.jl: focused test set (extension load, square/structured/complex/float32 LU, overdetermined and rank-deficient QR, cache reuse across re-factor for both), wired into the Core test group.

Registration

SpecializingFactorizations.jl is registered in the General registry (v0.1.0).

Caveats

  • SpecializedLUFactorization is square-only by construction; rectangular systems should use SpecializedQRFactorization.
  • Structure detection is structural (!iszero), not numerical/tolerant.
  • Cache reuse re-factors into the existing workspace; a different matrix size on a warm cache regrows buffers (handled by specializing*!).

🤖 Generated with Claude Code

@ChrisRackauckas

Copy link
Copy Markdown
Member

This should also get an opt-out #1023

Wraps SpecializingFactorizations.jl as two opt-in LinearSolve algorithms:

- SpecializedLUFactorization: type-stable, structure-detecting dense
  LU-style solver for square systems (diagonal/bidiagonal/tridiagonal/
  banded/triangular/symmetric specializations behind one workspace type).
- SpecializedQRFactorization: rank-revealing, never-throwing dense QR
  least-squares solver for rectangular / rank-deficient systems
  (minimum-norm least-squares == pinv(A)*b).

Both are AbstractDenseFactorization subtypes gated behind a weakdep
extension (ext/LinearSolveSpecializingFactorizationsExt.jl), implementing
init_cacheval + SciMLBase.solve! with cache reuse via specializinglu!/
specializingqr!. Added a focused test set (extension load + solve +
caching across LU/QR, structured, complex, float32, overdetermined, and
rank-deficient cases) wired into the Core test group.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude ChrisRackauckas-Claude force-pushed the add-specializingfactorizations-ext branch from 16310f0 to a81d68d Compare June 10, 2026 01:16
@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (was conflicting after the test-suite restructure in #1022 and the test-target slimming).

Conflict resolution:

  • Project.toml: kept main's slimmed compat/extras/test target and added SpecializingFactorizations to each (it stays a base test dep since the test runs in the Core group).
  • test/runtests.jl: took main's new grouped structure and re-added the test under the Core group as core/specializing_factorizations.jl (file moved from test/specializing_factorizations.jl to match the new test/<group>/ layout).

Verified locally on Julia 1.12: the LinearSolveSpecializingFactorizationsExt extension precompiles and all 18 tests in test/core/specializing_factorizations.jl pass. Runic reports no formatting changes on the touched files.

Per review on SciML#1005 (matching SciML#1023): SpecializingFactorizations has
generic-element fallbacks, so SpecializedLUFactorization and
SpecializedQRFactorization solve Dual-number problems directly via
_use_direct_dual_solve instead of the primal/partials splitting path.
The split-path math is square-only, so this also makes the QR
least-squares solver correct for Dual rectangular systems.

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

Copy link
Copy Markdown
Contributor Author

Rebased on main and added the ForwardDiff opt-out per the review comment (same pattern as #1023):

  • _use_direct_dual_solve in ext/LinearSolveForwardDiffExt.jl now returns true for SpecializedLUFactorization and SpecializedQRFactorization, so Dual-number problems are solved directly instead of through the primal/partials splitting path. This is valid because SpecializingFactorizations.jl has generic-element fallbacks for non-BlasFloat types (generic dense LU, generic column-pivoted QR). It also matters for correctness on the QR side: the split-path math (∂x = A⁻¹(∂b − ∂A·x)) is square-only, while the direct path handles Dual rectangular least-squares.
  • Added tests in test/core/forwarddiff_overloads.jl: square Dual systems through both algorithms plus a rectangular Dual least-squares solve through SpecializedQRFactorization, all checked against A \\ b / qr(A) \\ b. Note the tests compare values and partials separately: when the primal diff is exactly zero but partials differ by an ulp, isapprox on Dual vectors returns false because norm(diff) evaluates to Dual(NaN,…) (sqrt has infinite slope at 0), so the plain ≈(sol, ref) pattern only passes for bitwise-identical results.

Ran locally on the rebased branch: test/core/forwarddiff_overloads.jl (full file) and test/core/specializing_factorizations.jl both pass; Runic reports no formatting changes.

Note for whichever of this and #1023 merges second: both touch the same line of _use_direct_dual_solve, so there will be a trivial conflict.

Rename the Float32 test variable solf (flagged by typos as a misspelling
of solve) to sol32, and add SpecializedLUFactorization /
SpecializedQRFactorization to the docs solver list so the Documenter
missing-docs check covers them.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
resolve.jl enumerates every AbstractDenseFactorization subtype, so the
new SpecializedLU/QRFactorization hit the generic do_factorization
fallback when their extension is not loaded. Load the package (already
a test dep) so both algorithms go through the extension and get
re-solve coverage.

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

Copy link
Copy Markdown
Contributor Author

CI status on 3bd3d2a8: everything this PR touches is green — Core (julia 1 / lts / pre), QA, Spell Check, Runic, format-check, Sublibrary CI all pass (the earlier Core failure was resolve.jl's subtype sweep hitting the new algs without the extension loaded; fixed by loading SpecializingFactorizations there, which also gives both algorithms re-solve coverage).

Remaining red is not from this PR:

@ChrisRackauckas ChrisRackauckas marked this pull request as ready for review June 10, 2026 08:56
@ChrisRackauckas ChrisRackauckas merged commit 2f00829 into SciML:main Jun 10, 2026
30 of 50 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