Skip to content

Commit 19919b7

Browse files
authored
Use copyto! in converting Diagonal/Bidiagonal/Tridiagonal to Matrix (#53912)
With this, we may convert a structured matrix to `Matrix` even if its `eltype` doesn't support `zero(T)`, as long as we may index into the matrix and the elements have `zero` defined for themselves. This makes the following work: ```julia julia> D = Diagonal(fill(Diagonal([1,3]), 2)) 2×2 Diagonal{Diagonal{Int64, Vector{Int64}}, Vector{Diagonal{Int64, Vector{Int64}}}}: [1 0; 0 3] ⋅ ⋅ [1 0; 0 3] julia> Matrix{eltype(D)}(D) 2×2 Matrix{Diagonal{Int64, Vector{Int64}}}: [1 0; 0 3] [0 0; 0 0] [0 0; 0 0] [1 0; 0 3] ``` We also may materialize partly initialized matrices: ```julia julia> D = Diagonal(Vector{BigInt}(undef, 2)) 2×2 Diagonal{BigInt, Vector{BigInt}}: #undef ⋅ ⋅ #undef julia> Matrix{eltype(D)}(D) 2×2 Matrix{BigInt}: #undef 0 0 #undef ``` The performance seems identical for numeric matrices.
1 parent 286e339 commit 19919b7

File tree

7 files changed

+67
-32
lines changed

7 files changed

+67
-32
lines changed

stdlib/LinearAlgebra/src/bidiag.jl

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,19 +195,14 @@ end
195195

196196
#Converting from Bidiagonal to dense Matrix
197197
function Matrix{T}(A::Bidiagonal) where T
198-
n = size(A, 1)
199-
B = Matrix{T}(undef, n, n)
200-
n == 0 && return B
201-
n > 1 && fill!(B, zero(T))
202-
@inbounds for i = 1:n - 1
203-
B[i,i] = A.dv[i]
204-
if A.uplo == 'U'
205-
B[i,i+1] = A.ev[i]
206-
else
207-
B[i+1,i] = A.ev[i]
208-
end
198+
B = Matrix{T}(undef, size(A))
199+
if haszero(T) # optimized path for types with zero(T) defined
200+
size(B,1) > 1 && fill!(B, zero(T))
201+
copyto!(view(B, diagind(B)), A.dv)
202+
copyto!(view(B, diagind(B, A.uplo == 'U' ? 1 : -1)), A.ev)
203+
else
204+
copyto!(B, A)
209205
end
210-
B[n,n] = A.dv[n]
211206
return B
212207
end
213208
Matrix(A::Bidiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(A)

stdlib/LinearAlgebra/src/diagonal.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ AbstractMatrix{T}(D::Diagonal{T}) where {T} = copy(D)
116116
Matrix(D::Diagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(D)
117117
Array(D::Diagonal{T}) where {T} = Matrix(D)
118118
function Matrix{T}(D::Diagonal) where {T}
119-
n = size(D, 1)
120-
B = Matrix{T}(undef, n, n)
121-
n > 1 && fill!(B, zero(T))
122-
@inbounds for i in 1:n
123-
B[i,i] = D.diag[i]
119+
B = Matrix{T}(undef, size(D))
120+
if haszero(T) # optimized path for types with zero(T) defined
121+
size(B,1) > 1 && fill!(B, zero(T))
122+
copyto!(view(B, diagind(B)), D.diag)
123+
else
124+
copyto!(B, D)
124125
end
125126
return B
126127
end

stdlib/LinearAlgebra/src/tridiag.jl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,17 @@ function Matrix{T}(M::SymTridiagonal) where T
135135
n = size(M, 1)
136136
Mf = Matrix{T}(undef, n, n)
137137
n == 0 && return Mf
138-
n > 2 && fill!(Mf, zero(T))
139-
@inbounds for i = 1:n-1
140-
Mf[i,i] = symmetric(M.dv[i], :U)
141-
Mf[i+1,i] = transpose(M.ev[i])
142-
Mf[i,i+1] = M.ev[i]
138+
if haszero(T) # optimized path for types with zero(T) defined
139+
n > 2 && fill!(Mf, zero(T))
140+
@inbounds for i = 1:n-1
141+
Mf[i,i] = symmetric(M.dv[i], :U)
142+
Mf[i+1,i] = transpose(M.ev[i])
143+
Mf[i,i+1] = M.ev[i]
144+
end
145+
Mf[n,n] = symmetric(M.dv[n], :U)
146+
else
147+
copyto!(Mf, M)
143148
end
144-
Mf[n,n] = symmetric(M.dv[n], :U)
145149
return Mf
146150
end
147151
Matrix(M::SymTridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M)
@@ -586,15 +590,14 @@ axes(M::Tridiagonal) = (ax = axes(M.d,1); (ax, ax))
586590

587591
function Matrix{T}(M::Tridiagonal) where {T}
588592
A = Matrix{T}(undef, size(M))
589-
n = length(M.d)
590-
n == 0 && return A
591-
n > 2 && fill!(A, zero(T))
592-
for i in 1:n-1
593-
A[i,i] = M.d[i]
594-
A[i+1,i] = M.dl[i]
595-
A[i,i+1] = M.du[i]
596-
end
597-
A[n,n] = M.d[n]
593+
if haszero(T) # optimized path for types with zero(T) defined
594+
size(A,1) > 2 && fill!(A, zero(T))
595+
copyto!(view(A, diagind(A)), M.d)
596+
copyto!(view(A, diagind(A,1)), M.du)
597+
copyto!(view(A, diagind(A,-1)), M.dl)
598+
else
599+
copyto!(A, M)
600+
end
598601
A
599602
end
600603
Matrix(M::Tridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M)

stdlib/LinearAlgebra/test/bidiag.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,4 +884,17 @@ end
884884
@test mul!(C1, B, sv, 1, 2) == mul!(C2, B, v, 1 ,2)
885885
end
886886

887+
@testset "Matrix conversion for non-numeric and undef" begin
888+
B = Bidiagonal(Vector{BigInt}(undef, 4), fill(big(3), 3), :U)
889+
M = Matrix(B)
890+
B[diagind(B)] .= 4
891+
M[diagind(M)] .= 4
892+
@test diag(B) == diag(M)
893+
894+
B = Bidiagonal(fill(Diagonal([1,3]), 3), fill(Diagonal([1,3]), 2), :U)
895+
M = Matrix{eltype(B)}(B)
896+
@test M isa Matrix{eltype(B)}
897+
@test M == B
898+
end
899+
887900
end # module TestBidiagonal

stdlib/LinearAlgebra/test/diagonal.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,4 +1292,17 @@ end
12921292
@test yadj == x'
12931293
end
12941294

1295+
@testset "Matrix conversion for non-numeric and undef" begin
1296+
D = Diagonal(Vector{BigInt}(undef, 4))
1297+
M = Matrix(D)
1298+
D[diagind(D)] .= 4
1299+
M[diagind(M)] .= 4
1300+
@test diag(D) == diag(M)
1301+
1302+
D = Diagonal(fill(Diagonal([1,3]), 2))
1303+
M = Matrix{eltype(D)}(D)
1304+
@test M isa Matrix{eltype(D)}
1305+
@test M == D
1306+
end
1307+
12951308
end # module TestDiagonal

stdlib/LinearAlgebra/test/special.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ Random.seed!(1)
113113
Base.convert(::Type{TypeWithZero}, ::TypeWithoutZero) = TypeWithZero()
114114
Base.zero(::Type{<:Union{TypeWithoutZero, TypeWithZero}}) = TypeWithZero()
115115
LinearAlgebra.symmetric(::TypeWithoutZero, ::Symbol) = TypeWithoutZero()
116+
LinearAlgebra.symmetric_type(::Type{TypeWithoutZero}) = TypeWithoutZero
117+
Base.copy(A::TypeWithoutZero) = A
116118
Base.transpose(::TypeWithoutZero) = TypeWithoutZero()
117119
d = fill(TypeWithoutZero(), 3)
118120
du = fill(TypeWithoutZero(), 2)

stdlib/LinearAlgebra/test/tridiag.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,4 +830,12 @@ end
830830
@test axes(B) === (ax, ax)
831831
end
832832

833+
@testset "Matrix conversion for non-numeric and undef" begin
834+
T = Tridiagonal(fill(big(3), 3), Vector{BigInt}(undef, 4), fill(big(3), 3))
835+
M = Matrix(T)
836+
T[diagind(T)] .= 4
837+
M[diagind(M)] .= 4
838+
@test diag(T) == diag(M)
839+
end
840+
833841
end # module TestTridiagonal

0 commit comments

Comments
 (0)