-
-
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
Address type stability issues in #12574 and fix a bug or two #12594
Conversation
@@ -108,7 +108,7 @@ ctranspose(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), !M.isupper) | |||
istriu(M::Bidiagonal) = M.isupper || all(M.ev .== 0) | |||
istril(M::Bidiagonal) = !M.isupper || all(M.ev .== 0) | |||
|
|||
function tril(M::Bidiagonal, k::Integer=0) | |||
function tril!(M::Bidiagonal, k::Integer=0) |
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 use fill!
instead of allocating new zeros
arrays, 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.
I tested this locally and it works. Will wait for CI to finish then update.
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 this applies throughout for (most of?) the rest of the types too. Anywhere it's possible to make the !
versions allocation-free and in-place, then I think that would be a good goal. And aim for type-stability but with the smallest amount of type widening that makes sense. The one-arg case could possibly be made to return a more specialized type than the two-arg case for some types since it's more specific in its behavior, but that would require extra methods rather than using default argument values and could be left as a future enhancement.
Updated! Should be far fewer allocs now. |
elseif k == 0 | ||
return D | ||
else | ||
return zeros(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.
fill!(D.diag, 0)
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.
Noooo ok and I need a whitespace fix. This is not my night.
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.
Not a big deal, stop me if I'm getting too nitpicky.
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.
No it's good. I want this to work and work well.
Not this PR's fault, but are the docs for |
@jiahao wanted that phrasing, ask him :) |
elseif k == 0 | ||
return UnitUpperTriangular(eye(A)) | ||
else | ||
return UnitUpperTriangular(triu(tril(A.data,k))) | ||
return UnitUpperTriangular(triu!(tril!(A.data,k))) |
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 one hurts my head a little - maybe the easiest way to get this right, and get the rest of tril!(A::UnitUpperTriangular, k)
for almost free would be first set the diagonals of A.data
to one, then return tril!(UpperTriangular(A.data), k)
?
But that would be the opposite meaning. What the docs describe is not the implemented behavior:
|
tril(A::UnitLowerTriangular,k::Integer=0) = UnitLowerTriangular(tril(tril(A.data),k)) | ||
tril!(A::UnitLowerTriangular,k::Integer=0) = UnitLowerTriangular(tril!(tril!(A.data),k)) | ||
|
||
tril(A::UpperTriangular,k::Integer=0) = tril!(copy(A),k) |
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 generic fallbacks here
Lines 41 to 44 in 24a92a9
triu(M::AbstractMatrix) = triu!(copy(M)) | |
tril(M::AbstractMatrix) = tril!(copy(M)) | |
triu!(M::AbstractMatrix) = triu!(M,0) | |
tril!(M::AbstractMatrix) = tril!(M,0) |
be tweaked to cover all of these?
This is turning out to be a surprisingly complicated set of methods to get right. Someone who's good at diagrams could make a pretty neat picture about the algebraic closedness of the different operations here for all types and super/sub diagonal cases. |
OK I think I have something semi-reasonable now. Let's give it a whirl. |
n = size(D,1) | ||
if abs(k) > n | ||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)")) | ||
elseif k != 0 |
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.
only if > 0
, 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.
Can't Oh, derp, I see.k
be negative?
See #8240 where I discuss how the algebraic structure shows up as banded matrices.
I don't recall... but certainly |
for i in diagind(A) | ||
A.data[i] = one(eltype(A)) | ||
end | ||
return UpperTriangular(triu!(tril!(A.data,k))) |
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.
triu!
still not necessary here
Right, so either the docs should say " |
yes |
Well, banded has a much nicer, easier to deal with algebraic structure than our current menagerie. The less uniform more complicated version we have now would make for a more interesting picture, but that's not necessarily an endorsement. |
end | ||
|
||
tril(A::UnitLowerTriangular,k::Integer=0) = UnitLowerTriangular(tril(tril(A.data),k)) | ||
tril(A::UpperTriangular,k::Integer=0) = tril!(copy(A),k) |
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.
Not sure why github collapsed #12594 (comment), but that still applies. I think the others that have been collapsed as of df4c774 have been addressed, but will review again tomorrow.
edit: links to collapsed comments apparently don't always work so well, that was
should the generic fallbacks here
Lines 41 to 44 in 24a92a9
triu(M::AbstractMatrix) = triu!(copy(M)) tril(M::AbstractMatrix) = tril!(copy(M)) triu!(M::AbstractMatrix) = triu!(M,0) tril!(M::AbstractMatrix) = tril!(M,0)
be tweaked to cover all of these?
triu(A::Symmetric,k::Integer=0) = triu(A.data,k) | ||
function tril(A::Hermitian, k::Integer=0) | ||
if A.uplo == 'U' && k <= 0 | ||
return tril(A.data',k) |
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.
Shouldn't this be tril!(A.data',k)
, since the data'
already makes a copy? Similarly below.
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.
Good catch. I've updated the PR to fix this.
That's an excellent sign of something worth having in a library :-) |
triu(A::Hermitian,k::Integer=0) = triu(A.data,k) | ||
tril(A::Symmetric,k::Integer=0) = tril(A.data,k) | ||
triu(A::Symmetric,k::Integer=0) = triu(A.data,k) | ||
function tril(A::Hermitian, k::Integer=0) |
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 be Union{Hermitian,Symmetric}
right? The implementations look like exact 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.
It passes tests locally with this change. Do we need to run CI again?
Getting there, but not quite. Not yet addressed:
And will want to double-check that comments left so far are fixed for both mirrored sets of methods. |
Ok, I've gone through on my end and dealt with everything but the last one. #12594 (comment). I'm not 100% how to tweak the fallbacks - advice? |
The decision to make there is whether adding general fallbacks that look like
makes sense, now that you've implemented a whole bunch of these methods for many of the subtypes of edit: sorry, just the first 2, the 3rd and 4th lines there were meaningless |
Shall I give a go and report back? E: It's working!!! |
triu(A::Hermitian,k::Integer=0) = triu(A.data,k) | ||
tril(A::Symmetric,k::Integer=0) = tril(A.data,k) | ||
triu(A::Symmetric,k::Integer=0) = triu(A.data,k) | ||
function tril(A::Union{Hermitian,Symmetric}, k::Integer=0) |
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.
Ack, I was wrong, while the earlier implementations were identical for Hermitian vs Symmetric, they actually shouldn't be. We can have Symmetric for a complex element type in which case these should be using .'
instead of '
. But for Hermitian they should use '
.
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 nooooo!
Ok, let CI run, then update?
Address type stability issues in #12574 and fix a bug or two
I noticed when reviewing the previous change for error messages and coverage, and looking at this one, that the |
That inconsistency is a bit annoying, but they serve slightly different purposes. For the |
I had looked at the bindings, but even so, I think overall it would be more efficient if all of them were changed to use |
Remember these are Fortran API's, so you have to pass a reference to the char anyway. I doubt such a PR would be especially popular with the people who've been developing and maintaining the blas and lapack bindings. |
I wasn't aware of that for the Fortran interface. Why do you believe something that could simplify the code would not be popular with them? |
I'm guessing, but it seems like unnecessary churn that would make our code map less directly to the standard-for-decades, widely used API's for linear algebra. You can always open a PR, but "simplify the code" is a relative term that is going to mean different things to different people. |
There may need to be some "churn", it looks like the code as is won't even work on a big-endian machine. It's passing a pointer to a 32-bit |
@ScottPJones, if you pass |
The
trix
methods should have beentrix!
since they return references. The methods forUnit{Upper/Lower]Triangular
matrices were wrong (now fixed) and I also caught a bug inistriu/istril
forTridiagonal
s.cc: @andreasnoack and @tkelman