Skip to content

Commit 42c1b95

Browse files
authored
Merge pull request #17685 from Sacha0/specialcat
Fix #17675 (concatenations involving special matrices) and #17738 (concatenations involving sparse vectors)
2 parents 7f074e9 + 1e53916 commit 42c1b95

File tree

4 files changed

+127
-35
lines changed

4 files changed

+127
-35
lines changed

base/sparse/sparsematrix.jl

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,37 +3264,6 @@ function hcat(X::SparseMatrixCSC...)
32643264
SparseMatrixCSC(m, n, colptr, rowval, nzval)
32653265
end
32663266

3267-
3268-
# Sparse/dense concatenation
3269-
3270-
function hcat(Xin::Union{Vector, Matrix, SparseMatrixCSC}...)
3271-
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
3272-
hcat(X...)
3273-
end
3274-
3275-
function vcat(Xin::Union{Vector, Matrix, SparseMatrixCSC}...)
3276-
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
3277-
vcat(X...)
3278-
end
3279-
3280-
function hvcat(rows::Tuple{Vararg{Int}}, X::Union{Vector, Matrix, SparseMatrixCSC}...)
3281-
nbr = length(rows) # number of block rows
3282-
3283-
tmp_rows = Array{SparseMatrixCSC}(nbr)
3284-
k = 0
3285-
@inbounds for i = 1 : nbr
3286-
tmp_rows[i] = hcat(X[(1 : rows[i]) + k]...)
3287-
k += rows[i]
3288-
end
3289-
vcat(tmp_rows...)
3290-
end
3291-
3292-
function cat(catdims, Xin::Union{Vector, Matrix, SparseMatrixCSC}...)
3293-
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
3294-
T = promote_eltype(Xin...)
3295-
Base.cat_t(catdims, T, X...)
3296-
end
3297-
32983267
"""
32993268
blkdiag(A...)
33003269

base/sparse/sparsevector.jl

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,13 @@ complex(x::AbstractSparseVector) =
720720

721721
### Concatenation
722722

723-
function hcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
723+
# Without the first of these methods, horizontal concatenations of SparseVectors fall
724+
# back to the horizontal concatenation method that ensures that combinations of
725+
# sparse/special/dense matrix/vector types concatenate to SparseMatrixCSCs, instead
726+
# of _absspvec_hcat below. The <:Integer qualifications are necessary for correct dispatch.
727+
hcat{Tv,Ti<:Integer}(X::SparseVector{Tv,Ti}...) = _absspvec_hcat(X...)
728+
hcat{Tv,Ti<:Integer}(X::AbstractSparseVector{Tv,Ti}...) = _absspvec_hcat(X...)
729+
function _absspvec_hcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
724730
# check sizes
725731
n = length(X)
726732
m = length(X[1])
@@ -749,7 +755,13 @@ function hcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
749755
SparseMatrixCSC{Tv,Ti}(m, n, colptr, nzrow, nzval)
750756
end
751757

752-
function vcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
758+
# Without the first of these methods, vertical concatenations of SparseVectors fall
759+
# back to the vertical concatenation method that ensures that combinations of
760+
# sparse/special/dense matrix/vector types concatenate to SparseMatrixCSCs, instead
761+
# of _absspvec_vcat below. The <:Integer qualifications are necessary for correct dispatch.
762+
vcat{Tv,Ti<:Integer}(X::SparseVector{Tv,Ti}...) = _absspvec_vcat(X...)
763+
vcat{Tv,Ti<:Integer}(X::AbstractSparseVector{Tv,Ti}...) = _absspvec_vcat(X...)
764+
function _absspvec_vcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
753765
# check sizes
754766
n = length(X)
755767
tnnz = 0
@@ -777,11 +789,49 @@ function vcat{Tv,Ti}(X::AbstractSparseVector{Tv,Ti}...)
777789
SparseVector(len, rnzind, rnzval)
778790
end
779791

780-
hcat(Xin::Union{AbstractSparseVector, SparseMatrixCSC}...) = hcat(map(SparseMatrixCSC, Xin)...)
781-
vcat(Xin::Union{AbstractSparseVector, SparseMatrixCSC}...) = vcat(map(SparseMatrixCSC, Xin)...)
782792
hcat(Xin::Union{Vector, AbstractSparseVector}...) = hcat(map(sparse, Xin)...)
783793
vcat(Xin::Union{Vector, AbstractSparseVector}...) = vcat(map(sparse, Xin)...)
784794

795+
796+
### Sparse/special/dense vector/matrix concatenation
797+
798+
# TODO: These methods should be moved to a more appropriate location, particularly some
799+
# future equivalent of base/linalg/special.jl dedicated to interactions between a broader
800+
# set of matrix types.
801+
802+
# TODO: A similar definition also exists in base/linalg/bidiag.jl. These definitions should
803+
# be consolidated in a more appropriate location, for example base/linalg/special.jl.
804+
SpecialArrays = Union{Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal}
805+
806+
function hcat(Xin::Union{Vector, Matrix, SparseVector, SparseMatrixCSC, SpecialArrays}...)
807+
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
808+
hcat(X...)
809+
end
810+
811+
function vcat(Xin::Union{Vector, Matrix, SparseVector, SparseMatrixCSC, SpecialArrays}...)
812+
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
813+
vcat(X...)
814+
end
815+
816+
function hvcat(rows::Tuple{Vararg{Int}}, X::Union{Vector, Matrix, SparseVector, SparseMatrixCSC, SpecialArrays}...)
817+
nbr = length(rows) # number of block rows
818+
819+
tmp_rows = Array{SparseMatrixCSC}(nbr)
820+
k = 0
821+
@inbounds for i = 1 : nbr
822+
tmp_rows[i] = hcat(X[(1 : rows[i]) + k]...)
823+
k += rows[i]
824+
end
825+
vcat(tmp_rows...)
826+
end
827+
828+
function cat(catdims, Xin::Union{Vector, Matrix, SparseVector, SparseMatrixCSC, SpecialArrays}...)
829+
X = SparseMatrixCSC[issparse(x) ? x : sparse(x) for x in Xin]
830+
T = promote_eltype(Xin...)
831+
Base.cat_t(catdims, T, X...)
832+
end
833+
834+
785835
### math functions
786836

787837
### Unary Map

test/linalg/special.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,41 @@ for typ in [UpperTriangular,LowerTriangular,Base.LinAlg.UnitUpperTriangular,Base
128128
@test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) full(atri) * qrb[:Q]'
129129
@test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) full(atri) * qrb[:Q]'
130130
end
131+
132+
# Test that concatenations of combinations of special and other matrix types yield sparse arrays
133+
let
134+
N = 4
135+
# Test concatenating pairwise combinations of special matrices
136+
diagmat = Diagonal(ones(N))
137+
bidiagmat = Bidiagonal(ones(N), ones(N-1), true)
138+
tridiagmat = Tridiagonal(ones(N-1), ones(N), ones(N-1))
139+
symtridiagmat = SymTridiagonal(ones(N), ones(N-1))
140+
specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat)
141+
for specialmata in specialmats, specialmatb in specialmats
142+
@test issparse(hcat(specialmata, specialmatb))
143+
@test issparse(vcat(specialmata, specialmatb))
144+
@test issparse(hvcat((1,1), specialmata, specialmatb))
145+
@test issparse(cat((1,2), specialmata, specialmatb))
146+
end
147+
# Test concatenating pairwise combinations of special matrices with sparse matrices,
148+
# dense matrices, or dense vectors
149+
densevec = ones(N)
150+
densemat = diagm(ones(N))
151+
spmat = spdiagm(ones(N))
152+
for specialmat in specialmats
153+
# --> Tests applicable only to pairs of matrices
154+
for othermat in (spmat, densemat)
155+
@test issparse(vcat(specialmat, othermat))
156+
@test issparse(vcat(othermat, specialmat))
157+
end
158+
# --> Tests applicable also to pairs including vectors
159+
for specialmat in specialmats, othermatorvec in (spmat, densemat, densevec)
160+
@test issparse(hcat(specialmat, othermatorvec))
161+
@test issparse(hcat(othermatorvec, specialmat))
162+
@test issparse(hvcat((2,), specialmat, othermatorvec))
163+
@test issparse(hvcat((2,), othermatorvec, specialmat))
164+
@test issparse(cat((1,2), specialmat, othermatorvec))
165+
@test issparse(cat((1,2), othermatorvec, specialmat))
166+
end
167+
end
168+
end

test/sparsedir/sparsevector.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,41 @@ let m = 80, n = 100
404404
@test full(V) == Vr
405405
end
406406

407+
# Test that concatenations of combinations of sparse vectors with various other
408+
# matrix/vector types yield sparse arrays
409+
let
410+
N = 4
411+
spvec = spzeros(N)
412+
spmat = spzeros(N, 1)
413+
densevec = ones(N)
414+
densemat = ones(N, 1)
415+
diagmat = Diagonal(ones(4))
416+
# Test that concatenations of pairwise combinations of sparse vectors with dense
417+
# vectors/matrices, sparse matrices, or special matrices yield sparse arrays
418+
for othervecormat in (densevec, densemat, spmat)
419+
@test issparse(vcat(spvec, othervecormat))
420+
@test issparse(vcat(othervecormat, spvec))
421+
end
422+
for othervecormat in (densevec, densemat, spmat, diagmat)
423+
@test issparse(hcat(spvec, othervecormat))
424+
@test issparse(hcat(othervecormat, spvec))
425+
@test issparse(hvcat((2,), spvec, othervecormat))
426+
@test issparse(hvcat((2,), othervecormat, spvec))
427+
@test issparse(cat((1,2), spvec, othervecormat))
428+
@test issparse(cat((1,2), othervecormat, spvec))
429+
end
430+
# The preceding tests should cover multi-way combinations of those types, but for good
431+
# measure test a few multi-way combinations involving those types
432+
@test issparse(vcat(spvec, densevec, spmat, densemat))
433+
@test issparse(vcat(densevec, spvec, densemat, spmat))
434+
@test issparse(hcat(spvec, densemat, spmat, densevec, diagmat))
435+
@test issparse(hcat(densemat, spmat, spvec, densevec, diagmat))
436+
@test issparse(hvcat((5,), diagmat, densevec, spvec, densemat, spmat))
437+
@test issparse(hvcat((5,), spvec, densemat, diagmat, densevec, spmat))
438+
@test issparse(cat((1,2), densemat, diagmat, spmat, densevec, spvec))
439+
@test issparse(cat((1,2), spvec, diagmat, densevec, spmat, densemat))
440+
end
441+
407442

408443
## sparsemat: combinations with sparse matrix
409444

0 commit comments

Comments
 (0)