Skip to content

Commit e0e9d4c

Browse files
committed
Specialize copy! for SparseVectors.
While the AbstractArray version works, treating sparse vectors special is much faster.
1 parent 4679ad1 commit e0e9d4c

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

base/sparse/sparsevector.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,60 @@ convert{Tv,TvS,TiS}(::Type{SparseVector{Tv}}, s::SparseVector{TvS,TiS}) =
320320
SparseVector{Tv,TiS}(s.n, s.nzind, convert(Vector{Tv}, s.nzval))
321321

322322

323+
### copying
324+
function prep_sparsevec_copy_dest!(A::SparseVector, lB, nnzB)
325+
lA = length(A)
326+
lA >= lB || throw(BoundsError())
327+
# If the two vectors have the same length then all the elements in A will be overwritten.
328+
if length(A) == lB
329+
resize!(A.nzval, nnzB)
330+
resize!(A.nzind, nnzB)
331+
else
332+
nnzA = nnz(A)
333+
334+
lastmodindA = searchsortedlast(A.nzind, lB)
335+
if lastmodindA >= nnzB
336+
# A will have fewer non-zero elements; unmodified elements are kept at the end.
337+
deleteat!(A.nzind, nnzB+1:lastmodindA)
338+
deleteat!(A.nzval, nnzB+1:lastmodindA)
339+
else
340+
# A will have more non-zero elements; unmodified elements are kept at the end.
341+
resize!(A.nzind, nnzB + nnzA - lastmodindA)
342+
resize!(A.nzval, nnzB + nnzA - lastmodindA)
343+
copy!(A.nzind, nnzB+1, A.nzind, lastmodindA+1, nnzA-lastmodindA)
344+
copy!(A.nzval, nnzB+1, A.nzval, lastmodindA+1, nnzA-lastmodindA)
345+
end
346+
end
347+
end
348+
349+
function copy!(A::SparseVector, B::SparseVector)
350+
prep_sparsevec_copy_dest!(A, length(B), nnz(B))
351+
copy!(A.nzind, B.nzind)
352+
copy!(A.nzval, B.nzval)
353+
return A
354+
end
355+
356+
function copy!(A::SparseVector, B::SparseMatrixCSC)
357+
prep_sparsevec_copy_dest!(A, length(B), nnz(B))
358+
359+
ptr = 1
360+
@assert length(A.nzind) >= length(B.rowval)
361+
maximum(B.colptr)-1 <= length(B.rowval) || throw(BoundsError())
362+
@inbounds for col=1:length(B.colptr)-1
363+
offsetA = (col - 1) * B.m
364+
while ptr <= B.colptr[col+1]-1
365+
A.nzind[ptr] = B.rowval[ptr] + offsetA
366+
ptr += 1
367+
end
368+
end
369+
copy!(A.nzval, B.nzval)
370+
return A
371+
end
372+
373+
copy!{TvB, TiB}(A::SparseMatrixCSC, B::SparseVector{TvB,TiB}) =
374+
copy!(A, SparseMatrixCSC{TvB,TiB}(B.n, 1, TiB[1, length(B.nzind)+1], B.nzind, B.nzval))
375+
376+
323377
### Rand Construction
324378
sprand{T}(n::Integer, p::AbstractFloat, rfn::Function, ::Type{T}) = sprand(GLOBAL_RNG, n, p, rfn, T)
325379
function sprand{T}(r::AbstractRNG, n::Integer, p::AbstractFloat, rfn::Function, ::Type{T})

test/sparsedir/sparsevector.jl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,90 @@ let a = SparseVector(8, [2, 5, 6], Int32[12, 35, 72])
243243
@test sparsevec(ctranspose(ctranspose(acp))) == acp
244244
end
245245

246+
let x1 = SparseVector(8, [2, 5, 6], [12.2, 1.4, 5.0])
247+
x2 = SparseVector(8, [3, 4], [1.2, 3.4])
248+
copy!(x2, x1)
249+
@test x2 == x1
250+
x2 = SparseVector(8, [2, 4, 8], [10.3, 7.4, 3.1])
251+
copy!(x2, x1)
252+
@test x2 == x1
253+
x2 = SparseVector(8, [1, 3, 4, 7], [0.3, 1.2, 3.4, 0.1])
254+
copy!(x2, x1)
255+
@test x2 == x1
256+
x2 = SparseVector(10, [3, 4], [1.2, 3.4])
257+
copy!(x2, x1)
258+
@test x2[1:8] == x1
259+
@test x2[9:10] == spzeros(2)
260+
x2 = SparseVector(10, [3, 4, 9], [1.2, 3.4, 17.8])
261+
copy!(x2, x1)
262+
@test x2[1:8] == x1
263+
@test x2[9] == 17.8
264+
@test x2[10] == 0
265+
x2 = SparseVector(10, [3, 4, 5, 6, 9], [8.3, 7.2, 1.2, 3.4, 17.8])
266+
copy!(x2, x1)
267+
@test x2[1:8] == x1
268+
@test x2[9] == 17.8
269+
@test x2[10] == 0
270+
x2 = SparseVector(6, [3, 4], [1.2, 3.4])
271+
@test_throws BoundsError copy!(x2, x1)
272+
end
273+
274+
let x1 = sparse([2, 1, 2], [1, 3, 3], [12.2, 1.4, 5.0], 2, 4)
275+
x2 = SparseVector(8, [3, 4], [1.2, 3.4])
276+
copy!(x2, x1)
277+
@test x2[:] == x1[:]
278+
x2 = SparseVector(8, [2, 4, 8], [10.3, 7.4, 3.1])
279+
copy!(x2, x1)
280+
@test x2[:] == x1[:]
281+
x2 = SparseVector(8, [1, 3, 4, 7], [0.3, 1.2, 3.4, 0.1])
282+
copy!(x2, x1)
283+
@test x2[:] == x1[:]
284+
x2 = SparseVector(10, [3, 4], [1.2, 3.4])
285+
copy!(x2, x1)
286+
@test x2[1:8] == x1[:]
287+
@test x2[9:10] == spzeros(2)
288+
x2 = SparseVector(10, [3, 4, 9], [1.2, 3.4, 17.8])
289+
copy!(x2, x1)
290+
@test x2[1:8] == x1[:]
291+
@test x2[9] == 17.8
292+
@test x2[10] == 0
293+
x2 = SparseVector(10, [3, 4, 5, 6, 9], [8.3, 7.2, 1.2, 3.4, 17.8])
294+
copy!(x2, x1)
295+
@test x2[1:8] == x1[:]
296+
@test x2[9] == 17.8
297+
@test x2[10] == 0
298+
x2 = SparseVector(6, [3, 4], [1.2, 3.4])
299+
@test_throws BoundsError copy!(x2, x1)
300+
end
301+
302+
let x1 = SparseVector(8, [2, 5, 6], [12.2, 1.4, 5.0])
303+
x2 = sparse([1, 2], [2, 2], [1.2, 3.4], 2, 4)
304+
copy!(x2, x1)
305+
@test x2[:] == x1[:]
306+
x2 = sparse([2, 2, 2], [1, 3, 4], [10.3, 7.4, 3.1], 2, 4)
307+
copy!(x2, x1)
308+
@test x2[:] == x1[:]
309+
x2 = sparse([1, 1, 2, 1], [1, 2, 2, 4], [0.3, 1.2, 3.4, 0.1], 2, 4)
310+
copy!(x2, x1)
311+
@test x2[:] == x1[:]
312+
x2 = sparse([1, 2], [2, 2], [1.2, 3.4], 2, 5)
313+
copy!(x2, x1)
314+
@test x2[1:8] == x1
315+
@test x2[9:10] == spzeros(2)
316+
x2 = sparse([1, 2, 1], [2, 2, 5], [1.2, 3.4, 17.8], 2, 5)
317+
copy!(x2, x1)
318+
@test x2[1:8] == x1
319+
@test x2[9] == 17.8
320+
@test x2[10] == 0
321+
x2 = sparse([1, 2, 1, 2, 1], [2, 2, 3, 3, 5], [8.3, 7.2, 1.2, 3.4, 17.8], 2, 5)
322+
copy!(x2, x1)
323+
@test x2[1:8] == x1
324+
@test x2[9] == 17.8
325+
@test x2[10] == 0
326+
x2 = sparse([1, 2], [2, 2], [1.2, 3.4], 2, 3)
327+
@test_throws BoundsError copy!(x2, x1)
328+
end
329+
246330
### Type conversion
247331

248332
let x = convert(SparseVector, sparse([2, 5, 6], [1, 1, 1], [1.25, -0.75, 3.5], 8, 1))

0 commit comments

Comments
 (0)