From e5c3f3a12d886ddf2d9ac8b5520dacf10ecd18a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 12 Jun 2020 15:39:49 +0200 Subject: [PATCH] Fix Matrix on diagonal and triangular matrices for types without zero --- stdlib/LinearAlgebra/src/bidiag.jl | 2 +- stdlib/LinearAlgebra/src/dense.jl | 10 +++++++--- stdlib/LinearAlgebra/src/triangular.jl | 2 +- stdlib/LinearAlgebra/src/tridiag.jl | 4 ++-- stdlib/LinearAlgebra/test/dense.jl | 23 +++++++++++++++++++---- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 3f7218a61eb27..871ad457239ea 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -163,7 +163,7 @@ function Matrix{T}(A::Bidiagonal) where T B[n,n] = A.dv[n] return B end -Matrix(A::Bidiagonal{T}) where {T} = Matrix{T}(A) +Matrix(A::Bidiagonal{T}) where {T} = Matrix{promote_with_zero(T)}(A) Array(A::Bidiagonal) = Matrix(A) promote_rule(::Type{Matrix{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = @isdefined(T) && @isdefined(S) ? Matrix{promote_type(T,S)} : Matrix diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index 4ea27e26bc7b8..99dd97ec5e706 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -299,11 +299,15 @@ function diagm_size(size::Tuple{Int,Int}, kv::Pair{<:Integer,<:AbstractVector}.. (m ≥ mmax && n ≥ nmax) || throw(DimensionMismatch("invalid size=$size")) return m, n end +# For some type `T`, `zero(T)` is not a `T` and `zeros(T, ...)` fails. +# `zero_type(T::Type) = typeof(zero(T))` would not work for types such +# as `Array` for which `zero(::T)` is defined but not `zero(::Type{T})` +# so we use `promote_op` instead. +zero_type(T::Type) = promote_op(zero, T) +promote_with_zero(T::Type) = promote_type(T, zero_type(T)) function diagm_container(size, kv::Pair{<:Integer,<:AbstractVector}...) T = promote_type(map(x -> eltype(x.second), kv)...) - # For some type `T`, `zero(T)` is not a `T` and `zeros(T, ...)` fails. - U = promote_type(T, typeof(zero(T))) - return zeros(U, diagm_size(size, kv...)...) + return zeros(promote_with_zero(T), diagm_size(size, kv...)...) end diagm_container(size, kv::Pair{<:Integer,<:BitVector}...) = falses(diagm_size(size, kv...)...) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 106443a0df648..2eaccd821e619 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -31,7 +31,7 @@ for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, Anew = convert(AbstractMatrix{T}, A.data) $t(Anew) end - Matrix(A::$t{T}) where {T} = Matrix{T}(A) + Matrix(A::$t{T}) where {T} = Matrix{promote_with_zero(T)}(A) size(A::$t, d) = size(A.data, d) size(A::$t) = size(A.data) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index ffa7cdeeff091..0a7f170702fcd 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -138,7 +138,7 @@ function Matrix{T}(M::SymTridiagonal) where T end return Mf end -Matrix(M::SymTridiagonal{T}) where {T} = Matrix{T}(M) +Matrix(M::SymTridiagonal{T}) where {T} = Matrix{promote_with_zero(T)}(M) Array(M::SymTridiagonal) = Matrix(M) size(A::SymTridiagonal) = (length(A.dv), length(A.dv)) @@ -579,7 +579,7 @@ function Matrix{T}(M::Tridiagonal{T}) where T end A end -Matrix(M::Tridiagonal{T}) where {T} = Matrix{T}(M) +Matrix(M::Tridiagonal{T}) where {T} = Matrix{promote_with_zero(T)}(M) Array(M::Tridiagonal) = Matrix(M) # For M<:Tridiagonal, similar(M[, neweltype]) should yield a Tridiagonal matrix. diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index 2dec8e95402d7..fb5b0856377bf 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -940,14 +940,29 @@ end end struct TypeWithoutZero end -Base.zero(::Type{TypeWithoutZero}) = TypeWithZero() +Base.zero(::Union{TypeWithoutZero, Type{TypeWithoutZero}}) = TypeWithZero() struct TypeWithZero end Base.promote_rule(::Type{TypeWithoutZero}, ::Type{TypeWithZero}) = TypeWithZero -Base.zero(::Type{<:Union{TypeWithoutZero, TypeWithZero}}) = TypeWithZero() +Base.convert(::Type{TypeWithZero}, ::TypeWithoutZero) = TypeWithZero() +Base.zero(::Union{TypeWithZero, Type{<:Union{TypeWithoutZero, TypeWithZero}}}) = TypeWithZero() Base.:+(x::TypeWithZero, ::TypeWithoutZero) = x -@testset "diagm for type with no zero" begin - @test diagm(0 => [TypeWithoutZero()]) isa Matrix{TypeWithZero} +@testset "diagm and Matrix for type with no zero" begin + x = TypeWithoutZero() + @test (@inferred diagm(0 => [x])) isa Matrix{TypeWithZero} + diag = [x, x, x] + offdiag = [x, x] + X = [x x + x x] + for A in [ + Bidiagonal(diag, offdiag, :U), + Tridiagonal(offdiag, diag, offdiag), + SymTridiagonal(diag, offdiag), + LowerTriangular(X), + UpperTriangular(X) + ] + @test (@inferred Matrix(A)) isa Matrix{TypeWithZero} + end end end # module TestDense