From 57ea2f93283c8de66b03cfc6390612d26a9d980c Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Sun, 3 Jul 2016 16:38:41 -0700 Subject: [PATCH] Transition vectorized unary functions over `SparseMatrixCSC`s to compact broadcast syntax, accordingly revise and expand the associated tests, and add deprecations for the vectorized syntax. --- base/deprecated.jl | 13 +++++++ base/sparse/sparsematrix.jl | 22 ++++++------ test/sparsedir/sparse.jl | 67 ++++++++++++++++++++++--------------- 3 files changed, 64 insertions(+), 38 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index b22c1a3cc6523..b04f2a10b6b23 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -788,6 +788,19 @@ function symperm{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, pinv::Vector{Ti}) "Pkg.add(\"SuiteSparse\") to install SuiteSparse on Julia v0.5.")) end +# Deprecate vectorized unary functions over sparse matrices in favor of compact broadcast syntax +for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, + :tan, :tanh, :tand, :atan, :atanh, :atand, + :sinpi, :cosc, :ceil, :floor, :trunc, :round, :real, :imag, + :log1p, :expm1, :abs, :abs2, :conj, + :log, :log2, :log10, :exp, :exp2, :exp10, :sinc, :cospi, + :cos, :cosh, :cosd, :acos, :acosd, + :cot, :coth, :cotd, :acot, :acotd, + :sec, :sech, :secd, :asech, + :csc, :csch, :cscd, :acsch) + @eval @deprecate $f(A::SparseMatrixCSC) $f.(A) +end + # During the 0.5 development cycle, do not add any deprecations below this line # To be deprecated in 0.6 diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index edf62c4ad4d1e..292309d0c9ca2 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1036,12 +1036,12 @@ end """ Helper macro for the unary broadcast definitions below. Takes parent method `fp` and a set of desired child methods `fcs`, and builds an expression defining each of the child methods -such that `fc(A::SparseMatrixCSC) = fp(fc, A)`. +such that `broadcast(::typeof(fc), A::SparseMatrixCSC) = fp(fc, A)`. """ macro _enumerate_childmethods(fp, fcs...) fcexps = Expr(:block) for fc in fcs - push!(fcexps.args, :( $(esc(fc))(A::SparseMatrixCSC) = $(esc(fp))($(esc(fc)), A) ) ) + push!(fcexps.args, :( broadcast(::typeof($(esc(fc))), A::SparseMatrixCSC) = $(esc(fp))($(esc(fc)), A) ) ) end return fcexps end @@ -1080,10 +1080,10 @@ end sin, sinh, sind, asin, asinh, asind, tan, tanh, tand, atan, atanh, atand, sinpi, cosc, ceil, floor, trunc, round) -real(A::SparseMatrixCSC) = copy(A) -imag{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}) = spzeros(Tv, Ti, A.m, A.n) -real{TTv}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2z_z2z_T(real, A, TTv) -imag{TTv}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2z_z2z_T(imag, A, TTv) +broadcast(::typeof(real), A::SparseMatrixCSC) = copy(A) +broadcast{Tv,Ti}(::typeof(imag), A::SparseMatrixCSC{Tv,Ti}) = spzeros(Tv, Ti, A.m, A.n) +broadcast{TTv}(::typeof(real), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2z_z2z_T(real, A, TTv) +broadcast{TTv}(::typeof(imag), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2z_z2z_T(imag, A, TTv) ceil{To}(::Type{To}, A::SparseMatrixCSC) = _broadcast_unary_nz2z_z2z_T(ceil, A, To) floor{To}(::Type{To}, A::SparseMatrixCSC) = _broadcast_unary_nz2z_z2z_T(floor, A, To) trunc{To}(::Type{To}, A::SparseMatrixCSC) = _broadcast_unary_nz2z_z2z_T(trunc, A, To) @@ -1110,10 +1110,10 @@ function _broadcast_unary_nz2nz_z2z{Tv}(f::Function, A::SparseMatrixCSC{Tv}) end @_enumerate_childmethods(_broadcast_unary_nz2nz_z2z, log1p, expm1, abs, abs2, conj) -abs2{TTv}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs2, A, TTv) -abs{TTv}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, TTv) -abs{TTv<:Integer}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, Float64) -abs{TTv<:BigInt}(A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, BigFloat) +broadcast{TTv}(::typeof(abs2), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs2, A, TTv) +broadcast{TTv}(::typeof(abs), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, TTv) +broadcast{TTv<:Integer}(::typeof(abs), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, Float64) +broadcast{TTv<:BigInt}(::typeof(abs), A::SparseMatrixCSC{Complex{TTv}}) = _broadcast_unary_nz2nz_z2z_T(abs, A, BigFloat) function conj!(A::SparseMatrixCSC) @inbounds @simd for k in 1:nnz(A) A.nzval[k] = conj(A.nzval[k]) @@ -1450,7 +1450,7 @@ end # macro (.^)(A::SparseMatrixCSC, B::Number) = B==0 ? sparse(ones(typeof(one(eltype(A)).^B), A.m, A.n)) : SparseMatrixCSC(A.m, A.n, copy(A.colptr), copy(A.rowval), A.nzval .^ B) -(.^)(::Irrational{:e}, B::SparseMatrixCSC) = exp(B) +(.^)(::Irrational{:e}, B::SparseMatrixCSC) = exp.(B) (.^)(A::Number, B::SparseMatrixCSC) = (.^)(A, full(B)) (.^)(A::SparseMatrixCSC, B::Array) = (.^)(full(A), B) (.^)(A::Array, B::SparseMatrixCSC) = (.^)(A, full(B)) diff --git a/test/sparsedir/sparse.jl b/test/sparsedir/sparse.jl index e68d43039d50f..95848ca311e87 100644 --- a/test/sparsedir/sparse.jl +++ b/test/sparsedir/sparse.jl @@ -286,7 +286,7 @@ end # conj cA = sprandn(5,5,0.2) + im*sprandn(5,5,0.2) -@test full(conj(cA)) == conj(full(cA)) +@test full(conj.(cA)) == conj(full(cA)) # transpose of SubArrays A = view(sprandn(10, 10, 0.3), 1:4, 1:4) @@ -402,22 +402,47 @@ end @test maximum(sparse(-ones(3,3))) == -1 @test minimum(sparse(ones(3,3))) == 1 -# Unary functions -a = sprand(5,15, 0.5) -afull = full(a) -for op in (:sin, :cos, :tan, :ceil, :floor, :abs, :abs2) - @eval begin - @test ($op)(afull) == full($(op)(a)) - end -end - -for op in (:ceil, :floor) - @eval begin - @test ($op)(Int,afull) == full($(op)(Int,a)) +# Test unary functions with specialized broadcast over SparseMatrixCSCs +let + A = sprand(5, 15, 0.5) + C = A + im*A + Afull = full(A) + Cfull = full(C) + # Test representatives of [unary functions that map zeros to zeros and may map nonzeros to zeros] + @test sin.(Afull) == full(sin.(A)) + @test tan.(Afull) == full(tan.(A)) # should be redundant with sin test + @test ceil.(Afull) == full(ceil.(A)) + @test floor.(Afull) == full(floor.(A)) # should be redundant with ceil test + @test real.(Afull) == full(real.(A)) + @test imag.(Afull) == full(imag.(A)) + @test real.(Cfull) == full(real.(C)) + @test imag.(Cfull) == full(imag.(C)) + # Test representatives of [unary functions that map zeros to zeros and nonzeros to nonzeros] + @test expm1.(Afull) == full(expm1.(A)) + @test abs.(Afull) == full(abs.(A)) + @test abs2.(Afull) == full(abs2.(A)) + @test abs.(Cfull) == full(abs.(C)) + @test abs2.(Cfull) == full(abs2.(C)) + # Test representatives of [unary functions that map both zeros and nonzeros to nonzeros] + @test cos.(Afull) == full(cos.(A)) + # Test representatives of remaining vectorized-nonbroadcast unary functions + @test ceil(Int, Afull) == full(ceil(Int, A)) + @test floor(Int, Afull) == full(floor(Int, A)) + # Tests of real, imag, abs, and abs2 for SparseMatrixCSC{Int,X}s previously elsewhere + for T in (Int, Float16, Float32, Float64, BigInt, BigFloat) + R = rand(T[1:100;], 2, 2) + I = rand(T[1:100;], 2, 2) + D = R + I*im + S = sparse(D) + @test R == real.(S) + @test I == imag.(S) + @test real.(sparse(R)) == R + @test nnz(imag.(sparse(R))) == 0 + @test abs.(S) == abs(D) + @test abs2.(S) == abs2(D) end end - # getindex tests ni = 23 nj = 32 @@ -696,7 +721,7 @@ end @test_throws ArgumentError sparsevec(Dict(-1=>1,1=>2)) # issue #8976 -@test conj(sparse([1im])) == sparse(conj([1im])) +@test conj.(sparse([1im])) == sparse(conj([1im])) @test conj!(sparse([1im])) == sparse(conj!([1im])) # issue #9525 @@ -862,18 +887,6 @@ end x = speye(100) @test_throws BoundsError x[-10:10] -for T in (Int, Float16, Float32, Float64, BigInt, BigFloat) - let R=rand(T[1:100;],2,2), I=rand(T[1:100;],2,2) - D = R + I*im - S = sparse(D) - @test R == real(S) - @test I == imag(S) - @test real(sparse(R)) == R - @test nnz(imag(sparse(R))) == 0 - @test abs(S) == abs(D) - @test abs2(S) == abs2(D) - end -end # issue #10407 @test maximum(spzeros(5, 5)) == 0.0