-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
How best to fix similar for triangular matrices? #271
Comments
No one responded to this issue - @andreasnoack, @jiahao, @tkelman I'm pinging the LinAlg crew. Is this still a problem? What do we want to do? |
I prefer options 1 or 3 here. |
BTW I would also like to say that this is a very nice issue with a well stated definition of the problem & various solutions. Nice work, @artkuo. |
I would like to postpone the introduction of a new method until we have better evidence for the need so I'll vote for 1 or 2. To make progress, I'll propose that we try out 1 and see how it goes. @artkuo Do you want to try prepare a pr? |
Thanks for the replies, but the more I delve into it, the less I like option 1. It is a temporary bandaid, and doesn't address a fairly deep problem, which is that sometimes So for now I'd like to put this on hold. I'd like to keep the issue open just as a benchmark for when the real problem is fixed, hopefully in a more meaningful way than option 1. |
I think the problem is quite limited and therefore that it's best to refrain from adding complexity by introducing a new generic function. Could you provide more examples for when |
I agree with the opinion that |
I agree with @tkelman. Even if in practice it more or less works, we'd better clarify what |
@andreasnoack As an example where a mutable There is no case I could find in base where you need If you believe that nobody should be relying on undocumented behavior, then the safer thing to do is to make it mutable across the board (option 2). I also think that the most basic and necessary closed algebra for triangulars is already implemented in triangular.jl, and does so without itself using My draconian self prefers option 2, which instantly facilitates the matrix multiplication fix, is consistent with documentation, and is reliable behavior that most people should expect. I have a couple dozen examples of these and other operations between |
I think the documentation for |
We could add a method like |
@artkuo I think it makes sense to always return a (completely) mutable array determined by the wrapped array, i.e. option 2, but I also wanted to close #258 and suggested option 1 as a compromise with little harm done. #258 is really a tiny issue and we'll probably never be able to completely avoid issues like that. I don't really want to defend option 1, but wouldn't your matrix multiply fix use a dimension argument such that option 1 would actually work? @tkelman @nalimilan |
Okay I have to admit the multiplication fix does indeed still work with option 1. But option 1 is really horrible, because there's a discrete switch in what you get back depending on how many arguments there are. I hope this would be a temporary thing, deprecated with a warning? Either way it seems like the long-term solution would be a separation of two needs: (1) A reliable, easy way to allocate a fully writable array; (2) A way to allocate another thing exactly like a structured matrix but not necessarily writable. Either one of these could have a different name, and I mostly like the |
Triangular is writable, subject to structural constraints. Diagonal, Bidiagonal, Tridiagonal etc are going to have the same issue. If we had a type for structurally-fixed sparse matrices that would be the same, allowing modification to the nonzero values but not introducing new ones. It depends what the result of similar is going to be used for. Is the contract for |
I believe that most cases in Base assume that the entire output is writable. The specific algebra is implemented within each structure, generally allocating a new structure without hoping that By the way, there is almost the same issue with |
The underlying issue is that I would ask the same question regarding what should Base doesn't really exercise the structured array types very well, aside from wrapping a few factorizations where they mostly don't interact with other code in that many ways. I'm less concerned with whether this would break code than if it would be the right abstraction design. |
Indeed, the issue is right abstraction design. I would argue that both are "correct": |
Memory, and more generally algorithmic efficiency, is much of the point of using structured array types. Triangular and Symmetric are the unusual cases for that since we're not using packed storage. We could pick separate names, neither of which reuse the existing |
If there are no (or very few) use cases for preserving the structure, wouldn't it be simpler to adjust the few |
I don't like that plan to throw out information and harm performance because we aren't very good yet at utilizing structural information. Structure is a complicated problem that isn't going to have a simple solution, and it deserves thinking carefully about rather than punting and doing the naive thing because it's too hard. Designing base to only be good at dealing with dense arrays is a really bad long-term plan. |
I just thought about another use case which introduces another interesting way of looking at this: NamedArrays and AxisArrays. Here, the question is not only whether the array is writable or not, but also whether to preserve information about dimensions. Clearly, I find this example useful because So I can see at least
|
You can certainly ask for a |
I don't see how that can be a fair conclusion to draw from my comment. |
@andreasnoack Indeed that's option 2, which would fix quite a few existing bugs and is not known to break any pre-existing code. @tkelman Do you have any specific plans that will really benefit from structured Also, my version of option 2 is not dense, merely unstructured; it still preserves sparsity. A And along those lines, @nalimilan I agree with part of what you say, but I wouldn't stick to |
It was in reaction to "If there are no (or very few) use cases for preserving the structure" - my point is I don't think we should be designing abstractions today that are only good at dealing with use cases that have been written right now in Base or registered packages.
Yes, my plans are roughly that for every current case of structured matrix types, I'd like to introduce an abstraction for iterating and operating over the stored elements that generalizes the current per-type custom loop implementations. The "uninitialized structural copy" operation for this does not have to be called |
That said, I'm not sure there are many use cases for writing generic code that creates a new sparse array with a different size than the original. And as regards the particular situation where you create a new array with some rows or columns added (or removed) compared to the original, another strategy will have to be found if we want such code to preserve dimension names for |
Thanks, this really helps. It sounds like the four cases of @naliman can be addressed with one extension to Also I would amend @naliman's case 3 to include different sizes as well, as is needed by Probably hard to resolve new method names now, but I kind of like |
@artkuo It doesn't sound likely to me that one would want to get a sparse matrix when computing reductions like mean over dimensions of a sparse array. At least, it would seem at least as common to want a standard |
Right, that's called "hypersparse" when the dimensions are larger than the number of nonzeros, and csc isn't really the best format for it. The algorithms there are a bit different. |
Unlikely to receive attention prior to 0.6. Best! |
Is this also unlikely to receive attention prior to 1.0? |
IMO: the long-term solution is a new |
That sounds like we could take it off the 1.0 milestone? |
JuliaLang/julia#15198 implemented the OP's first proposal for banded ( So I see three potential actions remaining for this issue: (1) Restore preservation of storage type of wrapped types when Proposal: Complete (1) and (2), then close this issue in favor of JuliaLang/julia#18161. Thoughts? Best! |
(Related: Should |
(#24162 and JuliaLang/julia#24163 together cover (1) and |
(#24170 covers (2).) |
With items (1) and (2) from #271 complete, closing in favor of JuliaLang/julia#18161 for a grander overhaul of |
In response to #258, the bug was traced to
mean
which relies onsimilar
(through reducedim) to prepare an array for its output, e.g.mean
of a 4x3 matrix along dim 1 should yield a 1x3. It ends up calling something likesimilar(triangularmatrix, LowerTriangular, (1,3))
(the template matrix, the desired type, and the dimensions), which fails because a 1x3 triangular matrix doesn't make sense. In this context,similar
means "give me a matrix with the same eltype" not "give me another triangular just like this one."similar
so that when optional dimensions are used, it automatically drops the triangular, which results in AbstractMatrix. When no dimension is given, it could yield a lower triangular. This actually works, and even preserves sparsity automatically. I find this a bit yucky, because the return type depends implicitly on how the call is made.similar
to always drop the triangular, again preserving sparsity where applicable. I've tried this fix and it doesn't break any existing tests (other than needing a new test forsimilar
). Downside is it is a potentially breaking change for user code wheresimilar
is intended to mean "give me another of same structure." Yucky as well.similar
is called and root out the problem at the level of reducedim. Where appropriate, it could be replaced bysameeltype
, meaning a more explicit version of what is meant already. In other cases, the replacement might besamecontainer
orsamestructure
or some such. Idea is to make explicit what you really want. This seems like a longer term solution, becausesimilar
occurs 600+ times in julia project (between method calls and definitions) and it may not be straightforward to know what is intended when.Previous discussions have complained about
similar
JuliaLang/julia#11574 (comment) and JuliaLang/julia#10064. The problem is thatsimilar
is vague. Present docs say that behavior for special types should be decided case by case. Right nowsimilar
for triangulars is undocumented, so the user must guess or look at the source code. Might be better to clarify and perhaps split off asameeltype
orsamestructure
in the long run.There are safe, conservative ways to do option 2, such as when triangular is input, to return a matrix that's not triangular but whose off-triangle has been zeroed. This makes it safe for the user expecting a triangular back and for the most part won't blow up on them. (Triangulars store a full matrix in their .data field, so there is potential danger if things are not zeroed.)
What is the right solution?
The text was updated successfully, but these errors were encountered: