From 2df3eb00aa62f8346427d51a37ee9311aee5549b Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 30 Mar 2016 16:55:58 +0200 Subject: [PATCH 1/2] remove use of functors for sparse matrices --- base/sparse/sparsematrix.jl | 60 ++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index f6d42a7939334..8b06bea6f9728 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -316,13 +316,13 @@ sparse{Tv}(A::AbstractMatrix{Tv}) = convert(SparseMatrixCSC{Tv,Int}, A) sparse(S::SparseMatrixCSC) = copy(S) -sparse_IJ_sorted!(I,J,V,m,n) = sparse_IJ_sorted!(I,J,V,m,n,AddFun()) +sparse_IJ_sorted!(I, J, V, m, n) = sparse_IJ_sorted!(I, J, V, m, n, +) -sparse_IJ_sorted!(I,J,V::AbstractVector{Bool},m,n) = sparse_IJ_sorted!(I,J,V,m,n,OrFun()) +sparse_IJ_sorted!(I, J, V::AbstractVector{Bool}, m, n) = sparse_IJ_sorted!(I, J, V, m, n, |) function sparse_IJ_sorted!{Ti<:Integer}(I::AbstractVector{Ti}, J::AbstractVector{Ti}, V::AbstractVector, - m::Integer, n::Integer, combine::Union{Function,Func}) + m::Integer, n::Integer, combine::Function) m = m < 0 ? 0 : m n = n < 0 ? 0 : n @@ -594,11 +594,11 @@ sparse(I,J,V::AbstractVector) = sparse(I, J, V, dimlub(I), dimlub(J)) sparse(I,J,v::Number,m,n) = sparse(I, J, fill(v,length(I)), Int(m), Int(n)) -sparse(I,J,V::AbstractVector,m,n) = sparse(I, J, V, Int(m), Int(n), AddFun()) +sparse(I,J,V::AbstractVector,m,n) = sparse(I, J, V, Int(m), Int(n), +) -sparse(I,J,V::AbstractVector{Bool},m,n) = sparse(I, J, V, Int(m), Int(n), OrFun()) +sparse(I,J,V::AbstractVector{Bool},m,n) = sparse(I, J, V, Int(m), Int(n), |) -sparse(I,J,v::Number,m,n,combine::Union{Function,Func}) = sparse(I, J, fill(v,length(I)), Int(m), Int(n), combine) +sparse(I,J,v::Number,m,n,combine::Function) = sparse(I, J, fill(v,length(I)), Int(m), Int(n), combine) function sparse(T::SymTridiagonal) m = length(T.dv) @@ -708,8 +708,8 @@ function qftranspose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti C end -transpose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) = qftranspose!(C, A, 1:A.n, Base.IdFun()) -ctranspose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) = qftranspose!(C, A, 1:A.n, Base.ConjFun()) +transpose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) = qftranspose!(C, A, 1:A.n, identity) +ctranspose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) = qftranspose!(C, A, 1:A.n, conj) "See `qftranspose!`" ftranspose!{Tv,Ti}(C::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}, f) = qftranspose!(C, A, 1:A.n, f) """ @@ -724,8 +724,8 @@ function qftranspose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, q::AbstractVector, f) Cnzval = Array{Tv}(Cnnz) qftranspose!(SparseMatrixCSC(Cm, Cn, Ccolptr, Crowval, Cnzval), A, q, f) end -transpose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}) = qftranspose(A, 1:A.n, Base.IdFun()) -ctranspose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}) = qftranspose(A, 1:A.n, Base.ConjFun()) +transpose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}) = qftranspose(A, 1:A.n, identity) +ctranspose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}) = qftranspose(A, 1:A.n, conj) "See `qftranspose`" ftranspose{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, f) = qftranspose(A, 1:A.n, f) @@ -788,30 +788,22 @@ function fkeep!{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, f, other, trim::Bool = true) A end -immutable TrilFunc <: Base.Func{4} end -immutable TriuFunc <: Base.Func{4} end -(::TrilFunc){Tv,Ti}(i::Ti, j::Ti, x::Tv, k::Integer) = i + k >= j -(::TriuFunc){Tv,Ti}(i::Ti, j::Ti, x::Tv, k::Integer) = j >= i + k function tril!(A::SparseMatrixCSC, k::Integer = 0, trim::Bool = true) if k > A.n-1 || k < 1-A.m throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($(A.m),$(A.n))")) end - fkeep!(A, TrilFunc(), k, trim) + fkeep!(A, (i, j, x, k) -> i + k >= j, k, trim) end function triu!(A::SparseMatrixCSC, k::Integer = 0, trim::Bool = true) if k > A.n-1 || k < 1-A.m throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($(A.m),$(A.n))")) end - fkeep!(A, TriuFunc(), k, trim) + fkeep!(A, (i, j, x, k) -> j >= i + k, k, trim) end -immutable DroptolFunc <: Base.Func{4} end -(::DroptolFunc){Tv,Ti}(i::Ti, j::Ti, x::Tv, tol::Real) = abs(x) > tol -droptol!(A::SparseMatrixCSC, tol, trim::Bool = true) = fkeep!(A, DroptolFunc(), tol, trim) +droptol!(A::SparseMatrixCSC, tol, trim::Bool = true) = fkeep!(A, (i, j, x, tol) -> abs(x) > tol, tol, trim) -immutable DropzerosFunc <: Base.Func{4} end -(::DropzerosFunc){Tv,Ti}(i::Ti, j::Ti, x::Tv, other) = x != 0 -dropzeros!(A::SparseMatrixCSC, trim::Bool = true) = fkeep!(A, DropzerosFunc(), Void, trim) +dropzeros!(A::SparseMatrixCSC, trim::Bool = true) = fkeep!(A, (i, j, x, other) -> x != 0, Void, trim) dropzeros(A::SparseMatrixCSC, trim::Bool = true) = dropzeros!(copy(A), trim) @@ -929,7 +921,7 @@ function sprand{T}(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloa N == 1 && return rand(r) <= density ? sparse(rfn(r,1)) : spzeros(T,1,1) I,J = sprand_IJ(r, m, n, density) - sparse_IJ_sorted!(I, J, rfn(r,length(I)), m, n, AddFun()) # it will never need to combine + sparse_IJ_sorted!(I, J, rfn(r,length(I)), m, n, +) # it will never need to combine end function sprand{T}(m::Integer, n::Integer, density::AbstractFloat, @@ -939,7 +931,7 @@ function sprand{T}(m::Integer, n::Integer, density::AbstractFloat, N == 1 && return rand() <= density ? sparse(rfn(1)) : spzeros(T,1,1) I,J = sprand_IJ(GLOBAL_RNG, m, n, density) - sparse_IJ_sorted!(I, J, rfn(length(I)), m, n, AddFun()) # it will never need to combine + sparse_IJ_sorted!(I, J, rfn(length(I)), m, n, +) # it will never need to combine end sprand(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density,rand,Float64) @@ -1445,7 +1437,7 @@ end # macro (.-)(A::Number, B::SparseMatrixCSC) = A .- full(B) ( -)(A::Array , B::SparseMatrixCSC) = A - full(B) -(.*)(A::AbstractArray, B::AbstractArray) = broadcast_zpreserving(MulFun(), A, B) +(.*)(A::AbstractArray, B::AbstractArray) = broadcast_zpreserving(Base.MulFun(), A, B) # TODO: Remove usage of functor (.*)(A::SparseMatrixCSC, B::Number) = SparseMatrixCSC(A.m, A.n, copy(A.colptr), copy(A.rowval), A.nzval .* B) (.*)(A::Number, B::SparseMatrixCSC) = SparseMatrixCSC(B.m, B.n, copy(B.colptr), copy(B.rowval), A .* B.nzval) @@ -1561,13 +1553,13 @@ function Base._mapreduce{T}(f, op, ::Base.LinearSlow, A::SparseMatrixCSC{T}) end end -# Specialized mapreduce for AddFun/MulFun -_mapreducezeros(f, ::Base.AddFun, T::Type, nzeros::Int, v0) = +# Specialized mapreduce for +/* +_mapreducezeros(f, ::typeof(+), T::Type, nzeros::Int, v0) = nzeros == 0 ? v0 : f(zero(T))*nzeros + v0 -_mapreducezeros(f, ::Base.MulFun, T::Type, nzeros::Int, v0) = +_mapreducezeros(f, ::typeof(*), T::Type, nzeros::Int, v0) = nzeros == 0 ? v0 : f(zero(T))^nzeros * v0 -function Base._mapreduce{T}(f, op::Base.MulFun, A::SparseMatrixCSC{T}) +function Base._mapreduce{T}(f, op::typeof(*), A::SparseMatrixCSC{T}) nzeros = length(A)-nnz(A) if nzeros == 0 # No zeros, so don't compute f(0) since it might throw @@ -1662,9 +1654,9 @@ function Base._mapreducedim!{T}(f, op, R::AbstractArray, A::SparseMatrixCSC{T}) R end -# Specialized mapreducedim for AddFun cols to avoid allocating a +# Specialized mapreducedim for + cols to avoid allocating a # temporary array when f(0) == 0 -function _mapreducecols!{Tv,Ti}(f, op::Base.AddFun, R::AbstractArray, A::SparseMatrixCSC{Tv,Ti}) +function _mapreducecols!{Tv,Ti}(f, op::typeof(+), R::AbstractArray, A::SparseMatrixCSC{Tv,Ti}) nzval = A.nzval m, n = size(A) if length(nzval) == m*n @@ -2942,11 +2934,11 @@ function blkdiag(X::SparseMatrixCSC...) end ## Structure query functions -issymmetric(A::SparseMatrixCSC) = is_hermsym(A, IdFun()) +issymmetric(A::SparseMatrixCSC) = is_hermsym(A, identity) -ishermitian(A::SparseMatrixCSC) = is_hermsym(A, ConjFun()) +ishermitian(A::SparseMatrixCSC) = is_hermsym(A, conj) -function is_hermsym(A::SparseMatrixCSC, check::Func) +function is_hermsym(A::SparseMatrixCSC, check::Function) m, n = size(A) if m != n; return false; end From ade0dedf16fb863ab4e942066a46406b785cae23 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 30 Mar 2016 17:08:52 +0200 Subject: [PATCH 2/2] remove use of functors for sparse vectors --- base/sparse.jl | 1 - base/sparse/sparsevector.jl | 85 +++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 48 deletions(-) diff --git a/base/sparse.jl b/base/sparse.jl index 963abc1a946f3..e6cb9e057c223 100644 --- a/base/sparse.jl +++ b/base/sparse.jl @@ -2,7 +2,6 @@ module SparseArrays -using Base: Func, AddFun, OrFun, ConjFun, IdFun using Base.Sort: Forward using Base.LinAlg: AbstractTriangular, PosDefException diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 72b675dae8777..b2e77f20c98dd 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -2,16 +2,7 @@ ### Common definitions -import Base: Func, AddFun, MulFun, MaxFun, MinFun, SubFun, sort - -immutable ComplexFun <: Func{2} end -(::ComplexFun)(x::Real, y::Real) = complex(x, y) - -immutable DotFun <: Func{2} end -(::DotFun)(x::Number, y::Number) = conj(x) * y - -typealias UnaryOp Union{Function, Func{1}} -typealias BinaryOp Union{Function, Func{2}} +import Base: sort ### The SparseVector @@ -64,7 +55,7 @@ function _sparsevector!{Ti<:Integer}(I::Vector{Ti}, V::Vector, len::Integer) SparseVector(len, I, V) end -function _sparsevector!{Tv,Ti<:Integer}(I::Vector{Ti}, V::Vector{Tv}, len::Integer, combine::BinaryOp) +function _sparsevector!{Tv,Ti<:Integer}(I::Vector{Ti}, V::Vector{Tv}, len::Integer, combine::Function) if !isempty(I) p = sortperm(I) permute!(I, p) @@ -125,7 +116,7 @@ Duplicates are combined using the `combine` function, which defaults to `+` if no `combine` argument is provided, unless the elements of `V` are Booleans in which case `combine` defaults to `|`. """ -function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, combine::BinaryOp) +function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, combine::Function) length(I) == length(V) || throw(ArgumentError("index and value vectors must be the same length")) len = 0 @@ -138,7 +129,7 @@ function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, _sparsevector!(collect(Ti, I), collect(Tv, V), len, combine) end -function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, len::Integer, combine::BinaryOp) +function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, len::Integer, combine::Function) length(I) == length(V) || throw(ArgumentError("index and value vectors must be the same length")) maxi = convert(Ti, len) @@ -149,23 +140,23 @@ function sparsevec{Tv,Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector{Tv}, end sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, V::Union{Number, AbstractVector}) = - sparsevec(I, V, AddFun()) + sparsevec(I, V, +) sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, V::Union{Number, AbstractVector}, len::Integer) = - sparsevec(I, V, len, AddFun()) + sparsevec(I, V, len, +) sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, V::Union{Bool, AbstractVector{Bool}}) = - sparsevec(I, V, OrFun()) + sparsevec(I, V, |) sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, V::Union{Bool, AbstractVector{Bool}}, len::Integer) = - sparsevec(I, V, len, OrFun()) + sparsevec(I, V, len, |) -sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, v::Number, combine::BinaryOp) = +sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, v::Number, combine::Function) = sparsevec(I, fill(v, length(I)), combine) -sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, v::Number, len::Integer, combine::BinaryOp) = +sparsevec{Ti<:Integer}(I::AbstractVector{Ti}, v::Number, len::Integer, combine::Function) = sparsevec(I, fill(v, length(I)), len, combine) @@ -887,7 +878,7 @@ end # 1: f(nz, nz) -> z/nz, f(z, nz) -> nz, f(nz, z) -> nz # 2: f(nz, nz) -> z/nz, f(z, nz) -> z/nz, f(nz, z) -> z/nz -function _binarymap{Tx,Ty}(f::BinaryOp, +function _binarymap{Tx,Ty}(f::Function, x::AbstractSparseVector{Tx}, y::AbstractSparseVector{Ty}, mode::Int) @@ -925,7 +916,7 @@ function _binarymap{Tx,Ty}(f::BinaryOp, return SparseVector(n, rind, rval) end -function _binarymap_mode_0!(f::BinaryOp, mx::Int, my::Int, +function _binarymap_mode_0!(f::Function, mx::Int, my::Int, xnzind, xnzval, ynzind, ynzval, rind, rval) # f(nz, nz) -> nz, f(z, nz) -> z, f(nz, z) -> z ir = 0; ix = 1; iy = 1 @@ -945,7 +936,7 @@ function _binarymap_mode_0!(f::BinaryOp, mx::Int, my::Int, return ir end -function _binarymap_mode_1!{Tx,Ty}(f::BinaryOp, mx::Int, my::Int, +function _binarymap_mode_1!{Tx,Ty}(f::Function, mx::Int, my::Int, xnzind, xnzval::AbstractVector{Tx}, ynzind, ynzval::AbstractVector{Ty}, rind, rval) @@ -983,7 +974,7 @@ function _binarymap_mode_1!{Tx,Ty}(f::BinaryOp, mx::Int, my::Int, return ir end -function _binarymap_mode_2!{Tx,Ty}(f::BinaryOp, mx::Int, my::Int, +function _binarymap_mode_2!{Tx,Ty}(f::Function, mx::Int, my::Int, xnzind, xnzval::AbstractVector{Tx}, ynzind, ynzval::AbstractVector{Ty}, rind, rval) @@ -1029,7 +1020,7 @@ function _binarymap_mode_2!{Tx,Ty}(f::BinaryOp, mx::Int, my::Int, return ir end -function _binarymap{Tx,Ty}(f::BinaryOp, +function _binarymap{Tx,Ty}(f::Function, x::AbstractVector{Tx}, y::AbstractSparseVector{Ty}, mode::Int) @@ -1072,7 +1063,7 @@ function _binarymap{Tx,Ty}(f::BinaryOp, return dst end -function _binarymap{Tx,Ty}(f::BinaryOp, +function _binarymap{Tx,Ty}(f::Function, x::AbstractSparseVector{Tx}, y::AbstractVector{Ty}, mode::Int) @@ -1118,13 +1109,13 @@ end ### Binary arithmetics: +, -, * -for (vop, fun, mode) in [(:_vadd, :AddFun, 1), - (:_vsub, :SubFun, 1), - (:_vmul, :MulFun, 0)] +for (vop, fun, mode) in [(:_vadd, :+, 1), + (:_vsub, :-, 1), + (:_vmul, :*, 0)] @eval begin - $(vop)(x::AbstractSparseVector, y::AbstractSparseVector) = _binarymap($(fun)(), x, y, $mode) - $(vop)(x::StridedVector, y::AbstractSparseVector) = _binarymap($(fun)(), x, y, $mode) - $(vop)(x::AbstractSparseVector, y::StridedVector) = _binarymap($(fun)(), x, y, $mode) + $(vop)(x::AbstractSparseVector, y::AbstractSparseVector) = _binarymap($(fun), x, y, $mode) + $(vop)(x::StridedVector, y::AbstractSparseVector) = _binarymap($(fun), x, y, $mode) + $(vop)(x::AbstractSparseVector, y::StridedVector) = _binarymap($(fun), x, y, $mode) end end @@ -1146,16 +1137,16 @@ end # definition of other binary functions -for (op, fun, TF, mode) in [(:max, :MaxFun, :Real, 2), - (:min, :MinFun, :Real, 2), - (:complex, :ComplexFun, :Real, 1)] +for (op, TF, mode) in [(:max, :Real, 2), + (:min, :Real, 2), + (:complex, :Real, 1)] @eval begin $(op){Tx<:$(TF),Ty<:$(TF)}(x::AbstractSparseVector{Tx}, y::AbstractSparseVector{Ty}) = - _binarymap($(fun)(), x, y, $mode) + _binarymap($(op), x, y, $mode) $(op){Tx<:$(TF),Ty<:$(TF)}(x::StridedVector{Tx}, y::AbstractSparseVector{Ty}) = - _binarymap($(fun)(), x, y, $mode) + _binarymap($(op), x, y, $mode) $(op){Tx<:$(TF),Ty<:$(TF)}(x::AbstractSparseVector{Tx}, y::StridedVector{Ty}) = - _binarymap($(fun)(), x, y, $mode) + _binarymap($(op), x, y, $mode) end end @@ -1192,8 +1183,8 @@ vecnorm(x::AbstractSparseVector, p::Real=2) = vecnorm(nonzeros(x), p) # Transpose # (The only sparse matrix structure in base is CSC, so a one-row sparse matrix is worse than dense) -transpose(x::SparseVector) = _ct(IdFun(), x) -ctranspose(x::SparseVector) = _ct(ConjFun(), x) +transpose(x::SparseVector) = _ct(identity, x) +ctranspose(x::SparseVector) = _ct(conj, x) function _ct{T}(f, x::SparseVector{T}) isempty(x) && return Array(T, 1, 0) A = zeros(T, 1, length(x)) @@ -1277,7 +1268,7 @@ function dot{Tx<:Number,Ty<:Number}(x::AbstractSparseVector{Tx}, y::AbstractVect return s end -function _spdot(f::BinaryOp, +function _spdot(f::Function, xj::Int, xj_last::Int, xnzind, xnzval, yj::Int, yj_last::Int, ynzind, ynzval) # dot product between ranges of non-zeros, @@ -1308,7 +1299,7 @@ function dot{Tx<:Number,Ty<:Number}(x::AbstractSparseVector{Tx}, y::AbstractSpar xnzval = nonzeros(x) ynzval = nonzeros(y) - _spdot(DotFun(), + _spdot(dot, 1, length(xnzind), xnzind, xnzval, 1, length(ynzind), ynzind, ynzval) end @@ -1459,15 +1450,15 @@ At_mul_B!{Tx,Ty}(y::StridedVector{Ty}, A::SparseMatrixCSC, x::AbstractSparseVect At_mul_B!(one(Tx), A, x, zero(Ty), y) At_mul_B!{Tx,Ty}(α::Number, A::SparseMatrixCSC, x::AbstractSparseVector{Tx}, β::Number, y::StridedVector{Ty}) = - _At_or_Ac_mul_B!(MulFun(), α, A, x, β, y) + _At_or_Ac_mul_B!(*, α, A, x, β, y) Ac_mul_B!{Tx,Ty}(y::StridedVector{Ty}, A::SparseMatrixCSC, x::AbstractSparseVector{Tx}) = Ac_mul_B!(one(Tx), A, x, zero(Ty), y) Ac_mul_B!{Tx,Ty}(α::Number, A::SparseMatrixCSC, x::AbstractSparseVector{Tx}, β::Number, y::StridedVector{Ty}) = - _At_or_Ac_mul_B!(DotFun(), α, A, x, β, y) + _At_or_Ac_mul_B!(dot, α, A, x, β, y) -function _At_or_Ac_mul_B!{Tx,Ty}(tfun::BinaryOp, +function _At_or_Ac_mul_B!{Tx,Ty}(tfun::Function, α::Number, A::SparseMatrixCSC, x::AbstractSparseVector{Tx}, β::Number, y::StridedVector{Ty}) m, n = size(A) @@ -1504,12 +1495,12 @@ function *(A::SparseMatrixCSC, x::AbstractSparseVector) end At_mul_B(A::SparseMatrixCSC, x::AbstractSparseVector) = - _At_or_Ac_mul_B(MulFun(), A, x) + _At_or_Ac_mul_B(*, A, x) Ac_mul_B(A::SparseMatrixCSC, x::AbstractSparseVector) = - _At_or_Ac_mul_B(DotFun(), A, x) + _At_or_Ac_mul_B(dot, A, x) -function _At_or_Ac_mul_B{TvA,TiA,TvX,TiX}(tfun::BinaryOp, A::SparseMatrixCSC{TvA,TiA}, x::AbstractSparseVector{TvX,TiX}) +function _At_or_Ac_mul_B{TvA,TiA,TvX,TiX}(tfun::Function, A::SparseMatrixCSC{TvA,TiA}, x::AbstractSparseVector{TvX,TiX}) m, n = size(A) length(x) == m || throw(DimensionMismatch()) Tv = promote_type(TvA, TvX)