-
-
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
Semantics of map! and broadcast! over sparse arrays #19372
Comments
I think silently ignoring a nonzero result is dangerous, especially in generic code. If |
Since the semantic of broadcasting over only nonzero/structural values is different than broadcasting over all elements, why not define a new set of methods for doing this, e.g., mapnz, broadcastnz? |
My first inclination would be to use the output argument's nonzero structure, and throw an error if this is not possible (i.e. if I don't see the point of using |
Cheers, seems we can easily eliminate (2b) and (2c).
My thinking as well. A slight generalization of (2a): Rather than require that the result's nonzero pattern fit within the output argument's existing structure (pattern), we could require the weaker condition that the result fit within the output argument's existing storage. Then, instead of needing to know the result's nonzero pattern, you need only know how much space it requires. Regarding implementation, this approach should also be less complex and more efficient.
Agreed. The only counterargument I see is generic programming. And on top of the variation of (2a) described above, the implementation and performance costs of allowing reallocation in Proposal for action: If the variation of (2a) described above (i.e. without reallocation) seems agreeable, I'll sketch an implementation of Thanks and best! |
I'm more in favor of 1. The number of nonzero entries for this kind of operation might be a bit complicated to determine ahead of time. |
Is that to say you would also be on board with the variation of (2a) described in #19372 (comment) with reallocation (which is basically (1) but only allocates if necessary)? Thanks! |
…ith new (JuliaLang#19372) semantics / less pessimistic allocation behavior and optimize a few inefficiently handled common cases. Also provide a broadcast/broadcast! implementation capable of (relatively) efficiently handling an arbitrary number of (input) sparse matrices. Integrate with the map/map! implementation, and touch up a few rough edges of the map/map! implementation. Test.
…ith new (JuliaLang#19372) semantics / less pessimistic allocation behavior and optimize a few inefficiently handled common cases. Also provide a broadcast/broadcast! implementation capable of (relatively) efficiently handling an arbitrary number of (input) sparse matrices. Integrate with the map/map! implementation, and touch up a few rough edges of the map/map! implementation. Test.
…ith new (JuliaLang#19372) semantics / less pessimistic allocation behavior and optimize a few inefficiently handled common cases. Also provide a broadcast/broadcast! implementation capable of (relatively) efficiently handling an arbitrary number of (input) sparse matrices. Integrate with the map/map! implementation, and touch up a few rough edges of the map/map! implementation. Test.
…ith new (JuliaLang#19372) semantics / less pessimistic allocation behavior and optimize a few inefficiently handled common cases. Also provide a broadcast/broadcast! implementation capable of (relatively) efficiently handling an arbitrary number of (input) sparse matrices. Integrate with the map/map! implementation, and touch up a few rough edges of the map/map! implementation. Test.
…ith new (#19372) semantics / less pessimistic allocation behavior and optimize a few inefficiently handled common cases. Also provide a broadcast/broadcast! implementation capable of (relatively) efficiently handling an arbitrary number of (input) sparse matrices. Integrate with the map/map! implementation, and touch up a few rough edges of the map/map! implementation. Test. (#19518)
Can we at least have a generic way to loop over nonzeros of a matrix as part of Julia 1.0? This is crucial for writing any kind of sparse matrix support in differentiation tooling for example. Right now I am not sure of a better way to handle it other than to handle every matrix type differently, which seems like far too much work when indexing something like a Even with sparse matrices the only way I could find out how to do this without allocating was a little bit weird: julia> idxs = SparseArrays.nonzeroinds(vec(A))
973-element Array{Int64,1}:
1
10
19
21
27
36
64
65
70
71
⋮
9920
9931
9935
9940
9941
9942
9953
9977
9978
9996
julia> ind2sub(size(A),idxs[3])
(19, 1) I think the first step here is to have tooling that allows for writing an efficient sparse map/broadcast, while the actual convenience of a sparse map/broadcast can come later. |
Apart from the discussion/pointers in #25760, section (1) of stdlib/SparseArrays/src/higherorderfns.jl begins work towards such an interface. Regrettably, further work along such lines is very much post-1.0. Discussing unified iteration over sparse and/or structured objects is mostly orthogonal to this issue's purpose. The focal questions of this issue having largely been resolved and the decisions implemented, this issue should be closed. Best! |
#19239 concluded with generic
map
/broadcast
over sparse arrays not storing numerical zeros in the result. But whethermap!
/broadcast!
should do the same, or instead store some numerical zeros dependent on / reflecting the inputs' structures, wasn't settled. Hence this issue.Originally (c. #19239) I thought
map!
/broadcast!
should retain the inputs' structure. But working on #19371 changed my thinking:map!
/broadcast!
should either (1) store the same result asmap
/broadcast
or (2) retain solely the output argument's structure, perhaps throwing an error wherebroadcast(f, inputargs...)
does not fit within the output argument's structure. Primary reason:What "retaining the inputs' structure" means is not well defined: Should
broadcast!(f, C, A, B)
retainA
's structure,B
's structure, the union ofA
's andB
's structures, the union ofA
's,B
's, andC
's structures, or the intersection ofC
's structure with the union ofA
's andB
's structures? What if either ofA
orB
has a singleton dimension or is scalar?Only the output argument seems logically privileged, hence proposal (2) above. (2) has a few possible variations: When
broadcast(f, inargs...)
contains a nonzero value outside the equivalentbroadcast!
's output argument's structure,broadcast!
could either (2a) error, (2b) ignore that nonzero, or (2c) grow the output argument's structure.Some thoughts on proposals (1) and (2):
(1) has the advantages of ease of use (it always works) and strict consistency with
map
/broadcast
.(2a) behaves like
broadcast!(f, S, args...)
whereS
is a structured (e.g.Diagonal
,Tridiagonal
, etc) matrix: strictly non-allocating, but not as flexible / easy to use as (1).(2b) is interesting: On the one hand, it requires iteration only over the parts of the input arguments' structure that intersect the output argument's structure, potentially enabling greater efficiency where the result's structure is known/predictable. In other words, this approach provides a form of escape hatch for exploiting known detailed zero-preserving behavior of the
map
/broadcast
function (ref. discussion in #18590), and that escape hatch could be extended tomap!
/broadcast!
for structured matrices. On the other hand,map
/broadcast
andmap!
/broadcast!
could produce inconsistent results.(2c) seems like a half measure with little if any advantage over either (1) or (2a).
Thoughts? Thanks and best!
The text was updated successfully, but these errors were encountered: