Unify PDMat and PDSparseMat + move SparseArrays support to an extension#188
Unify PDMat and PDSparseMat + move SparseArrays support to an extension#188devmotion wants to merge 19 commits into
PDMat and PDSparseMat + move SparseArrays support to an extension#188Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #188 +/- ##
==========================================
+ Coverage 93.37% 94.13% +0.75%
==========================================
Files 11 13 +2
Lines 800 767 -33
==========================================
- Hits 747 722 -25
+ Misses 53 45 -8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dkarrasch
left a comment
There was a problem hiding this comment.
Thank you so much for taking up the challenge and moving this forward! I think this is a great service to the ecosystem. Perhaps we can make this work even for older Julia versions by using SuiteSparse or SparseArrays.SuiteSparse in the extension? On v1.9+, this should be a no-op, but on older versions (without Pkg extensions) this will just get loaded by including the extension code files. I'm not sure though if that would require "version-dependent" Project.toml files or other hassle.
AFAICT on older Julia versions we would have to keep both SparseArrays and SuiteSparse as dependencies. However, if only Julia >= 1.9 the extension only depends on SparseArrays, then SuiteSparse would still be a regular dependency and since it depends on SparseArrays the extension would be loaded unconditionally. Alternatively, the extension could depend on both SparseArrays and SuiteSparse (or only SuiteSparse) - but then users would have to load both SparseArrays and SuiteSparse (or only SuiteSparse). Both alternatives seem suboptimal. However, I just thought of another option that I will try later: Making SparseArrays and SuiteSparse both a weak dependency but letting the extension only depend on SparseArrays. |
|
Seems to work 🙂 On Julia 1.0 and 1.6, PDMats depends on SparseArrays and SuiteSparse (https://github.com/JuliaStats/PDMats.jl/actions/runs/6404900221/job/17386347115?pr=188#step:5:36, https://github.com/JuliaStats/PDMats.jl/actions/runs/6404900221/job/17386347711?pr=188#step:5:27) and on Julia 1.9 and nightly neither SparseArrays nor SuiteSparse are dependencies (https://github.com/JuliaStats/PDMats.jl/actions/runs/6404900221/job/17386348265?pr=188#step:5:24, https://github.com/JuliaStats/PDMats.jl/actions/runs/6404900221/job/17386348761?pr=188#step:5:24) and the tests pass successfully with installing and loading only SparseArrays. |
|
I'll update the PR once the other PRs (non-breaking bugfixes and possibly a new feature) are merged. |
timholy
left a comment
There was a problem hiding this comment.
Nice work!
One other thing we might want to look at, quite a few of the methods starting with ### (un)whitening rely on the Choleksy factorization. If there isn't time to review/generalize those algorithms, perhaps it might make sense to define a PDMatCholesky{T,S<:AbstractMatrix{T}} = PDMat{T,S,<:Cholesky} and then restrict the argument of those methods to be a PDMatCholesky? That way we're at least not promising things we can't deliver.
| end | ||
|
|
||
| function PDMat(mat::AbstractMatrix,chol::Cholesky{T,S}) where {T,S} | ||
| function PDMat(mat::AbstractMatrix{T}, chol::Cholesky) where {T<:Real} |
There was a problem hiding this comment.
| function PDMat(mat::AbstractMatrix{T}, chol::Cholesky) where {T<:Real} | |
| function PDMat(mat::AbstractMatrix{T}, fmat::Factorization) where {T<:Real} |
There was a problem hiding this comment.
Maybe we should wait with generalizing the constructor until we actually support and test other factorizations.
Co-authored-by: Tim Holy <tim.holy@gmail.com>
timholy
left a comment
There was a problem hiding this comment.
Needs the conflicts resolved, but otherwise LGTM.
|
Bump |
|
The PR is the result of some discussions I had with @andreasnoack about |
|
Right, I wasn't bumping you 🙂 |
| struct PDMat{T<:Real,S<:AbstractMatrix{T},F<:Factorization} <: AbstractPDMat{T} | ||
| mat::S # input matrix | ||
| chol::Cholesky{T,S} # Cholesky factorization of mat | ||
| fact::F # factorization of mat |
There was a problem hiding this comment.
One needs an inner constructor that checks isposdef(fact) before calling new. Otherwise I could call
E = eigen(A)
PDMat{eltype(A),typeof(A),typeof(E)}(A, E)even if A is not posdef.
There was a problem hiding this comment.
Currently none of the other constructors enforces positive semi-definite matrices: #22
So ideally this should be addressed more generally, to ensure that such checks are performed by all AbstractPDMat types. But I wonder if it should be left for a separate PR?
There was a problem hiding this comment.
Sure. But it's not a hard change, we should just do it once other dust has settled. Given that the PDMat wrapper promises positive-definiteness, it is kinda important to enforce this, especially if we widen the type definition so that it can no longer be guaranteed from the input types alone.
| @test M isa PDMat{Int,<:SymTridiagonal,<:Cholesky} | ||
| @test M == S | ||
| end | ||
| end |
There was a problem hiding this comment.
Probably want to add tests for what happens if one tries to construct with factorizations besides cholesky.
|
Now that the lower compat has been raised to v1.10, this would be worthwhile to revisit and finish. I don't have current loading/latency times, but at the time this would have helped avoiding to load SparseArrays.jl unconditionally and therefore help all the dependent packages to keep loading times low(er). |
Resolve conflicts from master, preserving all master improvements while keeping the PR's unification of `PDMat`/`PDSparseMat` and the SparseArrays package extension. Key resolutions: - `PDMat` keeps the generalized 3-parameter struct (`mat::S`, `fact::F`); master's coercing constructors and `convert` methods are re-expressed against `fact`/`cholesky(...)` (with an added 3-parameter `convert`). - Dense Cholesky operations stay on the general `PDMat`; `cholesky` is the only method restricted to the `PDMatCholesky` alias. The sparse extension overrides the CHOLMOD-specific methods by dispatch. - Master's sparse `invwhiten`/`invunwhiten` methods and modernized `quad`/`quad!` are folded into the extension's `pdsparsemat.jl`; generic sparse operations now flow through the unified core `PDMat` methods. - `congruence.jl` and `StatsBaseExt.jl` are narrowed to `PDMatCholesky` and switched from `.chol` to the `cholesky(...)` accessor (avoids catching sparse matrices and resolves dispatch ambiguities). - Preserved master features: coercing constructors/convert (#212, #210), `invwhiten`/`invunwhiten` (#221), `ldiv!` (#228), the `kron` fix (#218), ForwardDiff-safe `whiten!`/`unwhiten!` (#217), `congruence.jl`, the StatsBase extension, and the Runic formatting (#220). - Dropped pre-1.9 / Julia-1.0 compat shims (compat is now Julia >= 1.10); dropped the now-unused SuiteSparse dependency. - Fixed a latent dead `quad`/`invquad` specialization (`<:Vector` -> `Matrix{T}`). - Version bumped to 0.12.0 (breaking: `PDMat` struct parameters changed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR unifies
PDMatandPDSparseMatand makesPDSparseMatan (unexported) alias ofPDMat. This makes it also possible to move support forSparseArrays to an extension, without any workarounds such as non-functional types in the main package (see #174).Initially,
PDMatandPDSparseMatonly supportedMatrix{Float64}andSparseMatrixCSC{Float64}matrices. This restriction was lifted in #37 but the separate types with support forCholeskyandCHOLMOD.Factors were kept.Due to the refactoring of SuiteSparse and SparseArrays (it was integrated in SparseArrays in Julia 1.9), on Julia >= 1.9 only the SparseArrays dependency is needed and SuiteSparse only loads functionality from SparseArrays (https://github.com/JuliaSparse/SuiteSparse.jl/blob/e8285dd13a6d5b5cf52d8124793fc4d622d07554/src/SuiteSparse.jl). It seemed inconvenient to demand that users on Julia >= 1.9 load both SparseArrays and SuiteSparse for the sparse extension, and hence I decided to let the extension only depend on SparseArrays. IIUC, however, this means that we can't support Julia < 1.9 anymore since there SparseArrays does not contain the requiredCHOLMODfunctionality. @dkarrasch, is this correct?Since this means we drop support for the current LTS version, my suggestion would be to backport bugfixes to older Julia versions (at least >= 1.6) until another Julia version >= 1.9 becomes the new LTS version.Edit: The PR keeps support for Julia 1.0-, I found a workaround: #188 (comment)
Fixes #174.