diff --git a/NEWS.md b/NEWS.md index 1166d6d1218eb..f1bc83143eba3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -48,6 +48,7 @@ Standard library changes #### SparseArrays +* Products involving sparse arrays now allow more general sparse `eltype`s, such as `StaticArrays` ([#33205]) #### Dates diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index 2576a0752086e..3c1a2c1d1a11c 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -3,31 +3,6 @@ import LinearAlgebra: checksquare using Random: rand! -## sparse matrix multiplication - -*(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} = - *(sppromote(A, B)...) -*(A::AbstractSparseMatrixCSC{TvA,TiA}, transB::Transpose{<:Any,<:AbstractSparseMatrixCSC{TvB,TiB}}) where {TvA,TiA,TvB,TiB} = - (B = transB.parent; (pA, pB) = sppromote(A, B); *(pA, transpose(pB))) -*(A::AbstractSparseMatrixCSC{TvA,TiA}, adjB::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TvB,TiB}}) where {TvA,TiA,TvB,TiB} = - (B = adjB.parent; (pA, pB) = sppromote(A, B); *(pA, adjoint(pB))) -*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} = - (A = transA.parent; (pA, pB) = sppromote(A, B); *(transpose(pA), pB)) -*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} = - (A = adjA.parent; (pA, pB) = sppromote(A, B); *(adjoint(pA), pB)) -*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}, transB::Transpose{<:Any,<:AbstractSparseMatrixCSC{TvB,TiB}}) where {TvA,TiA,TvB,TiB} = - (A = transA.parent; B = transB.parent; (pA, pB) = sppromote(A, B); *(transpose(pA), transpose(pB))) -*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}, adjB::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TvB,TiB}}) where {TvA,TiA,TvB,TiB} = - (A = adjA.parent; B = adjB.parent; (pA, pB) = sppromote(A, B); *(adjoint(pA), adjoint(pB))) - -function sppromote(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} - Tv = promote_type(TvA, TvB) - Ti = promote_type(TiA, TiB) - A = convert(SparseMatrixCSC{Tv,Ti}, A) - B = convert(SparseMatrixCSC{Tv,Ti}, B) - A, B -end - # In matrix-vector multiplication, the correct orientation of the vector is assumed. const AdjOrTransStridedMatrix{T} = Union{StridedMatrix{T},Adjoint{<:Any,<:StridedMatrix{T}},Transpose{<:Any,<:StridedMatrix{T}}} @@ -50,10 +25,10 @@ function mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVe end C end -*(A::AbstractSparseMatrixCSC{TA,S}, x::StridedVector{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(A, 1)), A, x, one(T), zero(T))) -*(A::AbstractSparseMatrixCSC{TA,S}, B::StridedMatrix{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(A, 1), size(B, 2))), A, B, one(T), zero(T))) +*(A::AbstractSparseMatrixCSC{TA}, x::StridedVector{Tx}) where {TA,Tx} = + (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(A, 1)), A, x, true, false)) +*(A::AbstractSparseMatrixCSC{TA}, B::StridedMatrix{Tx}) where {TA,Tx} = + (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(A, 1), size(B, 2))), A, B, true, false)) function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}, α::Number, β::Number) A = adjA.parent @@ -76,10 +51,10 @@ function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC} end C end -*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TA,S}}, x::StridedVector{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(adjA, 1)), adjA, x, one(T), zero(T))) -*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TA,S}}, B::AdjOrTransStridedMatrix{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(adjA, 1), size(B, 2))), adjA, B, one(T), zero(T))) +*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, x::StridedVector{Tx}) where {Tx} = + (T = promote_op(matprod, eltype(adjA), Tx); mul!(similar(x, T, size(adjA, 1)), adjA, x, true, false)) +*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedMatrix) = + (T = promote_op(matprod, eltype(adjA), eltype(B)); mul!(similar(B, T, (size(adjA, 1), size(B, 2))), adjA, B, true, false)) function mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}, α::Number, β::Number) A = transA.parent @@ -102,19 +77,19 @@ function mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrix end C end -*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC{TA,S}}, x::StridedVector{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(transA, 1)), transA, x, one(T), zero(T))) -*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC{TA,S}}, B::AdjOrTransStridedMatrix{Tx}) where {TA,S,Tx} = - (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(transA, 1), size(B, 2))), transA, B, one(T), zero(T))) +*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, x::StridedVector{Tx}) where {Tx} = + (T = promote_op(matprod, eltype(transA), Tx); mul!(similar(x, T, size(transA, 1)), transA, x, true, false)) +*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedMatrix) = + (T = promote_op(matprod, eltype(transA), eltype(B)); mul!(similar(B, T, (size(transA, 1), size(B, 2))), transA, B, true, false)) # For compatibility with dense multiplication API. Should be deleted when dense multiplication # API is updated to follow BLAS API. mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVector,AdjOrTransStridedMatrix}) = - mul!(C, A, B, one(eltype(B)), zero(eltype(C))) + mul!(C, A, B, true, false) mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}) = - mul!(C, adjA, B, one(eltype(B)), zero(eltype(C))) + mul!(C, adjA, B, true, false) mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}) = - mul!(C, transA, B, one(eltype(B)), zero(eltype(C))) + mul!(C, transA, B, true, false) function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, A::AbstractSparseMatrixCSC, α::Number, β::Number) mX, nX = size(X) @@ -131,8 +106,8 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, A::AbstractSparseM end C end -*(X::AdjOrTransStridedMatrix{TX}, A::AbstractSparseMatrixCSC{TvA,TiA}) where {TX,TvA,TiA} = - (T = promote_op(matprod, TX, TvA); mul!(similar(X, T, (size(X, 1), size(A, 2))), X, A, one(T), zero(T))) +*(X::AdjOrTransStridedMatrix, A::AbstractSparseMatrixCSC{TvA}) where {TvA} = + (T = promote_op(matprod, eltype(X), TvA); mul!(similar(X, T, (size(X, 1), size(A, 2))), X, A, true, false)) function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) A = adjA.parent @@ -150,8 +125,8 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, adjA::Adjoint{<:An end C end -*(X::AdjOrTransStridedMatrix{TX}, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}) where {TX,TvA,TiA} = - (T = promote_op(matprod, TX, TvA); mul!(similar(X, T, (size(X, 1), size(adjA, 2))), X, adjA, one(T), zero(T))) +*(X::AdjOrTransStridedMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = + (T = promote_op(matprod, eltype(X), eltype(adjA)); mul!(similar(X, T, (size(X, 1), size(adjA, 2))), X, adjA, true, false)) function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) A = transA.parent @@ -169,8 +144,8 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, transA::Transpose{ end C end -*(X::AdjOrTransStridedMatrix{TX}, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC{TvA,TiA}}) where {TX,TvA,TiA} = - (T = promote_op(matprod, TX, TvA); mul!(similar(X, T, (size(X, 1), size(transA, 2))), X, transA, one(T), zero(T))) +*(X::AdjOrTransStridedMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = + (T = promote_op(matprod, eltype(X), eltype(transA)); mul!(similar(X, T, (size(X, 1), size(transA, 2))), X, transA, true, false)) function (*)(D::Diagonal, A::AbstractSparseMatrixCSC) T = Base.promote_op(*, eltype(D), eltype(A)) @@ -184,13 +159,13 @@ end # Sparse matrix multiplication as described in [Gustavson, 1978]: # http://dl.acm.org/citation.cfm?id=355796 -*(A::AbstractSparseMatrixCSC{Tv,Ti}, B::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(A,B) -*(A::AbstractSparseMatrixCSC{Tv,Ti}, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = spmatmul(A, copy(B)) -*(A::AbstractSparseMatrixCSC{Tv,Ti}, B::Transpose{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = spmatmul(A, copy(B)) -*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}, B::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(copy(A), B) -*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}, B::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(copy(A), B) -*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = spmatmul(copy(A), copy(B)) -*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}, B::Transpose{<:Any,<:AbstractSparseMatrixCSC{Tv,Ti}}) where {Tv,Ti} = spmatmul(copy(A), copy(B)) +*(A::AbstractSparseMatrixCSC, B::AbstractSparseMatrixCSC) = spmatmul(A,B) +*(A::AbstractSparseMatrixCSC, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) +*(A::AbstractSparseMatrixCSC, B::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(A, copy(B)) +*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::AbstractSparseMatrixCSC) = spmatmul(copy(A), B) +*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::AbstractSparseMatrixCSC) = spmatmul(copy(A), B) +*(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(copy(A), copy(B)) +*(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = spmatmul(copy(A), copy(B)) # Gustavsen's matrix multiplication algorithm revisited. # The result rowval vector is already sorted by construction. @@ -200,7 +175,9 @@ end # done by a quicksort of the row indices or by a full scan of the dense result vector. # The last is faster, if more than ≈ 1/32 of the result column is nonzero. # TODO: extend to SparseMatrixCSCUnion to allow for SubArrays (view(X, :, r)). -function spmatmul(A::AbstractSparseMatrixCSC{Tv,Ti}, B::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} +function spmatmul(A::AbstractSparseMatrixCSC{TvA,TiA}, B::AbstractSparseMatrixCSC{TvB,TiB}) where {TvA,TiA,TvB,TiB} + Tv = promote_op(matprod, TvA, TvB) + Ti = promote_type(TiA, TiB) mA, nA = size(A) nB = size(B, 2) nA == size(B, 1) || throw(DimensionMismatch()) @@ -371,8 +348,8 @@ end ## triangular sparse handling -possible_adjoint(adj::Bool, a::Real ) = a -possible_adjoint(adj::Bool, a ) = adj ? adjoint(a) : a +possible_adjoint(adj::Bool, a::Real) = a +possible_adjoint(adj::Bool, a) = adj ? adjoint(a) : a const UnitDiagonalTriangular = Union{UnitUpperTriangular,UnitLowerTriangular} @@ -776,7 +753,6 @@ mul!(y::StridedVecOrMat, A::SparseMatrixCSCSymmHerm, x::StridedVecOrMat) = mul!( # C .= α * A * B + β * C function mul!(C::StridedVecOrMat{T}, sA::SparseMatrixCSCSymmHerm, B::StridedVecOrMat, α::Number, β::Number) where T - fuplo = sA.uplo == 'U' ? nzrangeup : nzrangelo _mul!(fuplo, C, sA, B, T(α), T(β)) end diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 6b01e4e51a1ff..8334b9e8d70df 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -856,16 +856,17 @@ sparse(I,J,v::Number,m,n,combine::Function) = sparse(I, J, fill(v,length(I)), In ## Transposition and permutation methods """ - halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{Tv,Ti}, - q::AbstractVector{<:Integer}, f::Function = identity) where {Tv,Ti} + halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{TvA,Ti}, + q::AbstractVector{<:Integer}, f::Function = identity) where {Tv,TvA,Ti} Column-permute and transpose `A`, simultaneously applying `f` to each entry of `A`, storing the result `(f(A)Q)^T` (`map(f, transpose(A[:,q]))`) in `X`. -`X`'s dimensions must match those of `transpose(A)` (`size(X, 1) == size(A, 2)` and `size(X, 2) == size(A, 1)`), and `X` -must have enough storage to accommodate all allocated entries in `A` (`length(rowvals(X)) >= nnz(A)` -and `length(nonzeros(X)) >= nnz(A)`). Column-permutation `q`'s length must match `A`'s column -count (`length(q) == size(A, 2)`). +Element type `Tv` of `X` must match `f(::TvA)`, where `TvA` is the element type of `A`. +`X`'s dimensions must match those of `transpose(A)` (`size(X, 1) == size(A, 2)` and +`size(X, 2) == size(A, 1)`), and `X` must have enough storage to accommodate all allocated +entries in `A` (`length(rowvals(X)) >= nnz(A)` and `length(nonzeros(X)) >= nnz(A)`). +Column-permutation `q`'s length must match `A`'s column count (`length(q) == size(A, 2)`). This method is the parent of several methods performing transposition and permutation operations on [`SparseMatrixCSC`](@ref)s. As this method performs no argument checking, @@ -876,8 +877,8 @@ algorithms for sparse matrices: multiplication and permuted transposition," ACM 250-269 (1978). The algorithm runs in `O(size(A, 1), size(A, 2), nnz(A))` time and requires no space beyond that passed in. """ -function halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{Tv,Ti}, - q::AbstractVector{<:Integer}, f::Function = identity) where {Tv,Ti} +function halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{TvA,Ti}, + q::AbstractVector{<:Integer}, f::Function = identity) where {Tv,TvA,Ti} _computecolptrs_halfperm!(X, A) _distributevals_halfperm!(X, A, q, f) return X @@ -886,7 +887,7 @@ end Helper method for `halfperm!`. Computes `transpose(A[:,q])`'s column pointers, storing them shifted one position forward in `getcolptr(X)`; `_distributevals_halfperm!` fixes this shift. """ -function _computecolptrs_halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} +function _computecolptrs_halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{TvA,Ti}) where {Tv,TvA,Ti} # Compute `transpose(A[:,q])`'s column counts. Store shifted forward one position in getcolptr(X). fill!(getcolptr(X), 0) @inbounds for k in 1:nnz(A) @@ -908,7 +909,7 @@ distributing `rowvals(A)` and `f`-transformed `nonzeros(A)` into `rowvals(X)` an respectively. Simultaneously fixes the one-position-forward shift in `getcolptr(X)`. """ @noinline function _distributevals_halfperm!(X::AbstractSparseMatrixCSC{Tv,Ti}, - A::AbstractSparseMatrixCSC{Tv,Ti}, q::AbstractVector{<:Integer}, f::Function) where {Tv,Ti} + A::AbstractSparseMatrixCSC{TvA,Ti}, q::AbstractVector{<:Integer}, f::Function) where {Tv,TvA,Ti} @inbounds for Xi in 1:size(A, 2) Aj = q[Xi] for Ak in nzrange(A, Aj) @@ -944,7 +945,8 @@ end transpose!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, identity) adjoint!(X::AbstractSparseMatrixCSC{Tv,Ti}, A::AbstractSparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, conj) -function ftranspose(A::AbstractSparseMatrixCSC{Tv,Ti}, f::Function) where {Tv,Ti} +# manually specifying eltype allows to avoid calling return_type of f on TvA +function ftranspose(A::AbstractSparseMatrixCSC{TvA,Ti}, f::Function, eltype::Type{Tv} = TvA) where {Tv,TvA,Ti} X = SparseMatrixCSC(size(A, 2), size(A, 1), ones(Ti, size(A, 1)+1), Vector{Ti}(undef, nnz(A)), @@ -953,8 +955,10 @@ function ftranspose(A::AbstractSparseMatrixCSC{Tv,Ti}, f::Function) where {Tv,Ti end adjoint(A::AbstractSparseMatrixCSC) = Adjoint(A) transpose(A::AbstractSparseMatrixCSC) = Transpose(A) -Base.copy(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = ftranspose(A.parent, x -> copy(adjoint(x))) -Base.copy(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = ftranspose(A.parent, x -> copy(transpose(x))) +Base.copy(A::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = + ftranspose(A.parent, x -> adjoint(copy(x)), eltype(A)) +Base.copy(A::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = + ftranspose(A.parent, x -> transpose(copy(x)), eltype(A)) function Base.permutedims(A::AbstractSparseMatrixCSC, (a,b)) (a, b) == (2, 1) && return ftranspose(A, identity) (a, b) == (1, 2) && return copy(A) diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index b4edbad6ce242..d182e37baed76 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -1438,7 +1438,7 @@ 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, - s = zero(eltype(xnzval)) * zero(eltype(ynzval)) + s = f(zero(eltype(xnzval)), zero(eltype(ynzval))) @inbounds while xj <= xj_last && yj <= yj_last ix = xnzind[xj] iy = ynzind[yj] @@ -1493,13 +1493,13 @@ function (*)(A::StridedMatrix{Ta}, x::AbstractSparseVector{Tx}) where {Ta,Tx} require_one_based_indexing(A, x) m, n = size(A) length(x) == n || throw(DimensionMismatch()) - Ty = promote_op(matprod, Ta, Tx) + Ty = promote_op(matprod, eltype(A), eltype(x)) y = Vector{Ty}(undef, m) mul!(y, A, x) end mul!(y::AbstractVector{Ty}, A::StridedMatrix, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - mul!(y, A, x, one(Tx), zero(Ty)) + mul!(y, A, x, true, false) function mul!(y::AbstractVector, A::StridedMatrix, x::AbstractSparseVector, α::Number, β::Number) require_one_based_indexing(y, A, x) @@ -1532,13 +1532,13 @@ function *(transA::Transpose{<:Any,<:StridedMatrix{Ta}}, x::AbstractSparseVector require_one_based_indexing(transA, x) m, n = size(transA) length(x) == n || throw(DimensionMismatch()) - Ty = promote_op(matprod, Ta, Tx) + Ty = promote_op(matprod, eltype(transA), eltype(x)) y = Vector{Ty}(undef, m) mul!(y, transA, x) end mul!(y::AbstractVector{Ty}, transA::Transpose{<:Any,<:StridedMatrix}, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - mul!(y, transA, x, one(Tx), zero(Ty)) + mul!(y, transA, x, true, false) function mul!(y::AbstractVector, transA::Transpose{<:Any,<:StridedMatrix}, x::AbstractSparseVector, α::Number, β::Number) require_one_based_indexing(y, transA, x) @@ -1573,13 +1573,13 @@ function *(adjA::Adjoint{<:Any,<:StridedMatrix{Ta}}, x::AbstractSparseVector{Tx} require_one_based_indexing(adjA, x) m, n = size(adjA) length(x) == n || throw(DimensionMismatch()) - Ty = promote_op(matprod, Ta, Tx) + Ty = promote_op(matprod, eltype(adjA), eltype(x)) y = Vector{Ty}(undef, m) mul!(y, adjA, x) end mul!(y::AbstractVector{Ty}, adjA::Adjoint{<:Any,<:StridedMatrix}, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - mul!(y, adjA, x, one(Tx), zero(Ty)) + mul!(y, adjA, x, true, false) function mul!(y::AbstractVector, adjA::Adjoint{<:Any,<:StridedMatrix}, x::AbstractSparseVector, α::Number, β::Number) require_one_based_indexing(y, adjA, x) @@ -1640,7 +1640,7 @@ end # * and mul! mul!(y::AbstractVector{Ty}, A::AbstractSparseMatrixCSC, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - mul!(y, A, x, one(Tx), zero(Ty)) + mul!(y, A, x, true, false) function mul!(y::AbstractVector, A::AbstractSparseMatrixCSC, x::AbstractSparseVector, α::Number, β::Number) require_one_based_indexing(y, A, x) @@ -1674,16 +1674,16 @@ end # * and *(Tranpose(A), B) mul!(y::AbstractVector{Ty}, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - (A = transA.parent; mul!(y, transpose(A), x, one(Tx), zero(Ty))) + (A = transA.parent; mul!(y, transpose(A), x, true, false)) mul!(y::AbstractVector, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector, α::Number, β::Number) = - (A = transA.parent; _At_or_Ac_mul_B!(*, y, A, x, α, β)) + (A = transA.parent; _At_or_Ac_mul_B!((a,b) -> transpose(a) * b, y, A, x, α, β)) mul!(y::AbstractVector{Ty}, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector{Tx}) where {Tx,Ty} = - (A = adjA.parent; mul!(y, adjoint(A), x, one(Tx), zero(Ty))) + (A = adjA.parent; mul!(y, adjoint(A), x, true, false)) mul!(y::AbstractVector, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector, α::Number, β::Number) = - (A = adjA.parent; _At_or_Ac_mul_B!(dot, y, A, x, α, β)) + (A = adjA.parent; _At_or_Ac_mul_B!((a,b) -> adjoint(a) * b, y, A, x, α, β)) function _At_or_Ac_mul_B!(tfun::Function, y::AbstractVector, A::AbstractSparseMatrixCSC, x::AbstractSparseVector, @@ -1724,16 +1724,16 @@ function *(A::AbstractSparseMatrixCSC, x::AbstractSparseVector) end *(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector) = - (A = transA.parent; _At_or_Ac_mul_B(*, A, x)) + (A = transA.parent; _At_or_Ac_mul_B((a,b) -> transpose(a) * b, A, x, promote_op(matprod, eltype(transA), eltype(x)))) *(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, x::AbstractSparseVector) = - (A = adjA.parent; _At_or_Ac_mul_B(dot, A, x)) + (A = adjA.parent; _At_or_Ac_mul_B((a,b) -> adjoint(a) * b, A, x, promote_op(matprod, eltype(adjA), eltype(x)))) -function _At_or_Ac_mul_B(tfun::Function, A::AbstractSparseMatrixCSC{TvA,TiA}, x::AbstractSparseVector{TvX,TiX}) where {TvA,TiA,TvX,TiX} +function _At_or_Ac_mul_B(tfun::Function, A::AbstractSparseMatrixCSC{TvA,TiA}, x::AbstractSparseVector{TvX,TiX}, + Tv = promote_op(matprod, TvA, TvX)) where {TvA,TiA,TvX,TiX} require_one_based_indexing(A, x) m, n = size(A) length(x) == m || throw(DimensionMismatch()) - Tv = promote_op(matprod, TvA, TvX) Ti = promote_type(TiA, TiX) xnzind = nonzeroinds(x) diff --git a/stdlib/SparseArrays/test/simplesmatrix.jl b/stdlib/SparseArrays/test/simplesmatrix.jl new file mode 100644 index 0000000000000..04c971246ea50 --- /dev/null +++ b/stdlib/SparseArrays/test/simplesmatrix.jl @@ -0,0 +1,52 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct SimpleSMatrix{N,M,T} <: AbstractMatrix{T} + m::Matrix{T} +end + +SimpleSMatrix{N,M}(m::AbstractMatrix{T}) where {N,M,T} = + size(m) == (N, M) ? SimpleSMatrix{N,M,T}(m) : throw(error("Wrong matrix size")) + +Base.:*(a::SimpleSMatrix{N,O}, b::SimpleSMatrix{O,M}) where {N,O,M} = + SimpleSMatrix{N,M}(a.m * b.m) + +Base.:*(a::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{O,N}}, b::SimpleSMatrix{O,M}) where {N,O,M} = + SimpleSMatrix{N,M}(adjoint(a.parent.m) * b.m) + +Base.:*(a::SimpleSMatrix{N,O}, b::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{M,O}}) where {N,O,M} = + SimpleSMatrix{N,M}(a.m * adjoint(b.parent.m)) + +Base.:+(a::SimpleSMatrix{N,M}, b::SimpleSMatrix{N,M}) where {N,M} = + SimpleSMatrix{N,M}(a.m + b.m) + +Base.:+(a::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{M,N}}, b::SimpleSMatrix{N,M}) where {N,M} = + SimpleSMatrix{N,M}(adjoint(a.parent.m) + b.m) + +Base.:+(a::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix}, b::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix}) = + (a' + b')' + +Base.:+(a::SimpleSMatrix{N,M}, b::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{M,N}}) where {N,M} = + SimpleSMatrix{N,M}(a.m + adjoint(b.parent.m)) + +Base.:-(a::SimpleSMatrix{N,M}, b::SimpleSMatrix{N,M}) where {N,M} = + SimpleSMatrix{N,M}(a.m - b.m) + +Base.:-(a::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{M,N}}, b::SimpleSMatrix{N,M}) where {N,M} = + SimpleSMatrix{N,M}(adjoint(a.parent.m) - b.m) + +Base.:-(a::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix}, b::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix}) = + (a' - b')' + +Base.:-(a::SimpleSMatrix{N,M}, b::LinearAlgebra.Adjoint{<:Any, <:SimpleSMatrix{M,N}}) where {N,M} = + SimpleSMatrix{N,M}(a.m - adjoint(b.parent.m)) + +Base.size(a::SimpleSMatrix{N,M}) where {N,M} = (N, M) + +Base.length(a::SimpleSMatrix{N,M}) where {N,M} = N * M + +Base.zero(::Type{S}) where {N,M,T,S<:SimpleSMatrix{N,M,T}} = SimpleSMatrix{N,M}(zeros(T, N, M)) + +Base.getindex(s::SimpleSMatrix, inds...) = getindex(s.m, inds...) + +Base.convert(::Type{S}, value::Matrix) where {N,M,T,S<:SimpleSMatrix{N,M,T}} = + SimpleSMatrix{N,M}(T.(value)) diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index 88ed85bbfc4d8..1f2b450d5c3ac 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -12,6 +12,26 @@ using Test: guardseed using InteractiveUtils: @which using Dates include("forbidproperties.jl") +include("simplesmatrix.jl") + +@testset "Issue #33169" begin + m21 = sparse([1, 2], [2, 2], SimpleSMatrix{2,1}.([rand(2, 1), rand(2, 1)]), 2, 2) + m12 = sparse([1, 2], [2, 2], SimpleSMatrix{1,2}.([rand(1, 2), rand(1, 2)]), 2, 2) + m22 = sparse([1, 2], [2, 2], SimpleSMatrix{2,2}.([rand(2, 2), rand(2, 2)]), 2, 2) + m23 = sparse([1, 2], [2, 2], SimpleSMatrix{2,3}.([rand(2, 3), rand(2, 3)]), 2, 2) + v12 = sparsevec([2], SimpleSMatrix{1,2}.([rand(1, 2)])) + v21 = sparsevec([2], SimpleSMatrix{2,1}.([rand(2, 1)])) + @test m22 * m21 ≈ Matrix(m22) * Matrix(m21) + @test m22' * m21 ≈ Matrix(m22') * Matrix(m21) + @test m21' * m22 ≈ Matrix(m21') * Matrix(m22) + @test m23' * m22 * m21 ≈ Matrix(m23') * Matrix(m22) * Matrix(m21) + @test m21 * v12 ≈ Matrix(m21) * Vector(v12) + @test m12' * v12 ≈ Matrix(m12') * Vector(v12) + @test v21' * m22 ≈ Vector(v21)' * Matrix(m22) + @test v12' * m21' ≈ Vector(v12)' * Matrix(m21)' + @test v21' * v21 ≈ Vector(v21)' * Vector(v21) + @test v21' * m22 * v21 ≈ Vector(v21)' * Matrix(m22) * Vector(v21) +end @testset "issparse" begin @test issparse(sparse(fill(1,5,5))) @@ -2733,7 +2753,7 @@ end @test sparse(UInt8.(1:254), fill(UInt8(1), 254), fill(1, 254), 255, 255) !== nothing end -@testset "sppromote and sparse matmul" begin +@testset "Sparse promotion in sparse matmul" begin A = SparseMatrixCSC{Float32, Int8}(2, 2, Int8[1, 2, 3], Int8[1, 2], Float32[1., 2.]) B = SparseMatrixCSC{ComplexF32, Int32}(2, 2, Int32[1, 2, 3], Int32[1, 2], ComplexF32[1. + im, 2. - im]) @test A*transpose(B) ≈ Array(A) * transpose(Array(B))