From 913f637364fc8230fa17dce7eb678f99654bd4b3 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 22 Dec 2016 15:42:06 -0800 Subject: [PATCH] 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. --- test/choosetests.jl | 2 +- test/sparse/higherorderfns.jl | 262 ++++++++++++++++++++++++++++++++++ test/sparse/sparse.jl | 239 ------------------------------- test/sparse/sparsevector.jl | 4 +- 4 files changed, 264 insertions(+), 243 deletions(-) create mode 100644 test/sparse/higherorderfns.jl diff --git a/test/choosetests.jl b/test/choosetests.jl index 98634c0c87073..bd1fc5efcf72c 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -103,7 +103,7 @@ function choosetests(choices = []) end - sparsetests = ["sparse/sparse", "sparse/sparsevector"] + sparsetests = ["sparse/sparse", "sparse/sparsevector", "sparse/higherorderfns"] if Base.USE_GPL_LIBS append!(sparsetests, ["sparse/umfpack", "sparse/cholmod", "sparse/spqr"]) end diff --git a/test/sparse/higherorderfns.jl b/test/sparse/higherorderfns.jl new file mode 100644 index 0000000000000..f91d234f3209a --- /dev/null +++ b/test/sparse/higherorderfns.jl @@ -0,0 +1,262 @@ +# 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 broadcast 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, Q, X, Y, Z)) == 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 broadcast 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 A[:,1] .* 3 == AF[:,1] .* 3 + + @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 diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 07171c22a5acf..7028c96223bd6 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1170,89 +1170,6 @@ end @test spdiagm(([1,2],[3.5],[4+5im]), (0,1,-1), 2,2) == [1 3.5; 4+5im 2] end -@testset "sparse matrix broadcasting" begin - A = sprand(10,10,0.3) - B = sprand(10,10,0.3) - CF = rand(10,10) - AF = Array(A) - BF = Array(B) - C = 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 -end - -@testset "empty matrix broadcasting" begin - @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 - @testset "error conditions for reinterpret, reshape, and squeeze" begin A = sprand(Bool, 5,5,0.2) @test_throws ArgumentError reinterpret(Complex128,A) @@ -1772,31 +1689,6 @@ end @inferred hcat(sparse(rand(2,1)), eye(2,2)) end -# Test that broadcast[!](f, [C::SparseMatrixCSC], A::SparseMatrixCSC, B::SparseMatrixCSC) -# returns the correct (densely populated) result when f(zero(eltype(A)), zero(eltype(B))) != 0 -@testset "check that broadcast[!](f, ... yields the correct (densely populated) result when f does not preserve zeros" begin - N = 5 - sparsesqrmat = sprand(N, N, 0.5) - sparsesqrmat2 = sprand(N, N, 0.5) - sparserowmat = sprand(1, N, 0.5) - sparsecolmat = sprand(N, 1, 0.5) - sparse1x1matz = spzeros(1, 1) - sparse1x1mato = spones(sparse1x1matz) - zeroscourge = (x, y) -> x + y + 1 - # test case where the matrices have the same shape and no singleton dimensions - @test broadcast(zeroscourge, sparsesqrmat, sparsesqrmat2) == - broadcast(zeroscourge, Matrix(sparsesqrmat), Matrix(sparsesqrmat2)) - # test combinations where either or both matrices have one or more singleton dimensions - sparsemats = (sparsesqrmat, sparserowmat, sparsecolmat, sparse1x1matz, sparse1x1mato) - densemats = map(Matrix, sparsemats) - for (sparseA, denseA) in zip(sparsemats, densemats) - for (sparseB, denseB) in zip(sparsemats, densemats) - @test broadcast(zeroscourge, sparseA, sparseB) == - broadcast(zeroscourge, denseA, denseB) - end - end -end - # Check that `broadcast` methods specialized for unary operations over # `SparseMatrixCSC`s determine a reasonable return type. @testset "issue #18974" begin @@ -1807,134 +1699,3 @@ end @testset "issue #19503" begin @test which(-, (SparseMatrixCSC,)).module == Base.SparseArrays end - -@testset "map[!] over sparse matrices" begin - N, M = 10, 12 - # test map/map! implementation specialized for a single (input) sparse matrix - # (also tested through broadcast/broadcast! over a single (input) sparse matrix) - # --> test map entry point - A = sprand(N, M, 0.4) - fA = Array(A) - @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(N, M - 1)) - # test map/map! implementation specialized for a pair of (input) sparse matrices - f(x, y) = x + y + 1 - A = sprand(N, M, 0.3) - B = convert(SparseMatrixCSC{Float32,Int32}, sprand(N, M, 0.3)) - # 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(N, M - 1)) - # --> test map! entry point - fX = 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 = 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(N, M - 1)) - # test map/map! implementation for an arbitrary number of (input) sparse matrices - f(x, y, z) = x + y + z + 1 - A = sprand(N, M, 0.2) - B = sprand(N, M, 0.2) - C = convert(SparseMatrixCSC{Float32,Int32}, sprand(N, M, 0.2)) - # 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 = 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 = 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(N, M - 1)) -end - -@testset "test that broadcast! does not allocate unnecessarily" begin - # broadcast! over a single sparse matrix falls back to map!, tested above - N, M = 10, 12 - # test broadcast! implementation specialized for a pair of (input) sparse matrices - f(x, y) = x + y + 1 - A = sprand(N, M, 0.3) - B = convert(SparseMatrixCSC{Float32,Int32}, sprand(N, 1, 0.3)) - # use different types to check internal type stability via allocation tests below - X = sparse(Array(A) .+ Array(B)) - broadcast!(+, copy(X), A, B) # warmup for @allocated - @test (@allocated broadcast!(+, X, A, B)) == 0 - X = sparse(Array(A) .* Array(B)) - broadcast!(*, copy(X), A, B) # warmup for @allocated - @test (@allocated broadcast!(*, X, A, B)) == 0 - X = sparse(broadcast(f, Array(A), Array(B))) - broadcast!(f, copy(X), A, B) # warmup for @allocated - @test (@allocated broadcast!(f, X, A, B)) == 0 - # test broadcast! implementation for an arbitrary number of (input) sparse matrices - f(x, y, z) = x + y + z + 1 - A = sprand(N, M, 0.2) - B = sprand(N, 1, 0.2) - C = convert(SparseMatrixCSC{Float32,Int32}, sprand(1, M, 0.2)) - # use different types to check internal type stability via allocation tests below - X = sparse(Array(A) .+ Array(B) .+ Array(C)) - broadcast!(+, copy(X), A, B, C) # warmup for @allocated - @test (@allocated broadcast!(+, X, A, B, C)) == 0 - X = sparse(Array(A) .* Array(B) .* Array(C)) - broadcast!(*, copy(X), A, B, C) # warmup for @allocated - @test (@allocated broadcast!(*, X, A, B, C)) == 0 - X = sparse(broadcast(f, Array(A), Array(B), Array(C))) - broadcast!(f, copy(X), A, B, C) # warmup for @allocated - @test_broken (@allocated broadcast!(f, X, A, B, C)) == 0 - # this last 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. -end - -@testset "test basic correctness of broadcast/broadcast! implementation for more than two (input) sparse matrices" begin - N, M = 10, 12 - f(xs...) = sum(xs) + 1 - A = sprand(N, M, 0.2) - B = sprand(N, 1, 0.2) - C = sprand(1, M, 0.2) - D = sprand(1, 1, 1.0) - E = spzeros(1, 1) - fA, fB, fC, fD, fE = map(Array, (A, B, C, D, E)) - fX, fY = ones(fA), ones(fB) - X, Y = sparse(fX), sparse(fY) - for op in (+, *, f) - # horizontal expansion only - @test broadcast(op, A, B, B) == sparse(broadcast(op, fA, fB, fB)) - @test broadcast!(op, X, A, B, B) == sparse(broadcast!(op, fX, fA, fB, fB)) - # vertical expansion only - @test broadcast(op, B, D, E) == sparse(broadcast(op, fB, fD, fE)) - @test broadcast!(op, Y, B, D, E) == sparse(broadcast!(op, fY, fB, fD, fE)) - # separate horizontal and vertical expansion - @test broadcast(op, A, B, C) == sparse(broadcast(op, fA, fB, fC)) - @test broadcast!(op, X, A, B, C) == sparse(broadcast!(op, fX, fA, fB, fC)) - # simultaneous horizontal and vertical expansion - @test broadcast(op, A, B, C, D) == sparse(broadcast(op, fA, fB, fC, D)) - @test broadcast!(op, X, A, B, C, D) == sparse(broadcast!(op, fX, fA, fB, fC, fD)) - end - @test_throws DimensionMismatch broadcast(+, A, B, speye(N)) - @test_throws DimensionMismatch broadcast!(+, X, A, B, speye(N)) -end diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index 82c378fde39e8..c4608ce93ea7a 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -609,9 +609,7 @@ let x = spv_x1, x2 = spv_x2 # multiplies xm = SparseVector(8, [2, 6], [5.0, -19.25]) - let y=x # workaround for broadcast not preserving sparsity in general - @test exact_equal(x .* y, abs2(x)) - end + @test exact_equal(x .* x, abs2(x)) @test exact_equal(x .* x2, xm) @test exact_equal(x2 .* x, xm)