-
-
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
Bunchkaufman- and LU-decomposition based generalized eigenvalues and eigenvectors #50471
Conversation
…mutations, respectively.
…ved ordering stability.
Benchmark ResultsBenchmarking file and instructions
julia> using Revise
julia> using LinearAlgebra
julia> Revise.track(LinearAlgebra)
julia> using BenchmarkTools
julia> N=32; include("/home/ark/temp/bkmark.jl")
julia> N=1000; include("/home/ark/temp/bkmark.jl") Results (06.12.2023)Please see the comment diff for a comparison with the previous benchmark results. Please note that the absolute values of these figures may depend on external factors, while relative values and memory figures may provide intuition into the algorithm complexity. N=32
N=1000
Test Results (06.12.2023)Testing file and instructions
julia> using Revise
julia> using LinearAlgebra
julia> Revise.track(LinearAlgebra)
julia> include("symmetriceigen.jl") ResultsWARNING: replacing module TestSymmetricEigen.
Test Summary: | Pass Total Time
chol-eigen-eigvals | 4 4 0.9s
Test Summary: | Pass Total Time
issue #49533 | 12 12 0.4s
Test Summary: | Pass Total Time
bk-lu-eigen-eigvals | 24 24 1.7s
Main.TestSymmetricEigen |
Hello @dkarrasch, @jishnub, and @schneiderfelipe, I look forward to your comments and suggestions on this new PR. |
Is there a reference for this algorithm? |
Hello @stevengj. Unfortunately, I don't have a reference handy. But, the solution follows the same method as that of the Cholesky decomposition one in eigen/Cholesky and that in LAPACK (NIST)'s chegv(), and is explained below. Bunch-Kaufman DecompositionConsider an arbitrary matrix Generalised eigenvaluesWe note that the roots of the generalised eigenvalue problem Generalised eigenvectorsNow, let LU decompositionLet Generalised eigenvalues and eigenvectorsAnalogous to the BK-decomposition case, the generalised eigenvalues are the eigenvalues of eigenvalues of ValidationFor both decompositions, the eigenvalues and eigenvectors satisfy the relation Comments and suggestions are welcome. (1) For non-invertible |
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 tackling this. I see this is addressing one of my major concerns, that the algorithm is not really in-place of the left-hand matrix! Cool. Here are a couple of first comments.
Hello @dkarrasch. Thank you for the first round of comments. I've responded to the comments individually and incorporated the changes via a single commit aravindh-krishnamoorthy@8b8f556. Below, I'd like to address the following comment.
In the tests in However, due to numerical issues, the eigenvalue pairs between the functions differ as (1) However, even though using Gaussian random matrices as test matrices is advantageous (greater coverage as the matrices vary every test), it is still possible that we end up with rank deficient or closely spaced eigenvalues. In such cases, the resulting test failures will be difficult to reproduce. If you do not prefer to have this uncertainty, I can locally check with Gaussian random matrices but have a fixed matrix in the official test. Kindly let me know your opinion. |
I think this is a great opportunity to clean-up/extend a few things. I'd suggest to use more generic functions to achieve the same thing, but for specific cases use LAPACK functions. For instance:
|
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.
A few more suggestions for simplification and genericity. It would be good to use only generic functions (i.e., not use any LAPACK functions directly), and then not restrict on BlasFloat
unnecessarily.
Hello @dkarrasch. Thank you very much for your support with this PR and your comments. Sorry for the delay in my response. Please note that the intermediate version was inadvertently pushed to GitHub. It was primarily for my benchmarking use. I've removed the function and reverted the signatures.
Thank you. Indeed, my idea is to first understand the most optimal performance via BLAS/LAPACK and then ensure that the final Julia code can get there. I've incorporated most of the suggestions below. Unfortunately, in a few cases, the LAPACK routines were superior either in terms of complexity or memory usage. Any suggestions to rewrite them in Julia will be welcome. Details are as follows.
Unfortunately, the routines are for the LAPACK-style swap permutations and are best suited for the IPIV-style permutation vectors. Using these methods for permuting For permuting In our case, indeed, obtaining Hence, these routines may not be beneficial for our case.
Unfortunately, the julia> B = complex.(randn(1000,1000),randn(1000,1000)) ; B = (B+B')/2 ;
julia> K = bunchkaufman(B) ;
julia> @time K.D ;
0.002521 seconds (7 allocations: 15.320 MiB)
julia> @time K.uplo == 'U' ? K.U : K.L ;
0.010232 seconds (5 allocations: 15.274 MiB) On the other hand, as seen from the benchmark in the first comment above, the present code with duplicated
The above four suggestions are incorporated!
Unfortunately, as explained earlier, this may not be possible. |
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.
One quick suggestion, but actually, I think we should implement these in julia. After all, this is just a helper function. I'll post a suggestion shortly.
Hi! I'd like to propose to add the following methods for column and row permutations in permutecols!(a::AbstractMatrix, p::AbstractVector{<:Integer}) =
_permute!(a, p, Base.swapcols!)
permuterows!(a::AbstractMatrix, p::AbstractVector{<:Integer}) =
_permute!(a, p, Base.swaprows!)
@inline function _permute!(a::AbstractMatrix, p::AbstractVector{<:Integer}, swapfun!::F) where {F}
require_one_based_indexing(a, p)
p .= .-p
for i in 1:length(p)
p[i] > 0 && continue
j = i
in = p[j] = -p[j]
while p[in] < 0
swapfun!(a, in, j)
j = in
in = p[in] = -p[in]
end
end
a
end
invpermutecols!(a::AbstractMatrix, p::AbstractVector{<:Integer}) =
_invpermute!(a, p, Base.swapcols!)
invpermuterows!(a::AbstractMatrix, p::AbstractVector{<:Integer}) =
_invpermute!(a, p, Base.swaprows!)
@inline function _invpermute!(a::AbstractMatrix, p::AbstractVector{<:Integer}, swapfun!::F) where {F}
require_one_based_indexing(a, p)
p .= .-p
for i in 1:length(p)
p[i] > 0 && continue
j = p[i] = -p[i]
while j != i
swapfun!(a, j, i)
j = p[j] = -p[j]
end
end
a
end They are straightforward translations of the LAPACK functions, and the row permutation (IIRC) is even faster than LAPACK. They are written such that |
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.
A few more comments. Moreover, there's a generic BunchKaufman
decomposition in the pipeline (#51487), so we should keep this as generic as possible w.r.t. the eltype. After all, this is a generic linear algebra algorithm, not a LAPACK specific one.
Co-authored-by: Daniel Karrasch <[email protected]>
…combinatorics.jl, as proposed by @dkarrasch Co-authored-by: Daniel Karrasch <[email protected]>
…rows!, permutecols!, and invpermuterows!
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.
What a beautifully generic piece of code!!! If we keep the LAPACK permutation functions, then they need to be tested. Since they are new and still unused internally, perhaps we could leave them out here?
Thank you, @dkarrasch. Now, the code indeed is beautiful and generic. The main changes are done. I will review and benchmark everything again (esp with the new permutation routines) and incorporate other cosmetic changes soon. |
close to permutecols!! in Julia Bases combinatorics.jl
@dkarrasch Perhaps it makes sense to wait for #51763 to be merged so that |
Hello @dkarrasch. I see that #51763 is merged. Now, I'll resume work on this one incorporating those changes. Hope to finalise this PR soon. |
…ti-aliasing implementation in JuliaLang#51763 + include comments in symmetriceigen.jl
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.
Last "hygiene" comment, and then this is finally ready to go. Please add an entry in NEWS.md that announces these features.
@dkarrasch This now looks good to go from my side. I looked at the benchmarks and they seem Ok now. For both cases (N=32, 1000), the new functions significantly outperform the generic function (not optimised for symmetric matrices). Furthermore, I feel it makes sense to retain both Bunchkaufman and LU functions since each is optimal in a certain scenario. Also, I've completed a few last-minute checks and updated NEWS.md. Nevertheless, I do welcome any last-minute comments from you and will be happy to incorporate them -- ark Edit: Sadly, there's always a residual bug :) Added commit 485756d. |
To be "fair", one should perhaps include the factorization of |
Indeed, the factorisation overheads due to |
Introduction
This PR implements Bunchkaufman (BK)- and LU-decomposition based generalized eigenvalues and eigenvectors of$\boldsymbol{A}$ and $\boldsymbol{B}$ when $\boldsymbol{A}$ is arbitrary and $\boldsymbol{B}$ is (Hermitian) symmetric in the following new methods in
stdlib/LinearAlgebra/src/symmetriceigen.jl
:eigvals
andeigen
eigvals!
andeigen!
For$N \geq 32,$ these implementations outperform the equivalent native (
eigen.jl
) functions in both execution time and memory usage. Test and benchmark results will be updated here regularly.Comments are suggestions are welcome.
eigen
/eigvals
#49673. The comments there may also be relevant for this PR.Open points
Outputs when$B$ is singular.(Output is analogous to the Cholesky solution.)