-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Deprecate getindex(::Factorization, ::Symbol) in favor of dot overloading #25184
Conversation
badc334
to
7da8002
Compare
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
3×3 Array{Float64,2}: | ||
0.0 0.0 0.0 | ||
0.0 0.0 0.0 | ||
0.0 0.0 0.0 | ||
``` | ||
""" | ||
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.
3×3 Array{Float64,2}: | ||
0.0 0.0 0.0 | ||
0.0 0.0 0.0 | ||
0.0 0.0 0.0 | ||
|
||
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
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
@@ -3423,6 +3423,12 @@ workspace() = error("workspace() is discontinued, check out Revise.jl for an alt | |||
# PR #25113 | |||
@deprecate_binding CartesianRange CartesianIndices | |||
|
|||
# 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.
👍
end | ||
end | ||
|
||
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
sQf1 = size(Q.factors, 1) | ||
return (!full ? Array(Q) : mul!(Q, Matrix{eltype(Q)}(I, sQf1, sQf1))), R | ||
end | ||
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.
base/linalg/svd.jl
Outdated
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?
finalizer(free!, A) | ||
return A | ||
end | ||
end | ||
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/linalg/svd.jl
Outdated
@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? :)
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
getindex
methods forFactorization
s. Furthermore, with a little care in thegetproperty
methods, it is possible to make the constant propagation work such thatqr
andlu
are now inferred even though they extract the factors from theFactorization
.becomes
Fixes JuliaLang/LinearAlgebra.jl#493