From c308a8c374b34264688666f3e94232ed3743b850 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Thu, 18 Nov 2021 14:07:37 +0100 Subject: [PATCH 1/2] Make LinearAlgebra.jl independent of SparseArrays.jl --- NEWS.md | 15 +- stdlib/LinearAlgebra/Project.toml | 3 +- stdlib/LinearAlgebra/docs/src/index.md | 6 +- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 5 +- stdlib/LinearAlgebra/src/adjtrans.jl | 2 +- stdlib/LinearAlgebra/src/bidiag.jl | 11 +- stdlib/LinearAlgebra/src/diagonal.jl | 10 +- stdlib/LinearAlgebra/src/special.jl | 24 ++ stdlib/LinearAlgebra/src/svd.jl | 16 +- stdlib/LinearAlgebra/src/tridiag.jl | 12 +- stdlib/LinearAlgebra/src/uniformscaling.jl | 2 +- stdlib/LinearAlgebra/test/addmul.jl | 1 - stdlib/LinearAlgebra/test/adjtrans.jl | 10 +- stdlib/LinearAlgebra/test/ambiguous_exec.jl | 2 +- stdlib/LinearAlgebra/test/bidiag.jl | 8 +- stdlib/LinearAlgebra/test/diagonal.jl | 21 +- stdlib/LinearAlgebra/test/special.jl | 130 +++++------ stdlib/LinearAlgebra/test/symmetric.jl | 16 +- stdlib/LinearAlgebra/test/triangular.jl | 14 +- stdlib/LinearAlgebra/test/tridiag.jl | 17 +- stdlib/LinearAlgebra/test/uniformscaling.jl | 233 ++++++++++---------- stdlib/SparseArrays/src/SparseArrays.jl | 16 +- stdlib/SparseArrays/src/sparsevector.jl | 36 +-- stdlib/SparseArrays/test/sparse.jl | 230 +++++++++++++++++++ 24 files changed, 487 insertions(+), 353 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8b71822a34fda..5ff3cdd4f3756 100644 --- a/NEWS.md +++ b/NEWS.md @@ -95,11 +95,24 @@ Standard library changes #### Package Manager #### LinearAlgebra -* The BLAS submodule now supports the level-2 BLAS subroutine `spr!` ([#42830]). +* The BLAS submodule now supports the level-2 BLAS subroutine `spr!` ([#42830]). * `cholesky[!]` now supports `LinearAlgebra.PivotingStrategy` (singleton type) values as its optional `pivot` argument: the default is `cholesky(A, NoPivot())` (vs. `cholesky(A, RowMaximum())`); the former `Val{true/false}`-based calls are deprecated. ([#41640]) +* The standard library `LinearAlgebra.jl` is now completely independent of `SparseArrays.jl`, + both in terms of the source code as well as unit testing ([#43127]). As a consequence, + sparse arrays are no longer (silently) returned by methods from `LinearAlgebra` applied + to `Base` or `LinearAlgebra` objects. Specifically, this results in the following breaking + changes: + + * Concatenations involving special "sparse" matrices (`*diagonal`) now return dense matrices; + As a consequence, the `D1` and `D2` fields of `SVD` objects, constructed upon `getproperty` + calls are now dense matrices. + * 3-arg `similar(::SpecialSparseMatrix, ::Type, ::Dims)` returns a dense zero matrix. + As a consequence, products of bi-, tri- and symmetric tridiagonal matrices with each + other result in dense output. Moreover, constructing 3-arg similar matrices of special + "sparse" matrices of (nonstatic) matrices now fails for the lack of `zero(::Type{Matrix{T}})`. #### Markdown diff --git a/stdlib/LinearAlgebra/Project.toml b/stdlib/LinearAlgebra/Project.toml index e12d950774643..d7121d2e3868e 100644 --- a/stdlib/LinearAlgebra/Project.toml +++ b/stdlib/LinearAlgebra/Project.toml @@ -8,7 +8,6 @@ libblastrampoline_jll = "8e850b90-86db-534c-a0d3-1478176c7d93" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [targets] -test = ["Test", "Random", "SparseArrays"] +test = ["Test", "Random"] diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 38c48bfe6d8d2..6ce749aa66647 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -1,7 +1,7 @@ # [Linear Algebra](@id man-linalg) ```@meta -DocTestSetup = :(using LinearAlgebra, SparseArrays, SuiteSparse) +DocTestSetup = :(using LinearAlgebra) ``` In addition to (and as part of) its support for multi-dimensional arrays, Julia provides native implementations @@ -308,7 +308,9 @@ of the Linear Algebra documentation. ## Standard functions -Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). Sparse matrix factorizations call functions from [SuiteSparse](http://suitesparse.com). Other sparse solvers are available as Julia packages. +Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). +Sparse matrix factorizations call functions from [SuiteSparse](http://suitesparse.com). +Other sparse solvers are available as Julia packages. ```@docs Base.:*(::AbstractMatrix, ::AbstractMatrix) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index ae8b9d461ffc2..8afe9905e9a30 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -372,15 +372,14 @@ algorithm. See also: `copy_similar`, `copy_to_array`. """ -copy_oftype(A::AbstractArray, ::Type{T}) where {T} = copyto!(similar(A,T), A) +copy_oftype(A::AbstractArray, ::Type{T}) where {T} = copyto!(similar(A, T), A) """ copy_similar(A, T) Copy `A` to a mutable array with eltype `T` based on `similar(A, T, size(A))`. -Compared to `copy_oftype`, the result can be more flexible. For example, -supplying a tridiagonal matrix results in a sparse array. In general, the type +Compared to `copy_oftype`, the result can be more flexible. In general, the type of the output corresponds to that of the three-argument method `similar(A, T, size(s))`. See also: `copy_oftype`, `copy_to_array`. diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index f5903f380ee53..6c3e1f1125537 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -227,7 +227,7 @@ _adjoint_hcat(avs::Union{Number,AdjointAbsVec}...) = adjoint(vcat(map(adjoint, a _transpose_hcat(tvs::Union{Number,TransposeAbsVec}...) = transpose(vcat(map(transpose, tvs)...)) typed_hcat(::Type{T}, avs::Union{Number,AdjointAbsVec}...) where {T} = adjoint(typed_vcat(T, map(adjoint, avs)...)) typed_hcat(::Type{T}, tvs::Union{Number,TransposeAbsVec}...) where {T} = transpose(typed_vcat(T, map(transpose, tvs)...)) -# otherwise-redundant definitions necessary to prevent hitting the concat methods in sparse/sparsevector.jl +# otherwise-redundant definitions necessary to prevent hitting the concat methods in LinearAlgebra/special.jl hcat(avs::Adjoint{<:Any,<:Vector}...) = _adjoint_hcat(avs...) hcat(tvs::Transpose{<:Any,<:Vector}...) = _transpose_hcat(tvs...) hcat(avs::Adjoint{T,Vector{T}}...) where {T} = _adjoint_hcat(avs...) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 57f9c3fefe647..39fcd271c3ea7 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -204,12 +204,8 @@ AbstractMatrix{T}(A::Bidiagonal) where {T} = convert(Bidiagonal{T}, A) convert(T::Type{<:Bidiagonal}, m::AbstractMatrix) = m isa T ? m : T(m) -# For B<:Bidiagonal, similar(B[, neweltype]) should yield a Bidiagonal matrix. -# On the other hand, similar(B, [neweltype,] shape...) should yield a sparse matrix. -# The first method below effects the former, and the second the latter. similar(B::Bidiagonal, ::Type{T}) where {T} = Bidiagonal(similar(B.dv, T), similar(B.ev, T), B.uplo) -# The method below is moved to SparseArrays for now -# similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) +similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = zeros(T, dims...) ################### @@ -706,6 +702,11 @@ function *(A::SymTridiagonal, B::Diagonal) A_mul_B_td!(Tridiagonal(zeros(TS, size(A, 1)-1), zeros(TS, size(A, 1)), zeros(TS, size(A, 1)-1)), A, B) end +function *(A::BiTriSym, B::BiTriSym) + TS = promote_op(matprod, eltype(A), eltype(B)) + mul!(similar(A, TS, size(A)...), A, B) +end + function dot(x::AbstractVector, B::Bidiagonal, y::AbstractVector) require_one_based_indexing(x, y) nx, ny = length(x), length(y) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index ef01bb6cd3ba9..d7d48ce3561c4 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -87,12 +87,8 @@ Construct an uninitialized `Diagonal{T}` of length `n`. See `undef`. """ Diagonal{T}(::UndefInitializer, n::Integer) where T = Diagonal(Vector{T}(undef, n)) -# For D<:Diagonal, similar(D[, neweltype]) should yield a Diagonal matrix. -# On the other hand, similar(D, [neweltype,] shape...) should yield a sparse matrix. -# The first method below effects the former, and the second the latter. similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T)) -# The method below is moved to SparseArrays for now -# similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) +similar(::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = zeros(T, dims...) copyto!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1) @@ -114,8 +110,8 @@ end end r end -diagzero(::Diagonal{T},i,j) where {T} = zero(T) -diagzero(D::Diagonal{<:AbstractMatrix{T}},i,j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) +diagzero(::Diagonal{T}, i, j) where {T} = zero(T) +diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) function setindex!(D::Diagonal, v, i::Int, j::Int) @boundscheck checkbounds(D, i, j) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl index b71e588b87feb..e1618dbe5d0e5 100644 --- a/stdlib/LinearAlgebra/src/special.jl +++ b/stdlib/LinearAlgebra/src/special.jl @@ -339,3 +339,27 @@ end ==(A::Bidiagonal, B::SymTridiagonal) = iszero(_evview(B)) && iszero(A.ev) && A.dv == B.dv ==(B::SymTridiagonal, A::Bidiagonal) = A == B + +# concatenation +const _SpecialArrays = Union{Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal} +const _Symmetric_DenseArrays{T,A<:Matrix} = Symmetric{T,A} +const _Hermitian_DenseArrays{T,A<:Matrix} = Hermitian{T,A} +const _Triangular_DenseArrays{T,A<:Matrix} = AbstractTriangular{T,A} +const _Annotated_DenseArrays = Union{_SpecialArrays, _Triangular_DenseArrays, _Symmetric_DenseArrays, _Hermitian_DenseArrays} +const _Annotated_Typed_DenseArrays{T} = Union{_Triangular_DenseArrays{T}, _Symmetric_DenseArrays{T}, _Hermitian_DenseArrays{T}} +const _DenseConcatGroup = Union{Number, Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _Annotated_DenseArrays} +const _TypedDenseConcatGroup{T} = Union{Vector{T}, Adjoint{T,Vector{T}}, Transpose{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} + +promote_to_array_type(::Tuple{Vararg{Union{_DenseConcatGroup,UniformScaling}}}) = Matrix + +Base._cat(dims, xs::_DenseConcatGroup...) = Base.cat_t(promote_eltype(xs...), xs...; dims=dims) +vcat(A::Vector...) = Base.typed_vcat(promote_eltype(A...), A...) +vcat(A::_DenseConcatGroup...) = Base.typed_vcat(promote_eltype(A...), A...) +hcat(A::Vector...) = Base.typed_hcat(promote_eltype(A...), A...) +hcat(A::_DenseConcatGroup...) = Base.typed_hcat(promote_eltype(A...), A...) +hvcat(rows::Tuple{Vararg{Int}}, xs::_DenseConcatGroup...) = Base.typed_hvcat(promote_eltype(xs...), rows, xs...) +# For performance, specially handle the case where the matrices/vectors have homogeneous eltype +Base._cat(dims, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.cat_t(T, xs...; dims=dims) +vcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_vcat(T, A...) +hcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hcat(T, A...) +hvcat(rows::Tuple{Vararg{Int}}, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hvcat(T, rows, xs...) diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl index bee4c8451c0a6..1860cea167fe3 100644 --- a/stdlib/LinearAlgebra/src/svd.jl +++ b/stdlib/LinearAlgebra/src/svd.jl @@ -333,13 +333,13 @@ Q factor: 1.0 0.0 0.0 1.0 D1 factor: -2×2 SparseArrays.SparseMatrixCSC{Float64, Int64} with 2 stored entries: - 0.707107 ⋅ - ⋅ 0.707107 +2×2 Matrix{Float64}: + 0.707107 0.0 + 0.0 0.707107 D2 factor: -2×2 SparseArrays.SparseMatrixCSC{Float64, Int64} with 2 stored entries: - 0.707107 ⋅ - ⋅ 0.707107 +2×2 Matrix{Float64}: + 0.707107 0.0 + 0.0 0.707107 R0 factor: 2×2 Matrix{Float64}: 1.41421 0.0 @@ -352,8 +352,8 @@ julia> F.U*F.D1*F.R0*F.Q' julia> F.V*F.D2*F.R0*F.Q' 2×2 Matrix{Float64}: - 0.0 1.0 - 1.0 0.0 + -0.0 1.0 + 1.0 0.0 ``` """ struct GeneralizedSVD{T,S} <: Factorization{T} diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index ee8e553fd3aaa..138378b4a5fdb 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -148,12 +148,8 @@ function size(A::SymTridiagonal, d::Integer) end end -# For S<:SymTridiagonal, similar(S[, neweltype]) should yield a SymTridiagonal matrix. -# On the other hand, similar(S, [neweltype,] shape...) should yield a sparse matrix. -# The first method below effects the former, and the second the latter. similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal(similar(S.dv, T), similar(S.ev, T)) -# The method below is moved to SparseArrays for now -# similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) +similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = zeros(T, dims...) copyto!(dest::SymTridiagonal, src::SymTridiagonal) = (copyto!(dest.dv, src.dv); copyto!(dest.ev, _evview(src)); dest) @@ -584,12 +580,8 @@ end Matrix(M::Tridiagonal{T}) where {T} = Matrix{T}(M) Array(M::Tridiagonal) = Matrix(M) -# For M<:Tridiagonal, similar(M[, neweltype]) should yield a Tridiagonal matrix. -# On the other hand, similar(M, [neweltype,] shape...) should yield a sparse matrix. -# The first method below effects the former, and the second the latter. similar(M::Tridiagonal, ::Type{T}) where {T} = Tridiagonal(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) -# The method below is moved to SparseArrays for now -# similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) +similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = zeros(T, dims...) # Operations on Tridiagonal matrices copyto!(dest::Tridiagonal, src::Tridiagonal) = (copyto!(dest.dl, src.dl); copyto!(dest.d, src.d); copyto!(dest.du, src.du); dest) diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 94394c9ba1752..03e60fe804cb1 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -391,7 +391,7 @@ end # so that the same promotion code can be used for hvcat. We pass the type T # so that we can re-use this code for sparse-matrix hcat etcetera. promote_to_arrays_(n::Int, ::Type, a::Number) = a -promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = copyto!(Matrix{T}(undef, n,n), J) +promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n) promote_to_arrays_(n::Int, ::Type, A::AbstractVecOrMat) = A promote_to_arrays(n,k, ::Type) = () promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),) diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 42529f3f4f334..72fdf687bf5c3 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -6,7 +6,6 @@ using Base: rtoldefault using Test using LinearAlgebra using LinearAlgebra: AbstractTriangular -using SparseArrays using Random _rand(::Type{T}) where {T <: AbstractFloat} = T(randn()) diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 8226ddf004a72..7b782d463768d 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -2,7 +2,7 @@ module TestAdjointTranspose -using Test, LinearAlgebra, SparseArrays +using Test, LinearAlgebra const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") @@ -354,14 +354,6 @@ end @test broadcast(+, Transpose(vec), 1, Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == tvec + tvec .+ 1 @test broadcast(+, Adjoint(vec), 1im, Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == avec + avec .+ 1im @test broadcast(+, Transpose(vec), 1im, Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == tvec + tvec .+ 1im - # ascertain inference friendliness, ref. https://github.com/JuliaLang/julia/pull/25083#issuecomment-353031641 - sparsevec = SparseVector([1.0, 2.0, 3.0]) - @test map(-, Adjoint(sparsevec), Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} - @test map(-, Transpose(sparsevec), Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} - @test broadcast(-, Adjoint(sparsevec), Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} - @test broadcast(-, Transpose(sparsevec), Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} - @test broadcast(+, Adjoint(sparsevec), 1.0, Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} - @test broadcast(+, Transpose(sparsevec), 1.0, Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} end @testset "Adjoint/Transpose-wrapped vector multiplication" begin diff --git a/stdlib/LinearAlgebra/test/ambiguous_exec.jl b/stdlib/LinearAlgebra/test/ambiguous_exec.jl index e784c43bfa000..7b89c0a457afb 100644 --- a/stdlib/LinearAlgebra/test/ambiguous_exec.jl +++ b/stdlib/LinearAlgebra/test/ambiguous_exec.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -using Test, LinearAlgebra, SparseArrays +using Test, LinearAlgebra let ambig = detect_ambiguities(LinearAlgebra; recursive=true) @test isempty(ambig) ambig = Set{Any}(((m1.sig, m2.sig) for (m1, m2) in ambig)) diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 7aef50b446c85..96d31cc7c2ff5 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -2,7 +2,7 @@ module TestBidiagonal -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random using LinearAlgebra: BlasReal, BlasFloat const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") @@ -98,8 +98,8 @@ Random.seed!(1) @test similar(ubd).uplo == ubd.uplo @test isa(similar(ubd, Int), Bidiagonal{Int}) @test similar(ubd, Int).uplo == ubd.uplo - @test isa(similar(ubd, (3, 2)), SparseMatrixCSC) - @test isa(similar(ubd, Int, (3, 2)), SparseMatrixCSC{Int}) + @test isa(similar(ubd, (3, 2)), Matrix) + @test isa(similar(ubd, Int, (3, 2)), Matrix{Int}) # setindex! when off diagonal is zero bug Bu = Bidiagonal(rand(elty, 10), zeros(elty, 9), 'U') @@ -432,9 +432,7 @@ using LinearAlgebra: fillstored!, UnitLowerTriangular exotic_arrays = Any[Tridiagonal(randn(3), randn(4), randn(3)), Bidiagonal(randn(3), randn(2), rand([:U,:L])), SymTridiagonal(randn(3), randn(2)), - sparse(randn(3,4)), Diagonal(randn(5)), - sparse(rand(3)), # LowerTriangular(randn(3,3)), # AbstractTriangular fill! deprecated, see below # UpperTriangular(randn(3,3)) # AbstractTriangular fill! deprecated, see below ] diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 42355c2b20576..6f4aae5358a39 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -2,7 +2,7 @@ module TestDiagonal -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random using LinearAlgebra: BlasFloat, BlasComplex n=12 #Size of matrix problem to test @@ -147,7 +147,6 @@ Random.seed!(1) @test_throws DimensionMismatch ldiv!(D, fill(elty(1), n + 1)) @test_throws SingularException ldiv!(Diagonal(zeros(relty, n)), copy(v)) b = rand(elty, n, n) - b = sparse(b) @test ldiv!(D, copy(b)) ≈ Array(D)\Array(b) @test_throws SingularException ldiv!(Diagonal(zeros(elty, n)), copy(b)) b = view(rand(elty, n), Vector(1:n)) @@ -157,7 +156,6 @@ Random.seed!(1) @test c ≈ d @test_throws SingularException ldiv!(Diagonal(zeros(elty, n)), b) b = rand(elty, n+1, n+1) - b = sparse(b) @test_throws DimensionMismatch ldiv!(D, copy(b)) b = view(rand(elty, n+1), Vector(1:n+1)) @test_throws DimensionMismatch ldiv!(D, b) @@ -190,7 +188,7 @@ Random.seed!(1) end if relty <: BlasFloat - for b in (rand(elty,n,n), sparse(rand(elty,n,n)), rand(elty,n), sparse(rand(elty,n))) + for b in (rand(elty,n,n), rand(elty,n)) @test lmul!(copy(D), copy(b)) ≈ Array(D)*Array(b) @test lmul!(transpose(copy(D)), copy(b)) ≈ transpose(Array(D))*Array(b) @test lmul!(adjoint(copy(D)), copy(b)) ≈ Array(D)'*Array(b) @@ -388,8 +386,8 @@ Random.seed!(1) @testset "similar" begin @test isa(similar(D), Diagonal{elty}) @test isa(similar(D, Int), Diagonal{Int}) - @test isa(similar(D, (3,2)), SparseMatrixCSC{elty}) - @test isa(similar(D, Int, (3,2)), SparseMatrixCSC{Int}) + @test isa(similar(D, (3,2)), Matrix{elty}) + @test isa(similar(D, Int, (3,2)), Matrix{Int}) end # Issue number 10036 @@ -605,10 +603,10 @@ end mul!(D2, D, D) @test D2 == D * D - D2[diagind(D2)] .= D[diagind(D)] + copyto!(D2, D) lmul!(D, D2) @test D2 == D * D - D2[diagind(D2)] .= D[diagind(D)] + copyto!(D2, D) rmul!(D2, D) @test D2 == D * D end @@ -651,13 +649,6 @@ end @test tr(D) == 10 @test det(D) == 4 - - # sparse matrix block diagonals - s = SparseArrays.sparse([1 2; 3 4]) - D = Diagonal([s, s]) - @test D[1, 1] == s - @test D[1, 2] == zero(s) - @test isa(D[2, 1], SparseMatrixCSC) end @testset "linear solve for block diagonal matrices" begin diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index e0c5f87111b07..55f14f42d87f0 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -2,7 +2,7 @@ module TestSpecial -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random using LinearAlgebra: rmul! n= 10 #Size of matrix to test @@ -201,7 +201,6 @@ end end end -# should all yield sparse arrays @testset "concatenations of combinations of special and other matrix types" begin N = 4 # Test concatenating pairwise combinations of special matrices @@ -211,123 +210,104 @@ end symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat) for specialmata in specialmats, specialmatb in specialmats - @test issparse(hcat(specialmata, specialmatb)) - @test issparse(vcat(specialmata, specialmatb)) - @test issparse(hvcat((1,1), specialmata, specialmatb)) - @test issparse(cat(specialmata, specialmatb; dims=(1,2))) + MA = Matrix(specialmata); MB = Matrix(specialmatb) + @test hcat(specialmata, specialmatb) == hcat(MA, MB) + @test vcat(specialmata, specialmatb) == vcat(MA, MB) + @test hvcat((1,1), specialmata, specialmatb) == hvcat((1,1), MA, MB) + @test cat(specialmata, specialmatb; dims=(1,2)) == cat(MA, MB; dims=(1,2)) end # Test concatenating pairwise combinations of special matrices with sparse matrices, # dense matrices, or dense vectors densevec = fill(1., N) densemat = diagm(0 => densevec) - spmat = spdiagm(0 => densevec) for specialmat in specialmats + SM = Matrix(specialmat) # --> Tests applicable only to pairs of matrices - for othermat in (spmat, densemat) - @test issparse(vcat(specialmat, othermat)) - @test issparse(vcat(othermat, specialmat)) - end + @test vcat(specialmat, densemat) == vcat(SM, densemat) + @test vcat(densemat, specialmat) == vcat(densemat, SM) # --> Tests applicable also to pairs including vectors - for specialmat in specialmats, othermatorvec in (spmat, densemat, densevec) - @test issparse(hcat(specialmat, othermatorvec)) - @test issparse(hcat(othermatorvec, specialmat)) - @test issparse(hvcat((2,), specialmat, othermatorvec)) - @test issparse(hvcat((2,), othermatorvec, specialmat)) - @test issparse(cat(specialmat, othermatorvec; dims=(1,2))) - @test issparse(cat(othermatorvec, specialmat; dims=(1,2))) + for specialmat in specialmats, othermatorvec in (densemat, densevec) + SM = Matrix(specialmat); OM = Array(othermatorvec) + @test hcat(specialmat, othermatorvec) == hcat(SM, OM) + @test hcat(othermatorvec, specialmat) == hcat(OM, SM) + @test hvcat((2,), specialmat, othermatorvec) == hvcat((2,), SM, OM) + @test hvcat((2,), othermatorvec, specialmat) == hvcat((2,), OM, SM) + @test cat(specialmat, othermatorvec; dims=(1,2)) == cat(SM, OM; dims=(1,2)) + @test cat(othermatorvec, specialmat; dims=(1,2)) == cat(OM, SM; dims=(1,2)) end end end -# Test that concatenations of annotated sparse/special matrix types with other matrix -# types yield sparse arrays, and that the code which effects that does not make concatenations -# strictly involving un/annotated dense matrices yield sparse arrays -# -# TODO: As with the associated code, these tests should be moved to a more appropriate -# location, particularly some future equivalent of base/linalg/special.jl dedicated to -# intereactions between a broader set of matrix types @testset "concatenations of annotated types" begin N = 4 # The tested annotation types testfull = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) - utriannotations = (UpperTriangular, LinearAlgebra.UnitUpperTriangular) - ltriannotations = (LowerTriangular, LinearAlgebra.UnitLowerTriangular) + utriannotations = (UpperTriangular, UnitUpperTriangular) + ltriannotations = (LowerTriangular, UnitLowerTriangular) triannotations = (utriannotations..., ltriannotations...) symannotations = (Symmetric, Hermitian) annotations = testfull ? (triannotations..., symannotations...) : (LowerTriangular, Symmetric) - # Concatenations involving these types, un/annotated, should yield sparse arrays - spvec = spzeros(N) - spmat = sparse(1.0I, N, N) + # Concatenations involving these types, un/annotated diagmat = Diagonal(1:N) bidiagmat = Bidiagonal(1:N, 1:(N-1), :U) tridiagmat = Tridiagonal(1:(N-1), 1:N, 1:(N-1)) symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) - sparseconcatmats = testfull ? (spmat, diagmat, bidiagmat, tridiagmat, symtridiagmat) : (spmat, diagmat) - # Concatenations involving strictly these types, un/annotated, should yield dense arrays + specialconcatmats = testfull ? (diagmat, bidiagmat, tridiagmat, symtridiagmat) : (diagmat,) + # Concatenations involving strictly these types, un/annotated densevec = fill(1., N) densemat = fill(1., N, N) # Annotated collections annodmats = [annot(densemat) for annot in annotations] - annospcmats = [annot(spcmat) for annot in annotations, spcmat in sparseconcatmats] - # Test that concatenations of pairwise combinations of annotated sparse/special - # yield sparse matrices + annospcmats = [annot(spcmat) for annot in annotations, spcmat in specialconcatmats] + # Test concatenations of pairwise combinations of annotated special matrices for annospcmata in annospcmats, annospcmatb in annospcmats - @test issparse(vcat(annospcmata, annospcmatb)) - @test issparse(hcat(annospcmata, annospcmatb)) - @test issparse(hvcat((2,), annospcmata, annospcmatb)) - @test issparse(cat(annospcmata, annospcmatb; dims=(1,2))) + AM = Array(annospcmata); BM = Array(annospcmatb) + @test vcat(annospcmata, annospcmatb) == vcat(AM, BM) + @test hcat(annospcmata, annospcmatb) == hcat(AM, BM) + @test hvcat((2,), annospcmata, annospcmatb) == hvcat((2,), AM, BM) + @test cat(annospcmata, annospcmatb; dims=(1,2)) == cat(AM, BM; dims=(1,2)) end - # Test that concatenations of pairwise combinations of annotated sparse/special - # matrices and other matrix/vector types yield sparse matrices + # Test concatenations of pairwise combinations of annotated special matrices and other matrix/vector types for annospcmat in annospcmats + AM = Array(annospcmat) # --> Tests applicable to pairs including only matrices - for othermat in (densemat, annodmats..., sparseconcatmats...) - @test issparse(vcat(annospcmat, othermat)) - @test issparse(vcat(othermat, annospcmat)) + for othermat in (densemat, annodmats..., specialconcatmats...) + OM = Array(othermat) + @test vcat(annospcmat, othermat) == vcat(AM, OM) + @test vcat(othermat, annospcmat) == vcat(OM, AM) end # --> Tests applicable to pairs including other vectors or matrices - for other in (spvec, densevec, densemat, annodmats..., sparseconcatmats...) - @test issparse(hcat(annospcmat, other)) - @test issparse(hcat(other, annospcmat)) - @test issparse(hvcat((2,), annospcmat, other)) - @test issparse(hvcat((2,), other, annospcmat)) - @test issparse(cat(annospcmat, other; dims=(1,2))) - @test issparse(cat(other, annospcmat; dims=(1,2))) + for other in (densevec, densemat, annodmats..., specialconcatmats...) + OM = Array(other) + @test hcat(annospcmat, other) == hcat(AM, OM) + @test hcat(other, annospcmat) == hcat(OM, AM) + @test hvcat((2,), annospcmat, other) == hvcat((2,), AM, OM) + @test hvcat((2,), other, annospcmat) == hvcat((2,), OM, AM) + @test cat(annospcmat, other; dims=(1,2)) == cat(AM, OM; dims=(1,2)) + @test cat(other, annospcmat; dims=(1,2)) == cat(OM, AM; dims=(1,2)) end end - # The preceding tests should cover multi-way combinations of those types, but for good - # measure test a few multi-way combinations involving those types - @test issparse(vcat(spmat, densemat, annospcmats[1], annodmats[2])) - @test issparse(vcat(densemat, spmat, annodmats[1], annospcmats[2])) - @test issparse(hcat(spvec, annodmats[1], annospcmats[3], densevec, diagmat)) - @test issparse(hcat(annodmats[2], annospcmats[4], spvec, densevec, diagmat)) - @test issparse(hvcat((5,), diagmat, densevec, spvec, annodmats[1], annospcmats[1])) - @test issparse(hvcat((5,), spvec, annodmats[2], diagmat, densevec, annospcmats[2])) - @test issparse(cat(annodmats[1], diagmat, annospcmats[3], densevec, spvec; dims=(1,2))) - @test issparse(cat(spvec, diagmat, densevec, annospcmats[4], annodmats[2]; dims=(1,2))) - # Test that concatenations strictly involving un/annotated dense matrices/vectors - # yield dense arrays + # Test concatenations strictly involving un/annotated dense matrices/vectors for densemata in (densemat, annodmats...) + AM = Array(densemata) # --> Tests applicable to pairs including only matrices for densematb in (densemat, annodmats...) - @test !issparse(vcat(densemata, densematb)) - @test !issparse(vcat(densematb, densemata)) + BM = Array(densematb) + @test vcat(densemata, densematb) == vcat(AM, BM) + @test vcat(densematb, densemata) == vcat(BM, AM) end # --> Tests applicable to pairs including vectors or matrices for otherdense in (densevec, densemat, annodmats...) - @test !issparse(hcat(densemata, otherdense)) - @test !issparse(hcat(otherdense, densemata)) - @test !issparse(hvcat((2,), densemata, otherdense)) - @test !issparse(hvcat((2,), otherdense, densemata)) - @test !issparse(cat(densemata, otherdense; dims=(1,2))) - @test !issparse(cat(otherdense, densemata; dims=(1,2))) + OM = Array(otherdense) + @test hcat(densemata, otherdense) == hcat(AM, OM) + @test hcat(otherdense, densemata) == hcat(OM, AM) + @test hvcat((2,), densemata, otherdense) == hvcat((2,), AM, OM) + @test hvcat((2,), otherdense, densemata) == hvcat((2,), OM, AM) + @test cat(densemata, otherdense; dims=(1,2)) == cat(AM, OM; dims=(1,2)) + @test cat(otherdense, densemata; dims=(1,2)) == cat(OM, AM; dims=(1,2)) end end end -@testset "vcat of Vectors with SparseVectors should yield SparseVector (#22225)" begin - @test isa((@inferred vcat(Float64[], spzeros(1))), SparseVector) -end - # for testing types with a dimension const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 169dfb0071718..47a36df5e7883 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -2,7 +2,7 @@ module TestSymmetric -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random Random.seed!(1010) @@ -543,20 +543,6 @@ end end end -@testset "similar should preserve underlying storage type and uplo flag" begin - m, n = 4, 3 - sparsemat = sprand(m, m, 0.5) - for SymType in (Symmetric, Hermitian) - symsparsemat = SymType(sparsemat) - @test isa(similar(symsparsemat), typeof(symsparsemat)) - @test similar(symsparsemat).uplo == symsparsemat.uplo - @test isa(similar(symsparsemat, Float32), SymType{Float32,<:SparseMatrixCSC{Float32}}) - @test similar(symsparsemat, Float32).uplo == symsparsemat.uplo - @test isa(similar(symsparsemat, (n, n)), typeof(sparsemat)) - @test isa(similar(symsparsemat, Float32, (n, n)), SparseMatrixCSC{Float32}) - end -end - const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) using .Main.ImmutableArrays diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 6950d7a956b87..aafee487935ea 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -3,7 +3,7 @@ module TestTriangular debug = false -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random using LinearAlgebra: BlasFloat, errorbounds, full!, transpose!, UnitUpperTriangular, UnitLowerTriangular, mul!, rdiv!, rmul!, lmul! @@ -689,18 +689,6 @@ let A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) @test sqrt(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) end -@testset "similar should preserve underlying storage type" begin - local m, n = 4, 3 - sparsemat = sprand(m, m, 0.5) - for TriType in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - trisparsemat = TriType(sparsemat) - @test isa(similar(trisparsemat), typeof(trisparsemat)) - @test isa(similar(trisparsemat, Float32), TriType{Float32,<:SparseMatrixCSC{Float32}}) - @test isa(similar(trisparsemat, (n, n)), typeof(sparsemat)) - @test isa(similar(trisparsemat, Float32, (n, n)), SparseMatrixCSC{Float32}) - end -end - isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) using .Main.ImmutableArrays diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl index eeb4c615c12ad..d0031c566437d 100644 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ b/stdlib/LinearAlgebra/test/tridiag.jl @@ -2,7 +2,7 @@ module TestTridiagonal -using Test, LinearAlgebra, SparseArrays, Random +using Test, LinearAlgebra, Random const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") @@ -221,8 +221,8 @@ end @test B == A @test isa(similar(A), mat_type{elty}) @test isa(similar(A, Int), mat_type{Int}) - @test isa(similar(A, (3, 2)), SparseMatrixCSC) - @test isa(similar(A, Int, (3, 2)), SparseMatrixCSC{Int}) + @test isa(similar(A, (3, 2)), Matrix) + @test isa(similar(A, Int, (3, 2)), Matrix{Int}) @test size(A, 3) == 1 @test size(A, 1) == n @test size(A) == (n, n) @@ -400,8 +400,8 @@ end @testset "similar" begin @test isa(similar(Ts), SymTridiagonal{elty}) @test isa(similar(Ts, Int), SymTridiagonal{Int}) - @test isa(similar(Ts, (3, 2)), SparseMatrixCSC) - @test isa(similar(Ts, Int, (3, 2)), SparseMatrixCSC{Int}) + @test isa(similar(Ts, (3, 2)), Matrix) + @test isa(similar(Ts, Int, (3, 2)), Matrix{Int}) end @test first(logabsdet(Tldlt)) ≈ first(logabsdet(Fs)) @@ -476,13 +476,6 @@ end @test SymTridiagonal(ones(0), ones(0)) * ones(0, 2) == ones(0, 2) end -@testset "issue #29644" begin - F = lu(Tridiagonal(sparse(1.0I, 3, 3))) - @test F.L == Matrix(I, 3, 3) - @test startswith(sprint(show, MIME("text/plain"), F), - "$(LinearAlgebra.LU){Float64, $(LinearAlgebra.Tridiagonal){Float64, SparseArrays.SparseVector") -end - @testset "Issue 29630" begin function central_difference_discretization(N; dfunc = x -> 12x^2 - 2N^2, dufunc = x -> N^2 + 4N*x, diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl index 2080ace77df88..be1b9887d570f 100644 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ b/stdlib/LinearAlgebra/test/uniformscaling.jl @@ -2,7 +2,7 @@ module TestUniformscaling -using Test, LinearAlgebra, Random, SparseArrays +using Test, LinearAlgebra, Random const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) @@ -22,8 +22,6 @@ Random.seed!(1234543) @test one(UniformScaling(rand(ComplexF64))) == one(UniformScaling{ComplexF64}) @test eltype(one(UniformScaling(rand(ComplexF64)))) == ComplexF64 @test -one(UniformScaling(2)) == UniformScaling(-1) - @test sparse(3I,4,5) == sparse(1:4, 1:4, 3, 4, 5) - @test sparse(3I,5,4) == sparse(1:4, 1:4, 3, 5, 4) @test opnorm(UniformScaling(1+im)) ≈ sqrt(2) @test convert(UniformScaling{Float64}, 2I) === 2.0I end @@ -240,97 +238,91 @@ let @test B + I == B + Matrix(I, size(B)) @test I + B == B + Matrix(I, size(B)) AA = randn(2, 2) - for SS in (sprandn(3,3, 0.5), sparse(Int(1)I, 3, 3)) - for (A, S) in ((AA, SS), (view(AA, 1:2, 1:2), view(SS, 1:3, 1:3))) - I22 = Matrix(I, size(A)) - @test @inferred(A + I) == A + I22 - @test @inferred(I + A) == A + I22 - @test @inferred(I - I) === UniformScaling(0) - @test @inferred(B - I) == B - I22 - @test @inferred(I - B) == I22 - B - @test @inferred(A - I) == A - I22 - @test @inferred(I - A) == I22 - A - @test @inferred(I*J) === UniformScaling(λ) - @test @inferred(B*J) == B*λ - @test @inferred(J*B) == B*λ - @test @inferred(I*A) !== A # Don't alias - @test @inferred(I*S) !== S # Don't alias - @test @inferred(A*I) !== A # Don't alias - @test @inferred(S*I) !== S # Don't alias - - @test @inferred(S*J) == S*λ - @test @inferred(J*S) == S*λ - @test @inferred(A*J) == A*λ - @test @inferred(J*A) == A*λ - @test @inferred(J*fill(1, 3)) == fill(λ, 3) - @test @inferred(λ*J) === UniformScaling(λ*J.λ) - @test @inferred(J*λ) === UniformScaling(λ*J.λ) - @test @inferred(J/I) === J - @test @inferred(I/A) == inv(A) - @test @inferred(A/I) == A - @test @inferred(I/λ) === UniformScaling(1/λ) - @test @inferred(I\J) === J - - if isa(A, Array) - T = LowerTriangular(randn(3,3)) - else - T = LowerTriangular(view(randn(3,3), 1:3, 1:3)) - end - @test @inferred(T + J) == Array(T) + J - @test @inferred(J + T) == J + Array(T) - @test @inferred(T - J) == Array(T) - J - @test @inferred(J - T) == J - Array(T) - @test @inferred(T\I) == inv(T) - - if isa(A, Array) - T = LinearAlgebra.UnitLowerTriangular(randn(3,3)) - else - T = LinearAlgebra.UnitLowerTriangular(view(randn(3,3), 1:3, 1:3)) - end - @test @inferred(T + J) == Array(T) + J - @test @inferred(J + T) == J + Array(T) - @test @inferred(T - J) == Array(T) - J - @test @inferred(J - T) == J - Array(T) - @test @inferred(T\I) == inv(T) - - if isa(A, Array) - T = UpperTriangular(randn(3,3)) - else - T = UpperTriangular(view(randn(3,3), 1:3, 1:3)) - end - @test @inferred(T + J) == Array(T) + J - @test @inferred(J + T) == J + Array(T) - @test @inferred(T - J) == Array(T) - J - @test @inferred(J - T) == J - Array(T) - @test @inferred(T\I) == inv(T) + for A in (AA, view(AA, 1:2, 1:2)) + I22 = Matrix(I, size(A)) + @test @inferred(A + I) == A + I22 + @test @inferred(I + A) == A + I22 + @test @inferred(I - I) === UniformScaling(0) + @test @inferred(B - I) == B - I22 + @test @inferred(I - B) == I22 - B + @test @inferred(A - I) == A - I22 + @test @inferred(I - A) == I22 - A + @test @inferred(I*J) === UniformScaling(λ) + @test @inferred(B*J) == B*λ + @test @inferred(J*B) == B*λ + @test @inferred(I*A) !== A # Don't alias + @test @inferred(A*I) !== A # Don't alias + + @test @inferred(A*J) == A*λ + @test @inferred(J*A) == A*λ + @test @inferred(J*fill(1, 3)) == fill(λ, 3) + @test @inferred(λ*J) === UniformScaling(λ*J.λ) + @test @inferred(J*λ) === UniformScaling(λ*J.λ) + @test @inferred(J/I) === J + @test @inferred(I/A) == inv(A) + @test @inferred(A/I) == A + @test @inferred(I/λ) === UniformScaling(1/λ) + @test @inferred(I\J) === J + + if isa(A, Array) + T = LowerTriangular(randn(3,3)) + else + T = LowerTriangular(view(randn(3,3), 1:3, 1:3)) + end + @test @inferred(T + J) == Array(T) + J + @test @inferred(J + T) == J + Array(T) + @test @inferred(T - J) == Array(T) - J + @test @inferred(J - T) == J - Array(T) + @test @inferred(T\I) == inv(T) + + if isa(A, Array) + T = LinearAlgebra.UnitLowerTriangular(randn(3,3)) + else + T = LinearAlgebra.UnitLowerTriangular(view(randn(3,3), 1:3, 1:3)) + end + @test @inferred(T + J) == Array(T) + J + @test @inferred(J + T) == J + Array(T) + @test @inferred(T - J) == Array(T) - J + @test @inferred(J - T) == J - Array(T) + @test @inferred(T\I) == inv(T) + + if isa(A, Array) + T = UpperTriangular(randn(3,3)) + else + T = UpperTriangular(view(randn(3,3), 1:3, 1:3)) + end + @test @inferred(T + J) == Array(T) + J + @test @inferred(J + T) == J + Array(T) + @test @inferred(T - J) == Array(T) - J + @test @inferred(J - T) == J - Array(T) + @test @inferred(T\I) == inv(T) + + if isa(A, Array) + T = LinearAlgebra.UnitUpperTriangular(randn(3,3)) + else + T = LinearAlgebra.UnitUpperTriangular(view(randn(3,3), 1:3, 1:3)) + end + @test @inferred(T + J) == Array(T) + J + @test @inferred(J + T) == J + Array(T) + @test @inferred(T - J) == Array(T) - J + @test @inferred(J - T) == J - Array(T) + @test @inferred(T\I) == inv(T) + for elty in (Float64, ComplexF64) if isa(A, Array) - T = LinearAlgebra.UnitUpperTriangular(randn(3,3)) + T = Hermitian(randn(elty, 3,3)) else - T = LinearAlgebra.UnitUpperTriangular(view(randn(3,3), 1:3, 1:3)) + T = Hermitian(view(randn(elty, 3,3), 1:3, 1:3)) end @test @inferred(T + J) == Array(T) + J @test @inferred(J + T) == J + Array(T) @test @inferred(T - J) == Array(T) - J @test @inferred(J - T) == J - Array(T) - @test @inferred(T\I) == inv(T) - - for elty in (Float64, ComplexF64) - if isa(A, Array) - T = Hermitian(randn(elty, 3,3)) - else - T = Hermitian(view(randn(elty, 3,3), 1:3, 1:3)) - end - @test @inferred(T + J) == Array(T) + J - @test @inferred(J + T) == J + Array(T) - @test @inferred(T - J) == Array(T) - J - @test @inferred(J - T) == J - Array(T) - end - - @test @inferred(I\A) == A - @test @inferred(A\I) == inv(A) - @test @inferred(λ\I) === UniformScaling(1/λ) end + + @test @inferred(I\A) == A + @test @inferred(A\I) == inv(A) + @test @inferred(λ\I) === UniformScaling(1/λ) end end end @@ -341,42 +333,41 @@ end @test_throws ArgumentError vcat(I) @test_throws ArgumentError [I; I] @test_throws ArgumentError [I I; I] - for T in (Matrix, SparseMatrixCSC) - A = T(rand(3,4)) - B = T(rand(3,3)) - C = T(rand(0,3)) - D = T(rand(2,0)) - E = T(rand(1,3)) - F = T(rand(3,1)) - α = rand() - @test (hcat(A, 2I))::T == hcat(A, Matrix(2I, 3, 3)) - @test (hcat(E, α))::T == hcat(E, [α]) - @test (hcat(E, α, 2I))::T == hcat(E, [α], fill(2, 1, 1)) - @test (vcat(A, 2I))::T == vcat(A, Matrix(2I, 4, 4)) - @test (vcat(F, α))::T == vcat(F, [α]) - @test (vcat(F, α, 2I))::T == vcat(F, [α], fill(2, 1, 1)) - @test (hcat(C, 2I))::T == C - @test_throws DimensionMismatch hcat(C, α) - @test (vcat(D, 2I))::T == D - @test_throws DimensionMismatch vcat(D, α) - @test (hcat(I, 3I, A, 2I))::T == hcat(Matrix(I, 3, 3), Matrix(3I, 3, 3), A, Matrix(2I, 3, 3)) - @test (vcat(I, 3I, A, 2I))::T == vcat(Matrix(I, 4, 4), Matrix(3I, 4, 4), A, Matrix(2I, 4, 4)) - @test (hvcat((2,1,2), B, 2I, I, 3I, 4I))::T == - hvcat((2,1,2), B, Matrix(2I, 3, 3), Matrix(I, 6, 6), Matrix(3I, 3, 3), Matrix(4I, 3, 3)) - @test hvcat((3,1), C, C, I, 3I)::T == hvcat((2,1), C, C, Matrix(3I, 6,6)) - @test hvcat((2,2,2), I, 2I, 3I, 4I, C, C)::T == - hvcat((2,2,2), Matrix(I, 3, 3), Matrix(2I, 3,3 ), Matrix(3I, 3,3), Matrix(4I, 3,3), C, C) - @test hvcat((2,2,4), C, C, I, 2I, 3I, 4I, 5I, D)::T == - hvcat((2,2,4), C, C, Matrix(I, 3, 3), Matrix(2I,3,3), - Matrix(3I, 2, 2), Matrix(4I, 2, 2), Matrix(5I,2,2), D) - @test (hvcat((2,3,2), B, 2I, C, C, I, 3I, 4I))::T == - hvcat((2,2,2), B, Matrix(2I, 3, 3), C, C, Matrix(3I, 3, 3), Matrix(4I, 3, 3)) - @test hvcat((3,2,1), C, C, I, B ,3I, 2I)::T == - hvcat((2,2,1), C, C, B, Matrix(3I,3,3), Matrix(2I,6,6)) - @test (hvcat((1,2), A, E, α))::T == hvcat((1,2), A, E, [α]) == hvcat((1,2), A, E, α*I) - @test (hvcat((2,2), α, E, F, 3I))::T == hvcat((2,2), [α], E, F, Matrix(3I, 3, 3)) - @test (hvcat((2,2), 3I, F, E, α))::T == hvcat((2,2), Matrix(3I, 3, 3), F, E, [α]) - end + + A = rand(3,4) + B = rand(3,3) + C = rand(0,3) + D = rand(2,0) + E = rand(1,3) + F = rand(3,1) + α = rand() + @test (hcat(A, 2I))::Matrix == hcat(A, Matrix(2I, 3, 3)) + @test (hcat(E, α))::Matrix == hcat(E, [α]) + @test (hcat(E, α, 2I))::Matrix == hcat(E, [α], fill(2, 1, 1)) + @test (vcat(A, 2I))::Matrix == vcat(A, Matrix(2I, 4, 4)) + @test (vcat(F, α))::Matrix == vcat(F, [α]) + @test (vcat(F, α, 2I))::Matrix == vcat(F, [α], fill(2, 1, 1)) + @test (hcat(C, 2I))::Matrix == C + @test_throws DimensionMismatch hcat(C, α) + @test (vcat(D, 2I))::Matrix == D + @test_throws DimensionMismatch vcat(D, α) + @test (hcat(I, 3I, A, 2I))::Matrix == hcat(Matrix(I, 3, 3), Matrix(3I, 3, 3), A, Matrix(2I, 3, 3)) + @test (vcat(I, 3I, A, 2I))::Matrix == vcat(Matrix(I, 4, 4), Matrix(3I, 4, 4), A, Matrix(2I, 4, 4)) + @test (hvcat((2,1,2), B, 2I, I, 3I, 4I))::Matrix == + hvcat((2,1,2), B, Matrix(2I, 3, 3), Matrix(I, 6, 6), Matrix(3I, 3, 3), Matrix(4I, 3, 3)) + @test hvcat((3,1), C, C, I, 3I)::Matrix == hvcat((2,1), C, C, Matrix(3I, 6,6)) + @test hvcat((2,2,2), I, 2I, 3I, 4I, C, C)::Matrix == + hvcat((2,2,2), Matrix(I, 3, 3), Matrix(2I, 3,3 ), Matrix(3I, 3,3), Matrix(4I, 3,3), C, C) + @test hvcat((2,2,4), C, C, I, 2I, 3I, 4I, 5I, D)::Matrix == + hvcat((2,2,4), C, C, Matrix(I, 3, 3), Matrix(2I,3,3), + Matrix(3I, 2, 2), Matrix(4I, 2, 2), Matrix(5I,2,2), D) + @test (hvcat((2,3,2), B, 2I, C, C, I, 3I, 4I))::Matrix == + hvcat((2,2,2), B, Matrix(2I, 3, 3), C, C, Matrix(3I, 3, 3), Matrix(4I, 3, 3)) + @test hvcat((3,2,1), C, C, I, B ,3I, 2I)::Matrix == + hvcat((2,2,1), C, C, B, Matrix(3I,3,3), Matrix(2I,6,6)) + @test (hvcat((1,2), A, E, α))::Matrix == hvcat((1,2), A, E, [α]) == hvcat((1,2), A, E, α*I) + @test (hvcat((2,2), α, E, F, 3I))::Matrix == hvcat((2,2), [α], E, F, Matrix(3I, 3, 3)) + @test (hvcat((2,2), 3I, F, E, α))::Matrix == hvcat((2,2), Matrix(3I, 3, 3), F, E, [α]) end @testset "Matrix/Array construction from UniformScaling" begin diff --git a/stdlib/SparseArrays/src/SparseArrays.jl b/stdlib/SparseArrays/src/SparseArrays.jl index 727abddbb62e8..7db213b6cfc6b 100644 --- a/stdlib/SparseArrays/src/SparseArrays.jl +++ b/stdlib/SparseArrays/src/SparseArrays.jl @@ -38,21 +38,9 @@ include("linalg.jl") include("deprecated.jl") -# temporarily moved here and commented out from from base/linalg/diagonal.jl, base/linalg/tridiag.jl -# and base/linalg/bidiag.jl due to their usage of spzeros -similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) -similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) -similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) -similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) - zero(a::AbstractSparseArray) = spzeros(eltype(a), size(a)...) -const BiTriSym = Union{Bidiagonal,SymTridiagonal,Tridiagonal} -function *(A::BiTriSym, B::BiTriSym) - TS = promote_op(matprod, eltype(A), eltype(B)) - mul!(similar(A, TS, size(A)...), A, B) -end - -LinearAlgebra.diagzero(D::Diagonal{<:AbstractSparseMatrix{T}},i,j) where {T} = spzeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) +LinearAlgebra.diagzero(D::Diagonal{<:AbstractSparseMatrix{T}},i,j) where {T} = + spzeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) end diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index c8f4eaaada8e0..edf2dd4056b06 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -4,6 +4,7 @@ import Base: sort, findall, copy! import LinearAlgebra: promote_to_array_type, promote_to_arrays_ +using LinearAlgebra: _SpecialArrays, _DenseConcatGroup ### The SparseVector @@ -1062,30 +1063,16 @@ vcat(X::Union{Vector,SparseVector}...) = vcat(map(sparse, X)...) ### Concatenation of un/annotated sparse/special/dense vectors/matrices -# TODO: These methods and definitions should be moved to a more appropriate location, -# particularly some future equivalent of base/linalg/special.jl dedicated to interactions -# between a broader set of matrix types. - -# TODO: A definition similar to the third exists in base/linalg/bidiag.jl. These definitions -# should be consolidated in a more appropriate location, e.g. base/linalg/special.jl. const _SparseArrays = Union{SparseVector, AbstractSparseMatrixCSC, Adjoint{<:Any,<:SparseVector}, Transpose{<:Any,<:SparseVector}} -const _SpecialArrays = Union{Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal} const _SparseConcatArrays = Union{_SpecialArrays, _SparseArrays} const _Symmetric_SparseConcatArrays{T,A<:_SparseConcatArrays} = Symmetric{T,A} const _Hermitian_SparseConcatArrays{T,A<:_SparseConcatArrays} = Hermitian{T,A} const _Triangular_SparseConcatArrays{T,A<:_SparseConcatArrays} = LinearAlgebra.AbstractTriangular{T,A} const _Annotated_SparseConcatArrays = Union{_Triangular_SparseConcatArrays, _Symmetric_SparseConcatArrays, _Hermitian_SparseConcatArrays} - -const _Symmetric_DenseArrays{T,A<:Matrix} = Symmetric{T,A} -const _Hermitian_DenseArrays{T,A<:Matrix} = Hermitian{T,A} -const _Triangular_DenseArrays{T,A<:Matrix} = LinearAlgebra.AbstractTriangular{T,A} -const _Annotated_DenseArrays = Union{_Triangular_DenseArrays, _Symmetric_DenseArrays, _Hermitian_DenseArrays} -const _Annotated_Typed_DenseArrays{T} = Union{_Triangular_DenseArrays{T}, _Symmetric_DenseArrays{T}, _Hermitian_DenseArrays{T}} - -const _SparseConcatGroup = Union{Number, Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} -const _DenseConcatGroup = Union{Number, Vector, Adjoint{<:Any,<:Vector}, Transpose{<:Any,<:Vector}, Matrix, _Annotated_DenseArrays} -const _TypedDenseConcatGroup{T} = Union{Vector{T}, Adjoint{T,Vector{T}}, Transpose{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} +# It's important that _SparseConcatGroup is a larger union than _DenseConcatGroup to make +# sparse cat-methods less specific and to kick in only if there is some sparse array present +const _SparseConcatGroup = Union{_DenseConcatGroup, _SparseConcatArrays, _Annotated_SparseConcatArrays} # Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays _makesparse(x::Number) = x @@ -1123,23 +1110,8 @@ _hvcat_rows(::Tuple{}, X::_SparseConcatGroup...) = () # make sure UniformScaling objects are converted to sparse matrices for concatenation promote_to_array_type(A::Tuple{Vararg{Union{_SparseConcatGroup,UniformScaling}}}) = SparseMatrixCSC -promote_to_array_type(A::Tuple{Vararg{Union{_DenseConcatGroup,UniformScaling}}}) = Matrix promote_to_arrays_(n::Int, ::Type{SparseMatrixCSC}, J::UniformScaling) = sparse(J, n, n) -# Concatenations strictly involving un/annotated dense matrices/vectors should yield dense arrays -Base._cat(dims, xs::_DenseConcatGroup...) = Base.cat_t(promote_eltype(xs...), xs...; dims=dims) -vcat(A::Vector...) = Base.typed_vcat(promote_eltype(A...), A...) -vcat(A::_DenseConcatGroup...) = Base.typed_vcat(promote_eltype(A...), A...) -hcat(A::Vector...) = Base.typed_hcat(promote_eltype(A...), A...) -hcat(A::_DenseConcatGroup...) = Base.typed_hcat(promote_eltype(A...), A...) -hvcat(rows::Tuple{Vararg{Int}}, xs::_DenseConcatGroup...) = Base.typed_hvcat(promote_eltype(xs...), rows, xs...) -# For performance, specially handle the case where the matrices/vectors have homogeneous eltype -Base._cat(dims, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.cat_t(T, xs...; dims=dims) -vcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_vcat(T, A...) -hcat(A::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hcat(T, A...) -hvcat(rows::Tuple{Vararg{Int}}, xs::_TypedDenseConcatGroup{T}...) where {T} = Base.typed_hvcat(T, rows, xs...) - - ### math functions ### Unary Map diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index 4a9a6477af9f9..e849be2051cb0 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -132,6 +132,8 @@ end # constructor methods well-exercised by the immediately preceding testset @test sparse(2I, 3, 4)::SparseMatrixCSC{Int,Int} == Matrix(2I, 3, 4) @test sparse(2I, (3, 4))::SparseMatrixCSC{Int,Int} == Matrix(2I, 3, 4) + @test sparse(3I, 4, 5) == sparse(1:4, 1:4, 3, 4, 5) + @test sparse(3I, 5, 4) == sparse(1:4, 1:4, 3, 5, 4) end se33 = SparseMatrixCSC{Float64}(I, 3, 3) @@ -154,6 +156,33 @@ do33 = fill(1.,3) @test_throws DimensionMismatch map(|, sqrboolmat, colboolmat) @test_throws DimensionMismatch map(xor, sqrboolmat, colboolmat) end + + # ascertain inference friendliness, ref. https://github.com/JuliaLang/julia/pull/25083#issuecomment-353031641 + sparsevec = SparseVector([1.0, 2.0, 3.0]) + @test map(-, Adjoint(sparsevec), Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} + @test map(-, Transpose(sparsevec), Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} + @test broadcast(-, Adjoint(sparsevec), Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} + @test broadcast(-, Transpose(sparsevec), Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} + @test broadcast(+, Adjoint(sparsevec), 1.0, Adjoint(sparsevec)) isa Adjoint{Float64,SparseVector{Float64,Int}} + @test broadcast(+, Transpose(sparsevec), 1.0, Transpose(sparsevec)) isa Transpose{Float64,SparseVector{Float64,Int}} + + @testset "binary ops with matrices" begin + λ = complex(randn(),randn()) + J = UniformScaling(λ) + B = bitrand(2, 2) + @test B + I == B + Matrix(I, size(B)) + @test I + B == B + Matrix(I, size(B)) + AA = randn(2, 2) + for SS in (sprandn(3,3, 0.5), sparse(Int(1)I, 3, 3)) + for S in (SS, view(SS, 1:3, 1:3)) + @test @inferred(I*S) !== S # Don't alias + @test @inferred(S*I) !== S # Don't alias + + @test @inferred(S*J) == S*λ + @test @inferred(J*S) == S*λ + end + end + end end @testset "Issue #30006" begin @@ -194,6 +223,14 @@ end @test nnz(blockdiag()) == 0 end + @testset "Diagonal of sparse matrices" begin + s = sparse([1 2; 3 4]) + D = Diagonal([s, s]) + @test D[1, 1] == s + @test D[1, 2] == zero(s) + @test isa(D[2, 1], SparseMatrixCSC) + end + @testset "concatenation promotion" begin sz41_f32 = spzeros(Float32, 4, 1) se33_i32 = sparse(Int32(1)I, 3, 3) @@ -213,6 +250,141 @@ end @test [a[1:2,1:2] a[1:2,3:4]; a[3:5,1] [a[3:4,2:4]; a[5:5,2:4]]] == a end end + + # should all yield sparse arrays + @testset "concatenations of combinations of special and other matrix types" begin + N = 4 + diagmat = Diagonal(1:N) + bidiagmat = Bidiagonal(1:N, 1:(N-1), :U) + tridiagmat = Tridiagonal(1:(N-1), 1:N, 1:(N-1)) + symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) + specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat) + # Test concatenating pairwise combinations of special matrices with sparse matrices, + # dense matrices, or dense vectors + spmat = spdiagm(0 => fill(1., N)) + spvec = sparse(fill(1., N)) + for specialmat in specialmats + # --> Tests applicable only to pairs of matrices + @test issparse(vcat(specialmat, spmat)) + @test issparse(vcat(spmat, specialmat)) + # --> Tests applicable also to pairs including vectors + for specialmat in specialmats, othermatorvec in (spmat, spvec) + @test issparse(hcat(specialmat, othermatorvec)) + @test issparse(hcat(othermatorvec, specialmat)) + @test issparse(hvcat((2,), specialmat, othermatorvec)) + @test issparse(hvcat((2,), othermatorvec, specialmat)) + @test issparse(cat(specialmat, othermatorvec; dims=(1,2))) + @test issparse(cat(othermatorvec, specialmat; dims=(1,2))) + end + end + end + + # Test that concatenations of annotated sparse/special matrix types with other matrix + # types yield sparse arrays, and that the code which effects that does not make concatenations + # strictly involving un/annotated dense matrices yield sparse arrays + @testset "concatenations of annotated types" begin + N = 4 + # The tested annotation types + testfull = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) + utriannotations = (UpperTriangular, UnitUpperTriangular) + ltriannotations = (LowerTriangular, UnitLowerTriangular) + triannotations = (utriannotations..., ltriannotations...) + symannotations = (Symmetric, Hermitian) + annotations = testfull ? (triannotations..., symannotations...) : (LowerTriangular, Symmetric) + # Concatenations involving these types, un/annotated, should yield sparse arrays + spvec = spzeros(N) + spmat = sparse(1.0I, N, N) + diagmat = Diagonal(1:N) + bidiagmat = Bidiagonal(1:N, 1:(N-1), :U) + tridiagmat = Tridiagonal(1:(N-1), 1:N, 1:(N-1)) + symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) + sparseconcatmats = testfull ? (spmat, diagmat, bidiagmat, tridiagmat, symtridiagmat) : (spmat, diagmat) + # Concatenations involving strictly these types, un/annotated, should yield dense arrays + densevec = fill(1., N) + densemat = fill(1., N, N) + # Annotated collections + annodmats = [annot(densemat) for annot in annotations] + annospcmats = [annot(spmat) for annot in annotations] + # Test that concatenations of pairwise combinations of annotated sparse/special + # yield sparse matrices + for annospcmata in annospcmats, annospcmatb in annospcmats + @test issparse(vcat(annospcmata, annospcmatb)) + @test issparse(hcat(annospcmata, annospcmatb)) + @test issparse(hvcat((2,), annospcmata, annospcmatb)) + @test issparse(cat(annospcmata, annospcmatb; dims=(1,2))) + end + # Test that concatenations of pairwise combinations of annotated sparse/special + # matrices and other matrix/vector types yield sparse matrices + for annospcmat in annospcmats + # --> Tests applicable to pairs including only matrices + for othermat in (densemat, annodmats..., sparseconcatmats...) + @test issparse(vcat(annospcmat, othermat)) + @test issparse(vcat(othermat, annospcmat)) + end + # --> Tests applicable to pairs including other vectors or matrices + for other in (spvec, densevec, densemat, annodmats..., sparseconcatmats...) + @test issparse(hcat(annospcmat, other)) + @test issparse(hcat(other, annospcmat)) + @test issparse(hvcat((2,), annospcmat, other)) + @test issparse(hvcat((2,), other, annospcmat)) + @test issparse(cat(annospcmat, other; dims=(1,2))) + @test issparse(cat(other, annospcmat; dims=(1,2))) + end + end + # The preceding tests should cover multi-way combinations of those types, but for good + # measure test a few multi-way combinations involving those types + @test issparse(vcat(spmat, densemat, annospcmats[1], annodmats[2])) + @test issparse(vcat(densemat, spmat, annodmats[1], annospcmats[2])) + @test issparse(hcat(spvec, annodmats[1], annospcmats[1], densevec, diagmat)) + @test issparse(hcat(annodmats[2], annospcmats[2], spvec, densevec, diagmat)) + @test issparse(hvcat((5,), diagmat, densevec, spvec, annodmats[1], annospcmats[1])) + @test issparse(hvcat((5,), spvec, annodmats[2], diagmat, densevec, annospcmats[2])) + @test issparse(cat(annodmats[1], diagmat, annospcmats[2], densevec, spvec; dims=(1,2))) + @test issparse(cat(spvec, diagmat, densevec, annospcmats[1], annodmats[2]; dims=(1,2))) + end + + @testset "hcat and vcat involving UniformScaling" begin + @test_throws ArgumentError hcat(I) + @test_throws ArgumentError [I I] + @test_throws ArgumentError vcat(I) + @test_throws ArgumentError [I; I] + @test_throws ArgumentError [I I; I] + + A = SparseMatrixCSC(rand(3,4)) + B = SparseMatrixCSC(rand(3,3)) + C = SparseMatrixCSC(rand(0,3)) + D = SparseMatrixCSC(rand(2,0)) + E = SparseMatrixCSC(rand(1,3)) + F = SparseMatrixCSC(rand(3,1)) + α = rand() + @test (hcat(A, 2I))::SparseMatrixCSC == hcat(A, Matrix(2I, 3, 3)) + @test (hcat(E, α))::SparseMatrixCSC == hcat(E, [α]) + @test (hcat(E, α, 2I))::SparseMatrixCSC == hcat(E, [α], fill(2, 1, 1)) + @test (vcat(A, 2I))::SparseMatrixCSC == vcat(A, Matrix(2I, 4, 4)) + @test (vcat(F, α))::SparseMatrixCSC == vcat(F, [α]) + @test (vcat(F, α, 2I))::SparseMatrixCSC == vcat(F, [α], fill(2, 1, 1)) + @test (hcat(C, 2I))::SparseMatrixCSC == C + @test_throws DimensionMismatch hcat(C, α) + @test (vcat(D, 2I))::SparseMatrixCSC == D + @test_throws DimensionMismatch vcat(D, α) + @test (hcat(I, 3I, A, 2I))::SparseMatrixCSC == hcat(Matrix(I, 3, 3), Matrix(3I, 3, 3), A, Matrix(2I, 3, 3)) + @test (vcat(I, 3I, A, 2I))::SparseMatrixCSC == vcat(Matrix(I, 4, 4), Matrix(3I, 4, 4), A, Matrix(2I, 4, 4)) + @test (hvcat((2,1,2), B, 2I, I, 3I, 4I))::SparseMatrixCSC == + hvcat((2,1,2), B, Matrix(2I, 3, 3), Matrix(I, 6, 6), Matrix(3I, 3, 3), Matrix(4I, 3, 3)) + @test hvcat((3,1), C, C, I, 3I)::SparseMatrixCSC == hvcat((2,1), C, C, Matrix(3I, 6,6)) + @test hvcat((2,2,2), I, 2I, 3I, 4I, C, C)::SparseMatrixCSC == + hvcat((2,2,2), Matrix(I, 3, 3), Matrix(2I, 3,3 ), Matrix(3I, 3,3), Matrix(4I, 3,3), C, C) + @test hvcat((2,2,4), C, C, I, 2I, 3I, 4I, 5I, D)::SparseMatrixCSC == + hvcat((2,2,4), C, C, Matrix(I, 3, 3), Matrix(2I,3,3), + Matrix(3I, 2, 2), Matrix(4I, 2, 2), Matrix(5I,2,2), D) + @test (hvcat((2,3,2), B, 2I, C, C, I, 3I, 4I))::SparseMatrixCSC == + hvcat((2,2,2), B, Matrix(2I, 3, 3), C, C, Matrix(3I, 3, 3), Matrix(4I, 3, 3)) + @test hvcat((3,2,1), C, C, I, B ,3I, 2I)::SparseMatrixCSC == + hvcat((2,2,1), C, C, B, Matrix(3I,3,3), Matrix(2I,6,6)) + @test (hvcat((1,2), A, E, α))::SparseMatrixCSC == hvcat((1,2), A, E, [α]) == hvcat((1,2), A, E, α*I) + @test (hvcat((2,2), α, E, F, 3I))::SparseMatrixCSC == hvcat((2,2), [α], E, F, Matrix(3I, 3, 3)) + @test (hvcat((2,2), 3I, F, E, α))::SparseMatrixCSC == hvcat((2,2), Matrix(3I, 3, 3), F, E, [α]) + end end let @@ -2206,6 +2378,31 @@ end @test_throws LinearAlgebra.SingularException UpperTriangular(A)\b end +@testset "Diagonal linear solve" begin + n = 12 + for relty in (Float32, Float64), elty in (relty, Complex{relty}) + dd=convert(Vector{elty}, randn(n)) + if elty <: Complex + dd+=im*convert(Vector{elty}, randn(n)) + end + D = Diagonal(dd) + b = rand(elty, n, n) + b = sparse(b) + @test ldiv!(D, copy(b)) ≈ Array(D)\Array(b) + @test_throws SingularException ldiv!(Diagonal(zeros(elty, n)), copy(b)) + b = rand(elty, n+1, n+1) + b = sparse(b) + @test_throws DimensionMismatch ldiv!(D, copy(b)) + b = view(rand(elty, n+1), Vector(1:n+1)) + @test_throws DimensionMismatch ldiv!(D, b) + for b in (sparse(rand(elty,n,n)), sparse(rand(elty,n))) + @test lmul!(copy(D), copy(b)) ≈ Array(D)*Array(b) + @test lmul!(transpose(copy(D)), copy(b)) ≈ transpose(Array(D))*Array(b) + @test lmul!(adjoint(copy(D)), copy(b)) ≈ Array(D)'*Array(b) + end + end +end + @testset "issue described in https://groups.google.com/forum/#!topic/julia-dev/QT7qpIpgOaA" begin @test sparse([1,1], [1,1], [true, true]) == sparse([1,1], [1,1], [true, true], 1, 1) == fill(true, 1, 1) @test sparsevec([1,1], [true, true]) == sparsevec([1,1], [true, true], 1) == fill(true, 1) @@ -2394,6 +2591,13 @@ end @test_throws BoundsError setindex!(A, [4.0, 5.0, 6.0], 3, 4) end +@testset "issue #29644" begin + F = lu(Tridiagonal(sparse(1.0I, 3, 3))) + @test F.L == Matrix(I, 3, 3) + @test startswith(sprint(show, MIME("text/plain"), F), + "$(LinearAlgebra.LU){Float64, $(LinearAlgebra.Tridiagonal){Float64, SparseArrays.SparseVector") +end + @testset "isstored" begin m = 5 n = 4 @@ -2649,6 +2853,32 @@ end @test similar(A, Float32, Int8, 6) == similar(A, Float32, Int8, (6,)) end +@testset "similar should preserve underlying storage type and uplo flag" begin + m, n = 4, 3 + sparsemat = sprand(m, m, 0.5) + for SymType in (Symmetric, Hermitian) + symsparsemat = SymType(sparsemat) + @test isa(similar(symsparsemat), typeof(symsparsemat)) + @test similar(symsparsemat).uplo == symsparsemat.uplo + @test isa(similar(symsparsemat, Float32), SymType{Float32,<:SparseMatrixCSC{Float32}}) + @test similar(symsparsemat, Float32).uplo == symsparsemat.uplo + @test isa(similar(symsparsemat, (n, n)), typeof(sparsemat)) + @test isa(similar(symsparsemat, Float32, (n, n)), SparseMatrixCSC{Float32}) + end +end + +@testset "similar should preserve underlying storage type" begin + local m, n = 4, 3 + sparsemat = sprand(m, m, 0.5) + for TriType in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) + trisparsemat = TriType(sparsemat) + @test isa(similar(trisparsemat), typeof(trisparsemat)) + @test isa(similar(trisparsemat, Float32), TriType{Float32,<:SparseMatrixCSC{Float32}}) + @test isa(similar(trisparsemat, (n, n)), typeof(sparsemat)) + @test isa(similar(trisparsemat, Float32, (n, n)), SparseMatrixCSC{Float32}) + end +end + @testset "count specializations" begin # count should throw for sparse arrays for which zero(eltype) does not exist @test_throws MethodError count(SparseMatrixCSC(2, 2, Int[1, 2, 3], Int[1, 2], Any[true, true])) From 068c55daf98d619e55c5e906d9749753517eb9e1 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 24 Nov 2021 17:27:35 +0100 Subject: [PATCH 2/2] add `sparse_*cat` --- NEWS.md | 6 +++ stdlib/LinearAlgebra/src/uniformscaling.jl | 11 ++-- stdlib/SparseArrays/docs/src/index.md | 3 ++ stdlib/SparseArrays/src/SparseArrays.jl | 3 +- stdlib/SparseArrays/src/sparsevector.jl | 49 ++++++++++++++++++ stdlib/SparseArrays/test/sparse.jl | 58 ++++++++++++++-------- 6 files changed, 102 insertions(+), 28 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5ff3cdd4f3756..6b9c6ba02f504 100644 --- a/NEWS.md +++ b/NEWS.md @@ -141,6 +141,12 @@ Standard library changes #### SparseArrays +* New sparse concatenation functions `sparse_hcat`, `sparse_vcat`, and `sparse_hvcat` return + `SparseMatrixCSC` output independent from the types of the input arguments. They make + concatenation behavior available, in which the presence of some special "sparse" matrix + argument resulted in sparse output by multiple dispatch. This is no longer possible after + making `LinearAlgebra.jl` independent from `SparseArrays.jl` ([#43127]). + #### Dates #### Downloads diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl index 03e60fe804cb1..98e3ed4dfcc59 100644 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ b/stdlib/LinearAlgebra/src/uniformscaling.jl @@ -407,7 +407,7 @@ for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols" @eval begin @inline $f(A::Union{AbstractVecOrMat,UniformScaling}...) = $_f(A...) @inline $f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) = $_f(A...) - function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...) + function $_f(A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 for a in A if !isa(a, UniformScaling) @@ -420,14 +420,14 @@ for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols" end end n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size"))) - return cat(promote_to_arrays(fill(n, length(A)), 1, promote_to_array_type(A), A...)..., dims=Val(3-$dim)) + return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim)) end end end hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling}...) = _hvcat(rows, A...) hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling,Number}...) = _hvcat(rows, A...) -function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling,Number}...) +function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling,Number}...; array_type = promote_to_array_type(A)) require_one_based_indexing(A...) nr = length(rows) sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments")) @@ -479,14 +479,13 @@ function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScali j += rows[i] end end - Atyp = promote_to_array_type(A) - Amat = promote_to_arrays(n, 1, Atyp, A...) + Amat = promote_to_arrays(n, 1, array_type, A...) # We have two methods for promote_to_array_type, one returning Matrix and # another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense # case, we cannot call hvcat for the promoted UniformScalings because this # causes a stack overflow. In the sparse case, however, we cannot call # typed_hvcat because we need a sparse output. - if Atyp == Matrix + if array_type == Matrix return typed_hvcat(promote_eltype(Amat...), rows, Amat...) else return hvcat(rows, Amat...) diff --git a/stdlib/SparseArrays/docs/src/index.md b/stdlib/SparseArrays/docs/src/index.md index 286c46c8b8402..45103fafa48c2 100644 --- a/stdlib/SparseArrays/docs/src/index.md +++ b/stdlib/SparseArrays/docs/src/index.md @@ -214,6 +214,9 @@ SparseArrays.nnz SparseArrays.findnz SparseArrays.spzeros SparseArrays.spdiagm +SparseArrays.sparse_hcat +SparseArrays.sparse_vcat +SparseArrays.sparse_hvcat SparseArrays.blockdiag SparseArrays.sprand SparseArrays.sprandn diff --git a/stdlib/SparseArrays/src/SparseArrays.jl b/stdlib/SparseArrays/src/SparseArrays.jl index 7db213b6cfc6b..873a7fb360e20 100644 --- a/stdlib/SparseArrays/src/SparseArrays.jl +++ b/stdlib/SparseArrays/src/SparseArrays.jl @@ -27,7 +27,8 @@ using Random: default_rng, AbstractRNG, randsubseq, randsubseq! export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector, SparseMatrixCSC, SparseVector, blockdiag, droptol!, dropzeros!, dropzeros, issparse, nonzeros, nzrange, rowvals, sparse, sparsevec, spdiagm, - sprand, sprandn, spzeros, nnz, permute, findnz + sprand, sprandn, spzeros, nnz, permute, findnz, + sparse_hcat, sparse_vcat, sparse_hvcat include("abstractsparse.jl") include("sparsematrix.jl") diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index edf2dd4056b06..982939bb8ea26 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -1112,6 +1112,55 @@ _hvcat_rows(::Tuple{}, X::_SparseConcatGroup...) = () promote_to_array_type(A::Tuple{Vararg{Union{_SparseConcatGroup,UniformScaling}}}) = SparseMatrixCSC promote_to_arrays_(n::Int, ::Type{SparseMatrixCSC}, J::UniformScaling) = sparse(J, n, n) +""" + sparse_hcat(A...) + +Concatenate along dimension 2. Return a SparseMatrixCSC object. + +!!! compat "Julia 1.8" + This method was added in Julia 1.8. It mimicks previous concatenation behavior, where + the concatenation with specialized "sparse" matrix types from LinearAlgebra.jl + automatically yielded sparse output even in the absence of any SparseArray argument. +""" +sparse_hcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(map(_makesparse, Xin)..., dims=Val(2)) +function sparse_hcat(X::Union{AbstractVecOrMat,UniformScaling,Number}...) + LinearAlgebra._hcat(X...; array_type = SparseMatrixCSC) +end + +""" + sparse_vcat(A...) + +Concatenate along dimension 1. Return a SparseMatrixCSC object. + +!!! compat "Julia 1.8" + This method was added in Julia 1.8. It mimicks previous concatenation behavior, where + the concatenation with specialized "sparse" matrix types from LinearAlgebra.jl + automatically yielded sparse output even in the absence of any SparseArray argument. +""" +sparse_vcat(Xin::Union{AbstractVecOrMat,Number}...) = cat(map(_makesparse, Xin)..., dims=Val(1)) +function sparse_vcat(X::Union{AbstractVecOrMat,UniformScaling,Number}...) + LinearAlgebra._vcat(X...; array_type = SparseMatrixCSC) +end + +""" + sparse_hvcat(rows::Tuple{Vararg{Int}}, values...) + +Sparse horizontal and vertical concatenation in one call. This function is called +for block matrix syntax. The first argument specifies the number of +arguments to concatenate in each block row. + +!!! compat "Julia 1.8" + This method was added in Julia 1.8. It mimicks previous concatenation behavior, where + the concatenation with specialized "sparse" matrix types from LinearAlgebra.jl + automatically yielded sparse output even in the absence of any SparseArray argument. +""" +function sparse_hvcat(rows::Tuple{Vararg{Int}}, Xin::Union{AbstractVecOrMat,Number}...) + hvcat(rows, map(_makesparse, Xin)...) +end +function sparse_hvcat(rows::Tuple{Vararg{Int}}, X::Union{AbstractVecOrMat,UniformScaling,Number}...) + LinearAlgebra._hvcat(rows, X...; array_type = SparseMatrixCSC) +end + ### math functions ### Unary Map diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index e849be2051cb0..dc98831f336c0 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -262,19 +262,27 @@ end # Test concatenating pairwise combinations of special matrices with sparse matrices, # dense matrices, or dense vectors spmat = spdiagm(0 => fill(1., N)) + dmat = Array(spmat) spvec = sparse(fill(1., N)) + dvec = Array(spvec) for specialmat in specialmats # --> Tests applicable only to pairs of matrices @test issparse(vcat(specialmat, spmat)) @test issparse(vcat(spmat, specialmat)) + @test sparse_vcat(specialmat, dmat)::SparseMatrixCSC == vcat(specialmat, spmat) + @test sparse_vcat(dmat, specialmat)::SparseMatrixCSC == vcat(spmat, specialmat) # --> Tests applicable also to pairs including vectors - for specialmat in specialmats, othermatorvec in (spmat, spvec) - @test issparse(hcat(specialmat, othermatorvec)) - @test issparse(hcat(othermatorvec, specialmat)) - @test issparse(hvcat((2,), specialmat, othermatorvec)) - @test issparse(hvcat((2,), othermatorvec, specialmat)) - @test issparse(cat(specialmat, othermatorvec; dims=(1,2))) - @test issparse(cat(othermatorvec, specialmat; dims=(1,2))) + for specialmat in specialmats, (smatorvec, dmatorvec) in ((spmat, dmat), (spvec, dvec)) + @test issparse(hcat(specialmat, smatorvec)) + @test sparse_hcat(specialmat, dmatorvec)::SparseMatrixCSC == hcat(specialmat, smatorvec) + @test issparse(hcat(smatorvec, specialmat)) + @test sparse_hcat(dmatorvec, specialmat)::SparseMatrixCSC == hcat(smatorvec, specialmat) + @test issparse(hvcat((2,), specialmat, smatorvec)) + @test sparse_hvcat((2,), specialmat, dmatorvec)::SparseMatrixCSC == hvcat((2,), specialmat, smatorvec) + @test issparse(hvcat((2,), smatorvec, specialmat)) + @test sparse_hvcat((2,), dmatorvec, specialmat)::SparseMatrixCSC == hvcat((2,), smatorvec, specialmat) + @test issparse(cat(specialmat, smatorvec; dims=(1,2))) + @test issparse(cat(smatorvec, specialmat; dims=(1,2))) end end end @@ -300,8 +308,8 @@ end symtridiagmat = SymTridiagonal(1:N, 1:(N-1)) sparseconcatmats = testfull ? (spmat, diagmat, bidiagmat, tridiagmat, symtridiagmat) : (spmat, diagmat) # Concatenations involving strictly these types, un/annotated, should yield dense arrays - densevec = fill(1., N) - densemat = fill(1., N, N) + densevec = Array(spvec) + densemat = Array(spmat) # Annotated collections annodmats = [annot(densemat) for annot in annotations] annospcmats = [annot(spmat) for annot in annotations] @@ -321,6 +329,14 @@ end @test issparse(vcat(annospcmat, othermat)) @test issparse(vcat(othermat, annospcmat)) end + for (smat, dmat) in zip(annospcmats, annodmats), specialmat in sparseconcatmats + @test sparse_hcat(dmat, specialmat)::SparseMatrixCSC == hcat(smat, specialmat) + @test sparse_hcat(specialmat, dmat)::SparseMatrixCSC == hcat(specialmat, smat) + @test sparse_vcat(dmat, specialmat)::SparseMatrixCSC == vcat(smat, specialmat) + @test sparse_vcat(specialmat, dmat)::SparseMatrixCSC == vcat(specialmat, smat) + @test sparse_hvcat((2,), dmat, specialmat)::SparseMatrixCSC == hvcat((2,), smat, specialmat) + @test sparse_hvcat((2,), specialmat, dmat)::SparseMatrixCSC == hvcat((2,), specialmat, smat) + end # --> Tests applicable to pairs including other vectors or matrices for other in (spvec, densevec, densemat, annodmats..., sparseconcatmats...) @test issparse(hcat(annospcmat, other)) @@ -357,30 +373,30 @@ end E = SparseMatrixCSC(rand(1,3)) F = SparseMatrixCSC(rand(3,1)) α = rand() - @test (hcat(A, 2I))::SparseMatrixCSC == hcat(A, Matrix(2I, 3, 3)) + @test (hcat(A, 2I, I(3)))::SparseMatrixCSC == hcat(A, Matrix(2I, 3, 3), Matrix(I, 3, 3)) @test (hcat(E, α))::SparseMatrixCSC == hcat(E, [α]) @test (hcat(E, α, 2I))::SparseMatrixCSC == hcat(E, [α], fill(2, 1, 1)) - @test (vcat(A, 2I))::SparseMatrixCSC == vcat(A, Matrix(2I, 4, 4)) + @test (vcat(A, 2I))::SparseMatrixCSC == (vcat(A, 2I(4)))::SparseMatrixCSC == vcat(A, Matrix(2I, 4, 4)) @test (vcat(F, α))::SparseMatrixCSC == vcat(F, [α]) - @test (vcat(F, α, 2I))::SparseMatrixCSC == vcat(F, [α], fill(2, 1, 1)) + @test (vcat(F, α, 2I))::SparseMatrixCSC == (vcat(F, α, 2I(1)))::SparseMatrixCSC == vcat(F, [α], fill(2, 1, 1)) @test (hcat(C, 2I))::SparseMatrixCSC == C @test_throws DimensionMismatch hcat(C, α) @test (vcat(D, 2I))::SparseMatrixCSC == D @test_throws DimensionMismatch vcat(D, α) @test (hcat(I, 3I, A, 2I))::SparseMatrixCSC == hcat(Matrix(I, 3, 3), Matrix(3I, 3, 3), A, Matrix(2I, 3, 3)) @test (vcat(I, 3I, A, 2I))::SparseMatrixCSC == vcat(Matrix(I, 4, 4), Matrix(3I, 4, 4), A, Matrix(2I, 4, 4)) - @test (hvcat((2,1,2), B, 2I, I, 3I, 4I))::SparseMatrixCSC == + @test (hvcat((2,1,2), B, 2I, I(6), 3I, 4I))::SparseMatrixCSC == hvcat((2,1,2), B, Matrix(2I, 3, 3), Matrix(I, 6, 6), Matrix(3I, 3, 3), Matrix(4I, 3, 3)) - @test hvcat((3,1), C, C, I, 3I)::SparseMatrixCSC == hvcat((2,1), C, C, Matrix(3I, 6,6)) + @test hvcat((3,1), C, C, I, 3I)::SparseMatrixCSC == hvcat((2,1), C, C, Matrix(3I, 6, 6)) @test hvcat((2,2,2), I, 2I, 3I, 4I, C, C)::SparseMatrixCSC == - hvcat((2,2,2), Matrix(I, 3, 3), Matrix(2I, 3,3 ), Matrix(3I, 3,3), Matrix(4I, 3,3), C, C) - @test hvcat((2,2,4), C, C, I, 2I, 3I, 4I, 5I, D)::SparseMatrixCSC == - hvcat((2,2,4), C, C, Matrix(I, 3, 3), Matrix(2I,3,3), - Matrix(3I, 2, 2), Matrix(4I, 2, 2), Matrix(5I,2,2), D) - @test (hvcat((2,3,2), B, 2I, C, C, I, 3I, 4I))::SparseMatrixCSC == + hvcat((2,2,2), Matrix(I, 3, 3), Matrix(2I, 3, 3), Matrix(3I, 3, 3), Matrix(4I, 3, 3), C, C) + @test hvcat((2,2,4), C, C, I(3), 2I, 3I, 4I, 5I, D)::SparseMatrixCSC == + hvcat((2,2,4), C, C, Matrix(I, 3, 3), Matrix(2I, 3, 3), + Matrix(3I, 2, 2), Matrix(4I, 2, 2), Matrix(5I, 2, 2), D) + @test (hvcat((2,3,2), B, 2I(3), C, C, I, 3I, 4I))::SparseMatrixCSC == hvcat((2,2,2), B, Matrix(2I, 3, 3), C, C, Matrix(3I, 3, 3), Matrix(4I, 3, 3)) - @test hvcat((3,2,1), C, C, I, B ,3I, 2I)::SparseMatrixCSC == - hvcat((2,2,1), C, C, B, Matrix(3I,3,3), Matrix(2I,6,6)) + @test hvcat((3,2,1), C, C, I, B, 3I(3), 2I)::SparseMatrixCSC == + hvcat((2,2,1), C, C, B, Matrix(3I, 3, 3), Matrix(2I, 6, 6)) @test (hvcat((1,2), A, E, α))::SparseMatrixCSC == hvcat((1,2), A, E, [α]) == hvcat((1,2), A, E, α*I) @test (hvcat((2,2), α, E, F, 3I))::SparseMatrixCSC == hvcat((2,2), [α], E, F, Matrix(3I, 3, 3)) @test (hvcat((2,2), 3I, F, E, α))::SparseMatrixCSC == hvcat((2,2), Matrix(3I, 3, 3), F, E, [α])