-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Deprecate getindex(::Factorization, ::Symbol) in favor of dot overloading #25184
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
Conversation
badc334 to
7da8002
Compare
StefanKarpinski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gorgeous! Why are all the getproperty methods marked as @inline?
base/linalg/bunchkaufman.jl
Outdated
| ``` | ||
| """ | ||
| function getindex(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat} | ||
| @inline function getproperty(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like a lot to inline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think inlining is necessary to get the constant propagation working sufficiently well to make the factors inferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. Maybe that wasn't the issue. It seems to work now. I'll try to remove the annotations and see what happens.
| julia> F = bkfact(Symmetric(A)); | ||
| julia> F[:U]*F[:D]*F[:U]' - F[:P]*A*F[:P]' | ||
| julia> F.U*F.D*F.U' - F.P*A*F.P' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh boy, that is soooo much prettier
base/linalg/cholesky.jl
Outdated
| d == :L && return LowerTriangular(Symbol(C.uplo) == d ? C.factors : C.factors') | ||
| d == :p && return C.piv | ||
| if d == :P | ||
| @inline function getproperty(C::Cholesky, d::Symbol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, a lot to inline
38d96dd to
b474339
Compare
Sacha0
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beautiful! :) I will try to work in a proper review this evening.
|
This is really nice. Previously I wrapped SVD for |
base/deprecated.jl
Outdated
|
|
||
| # Use getproperty instead of getindex for Factorizations | ||
| function getindex(F::Factorization, s::Symbol) | ||
| depwarn("F[:$s] is deprecated, use F.$s instead.", :getindex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that this method's F may have any name in the caller's code, F[:...] might be a bit obscure. Perhaps expand this depwarn a bit? A sketch:
Extracting factorization components via getindex(F::Factorization, s::Symbol) methods, usually written F[:s] where F is the <:Factorization and :s identifies the factorization component (for example aQRfact[:Q]), has been deprecated in favor of F.s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed or disagree? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decided to think a little longer and then forgot. I've decided that I prefer the current shorter version. If users later report that it is unclear we can adjust but I wouldn't expect that.
| size(B::BunchKaufman) = size(B.LD) | ||
| size(B::BunchKaufman, d::Integer) = size(B.LD, d) | ||
| size(B::BunchKaufman) = size(getfield(B, :LD)) | ||
| size(B::BunchKaufman, d::Integer) = size(getfield(B, :LD), d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why switch to explicitly calling getfield here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid an infinite cycle between size and getproperty. The reason is that getproperty always calls size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cheers, and thanks for the explanation! :)
base/linalg/cholesky.jl
Outdated
| Cfactors = getfield(C, :factors) | ||
| Cuplo = getfield(C, :uplo) | ||
| if d == :U | ||
| return UpperTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming you would like an eager adjoint of Cfactors, Cfactors' -> adjoint(Cfactors)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't consider the possibility. I'm not sure. Should it really be eager when it could be lazy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want this type stable it needs to be eager, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. I thought it would be known at compile time but that is of course not the case for Cuplo. I'll change it.
base/linalg/cholesky.jl
Outdated
| if d == :U | ||
| return UpperTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') | ||
| elseif d == :L | ||
| return LowerTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise here re. eager versus lazy adjoint :).
base/linalg/cholesky.jl
Outdated
| Cfactors = getfield(C, :factors) | ||
| Cuplo = getfield(C, :uplo) | ||
| if d == :U | ||
| return UpperTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And likewise here re. eager versus lazy adjoint :).
base/linalg/cholesky.jl
Outdated
| if d == :U | ||
| return UpperTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') | ||
| elseif d == :L | ||
| return LowerTriangular(Symbol(Cuplo) == d ? Cfactors : Cfactors') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And likewise here re. eager versus lazy adjoint :).
| d == :values && return A.values | ||
| d == :vectors && return A.vectors | ||
| throw(KeyError(d)) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| getindex(A::LQPackedQ, i::Integer, j::Integer) = | ||
| mul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i] | ||
|
|
||
| getq(A::LQ) = LQPackedQ(A.factors, A.τ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯! Perhaps getq deserves a deprecation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| function _qr(A::Union{Number, AbstractMatrix}, ::Val{true}; full::Bool = false) | ||
| F = qrfact(A, Val(true)) | ||
| Q, R, p = getq(F), F[:R]::Matrix{eltype(F)}, F[:p]::Vector{BlasInt} | ||
| Q, R, p = F.Q, F.R, F.p |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic.
| return F.Vt' | ||
| function getproperty(F::SVD, d::Symbol) | ||
| if d == :V | ||
| return getfield(F, :Vt)' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adjoint(getfield(F, :Vt)) for eager adjoint?
| Sparse(p::Ptr{C_Sparse{Tv}}) where {Tv<:VTypes} = Sparse{Tv}(p) | ||
|
|
||
| Base.unsafe_convert(::Type{Ptr{Tv}}, A::Sparse{Tv}) where {Tv} = A.p | ||
| Base.unsafe_convert(::Type{Ptr{Tv}}, A::Sparse{Tv}) where {Tv} = getfield(A, :ptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch necessary? (Edit: I'm guessing so given you perform the same rewrite in the unsafe_convert def below as well?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to change the name of the field to ptr because F.p is now syntax for extracting a permutation vector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
test/linalg/cholesky.jl
Outdated
| @test_throws DimensionMismatch LinAlg.lowrankupdate(F, ones(eltype($v), length($v)+1)) | ||
| @test LinAlg.lowrankdowndate(G, $v).$uplo ≈ F.$uplo | ||
| @test_throws DimensionMismatch LinAlg.lowrankdowndate(G, ones(eltype($v), length($v)+1)) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps explicit getproperty calls (if possible?) would be simpler than metafication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to exercise the compile time version but that is probably not worth the metafication here.
| @test convert(Array, usv) ≈ a | ||
| @test usv[:Vt]' ≈ usv[:V] | ||
| @test_throws KeyError usv[:Z] | ||
| @test usv.Vt' ≈ usv.V |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eager adjoint? :)
Sacha0
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonderful. Simply wonderful :).
4da58d2 to
894cb01
Compare
|
Rebase and merge? :) |
894cb01 to
c4e0e30
Compare
|
🎉 |
Now that we have dot overloading, it is possible to use that instead of the special
getindexmethods forFactorizations. Furthermore, with a little care in thegetpropertymethods, it is possible to make the constant propagation work such thatqrandluare now inferred even though they extract the factors from theFactorization.becomes
Fixes JuliaLang/LinearAlgebra.jl#493