-
-
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
Implement sorting for Eigen #24536
Implement sorting for Eigen #24536
Conversation
base/linalg/eigen.jl
Outdated
``` | ||
""" | ||
function sort!(F::Eigen; kw...) | ||
perm = sortperm(F[:values]; kw...) |
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.
Seems like we need a default by=eigsortby
, if by
wasn't supplied in kw
, using e.g. the eigsortby
function from #21598.
Otherwise it will fail with no method matching isless
for complex eigenvalues.
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 was actually intentional, but maybe the error message is confusing? I wanted to avoid choosing any particular sorting order for complex eigenvalues, since I generally need by=angle
and others might want real
, imag
or eigsortby
.
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 can also see a good argument for having some default, and eigsortby
is a pretty good candidate.
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 could also be done by dispatching on the eltype
of the eigenvalues, defaulting to reim
, without the need to define eigsortby
.
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.
Definitely the caller should be able to supply their own by
, but I think there should be a default, and reim
seems like a good a choice for complex values (since it will match the sorting of real values in the case of complex numbers that are purely real).
base/linalg/eigen.jl
Outdated
permute!(F[:values], perm) | ||
Base.permutecols!!(F[:vectors], perm) | ||
function sort!(F::Eigen; by=eigsortby, kw...) | ||
if !issorted(F[:values], by=by) |
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.
need to pass kw...
to issorted
too.
test/linalg/eigen.jl
Outdated
@@ -105,6 +105,13 @@ end | |||
end | |||
end | |||
|
|||
let a = rand(10, 10) | |||
f = eigfact(a) | |||
sort!(f) |
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 f = sort(eigfact(a))
instead to make sure that both sort
and sort!
are tested.
base/linalg/eigen.jl
Outdated
return F | ||
end | ||
|
||
sort(F::Eigen; kw...) = sort!(deepcopy(F), kw...) |
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.
Could be worthwhile to define a copy
method for Eigen
. Such methods exists for e.g. Cholesky
and LU
factorizations. Something like:
Base.copy(F::Eigen) = Eigen(copy(F.values), copy(F.vectors))
base/linalg/eigen.jl
Outdated
Sort the eigenvectors and eigenvalues of an eigenfactorization `F` in place using the eigenvalues | ||
for comparison. See also `sort!(::AbstractVector)`. | ||
|
||
# Examples |
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.
Should the example perhaps be attached to the sort
method instead? It is a fairly common pattern throughout the documentation to have a more extensive docstring for the non-inplace method, and just reference that there is an inplace 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 think that makes sense. But for sort(::Vector)
, it actually references sort!
as the extensive docstring. Maybe this should be changed?
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.
Hmm okay, I guess we don't have an official policy, in any case it is unnecessary to repeat information, so one or the other should have a more extensive docstring. Perhaps you can simply add a short docstring for the non-inplace version instead then, and reference this. I guess most of the time you want to use sort!
anyway.
Also, why the reference to sort!(::AbstractVector)
? For docs about the keywords?
test/linalg/eigen.jl
Outdated
@@ -105,6 +105,12 @@ end | |||
end | |||
end | |||
|
|||
let a = rand(10, 10) |
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 add a b = a'a
and test the case with real eigenvalues too?
test/linalg/eigen.jl
Outdated
let a = rand(10, 10) | ||
f = sort(eigfact(a)) | ||
@test a ≈ f[:vectors] * Diagonal(f[:values]) / f[:vectors] | ||
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.
Since the purpose is to sort, should we test that too? E.g. @test issorted(...)
Should probably also add |
base/linalg/eigen.jl
Outdated
sort!(F::Base.LinAlg.Eigen; kw...) | ||
|
||
Sort the eigenvectors and eigenvalues of an eigenfactorization `F` in place using the eigenvalues | ||
for comparison. See also `sort!(::AbstractVector)`. |
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 point to sort!(::AbstractVector)
? Other docstrings for sort!
will be listed in the same place anyway.
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.
Unless you do something like ?> sort!(F)
to filter out all the noise. 🙂 Or in the manual.
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.
Even then, it's not clear what this reference means: does this function support all keyword arguments accepted by sort!(::AbstractVector)
? Is it just that they are similar? Also, the generic docstring is defined for any v
, not just ::AbstractVector
.
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 are right. It is because it accepts the same keyword arguments. I will change the wording to make it more clear. Also, AFAIU, the "generic" docstring is a fallback to all docstrings, but the one currently being shown is only for AbstractVector
, despite the missing ::AbstractVector
in the signature of the docstring.
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.
Overall, this is great to have a nice interface for, but I'm not convinced that adding this as a method to sort
and sort!
for the Eigen
type is the right way to go. Those functions are for sorting collections of values, which I'm not sure we can legitimately consider factorization objects to be. However, permuting the rows/columns of factorizations according to some sort order seems to be a more general thing, so perhaps it would make sense to have sortfact
and sortfact!
functions?
cols = indices(a,2) | ||
(i in cols && j in cols) || throw(BoundsError()) | ||
for k in indices(a,1) | ||
@inbounds a[k,i],a[k,j] = a[k,j],a[k,i] |
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.
would be clearer with spaces after the outer commas
end | ||
end | ||
# like permute!! applied to each row of a, in-place in a (overwriting p). | ||
function permutecols!!(a::AbstractMatrix, p::AbstractVector{<:Integer}) |
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.
might be generally useful to have and export, along with corresponding permutecols!
, permutecols
, permuterows!!
, permuterows!
and permuterows
(yikes, that's a lot variations).
How about adding a |
Or |
See @andreasnoack's comment about a |
The helper functions were taken verbatim from @stevengj's PR: #21598 (with a single minor deprecation fix). This PR implements the (probably) least radical solution to sorting eigenfactorisations proposed in #21598, with sorting of eigenvalues being opt-in rather than opt-out, and not enforcing any particular canonical sorting.