-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test generic sparse map[!]/broadcast[!] for sparse vectors/matrices.
Condense and systematize existing tests for generic sparse map[!]/broadcast[!], and extend to sparse vectors and vector/matrix combinations. Relocate new test code to a separate file test/sparse/higherorderfns.jl corresponding to base/sparse/higherorderfns.jl. (Test/sparse/sparsevector.jl is hypothetically confined to SparseVectors, and test/sparse/sparse.jl mostly dedicated to SparseMatrixCSCs.) Move older tests of sparse broadcast[!] into that new file as well.
- Loading branch information
Showing
3 changed files
with
255 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
# This file is a part of Julia. License is MIT: http://julialang.org/license | ||
# | ||
# These tests cover the higher order functions specialized for sparse arrays defined in | ||
# base/sparse/higherorderfns.jl, particularly map[!]/broadcast[!] for SparseVectors and | ||
# SparseMatrixCSCs at present. | ||
|
||
@testset "map[!] implementation specialized for a single (input) sparse vector/matrix" begin | ||
N, M = 10, 12 | ||
# (also the implementation for broadcast[!] over a single (input) sparse vector/matrix) | ||
for shapeA in ((N,), (N, M)) | ||
A = sprand(shapeA..., 0.4); fA = Array(A) | ||
# --> test map entry point | ||
@test map(sin, A) == sparse(map(sin, fA)) | ||
@test map(cos, A) == sparse(map(cos, fA)) | ||
# --> test map! entry point | ||
fX = copy(fA); X = sparse(fX) | ||
map!(sin, X, A); X = sparse(fX) # warmup for @allocated | ||
@test (@allocated map!(sin, X, A)) == 0 | ||
@test map!(sin, X, A) == sparse(map!(sin, fX, fA)) | ||
@test map!(cos, X, A) == sparse(map!(cos, fX, fA)) | ||
@test_throws DimensionMismatch map!(sin, X, spzeros((shapeA .- 1)...)) | ||
end | ||
end | ||
|
||
@testset "map[!] implementation specialized for a pair of (input) sparse vectors/matrices" begin | ||
N, M = 10, 12 | ||
f(x, y) = x + y + 1 | ||
for shapeA in ((N,), (N, M)) | ||
A, Bo = sprand(shapeA..., 0.3), sprand(shapeA..., 0.3) | ||
B = ndims(Bo) == 1 ? SparseVector{Float32, Int32}(Bo) : SparseMatrixCSC{Float32,Int32}(Bo) | ||
# use different types to check internal type stability via allocation tests below | ||
fA, fB = map(Array, (A, B)) | ||
# --> test map entry point | ||
@test map(+, A, B) == sparse(map(+, fA, fB)) | ||
@test map(*, A, B) == sparse(map(*, fA, fB)) | ||
@test map(f, A, B) == sparse(map(f, fA, fB)) | ||
@test_throws DimensionMismatch map(+, A, spzeros((shapeA .- 1)...)) | ||
# --> test map! entry point | ||
fX = map(+, fA, fB); X = sparse(fX) | ||
map!(+, X, A, B); X = sparse(fX) # warmup for @allocated | ||
@test (@allocated map!(+, X, A, B)) == 0 | ||
@test map!(+, X, A, B) == sparse(map!(+, fX, fA, fB)) | ||
fX = map(*, fA, fB); X = sparse(fX) | ||
map!(*, X, A, B); X = sparse(fX) # warmup for @allocated | ||
@test (@allocated map!(*, X, A, B)) == 0 | ||
@test map!(*, X, A, B) == sparse(map!(*, fX, fA, fB)) | ||
@test map!(f, X, A, B) == sparse(map!(f, fX, fA, fB)) | ||
@test_throws DimensionMismatch map!(f, X, A, spzeros((shapeA .- 1)...)) | ||
end | ||
end | ||
|
||
@testset "map[!] implementation capable of handling >2 (input) sparse vectors/matrices" begin | ||
N, M = 10, 12 | ||
f(x, y, z) = x + y + z + 1 | ||
for shapeA in ((N,), (N, M)) | ||
A, B, Co = sprand(shapeA..., 0.2), sprand(shapeA..., 0.2), sprand(shapeA..., 0.2) | ||
C = ndims(Co) == 1 ? SparseVector{Float32,Int32}(Co) : SparseMatrixCSC{Float32,Int32}(Co) | ||
# use different types to check internal type stability via allocation tests below | ||
fA, fB, fC = map(Array, (A, B, C)) | ||
# --> test map entry point | ||
@test map(+, A, B, C) == sparse(map(+, fA, fB, fC)) | ||
@test map(*, A, B, C) == sparse(map(*, fA, fB, fC)) | ||
@test map(f, A, B, C) == sparse(map(f, fA, fB, fC)) | ||
@test_throws DimensionMismatch map(+, A, B, spzeros(N, M - 1)) | ||
# --> test map! entry point | ||
fX = map(+, fA, fB, fC); X = sparse(fX) | ||
map!(+, X, A, B, C); X = sparse(fX) # warmup for @allocated | ||
@test (@allocated map!(+, X, A, B, C)) == 0 | ||
@test map!(+, X, A, B, C) == sparse(map!(+, fX, fA, fB, fC)) | ||
fX = map(*, fA, fB, fC); X = sparse(fX) | ||
map!(*, X, A, B, C); X = sparse(fX) # warmup for @allocated | ||
@test (@allocated map!(*, X, A, B, C)) == 0 | ||
@test map!(*, X, A, B, C) == sparse(map!(*, fX, fA, fB, fC)) | ||
@test map!(f, X, A, B, C) == sparse(map!(f, fX, fA, fB, fC)) | ||
@test_throws DimensionMismatch map!(f, X, A, B, spzeros((shapeA .- 1)...)) | ||
end | ||
end | ||
|
||
@testset "broadcast[!] implementation specialized for a single (input) sparse vector/matrix" begin | ||
# broadcast[!] for a single sparse vector/matrix falls back to map[!], tested extensively | ||
# above. here we simply lightly exercise the relevant broadcast[!] entry points. | ||
N, M, p = 10, 12, 0.4 | ||
a, A = sprand(N, p), sprand(N, M, p) | ||
fa, fA = Array(a), Array(A) | ||
@test broadcast(sin, a) == sparse(broadcast(sin, fa)) | ||
@test broadcast(sin, A) == sparse(broadcast(sin, fA)) | ||
@test broadcast!(sin, copy(a), a) == sparse(broadcast!(sin, copy(fa), fa)) | ||
@test broadcast!(sin, copy(A), A) == sparse(broadcast!(sin, copy(fA), fA)) | ||
end | ||
|
||
@testset "broadcast[!] implementation specialized for pairs of (input) sparse vectors/matrices" begin | ||
N, M, p = 10, 12, 0.3 | ||
f(x, y) = x + y + 1 | ||
mats = (sprand(N, M, p), sprand(N, 1, p), sprand(1, M, p), sprand(1, 1, 1.0), spzeros(1, 1)) | ||
vecs = (sprand(N, p), sprand(1, 1.0), spzeros(1)) | ||
tens = (mats..., vecs...) | ||
for Xo in tens | ||
X = ndims(Xo) == 1 ? SparseVector{Float32,Int32}(Xo) : SparseMatrixCSC{Float32,Int32}(Xo) | ||
# use different types to check internal type stability via allocation tests below | ||
shapeX, fX = size(X), Array(X) | ||
for Y in tens | ||
fY = Array(Y) | ||
# --> test broadcast entry point | ||
@test broadcast(+, X, Y) == sparse(broadcast(+, fX, fY)) | ||
@test broadcast(*, X, Y) == sparse(broadcast(*, fX, fY)) | ||
@test broadcast(f, X, Y) == sparse(broadcast(f, fX, fY)) | ||
try Base.Broadcast.broadcast_indices(spzeros((shapeX .- 1)...), Y) | ||
catch @test_throws DimensionMismatch broadcast(+, spzeros((shapeX .- 1)...), Y) end | ||
# --> test broadcast! entry point / +-like zero-preserving op | ||
fZ = broadcast(+, fX, fY); Z = sparse(fZ) | ||
broadcast!(+, Z, X, Y); Z = sparse(fZ) # warmup for @allocated | ||
@test (@allocated broadcast!(+, Z, X, Y)) == 0 | ||
@test broadcast!(+, Z, X, Y) == sparse(broadcast!(+, fZ, fX, fY)) | ||
# --> test broadcast! entry point / *-like zero-preserving op | ||
fZ = broadcast(*, fX, fY); Z = sparse(fZ) | ||
broadcast!(*, Z, X, Y); Z = sparse(fZ) # warmup for @allocated | ||
@test (@allocated broadcast!(*, Z, X, Y)) == 0 | ||
@test broadcast!(*, Z, X, Y) == sparse(broadcast!(*, fZ, fX, fY)) | ||
# --> test broadcast! entry point / not zero-preserving op | ||
fZ = broadcast(f, fX, fY); Z = sparse(fZ) | ||
broadcast!(f, Z, X, Y); Z = sparse(fZ) # warmup for @allocated | ||
@test (@allocated broadcast!(f, Z, X, Y)) == 0 | ||
@test broadcast!(f, Z, X, Y) == sparse(broadcast!(f, fZ, fX, fY)) | ||
# --> test shape checks for both braodcast and broadcast! entry points | ||
try Base.Broadcast.check_broadcast_indices(indices(Z), spzeros((shapeX .- 1)...), Y) | ||
catch @test_throws DimensionMismatch broadcast!(f, Z, spzeros((shapeX .- 1)...), Y) end | ||
end | ||
end | ||
end | ||
|
||
@testset "broadcast[!] implementation capable of handling >2 (input) sparse vectors/matrices" begin | ||
N, M, p = 10, 12, 0.3 | ||
f(x, y, z) = x + y + z + 1 | ||
mats = (sprand(N, M, p), sprand(N, 1, p), sprand(1, M, p), sprand(1, 1, 1.0), spzeros(1, 1)) | ||
vecs = (sprand(N, p), sprand(1, 1.0), spzeros(1)) | ||
tens = (mats..., vecs...) | ||
for Xo in tens | ||
X = ndims(Xo) == 1 ? SparseVector{Float32,Int32}(Xo) : SparseMatrixCSC{Float32,Int32}(Xo) | ||
# use different types to check internal type stability via allocation tests below | ||
shapeX, fX = size(X), Array(X) | ||
for Y in tens, Z in tens | ||
fY, fZ = Array(Y), Array(Z) | ||
# --> test broadcast entry point | ||
@test broadcast(+, X, Y, Z) == sparse(broadcast(+, fX, fY, fZ)) | ||
@test broadcast(*, X, Y, Z) == sparse(broadcast(*, fX, fY, fZ)) | ||
@test broadcast(f, X, Y, Z) == sparse(broadcast(f, fX, fY, fZ)) | ||
try Base.Broadcast.broadcast_indices(spzeros((shapeX .- 1)...), Y, Z) | ||
catch @test_throws DimensionMismatch broadcast(+, spzeros((shapeX .- 1)...), Y, Z) end | ||
# --> test broadcast! entry point / +-like zero-preserving op | ||
fQ = broadcast(+, fX, fY, fZ); Q = sparse(fQ) | ||
broadcast!(+, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated | ||
@test (@allocated broadcast!(+, Q, X, Y, Z)) == 0 | ||
@test broadcast!(+, Q, X, Y, Z) == sparse(broadcast!(+, fQ, fX, fY, fZ)) | ||
# --> test broadcast! entry point / *-like zero-preserving op | ||
fQ = broadcast(*, fX, fY, fZ); Q = sparse(fQ) | ||
broadcast!(*, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated | ||
@test (@allocated broadcast!(*, Q, X, Y, Z)) == 0 | ||
@test broadcast!(*, Q, X, Y, Z) == sparse(broadcast!(*, fQ, fX, fY, fZ)) | ||
# --> test broadcast! entry point / not zero-preserving op | ||
fQ = broadcast(f, fX, fY, fZ); Q = sparse(fQ) | ||
broadcast!(f, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated | ||
@test_broken (@allocated broadcast!(f, Z, X, Y)) == 0 | ||
# the preceding test allocates 16 bytes in the entry point for broadcast!, but | ||
# none of the earlier tests of the same code path allocate. no allocation shows | ||
# up with --track-allocation=user. allocation shows up on the first line of the | ||
# entry point for broadcast! with --track-allocation=all, but that first line | ||
# almost certainly should not allocate. so not certain what's going on. | ||
@test broadcast!(f, Q, X, Y, Z) == sparse(broadcast!(f, fQ, fX, fY, fZ)) | ||
# --> test shape checks for both braodcast and broadcast! entry points | ||
try Base.Broadcast.check_broadcast_indices(indices(Q), spzeros((shapeX .- 1)...), Y, Z) | ||
catch @test_throws DimensionMismatch broadcast!(f, Q, spzeros((shapeX .- 1)...), Y, Z) end | ||
end | ||
end | ||
end | ||
|
||
# Older tests of sparse broadcast, now largely covered by the tests above | ||
@testset "assorted tests of sparse broadcast over two input arguments" begin | ||
N, p = 10, 0.3 | ||
A, B, CF = sprand(N, N, p), sprand(N, N, p), rand(N, N) | ||
AF, BF, C = Array(A), Array(B), sparse(CF) | ||
|
||
@test A .* B == AF .* BF | ||
@test A[1,:] .* B == AF[1,:] .* BF | ||
@test A[:,1] .* B == AF[:,1] .* BF | ||
@test A .* B[1,:] == AF .* BF[1,:] | ||
@test A .* B[:,1] == AF .* BF[:,1] | ||
|
||
@test A .* B == AF .* BF | ||
@test A[1,:] .* BF == AF[1,:] .* BF | ||
@test A[:,1] .* BF == AF[:,1] .* BF | ||
@test A .* BF[1,:] == AF .* BF[1,:] | ||
@test A .* BF[:,1] == AF .* BF[:,1] | ||
|
||
@test A .* B == AF .* BF | ||
@test AF[1,:] .* B == AF[1,:] .* BF | ||
@test AF[:,1] .* B == AF[:,1] .* BF | ||
@test AF .* B[1,:] == AF .* BF[1,:] | ||
@test AF .* B[:,1] == AF .* BF[:,1] | ||
|
||
@test A .* B == AF .* BF | ||
@test A[1,:] .* B == AF[1,:] .* BF | ||
@test A[:,1] .* B == AF[:,1] .* BF | ||
@test A .* B[1,:] == AF .* BF[1,:] | ||
@test A .* B[:,1] == AF .* BF[:,1] | ||
|
||
@test A .* 3 == AF .* 3 | ||
@test 3 .* A == 3 .* AF | ||
#@test A[1,:] .* 3 == AF[1,:] .* 3 | ||
@test all(A[1,:] .* 3 .== AF[1,:] .* 3) | ||
#@test A[:,1] .* 3 == AF[:,1] .* 3 | ||
@test all(A[:,1] .* 3 .== AF[:,1] .* 3) | ||
#TODO: simple comparation with == returns false because the left side is a (two-dimensional) SparseMatrixCSC | ||
# while the right side is a Vector | ||
|
||
@test A .- 3 == AF .- 3 | ||
@test 3 .- A == 3 .- AF | ||
@test A .- B == AF .- BF | ||
@test A - AF == zeros(AF) | ||
@test AF - A == zeros(AF) | ||
@test A[1,:] .- B == AF[1,:] .- BF | ||
@test A[:,1] .- B == AF[:,1] .- BF | ||
@test A .- B[1,:] == AF .- BF[1,:] | ||
@test A .- B[:,1] == AF .- BF[:,1] | ||
|
||
@test A .+ 3 == AF .+ 3 | ||
@test 3 .+ A == 3 .+ AF | ||
@test A .+ B == AF .+ BF | ||
@test A + AF == AF + A | ||
@test (A .< B) == (AF .< BF) | ||
@test (A .!= B) == (AF .!= BF) | ||
|
||
@test A ./ 3 == AF ./ 3 | ||
@test A .\ 3 == AF .\ 3 | ||
@test 3 ./ A == 3 ./ AF | ||
@test 3 .\ A == 3 .\ AF | ||
@test A .\ C == AF .\ CF | ||
@test A ./ C == AF ./ CF | ||
@test A ./ CF[:,1] == AF ./ CF[:,1] | ||
@test A .\ CF[:,1] == AF .\ CF[:,1] | ||
@test BF ./ C == BF ./ CF | ||
@test BF .\ C == BF .\ CF | ||
|
||
@test A .^ 3 == AF .^ 3 | ||
@test 3 .^ A == 3 .^ AF | ||
@test A .^ BF[:,1] == AF .^ BF[:,1] | ||
@test BF[:,1] .^ A == BF[:,1] .^ AF | ||
|
||
@test spzeros(0,0) + spzeros(0,0) == zeros(0,0) | ||
@test spzeros(0,0) * spzeros(0,0) == zeros(0,0) | ||
@test spzeros(1,0) .+ spzeros(2,1) == zeros(2,0) | ||
@test spzeros(1,0) .* spzeros(2,1) == zeros(2,0) | ||
@test spzeros(1,2) .+ spzeros(0,1) == zeros(0,2) | ||
@test spzeros(1,2) .* spzeros(0,1) == zeros(0,2) | ||
end |
Oops, something went wrong.