-
-
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
Add dot
method for symmetric matrices
#32827
Add dot
method for symmetric matrices
#32827
Conversation
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 looks good, but I guess a few changes are required for block matrices, taking into account that dot
acts recursively.
9ff1e16
to
738b89a
Compare
I have done the requested changes by @dkarrasch. I don't quite understand the possible problem with block matrices. I didn't find them in the Julia stdlib, so I guess you mean the package BlockArrays.jl @dkarrasch? If you think there is an issue, feel free to elaborate. |
No, I meant matrices of matrices. Like X = [rand(ComplexF64, 10, 10) for _ in 1:3, _ in 1:3]
dot(Symmetric(X), Symmetric(X)) # should give a real, non-negative number |
Could this also support Hermitian? I think it'd be good to add more comprehensive tests, including the four code paths (L/R), complex and block matrices: mistakes in functions like these might not be trivial to spot, and could have very bad consequences. |
@dkarrasch Thanks for the clarification, I'm going to check this! @antoine-levitt Sure, I can add a similar method for |
@goggle thanks for this! It speeds up my code. |
There is indeed an issue with block matrices. using LinearAlgebra
a = [1 0 ; 1 0]
b = [-1 1; 0 1]
c = [-1 -1 ; 0 0]
d = [-1 2 ; -1 0]
A = [[a] [c] ; [b] [d]]
symA = Symmetric(A)
I will replace all the |
Alright, so I avoid to access the data directly, which should solve the issues with symmetric block matrices. As expected, it makes it slightly slower than the previous version. Here are the updated benchmarks:
I will extend the tests tomorrow. |
It took me a while to resolve many different issues. This PR can now be reviewed again (@dkarrasch). |
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 don’t see any need to restrict to numbers. The remaining issues for nested arrays are easily fixed. What was missing is using the implicit symmetry of diagonal elements, which may not be already contained in the data
field. For the Hermitian
version of this (not sure if we would also add mixed versions), you will need to replace the off-diagonal contributions by
dotprod += 2real(dot(A.data[i, j], B.data[i, j]))
otherwise this should be the same.
Thanks for these detailed comments @dkarrasch, this helps a lot. I would also prefer to have a generic implementation Here is the modified dot method with your suggestions from #32827 (comment) and #32827 (comment) applied: using LinearAlgebra
function mdot(A::Symmetric, B::Symmetric)
n = size(A, 2)
if n != size(B, 2)
throw(DimensionMismatch("A has dimensions $(size(A)) but B has dimensions $(size(B))"))
end
dotprod = zero(dot(first(A), first(B)))
@inbounds if A.uplo == 'U' && B.uplo == 'U'
for j in 1:n
for i in 1:(j - 1)
dotprod += 2 * dot(A.data[i, j], B.data[i, j])
end
dotprod += dot(A[j, j], B[j, j])
end
elseif A.uplo == 'L' && B.uplo == 'L'
for j in 1:n
dotprod += dot(A[j, j], B[j, j])
for i in (j + 1):n
dotprod += 2 * dot(A.data[i, j], B.data[i, j])
end
end
elseif A.uplo == 'U' && B.uplo == 'L'
for j in 1:n
for i in 1:(j - 1)
dotprod += 2 * dot(A.data[i, j], B.data[j, i])
end
dotprod += dot(A[j, j], B[j, j])
end
elseif A.uplo == 'L' && B.uplo == 'U'
for j in 1:n
dotprod += dot(A[j, j], B[j, j])
for i in (j + 1):n
dotprod += 2 * dot(A.data[i, j], B.data[j, i])
end
end
end
return dotprod
end I call it a = [1 0 ; 1 0]
b = [-1 1; 0 1]
c = [-1 -1 ; 0 0]
d = [-1 2 ; -1 0]
A = [[a] [c] ; [b] [d]]
sau = Symmetric(A, :U)
sal = Symmetric(A, :L) Now I get a wrong result with
It should be:
That's exactly the same issue that I could not resolve earlier... PS: I have also tested your second suggestion |
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.
There are transpose
s missing in the mixed cases, please double-check with the getindex
code.
Ok, I think I could solve all the remaining issues, thanks @dkarrasch. |
LGTM. How about adding the Hermitian-Hermitian case also? You would need to replace all |
I have added a second method for the Hermitian-Hermitian case and have added tests for the Hermitian case 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.
Just a trivial comment, otherwise looks good to me. Could you maybe post an update (or a reminder) of the performance improvements by this PR?
Thanks for being so thorough on this |
Here are some final benchmarks. using BenchmarkTools
using LinearAlgebra
n = 5000;
Au = Symmetric(randn(n,n));
Al = Symmetric(randn(n,n), :L);
Bu = Symmetric(randn(n,n));
Bl = Symmetric(randn(n,n), :L);
Hu = Hermitian(randn(n,n) + randn(n,n)*im);
Hl = Hermitian(randn(n,n) + randn(n,n)*im, :L);
Il = Hermitian(randn(n,n) + randn(n,n)*im, :L);
Iu = Hermitian(randn(n,n) + randn(n,n)*im);
@kshyatt Maybe you could add the |
Any news on this one? Does it need a second review? How can I proceed? |
Like a good piece of meat, this needs to hang a little... 😉 until someone with merge rights finds some time to review, and merges. |
@goggle Could you please rebase onto master/resolve the merge conflict, and then I'll merge! |
My previous code produced wrong results when A and B were symmetric block matrices, because they accessed the raw data in a way that only works if A and B are symmetric matrices of numbers.
I wronlgy assumed that the dot product is commutative. It is not for complex matrices. Since we do not access `A.data` or `B.data`, we can use the same code for the cases where `A.uplo` and `B.uplo` do not coincide.
8d75d17
to
35328ca
Compare
I have rebased the branch to resolve the conflicts. |
Don't worry about squashing, unless this is more convenient for you while developing. Squashing will be done upon merging at the end anyway. I'll wait for tests to pass... |
Seems like one |
Thanks @goggle! |
Also thanks to @dkarrasch for the great support! |
This PR adds a
dot
method for symmetric matrices, which makes the calculation of the dot product of two symmetric matrices significantly faster (see benchmarks below). It intends to fix JuliaLang/LinearAlgebra.jl#647.To perform the benchmarks, this code was used:
These are the results: