add specialized copy! for sparse matrices#15104
Conversation
|
I guess the potentially controversial thing can be the |
| copy!(A.rowval, B.rowval) | ||
| copy!(A.nzval, B.nzval) | ||
| else | ||
| invoke(Base.copy!, Tuple{AbstractMatrix{TvA}, AbstractMatrix{TvB}}, A, B) |
There was a problem hiding this comment.
This is always an error, right? Why not put an error message here?
There was a problem hiding this comment.
No error if prod(size(A)) > prod(size(B)) e.g
julia> a = rand(5,5); b = rand(2,2);
julia> copy!(a, b);
julia> a[1:4] == b[1:4]
trueThere was a problem hiding this comment.
I guess I should look if the prods are equal instead of the sizes since we overwrite every value in A then anyway.
There was a problem hiding this comment.
Wow. I thought the fallback would check that the sizes match or, at least, the lengths. I'm wondering if anyone is actually relying on copying a smaller array into a larger. I'd prefer an error if the lengths are not equal.
There was a problem hiding this comment.
You can also give offsets to both arrays
julia> a = rand(5); b = rand(3);
julia> copy!(a, 2, b, 2);
julia> a[2:3] == b[2:end]
trueI don't think it is too uncommon to want to copy a chunk of an array into some other place and for now I think copy! is the most efficient.
There was a problem hiding this comment.
I think that this is mainly reasonable behavior for Arrays. For AbstractArrays it would more sense to require equal sizes and thereby the use of SubArrays for copying parts of an array.
Do you think this branch is useful in practice?
There was a problem hiding this comment.
I just didn't want to change any current behavior.
There was a problem hiding this comment.
Fair point. If necessary, it can be handled in a different PR.
8877dda to
04c4cea
Compare
|
Updated to check for equality in number of elements instead of |
| # elements in A will be overwritten and we can simply copy the | ||
| # internal fields of B to A. | ||
| if prod(size(A)) == prod(size(B)) | ||
| copy!(A.colptr, B.colptr) |
There was a problem hiding this comment.
may need to resize colptr if number of columns does not match, and length should be more efficient than prod(size(A))
There was a problem hiding this comment.
should there be a test for this situation where size(A) != size(B) but length(A) == length(B), just to be on the safe side?
There was a problem hiding this comment.
or given @mbauman's catch below, maybe not worth the trouble to do the specialized path when size(A) != size(B)
fef892e to
b32e971
Compare
|
You'll have to do some extra reshaping work on the indices if the matrices aren't the same size. This currently leaves the destination matrix in a bad state: julia> A = sprandn(4,1,0.0);
B = sprandn(2,2,1.0);
copy!(A, B)
4x1 sparse matrix with 4 Float64 entries:
[1, 1] = -0.40374
[2, 1] = -1.01472 |
b32e971 to
aa12d96
Compare
|
Thanks @mbauman I changed back to only check for size equality. The only reason I checked for length is because I incorrectly assumed it would be trivial. I don't feel like messing around with reshaping the indices vectors etc, I just wanted something fast for when the matrices are the same size (which I think will be the vast majority of cases). |
3e596db to
be3cf62
Compare
when the matrices have the same size we can get away with simple copying of the internal fields.
|
This is good to go from my p.o.v. |
|
Unless anyone cringes in horror every time we add a usage of |
|
LGTM — this solves a performance problem many orders of magnitude worse than invoke. That branch can always be improved later. |
add specialized copy! for sparse matrices
|
Danke! 🎉 |
When the matrices have the same size we can get away with resizing + copy of internal fields.
Ex:
The use case I had for this was in a nonlinear solver package where I needed to update the input matrix in the gradient function. Since I always created my matrix from a triplet format I used
copy!to do the updating but noticed this was slow.