-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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 methods for dividing triangular by diagonal matrices. #27999
Conversation
@@ -455,4 +455,21 @@ end | |||
@test Transpose(x)*D*x == (Transpose(x)*D)*x == (Transpose(x)*Array(D))*x | |||
end | |||
|
|||
@testset "Triangular division by Diagonal #27989" begin | |||
for _ in 1:100 |
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 loop seems unnecessary?
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.
You mean that testing with multiple random values is unnecessary, and I should go for a single one?
The major cost is compilation, once compiled, additional random tests are relatively cheap.
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.
Right, but we don't do this anywhere else. For testing correctness of the code path it would be more useful with a loop over e.g. (Float64, ComplexF64)
.
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.
Thanks for the suggestion, this actually caught a bug.
L = LowerTriangular(randn(K, K)) | ||
D = Diagonal(randn(K)) | ||
@test U / D isa UpperTriangular | ||
@test U / D == UpperTriangular(Matrix(U) / Matrix(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.
Perhaps combine these two (and similar below) as
@test (U / D)::UpperTriangular == UpperTriangular(Matrix(U) / Matrix(D))
for the same effect?
suggested by @fredrikekre
For broadcasting, tranpose should be used instead of adjoint.
stdlib/LinearAlgebra/src/diagonal.jl
Outdated
@@ -356,6 +356,11 @@ rdiv!(A::AbstractMatrix{T}, transD::Transpose{<:Any,<:Diagonal{T}}) where {T} = | |||
(\)(A::Union{QR,QRCompactWY,QRPivoted}, B::Diagonal) = | |||
invoke(\, Tuple{Union{QR,QRCompactWY,QRPivoted}, AbstractVecOrMat}, A, B) | |||
|
|||
(/)(U::UpperTriangular, D::Diagonal) = UpperTriangular(parent(U) ./ transpose(D.diag)) |
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.
It would be great if you could define the in-place versions ldiv!
and rdiv!
instead and then adjust the methods in line 427-429 to use these.
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.
Probably just something like
function rdiv!(U::UpperTriangular, D::Diagonal)
broadcast!(/, parent(U), parent(U), diag(D))
return U
end
although using broadcast here is actually cheating a bit since broadcasting and linear algebra operations have slightly different semantics in corner cases. However, that is an existing issue so we can clean that up in another PR.
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. The transpose
is actually also not correct here since it is recursive so you probably want to reshape the vector instead.
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.
Thanks for the review. I did the change you requested, but I am not convinced that going though mutating methods like rdiv!
and ldiv!
is the right approach.
Sure, it removes duplicate code, but at the same time the required promotion code makes the implementation convoluted (cf the rather simple approach with the commit I just made). Maybe I am doing the promotion wrong?
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.
We'd need the rdiv!
and ldiv!
methods anyway and it would be confusing if the non-mutating versions didn't go through the mutating versions. Also, the promotion can be done in the more generic version so it's not that much code. Good idea to use broadcast of the type for that.
Removed previous direct implementations for upper/lower triangular, added ldiv!/rdiv! methods. Use reshape instead of transpose.
I did not run the full test suite before committing, and now the (freebsd) tests show an ambiguity which I expect will crop up in the other CI runs, too. Suggestions on how to handle this would be appreciated. |
You'd need to add definitions for (/)(A::Transpose, D::Diagonal)
(/)(A::Adjoint, D::Diagonal)
(/)(x::RowVector, D::Diagonal) where the latter should be defined as a deprecated method. The two former might be joined to |
Thanks, I am aware that defining the intersection is the standard solution for ambiguities, but I was wondering if there is a better method for streamlining the architecture. I am wondering about rolling back to 985891d, replacing the I am also skeptical about whether we need an
and |
I'm also not too happy about all the need for disambiguating methods. Maybe just restrict Regarding the formulation in the docstring for |
stdlib/LinearAlgebra/src/diagonal.jl
Outdated
@@ -325,6 +325,11 @@ ldiv!(adjD::Adjoint{<:Any,<:Diagonal{T}}, B::AbstractVecOrMat{T}) where {T} = | |||
ldiv!(transD::Transpose{<:Any,<:Diagonal{T}}, B::AbstractVecOrMat{T}) where {T} = | |||
(D = transD.parent; ldiv!(D, B)) | |||
|
|||
function ldiv!(D::Diagonal, A::Union{LowerTriangular,UpperTriangular}) | |||
broadcast!(/, parent(A), parent(A), diag(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.
I forgot the reshape here. Needs for be reshape(D.diag, 1, length(D.diag))
instead. (I recently learned that diag
now copies.)
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.
Happy to add this, but can you please explain why reshape
is needed here? In any case it should be
reshape(D.diag, length(D.diag), 1)
for the ldiv!
, but wouldn't using just the vector D.diag
be equivalent?
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 saw you were using /
and thought this was rdiv!
. This should then be
broadcast!(\, parent(A), D.diag, parent(A))
d\a
and a/d
is not always the same. E.g. if the elements are matrices.
stdlib/LinearAlgebra/src/diagonal.jl
Outdated
@@ -349,7 +349,7 @@ function rdiv!(A::AbstractMatrix{T}, D::Diagonal{T}) where {T} | |||
end | |||
|
|||
function rdiv!(A::Union{LowerTriangular,UpperTriangular}, D::Diagonal) | |||
broadcast!(/, parent(A), parent(A), reshape(diag(D), 1, :)) | |||
broadcast!(/, parent(A), parent(A), permutedims(D.diag)) |
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 know we now have that method.
Thanks for all the help! |
Fixes #27989.