Skip to content

Opt PureKLU and RFLU out of split dual AD path#1023

Open
ChrisRackauckas wants to merge 4 commits into
mainfrom
ChrisRackauckas-patch-1
Open

Opt PureKLU and RFLU out of split dual AD path#1023
ChrisRackauckas wants to merge 4 commits into
mainfrom
ChrisRackauckas-patch-1

Conversation

@ChrisRackauckas

Copy link
Copy Markdown
Member

No description provided.

Comment thread test/core/forwarddiff_overloads.jl Outdated
ChrisRackauckas-Claude pushed a commit to ChrisRackauckas-Claude/LinearSolve.jl that referenced this pull request Jun 10, 2026
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>
Two fixes for the direct dual opt-out path:

1. PureKLUFactorization's sparse init_cacheval methods were restricted to
   Float64/ComplexF64 eltypes, so Dual-eltype sparse matrices fell through
   to the nothing fallback and solve! crashed on cacheval.nzval. PureKLU's
   kernels are pure Julia and generic over Union{Real, Complex}, so widen
   the specializations accordingly.

2. The direct dual path built its inner problem from dual_A/dual_b as
   stored, but when only A carries duals b is a plain array, and __init
   takes the solution eltype from b, so the dual solution could not be
   stored (MethodError: no method matching Float64(::Dual)). This also
   affected the pre-existing GenericLUFactorization opt-out. Promote b to
   the cache's dual type in _solve_direct_dual!, and only take the direct
   path when A itself carries duals: with duals just in b, the split path
   (one primal factorization + partials back-solves) is strictly cheaper
   and works for all algorithms.

Adds test coverage for the duals-only-in-A and duals-only-in-b cases for
GenericLUFactorization, RFLUFactorization, and PureKLUFactorization.

Co-authored-by: Chris Rackauckas (Claude) <accounts@chrisrackauckas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
ChrisRackauckas added a commit that referenced this pull request Jun 10, 2026
* Add SpecializingFactorizations.jl package extension

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>

* Opt SpecializedLU/QR out of the split dual AD path

Per review on #1005 (matching #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>

* Fix typos-check name and document Specialized LU/QR in solvers.md

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>

* Load SpecializingFactorizations in resolve.jl re-solve sweep

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>

* Retrigger CI (tests/detect runner-infra failure, no steps ran)

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

---------

Co-authored-by: ChrisRackauckas-Claude <accounts@chrisrackauckas.com>
@ChrisRackauckas-Claude

Copy link
Copy Markdown
Contributor

Rebased onto current main (this branch went stale with merge conflicts in LinearSolveForwardDiffExt.jl and forwarddiff_overloads.jl). Since I can't push to this branch, the rebased + conflict-resolved version is at #1041 — it keeps the same commits and intent, with main's SpecializedLU/QR additions and dual_isapprox test helper merged in. forwarddiff_overloads.jl passes locally. Suggest closing this in favor of #1041.

ChrisRackauckas added a commit that referenced this pull request Jun 14, 2026
* Opt PureKLU and RFLU out of split dual AD path

* Update forwarddiff_overloads.jl

* Apply suggestion from @ChrisRackauckas

* Fix direct dual solve path for PureKLU and non-dual b (#1024)

Two fixes for the direct dual opt-out path:

1. PureKLUFactorization's sparse init_cacheval methods were restricted to
   Float64/ComplexF64 eltypes, so Dual-eltype sparse matrices fell through
   to the nothing fallback and solve! crashed on cacheval.nzval. PureKLU's
   kernels are pure Julia and generic over Union{Real, Complex}, so widen
   the specializations accordingly.

2. The direct dual path built its inner problem from dual_A/dual_b as
   stored, but when only A carries duals b is a plain array, and __init
   takes the solution eltype from b, so the dual solution could not be
   stored (MethodError: no method matching Float64(::Dual)). This also
   affected the pre-existing GenericLUFactorization opt-out. Promote b to
   the cache's dual type in _solve_direct_dual!, and only take the direct
   path when A itself carries duals: with duals just in b, the split path
   (one primal factorization + partials back-solves) is strictly cheaper
   and works for all algorithms.

Adds test coverage for the duals-only-in-A and duals-only-in-b cases for
GenericLUFactorization, RFLUFactorization, and PureKLUFactorization.

Co-authored-by: Chris Rackauckas (Claude) <accounts@chrisrackauckas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Drop redundant PureKLU init_cacheval widening (subsumed by #1037)

The original PR widened the `{T, Int64}` / `{T, Int32}` PureKLU `init_cacheval`
methods from `Union{Float64, ComplexF64}` to `Union{Real, Complex}` to let
ForwardDiff duals dispatch on the direct dual solve path. After rebasing onto
main, that is unnecessary: #1037 ("Default to PureKLU for generic-eltype sparse
LU") added a catch-all `where {T <: Number, Ti <: Integer}` method that already
builds the correct empty cache for any number eltype (duals included). The
widened specializations only duplicated it, so revert that file to main.
Verified: test/core/forwarddiff_overloads.jl (incl. the PureKLU sparse-dual
cases) passes with this file unchanged from main.

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

---------

Co-authored-by: Christopher Rackauckas <accounts@chrisrackauckas.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.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