Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension for PDMats #303

Merged
merged 6 commits into from
Nov 27, 2023
Merged

Conversation

devmotion
Copy link
Contributor

This PR adds an extension for PDMats that is optionally loaded on Julia < 1.9 via Requires.

The sole purpose of the extension is to define the PDMats.AbstractPDMat constructor (added in JuliaStats/PDMats.jl#172) for Diagonal matrices with AbstractFill diagonals, so it is extremely lightweight. This constructor is very useful for packages that work with positive-definite matrices and use the type system and/or traits in PDMats and want to support generic user-provided matrices. A concrete use case is Distributions: For instance, with this extension the constructors of MvNormal in https://github.com/JuliaStats/Distributions.jl/blob/87aebc29b2b9608801b70aae09fbc1d2dad56e3f/src/multivariate/mvnormal.jl#L201-L210 (and similar constructors for other distributions that are constructed from positive-definite matrices) could be simplified to

MvNormal::AbstractVector{<:Real}, Σ::AbstractMatrix{<:Real}) = MvNormal(μ, AbstractPDMat(Σ))
MvNormal::AbstractVector{<:Real}, Σ::UniformScaling{<:Real}) =
    MvNormal(μ, ScalMat(length(μ), Σ.λ))

Without this extension, a Diagonal matrix with an AbstractFill diagonal would be converted to a PDiagMat, a type for positive-definite diagonal matrices, instead of the more efficient ScalMat type for positive-definite scaled identity matrices.

Originally, I proposed to add this extension to PDMats (JuliaStats/PDMats.jl#192) but after some discussions it was decided that it would be more suitable to add the extension to FillArrays: As pointed out in JuliaStats/PDMats.jl#182 (comment), currently PDMats loads very quickly compared with FillArrays (possibly due to invalidations?). For instance on my computer the latest releases of FillArrays and PDMats load in

% julia --startup-file=no -e '@time using FillArrays'
  0.054321 seconds (118.30 k allocations: 8.031 MiB)
% julia --startup-file=no -e '@time using FillArrays'
  0.050830 seconds (118.30 k allocations: 8.031 MiB)
% julia --startup-file=no -e '@time using PDMats'
  0.014219 seconds (25.44 k allocations: 1.587 MiB)
% julia --startup-file=no -e '@time using PDMats'
  0.013974 seconds (25.44 k allocations: 1.586 MiB)
% julia --startup-file=no -e '@time using PDMats, FillArrays'
  0.061822 seconds (135.90 k allocations: 9.130 MiB)
% julia --startup-file=no -e '@time using PDMats, FillArrays'
  0.061529 seconds (135.90 k allocations: 9.126 MiB)

Conceptually, it is also not feasible to add extensions for every custom array type to PDMats, so adding an extension to FillArrays and, whenever suitable, to other array packages seems more practical.

It also seems that this PR does not have a significant impact on loading times of FillArrays. With this PR, on Julia 1.9.3 I see

% julia --startup-file=no -e '@time using FillArrays'
  0.051712 seconds (119.53 k allocations: 8.144 MiB)
% julia --startup-file=no -e '@time using FillArrays'
  0.051962 seconds (119.53 k allocations: 8.142 MiB)
% julia --startup-file=no -e '@time using PDMats, FillArrays'
  0.062234 seconds (137.70 k allocations: 9.309 MiB)
% julia --startup-file=no -e '@time using PDMats, FillArrays'
  0.062471 seconds (137.70 k allocations: 9.306 MiB)

@codecov
Copy link

codecov bot commented Oct 23, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (fef31cd) 99.88% compared to head (4380a97) 99.88%.
Report is 8 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #303   +/-   ##
=======================================
  Coverage   99.88%   99.88%           
=======================================
  Files           7        8    +1     
  Lines         871      903   +32     
=======================================
+ Hits          870      902   +32     
  Misses          1        1           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

src/FillArrays.jl Outdated Show resolved Hide resolved
@dlfivefifty
Copy link
Member

I accept your argument for this being the right place for the extension to live. (This is a flaw in the extension setup that one package "owns" an extension so these arguments happen....)

I personally don't particularly care about supporting <v1.9 but if someone else wants to comment on adding Requires.jl dependency please do.

@static if !isdefined(Base, :get_extension)
function __init__()
Requires.@require PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" begin
include("../ext/FillArraysPDMatsExt.jl")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This codecov annotation is wrong - the line is hit since otherwise tests on Julia 1.6 would fail.

@devmotion
Copy link
Contributor Author

Bump 🙂

@@ -0,0 +1,18 @@
module FillArraysPDMatsExt

if isdefined(Base, :get_extension)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style seems a bit unusual to me. Would it not be possible to import the packages directly, or is the namespace specification necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch is necessary, Requires needs relative imports (as shown also in the docs: https://github.com/JuliaPackaging/Requires.jl and https://pkgdocs.julialang.org/v1/creating-packages/#Requires.jl).

Moreover, in an extension one should import only the package itself and its weak dependency, and even stdlibs should be loaded indirectly: JuliaStats/LogExpFunctions.jl#63

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting, I had no idea about this transitive loading issue. Looks fine to me then

@jishnub
Copy link
Member

jishnub commented Nov 1, 2023

Since PDMats is lightweight, I wonder if it makes sense to convert this to an explicit dependency on versions older than v1.9, instead of using Requires? This will lead to better precompilation, which we may want for a low-level package such as FillArrays.

@devmotion
Copy link
Contributor Author

devmotion commented Nov 1, 2023

I would prefer that actually but I wasn't sure if you would accept a dependency on PDMats when I opened the PR.

What do you think @dlfivefifty?

@jishnub
Copy link
Member

jishnub commented Nov 20, 2023

Gentle bump @dlfivefifty. I think it's better to avoid Requires unless absolutely necessary, and make it a hard dependency on old Julia versions.

@dlfivefifty
Copy link
Member

Why are you bumping me? This PR isn't ready, I agree it shouldn't add a dependency on Requires.jl

@jishnub
Copy link
Member

jishnub commented Nov 21, 2023

I'm sorry, but I thought that this was stalled and waiting for your input

@devmotion
Copy link
Contributor Author

devmotion commented Nov 21, 2023

We were waiting for an answer to #303 (comment).

The PR was ready but apparently some recent changes on the master branch caused merge conflicts.

I updated the PR and removed Requires: devmotion@e661a20

Edit: Seems there is some Github issue, the new commits don't show up in the PR (yet).

@devmotion
Copy link
Contributor Author

Bump 🙂

@dlfivefifty dlfivefifty merged commit b6fa0b4 into JuliaArrays:master Nov 27, 2023
24 checks passed
@devmotion devmotion deleted the dw/pdmats branch November 27, 2023 21:11
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.

3 participants