|
| 1 | +# This file is a part of Julia. License is MIT: http://julialang.org/license |
| 2 | +# |
| 3 | +# These tests cover the higher order functions specialized for sparse arrays defined in |
| 4 | +# base/sparse/higherorderfns.jl, particularly map[!]/broadcast[!] for SparseVectors and |
| 5 | +# SparseMatrixCSCs at present. |
| 6 | + |
| 7 | +@testset "map[!] implementation specialized for a single (input) sparse vector/matrix" begin |
| 8 | + N, M = 10, 12 |
| 9 | + # (also the implementation for broadcast[!] over a single (input) sparse vector/matrix) |
| 10 | + for shapeA in ((N,), (N, M)) |
| 11 | + A = sprand(shapeA..., 0.4); fA = Array(A) |
| 12 | + # --> test map entry point |
| 13 | + @test map(sin, A) == sparse(map(sin, fA)) |
| 14 | + @test map(cos, A) == sparse(map(cos, fA)) |
| 15 | + # --> test map! entry point |
| 16 | + fX = copy(fA); X = sparse(fX) |
| 17 | + map!(sin, X, A); X = sparse(fX) # warmup for @allocated |
| 18 | + @test (@allocated map!(sin, X, A)) == 0 |
| 19 | + @test map!(sin, X, A) == sparse(map!(sin, fX, fA)) |
| 20 | + @test map!(cos, X, A) == sparse(map!(cos, fX, fA)) |
| 21 | + @test_throws DimensionMismatch map!(sin, X, spzeros((shapeA .- 1)...)) |
| 22 | + end |
| 23 | +end |
| 24 | + |
| 25 | +@testset "map[!] implementation specialized for a pair of (input) sparse vectors/matrices" begin |
| 26 | + N, M = 10, 12 |
| 27 | + f(x, y) = x + y + 1 |
| 28 | + for shapeA in ((N,), (N, M)) |
| 29 | + A, Bo = sprand(shapeA..., 0.3), sprand(shapeA..., 0.3) |
| 30 | + B = ndims(Bo) == 1 ? SparseVector{Float32, Int32}(Bo) : SparseMatrixCSC{Float32,Int32}(Bo) |
| 31 | + # use different types to check internal type stability via allocation tests below |
| 32 | + fA, fB = map(Array, (A, B)) |
| 33 | + # --> test map entry point |
| 34 | + @test map(+, A, B) == sparse(map(+, fA, fB)) |
| 35 | + @test map(*, A, B) == sparse(map(*, fA, fB)) |
| 36 | + @test map(f, A, B) == sparse(map(f, fA, fB)) |
| 37 | + @test_throws DimensionMismatch map(+, A, spzeros((shapeA .- 1)...)) |
| 38 | + # --> test map! entry point |
| 39 | + fX = map(+, fA, fB); X = sparse(fX) |
| 40 | + map!(+, X, A, B); X = sparse(fX) # warmup for @allocated |
| 41 | + @test (@allocated map!(+, X, A, B)) == 0 |
| 42 | + @test map!(+, X, A, B) == sparse(map!(+, fX, fA, fB)) |
| 43 | + fX = map(*, fA, fB); X = sparse(fX) |
| 44 | + map!(*, X, A, B); X = sparse(fX) # warmup for @allocated |
| 45 | + @test (@allocated map!(*, X, A, B)) == 0 |
| 46 | + @test map!(*, X, A, B) == sparse(map!(*, fX, fA, fB)) |
| 47 | + @test map!(f, X, A, B) == sparse(map!(f, fX, fA, fB)) |
| 48 | + @test_throws DimensionMismatch map!(f, X, A, spzeros((shapeA .- 1)...)) |
| 49 | + end |
| 50 | +end |
| 51 | + |
| 52 | +@testset "map[!] implementation capable of handling >2 (input) sparse vectors/matrices" begin |
| 53 | + N, M = 10, 12 |
| 54 | + f(x, y, z) = x + y + z + 1 |
| 55 | + for shapeA in ((N,), (N, M)) |
| 56 | + A, B, Co = sprand(shapeA..., 0.2), sprand(shapeA..., 0.2), sprand(shapeA..., 0.2) |
| 57 | + C = ndims(Co) == 1 ? SparseVector{Float32,Int32}(Co) : SparseMatrixCSC{Float32,Int32}(Co) |
| 58 | + # use different types to check internal type stability via allocation tests below |
| 59 | + fA, fB, fC = map(Array, (A, B, C)) |
| 60 | + # --> test map entry point |
| 61 | + @test map(+, A, B, C) == sparse(map(+, fA, fB, fC)) |
| 62 | + @test map(*, A, B, C) == sparse(map(*, fA, fB, fC)) |
| 63 | + @test map(f, A, B, C) == sparse(map(f, fA, fB, fC)) |
| 64 | + @test_throws DimensionMismatch map(+, A, B, spzeros(N, M - 1)) |
| 65 | + # --> test map! entry point |
| 66 | + fX = map(+, fA, fB, fC); X = sparse(fX) |
| 67 | + map!(+, X, A, B, C); X = sparse(fX) # warmup for @allocated |
| 68 | + @test (@allocated map!(+, X, A, B, C)) == 0 |
| 69 | + @test map!(+, X, A, B, C) == sparse(map!(+, fX, fA, fB, fC)) |
| 70 | + fX = map(*, fA, fB, fC); X = sparse(fX) |
| 71 | + map!(*, X, A, B, C); X = sparse(fX) # warmup for @allocated |
| 72 | + @test (@allocated map!(*, X, A, B, C)) == 0 |
| 73 | + @test map!(*, X, A, B, C) == sparse(map!(*, fX, fA, fB, fC)) |
| 74 | + @test map!(f, X, A, B, C) == sparse(map!(f, fX, fA, fB, fC)) |
| 75 | + @test_throws DimensionMismatch map!(f, X, A, B, spzeros((shapeA .- 1)...)) |
| 76 | + end |
| 77 | +end |
| 78 | + |
| 79 | +@testset "broadcast[!] implementation specialized for a single (input) sparse vector/matrix" begin |
| 80 | + # broadcast[!] for a single sparse vector/matrix falls back to map[!], tested extensively |
| 81 | + # above. here we simply lightly exercise the relevant broadcast[!] entry points. |
| 82 | + N, M, p = 10, 12, 0.4 |
| 83 | + a, A = sprand(N, p), sprand(N, M, p) |
| 84 | + fa, fA = Array(a), Array(A) |
| 85 | + @test broadcast(sin, a) == sparse(broadcast(sin, fa)) |
| 86 | + @test broadcast(sin, A) == sparse(broadcast(sin, fA)) |
| 87 | + @test broadcast!(sin, copy(a), a) == sparse(broadcast!(sin, copy(fa), fa)) |
| 88 | + @test broadcast!(sin, copy(A), A) == sparse(broadcast!(sin, copy(fA), fA)) |
| 89 | +end |
| 90 | + |
| 91 | +@testset "broadcast[!] implementation specialized for pairs of (input) sparse vectors/matrices" begin |
| 92 | + N, M, p = 10, 12, 0.3 |
| 93 | + f(x, y) = x + y + 1 |
| 94 | + mats = (sprand(N, M, p), sprand(N, 1, p), sprand(1, M, p), sprand(1, 1, 1.0), spzeros(1, 1)) |
| 95 | + vecs = (sprand(N, p), sprand(1, 1.0), spzeros(1)) |
| 96 | + tens = (mats..., vecs...) |
| 97 | + for Xo in tens |
| 98 | + X = ndims(Xo) == 1 ? SparseVector{Float32,Int32}(Xo) : SparseMatrixCSC{Float32,Int32}(Xo) |
| 99 | + # use different types to check internal type stability via allocation tests below |
| 100 | + shapeX, fX = size(X), Array(X) |
| 101 | + for Y in tens |
| 102 | + fY = Array(Y) |
| 103 | + # --> test broadcast entry point |
| 104 | + @test broadcast(+, X, Y) == sparse(broadcast(+, fX, fY)) |
| 105 | + @test broadcast(*, X, Y) == sparse(broadcast(*, fX, fY)) |
| 106 | + @test broadcast(f, X, Y) == sparse(broadcast(f, fX, fY)) |
| 107 | + try Base.Broadcast.broadcast_indices(spzeros((shapeX .- 1)...), Y) |
| 108 | + catch @test_throws DimensionMismatch broadcast(+, spzeros((shapeX .- 1)...), Y) end |
| 109 | + # --> test broadcast! entry point / +-like zero-preserving op |
| 110 | + fZ = broadcast(+, fX, fY); Z = sparse(fZ) |
| 111 | + broadcast!(+, Z, X, Y); Z = sparse(fZ) # warmup for @allocated |
| 112 | + @test (@allocated broadcast!(+, Z, X, Y)) == 0 |
| 113 | + @test broadcast!(+, Z, X, Y) == sparse(broadcast!(+, fZ, fX, fY)) |
| 114 | + # --> test broadcast! entry point / *-like zero-preserving op |
| 115 | + fZ = broadcast(*, fX, fY); Z = sparse(fZ) |
| 116 | + broadcast!(*, Z, X, Y); Z = sparse(fZ) # warmup for @allocated |
| 117 | + @test (@allocated broadcast!(*, Z, X, Y)) == 0 |
| 118 | + @test broadcast!(*, Z, X, Y) == sparse(broadcast!(*, fZ, fX, fY)) |
| 119 | + # --> test broadcast! entry point / not zero-preserving op |
| 120 | + fZ = broadcast(f, fX, fY); Z = sparse(fZ) |
| 121 | + broadcast!(f, Z, X, Y); Z = sparse(fZ) # warmup for @allocated |
| 122 | + @test (@allocated broadcast!(f, Z, X, Y)) == 0 |
| 123 | + @test broadcast!(f, Z, X, Y) == sparse(broadcast!(f, fZ, fX, fY)) |
| 124 | + # --> test shape checks for both braodcast and broadcast! entry points |
| 125 | + try Base.Broadcast.check_broadcast_indices(indices(Z), spzeros((shapeX .- 1)...), Y) |
| 126 | + catch @test_throws DimensionMismatch broadcast!(f, Z, spzeros((shapeX .- 1)...), Y) end |
| 127 | + end |
| 128 | + end |
| 129 | +end |
| 130 | + |
| 131 | +@testset "broadcast[!] implementation capable of handling >2 (input) sparse vectors/matrices" begin |
| 132 | + N, M, p = 10, 12, 0.3 |
| 133 | + f(x, y, z) = x + y + z + 1 |
| 134 | + mats = (sprand(N, M, p), sprand(N, 1, p), sprand(1, M, p), sprand(1, 1, 1.0), spzeros(1, 1)) |
| 135 | + vecs = (sprand(N, p), sprand(1, 1.0), spzeros(1)) |
| 136 | + tens = (mats..., vecs...) |
| 137 | + for Xo in tens |
| 138 | + X = ndims(Xo) == 1 ? SparseVector{Float32,Int32}(Xo) : SparseMatrixCSC{Float32,Int32}(Xo) |
| 139 | + # use different types to check internal type stability via allocation tests below |
| 140 | + shapeX, fX = size(X), Array(X) |
| 141 | + for Y in tens, Z in tens |
| 142 | + fY, fZ = Array(Y), Array(Z) |
| 143 | + # --> test broadcast entry point |
| 144 | + @test broadcast(+, X, Y, Z) == sparse(broadcast(+, fX, fY, fZ)) |
| 145 | + @test broadcast(*, X, Y, Z) == sparse(broadcast(*, fX, fY, fZ)) |
| 146 | + @test broadcast(f, X, Y, Z) == sparse(broadcast(f, fX, fY, fZ)) |
| 147 | + try Base.Broadcast.broadcast_indices(spzeros((shapeX .- 1)...), Y, Z) |
| 148 | + catch @test_throws DimensionMismatch broadcast(+, spzeros((shapeX .- 1)...), Y, Z) end |
| 149 | + # --> test broadcast! entry point / +-like zero-preserving op |
| 150 | + fQ = broadcast(+, fX, fY, fZ); Q = sparse(fQ) |
| 151 | + broadcast!(+, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated |
| 152 | + @test (@allocated broadcast!(+, Q, X, Y, Z)) == 0 |
| 153 | + @test broadcast!(+, Q, X, Y, Z) == sparse(broadcast!(+, fQ, fX, fY, fZ)) |
| 154 | + # --> test broadcast! entry point / *-like zero-preserving op |
| 155 | + fQ = broadcast(*, fX, fY, fZ); Q = sparse(fQ) |
| 156 | + broadcast!(*, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated |
| 157 | + @test (@allocated broadcast!(*, Q, X, Y, Z)) == 0 |
| 158 | + @test broadcast!(*, Q, X, Y, Z) == sparse(broadcast!(*, fQ, fX, fY, fZ)) |
| 159 | + # --> test broadcast! entry point / not zero-preserving op |
| 160 | + fQ = broadcast(f, fX, fY, fZ); Q = sparse(fQ) |
| 161 | + broadcast!(f, Q, X, Y, Z); Q = sparse(fQ) # warmup for @allocated |
| 162 | + @test_broken (@allocated broadcast!(f, Z, X, Y)) == 0 |
| 163 | + # the preceding test allocates 16 bytes in the entry point for broadcast!, but |
| 164 | + # none of the earlier tests of the same code path allocate. no allocation shows |
| 165 | + # up with --track-allocation=user. allocation shows up on the first line of the |
| 166 | + # entry point for broadcast! with --track-allocation=all, but that first line |
| 167 | + # almost certainly should not allocate. so not certain what's going on. |
| 168 | + @test broadcast!(f, Q, X, Y, Z) == sparse(broadcast!(f, fQ, fX, fY, fZ)) |
| 169 | + # --> test shape checks for both braodcast and broadcast! entry points |
| 170 | + try Base.Broadcast.check_broadcast_indices(indices(Q), spzeros((shapeX .- 1)...), Y, Z) |
| 171 | + catch @test_throws DimensionMismatch broadcast!(f, Q, spzeros((shapeX .- 1)...), Y, Z) end |
| 172 | + end |
| 173 | + end |
| 174 | +end |
| 175 | + |
| 176 | +# Older tests of sparse broadcast, now largely covered by the tests above |
| 177 | +@testset "assorted tests of sparse broadcast over two input arguments" begin |
| 178 | + N, p = 10, 0.3 |
| 179 | + A, B, CF = sprand(N, N, p), sprand(N, N, p), rand(N, N) |
| 180 | + AF, BF, C = Array(A), Array(B), sparse(CF) |
| 181 | + |
| 182 | + @test A .* B == AF .* BF |
| 183 | + @test A[1,:] .* B == AF[1,:] .* BF |
| 184 | + @test A[:,1] .* B == AF[:,1] .* BF |
| 185 | + @test A .* B[1,:] == AF .* BF[1,:] |
| 186 | + @test A .* B[:,1] == AF .* BF[:,1] |
| 187 | + |
| 188 | + @test A .* B == AF .* BF |
| 189 | + @test A[1,:] .* BF == AF[1,:] .* BF |
| 190 | + @test A[:,1] .* BF == AF[:,1] .* BF |
| 191 | + @test A .* BF[1,:] == AF .* BF[1,:] |
| 192 | + @test A .* BF[:,1] == AF .* BF[:,1] |
| 193 | + |
| 194 | + @test A .* B == AF .* BF |
| 195 | + @test AF[1,:] .* B == AF[1,:] .* BF |
| 196 | + @test AF[:,1] .* B == AF[:,1] .* BF |
| 197 | + @test AF .* B[1,:] == AF .* BF[1,:] |
| 198 | + @test AF .* B[:,1] == AF .* BF[:,1] |
| 199 | + |
| 200 | + @test A .* B == AF .* BF |
| 201 | + @test A[1,:] .* B == AF[1,:] .* BF |
| 202 | + @test A[:,1] .* B == AF[:,1] .* BF |
| 203 | + @test A .* B[1,:] == AF .* BF[1,:] |
| 204 | + @test A .* B[:,1] == AF .* BF[:,1] |
| 205 | + |
| 206 | + @test A .* 3 == AF .* 3 |
| 207 | + @test 3 .* A == 3 .* AF |
| 208 | + #@test A[1,:] .* 3 == AF[1,:] .* 3 |
| 209 | + @test all(A[1,:] .* 3 .== AF[1,:] .* 3) |
| 210 | + #@test A[:,1] .* 3 == AF[:,1] .* 3 |
| 211 | + @test all(A[:,1] .* 3 .== AF[:,1] .* 3) |
| 212 | + #TODO: simple comparation with == returns false because the left side is a (two-dimensional) SparseMatrixCSC |
| 213 | + # while the right side is a Vector |
| 214 | + |
| 215 | + @test A .- 3 == AF .- 3 |
| 216 | + @test 3 .- A == 3 .- AF |
| 217 | + @test A .- B == AF .- BF |
| 218 | + @test A - AF == zeros(AF) |
| 219 | + @test AF - A == zeros(AF) |
| 220 | + @test A[1,:] .- B == AF[1,:] .- BF |
| 221 | + @test A[:,1] .- B == AF[:,1] .- BF |
| 222 | + @test A .- B[1,:] == AF .- BF[1,:] |
| 223 | + @test A .- B[:,1] == AF .- BF[:,1] |
| 224 | + |
| 225 | + @test A .+ 3 == AF .+ 3 |
| 226 | + @test 3 .+ A == 3 .+ AF |
| 227 | + @test A .+ B == AF .+ BF |
| 228 | + @test A + AF == AF + A |
| 229 | + @test (A .< B) == (AF .< BF) |
| 230 | + @test (A .!= B) == (AF .!= BF) |
| 231 | + |
| 232 | + @test A ./ 3 == AF ./ 3 |
| 233 | + @test A .\ 3 == AF .\ 3 |
| 234 | + @test 3 ./ A == 3 ./ AF |
| 235 | + @test 3 .\ A == 3 .\ AF |
| 236 | + @test A .\ C == AF .\ CF |
| 237 | + @test A ./ C == AF ./ CF |
| 238 | + @test A ./ CF[:,1] == AF ./ CF[:,1] |
| 239 | + @test A .\ CF[:,1] == AF .\ CF[:,1] |
| 240 | + @test BF ./ C == BF ./ CF |
| 241 | + @test BF .\ C == BF .\ CF |
| 242 | + |
| 243 | + @test A .^ 3 == AF .^ 3 |
| 244 | + @test 3 .^ A == 3 .^ AF |
| 245 | + @test A .^ BF[:,1] == AF .^ BF[:,1] |
| 246 | + @test BF[:,1] .^ A == BF[:,1] .^ AF |
| 247 | + |
| 248 | + @test spzeros(0,0) + spzeros(0,0) == zeros(0,0) |
| 249 | + @test spzeros(0,0) * spzeros(0,0) == zeros(0,0) |
| 250 | + @test spzeros(1,0) .+ spzeros(2,1) == zeros(2,0) |
| 251 | + @test spzeros(1,0) .* spzeros(2,1) == zeros(2,0) |
| 252 | + @test spzeros(1,2) .+ spzeros(0,1) == zeros(0,2) |
| 253 | + @test spzeros(1,2) .* spzeros(0,1) == zeros(0,2) |
| 254 | +end |
0 commit comments