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

Avoid copy in chol_lower #144

Merged
merged 13 commits into from
Nov 4, 2021
Merged

Conversation

st--
Copy link
Contributor

@st-- st-- commented Nov 3, 2021

Fixes #143

@st--
Copy link
Contributor Author

st-- commented Nov 3, 2021

Side note: chol_lower(::CholTypeSparse) seems to be working fine, but I'm not sure I understand enough about the implementation details of SuiteSparse to guarantee this.

src/chol.jl Outdated Show resolved Hide resolved
Copy link
Member

@andreasnoack andreasnoack left a comment

Choose a reason for hiding this comment

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

I think you can just make this chol_lower(a::Matrix) = cholesky(Hermitian(a, :L)).L.

I agree that CholType is redundant but please make that change in a separate PR.

@st--

This comment has been minimized.

@st--
Copy link
Contributor Author

st-- commented Nov 3, 2021

I think you can just make this chol_lower(a::Matrix) = cholesky(Hermitian(a, :L)).L.

Writing it in terms of chol_lower(cholesky(a)) seems cleaner to me, and it should compile to the same code; what benefits do you see in writing it as this ?

@st-- st-- mentioned this pull request Nov 3, 2021
@andreasnoack
Copy link
Member

and it should compile to the same code

No. The default is upper

julia> cholesky(A).uplo
'U': ASCII/Unicode U+0055 (category Lu: Letter, uppercase)

julia> cholesky(Hermitian(A, :L)).uplo
'L': ASCII/Unicode U+004C (category Lu: Letter, uppercase)

@st--
Copy link
Contributor Author

st-- commented Nov 3, 2021

Ok, not literally the same code, but chol_lower(cholesky(A)) which is equivalent to cholesky(A).U' is no less efficient (more efficient, actually) than introducing the Hermitian:

julia> using LinearAlgebra, PDMats

julia> A=rand(100,100); C=A'A;

julia> @allocated cholesky(Hermitian(C, :L)).L;  # compile

julia> @allocated cholesky(Hermitian(C, :L)).L
80208

julia> @allocated cholesky(C).U';  # compile

julia> @allocated cholesky(C).U'
80192

julia> @allocated PDMats.chol_lower(cholesky(C));  # compile

julia> @allocated PDMats.chol_lower(cholesky(C))
80192

@andreasnoack
Copy link
Member

It's just an artifact of the not wrapping it in a function

julia> chol_lower_an(A::Matrix) = cholesky(Hermitian(A, :L)).L;

julia> @allocated chol_lower_an(A);

julia> @allocated chol_lower_an(A)
80144

It's also best to avoid wrapper types when possible.

@st--
Copy link
Contributor Author

st-- commented Nov 3, 2021

It's just an artifact of the not wrapping it in a function

Ah, thanks for pointing that out.

It's also best to avoid wrapper types when possible.

Could you expand on what you mean ?

@andreasnoack
Copy link
Member

Could you expand on what you mean ?

Sure. The more types layers you have the less likely it is that a specific method has been written for it and you risk hitting a less optimized fallback. The compiler can also sometimes completely remove these wrapper types but I believe that the more layers you have, the harder it is for the compiler to do so.

@st--
Copy link
Contributor Author

st-- commented Nov 3, 2021

Ok, so do I understand correctly, you would prefer

-chol_lower(a::Matrix) = chol_lower(cholesky(a))
+chol_lower(a::Matrix) = cholesky(Hermitian(a, :L)).L

because the former returns a LowerTriangular{Adjoint{Matrix}}, whereas the latter returns a LowerTriangular{Matrix} directly?

@andreasnoack
Copy link
Member

That is correct

@st-- st-- requested a review from andreasnoack November 4, 2021 07:40
@st--
Copy link
Contributor Author

st-- commented Nov 4, 2021

Thanks, that makes sense. I've changed it accordingly:)

@codecov-commenter
Copy link

codecov-commenter commented Nov 4, 2021

Codecov Report

Merging #144 (1ce765d) into master (a0e7191) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #144   +/-   ##
=======================================
  Coverage   88.62%   88.62%           
=======================================
  Files           8        8           
  Lines         378      378           
=======================================
  Hits          335      335           
  Misses         43       43           
Impacted Files Coverage Δ
src/chol.jl 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a0e7191...1ce765d. Read the comment docs.

@andreasnoack andreasnoack merged commit e1ef63b into JuliaStats:master Nov 4, 2021
@st--
Copy link
Contributor Author

st-- commented Nov 4, 2021

Downside of merging it in with Hermitian() is that this breaks AD :S

@andreasnoack
Copy link
Member

Oh no. Do you have an example?

@st--
Copy link
Contributor Author

st-- commented Nov 4, 2021

Yes, it turns out to be a Zygote issue, I've made a PR to fix it here: FluxML/Zygote.jl#1114

@st--
Copy link
Contributor Author

st-- commented Nov 5, 2021

@andreasnoack looks like the Zygote issue might be a bit more complex to resolve; how about (at least temporarily) changing it from Hermitian to Symmetric?

@andreasnoack
Copy link
Member

Given that we have this restriction

abstract type AbstractPDMat{T<:Real} <: AbstractMatrix{T} end
it should be fine

@st-- st-- mentioned this pull request Nov 16, 2021
@st-- st-- deleted the st/avoid_copy branch November 16, 2021 09:22
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.

chol_lower does not avoid allocations
3 participants