diff --git a/NEWS.md b/NEWS.md index 1e3c1729e1659..478b10b2a4f08 100644 --- a/NEWS.md +++ b/NEWS.md @@ -121,10 +121,11 @@ This section lists changes that do not have deprecation warnings. longer present. Use `first(R)` and `last(R)` to obtain start/stop. ([#20974]) - * The `Diagonal`, `Bidiagonal` and `SymTridiagonal` type definitions have changed from - `Diagonal{T}`, `Bidiagonal{T}` and `SymTridiagonal{T}` to `Diagonal{T,V<:AbstractVector{T}}`, - `Bidiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` - respectively ([#22718], [#22925], [#23035]). + * The `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` type definitions have + changed from `Diagonal{T}`, `Bidiagonal{T}`, `Tridiagonal{T}` and `SymTridiagonal{T}` + to `Diagonal{T,V<:AbstractVector{T}}`, `Bidiagonal{T,V<:AbstractVector{T}}`, + `Tridiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` + respectively ([#22718], [#22925], [#23035], [#23154]). * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero @@ -196,9 +197,10 @@ Library improvements * `Char`s can now be concatenated with `String`s and/or other `Char`s using `*` ([#22532]). - * `Diagonal`, `Bidiagonal` and `SymTridiagonal` are now parameterized on the type of the - wrapped vectors, allowing `Diagonal`, `Bidiagonal` and `SymTridiagonal` matrices with - arbitrary `AbstractVector`s ([#22718], [#22925], [#23035]). + * `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` are now parameterized on + the type of the wrapped vectors, allowing `Diagonal`, `Bidiagonal`, `Tridiagonal` and + `SymTridiagonal` matrices with arbitrary `AbstractVector`s + ([#22718], [#22925], [#23035], [#23154]). * Mutating versions of `randperm` and `randcycle` have been added: `randperm!` and `randcycle!` ([#22723]). @@ -261,8 +263,9 @@ Deprecated or removed * `Bidiagonal` constructors now use a `Symbol` (`:U` or `:L`) for the upper/lower argument, instead of a `Bool` or a `Char` ([#22703]). - * `Bidiagonal` and `SymTridiagonal` constructors that automatically converted the input - vectors to the same type are deprecated in favor of explicit conversion ([#22925], [#23035]). + * `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` constructors that automatically + converted the input vectors to the same type are deprecated in favor of explicit + conversion ([#22925], [#23035], [#23154]. * Calling `nfields` on a type to find out how many fields its instances have is deprecated. Use `fieldcount` instead. Use `nfields` only to get the number of fields in a specific object ([#22350]). diff --git a/base/deprecated.jl b/base/deprecated.jl index 97ec06ecb4484..bd0d992fd67fc 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1652,6 +1652,14 @@ function SymTridiagonal(dv::AbstractVector{T}, ev::AbstractVector{S}) where {T,S SymTridiagonal(convert(Vector{R}, dv), convert(Vector{R}, ev)) end +# PR #23154 +# also uncomment constructor tests in test/linalg/tridiag.jl +function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} + depwarn(string("Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) ", + "where {Tl, Td, Tu} is deprecated; convert all vectors to the same type instead."), :Tridiagonal) + Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) +end + # PR #23092 @eval LibGit2 begin function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index 4c18334f4f172..cce36f22dbb76 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -153,8 +153,10 @@ promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{prom #Converting from Bidiagonal to Tridiagonal Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M) function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T - z = zeros(T, size(A)[1]-1) - A.uplo == 'U' ? Tridiagonal(z, convert(Vector{T},A.dv), convert(Vector{T},A.ev)) : Tridiagonal(convert(Vector{T},A.ev), convert(Vector{T},A.dv), z) + dv = convert(AbstractVector{T}, A.dv) + ev = convert(AbstractVector{T}, A.ev) + z = fill!(similar(ev), zero(T)) + A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z) end promote_rule(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)} diff --git a/base/linalg/bunchkaufman.jl b/base/linalg/bunchkaufman.jl index 12763756fca5b..3d3865b64fbb6 100644 --- a/base/linalg/bunchkaufman.jl +++ b/base/linalg/bunchkaufman.jl @@ -116,7 +116,7 @@ julia> A = [1 2 3; 2 1 2; 3 2 1] julia> F = bkfact(Symmetric(A, :L)) Base.LinAlg.BunchKaufman{Float64,Array{Float64,2}} D factor: -3×3 Tridiagonal{Float64}: +3×3 Tridiagonal{Float64,Array{Float64,1}}: 1.0 3.0 ⋅ 3.0 1.0 0.0 ⋅ 0.0 -1.0 diff --git a/base/linalg/lu.jl b/base/linalg/lu.jl index d25f61a2a406f..fcab79e899e68 100644 --- a/base/linalg/lu.jl +++ b/base/linalg/lu.jl @@ -327,14 +327,14 @@ end # Tridiagonal # See dgttrf.f -function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(true)) where T +function lufact!(A::Tridiagonal{T,V}, pivot::Union{Val{false}, Val{true}} = Val(true)) where {T,V} n = size(A, 1) info = 0 ipiv = Vector{BlasInt}(n) dl = A.dl d = A.d du = A.du - du2 = A.du2 + du2 = fill!(similar(d, n-2), 0)::V @inbounds begin for i = 1:n @@ -389,12 +389,13 @@ function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(tr end end end - LU{T,Tridiagonal{T}}(A, ipiv, convert(BlasInt, info)) + B = Tridiagonal{T,V}(dl, d, du, du2) + LU{T,Tridiagonal{T,V}}(B, ipiv, convert(BlasInt, info)) end factorize(A::Tridiagonal) = lufact(A) -function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T +function getindex(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} m, n = size(F) if d == :L L = Array(Bidiagonal(ones(T, n), F.factors.dl, d)) @@ -419,7 +420,7 @@ function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T end # See dgtts2.f -function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function A_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -450,7 +451,7 @@ function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T return B end -function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function At_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -485,7 +486,7 @@ function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T end # Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) -function Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function Ac_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) @@ -528,7 +529,7 @@ convert(::Type{Matrix}, F::LU) = convert(Array, convert(AbstractArray, F)) convert(::Type{Array}, F::LU) = convert(Matrix, F) full(F::LU) = convert(AbstractArray, F) -function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where T +function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T,V}}) where {T,V} n = size(F, 1) dl = copy(F.factors.dl) @@ -562,12 +563,12 @@ function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where end return Tridiagonal(dl, d, du) end -convert(::Type{AbstractMatrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractMatrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Tridiagonal, F) -convert(::Type{AbstractArray}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractArray}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractMatrix, F) -convert(::Type{Matrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Matrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Array, convert(AbstractArray, F)) -convert(::Type{Array}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Array}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Matrix, F) -full(F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = convert(AbstractArray, F) +full(F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractArray, F) diff --git a/base/linalg/tridiag.jl b/base/linalg/tridiag.jl index 5fcaf92009201..d3ae5e1eb9bcf 100644 --- a/base/linalg/tridiag.jl +++ b/base/linalg/tridiag.jl @@ -399,71 +399,58 @@ function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) end ## Tridiagonal matrices ## -struct Tridiagonal{T} <: AbstractMatrix{T} - dl::Vector{T} # sub-diagonal - d::Vector{T} # diagonal - du::Vector{T} # sup-diagonal - du2::Vector{T} # supsup-diagonal for pivoting +struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} + dl::V # sub-diagonal + d::V # diagonal + du::V # sup-diagonal + du2::V # supsup-diagonal for pivoting in LU + function Tridiagonal{T}(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} + n = length(d) + if (length(dl) != n-1 || length(du) != n-1) + throw(ArgumentError(string("cannot construct Tridiagonal from incompatible ", + "lengths of subdiagonal, diagonal and superdiagonal: ", + "($(length(dl)), $(length(d)), $(length(du)))"))) + end + new{T,V}(dl, d, du) + end + # constructor used in lufact! + function Tridiagonal{T,V}(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} + new{T,V}(dl, d, du, du2) + end end """ - Tridiagonal(dl, d, du) + Tridiagonal(dl::V, d::V, du::V) where V <: AbstractVector Construct a tridiagonal matrix from the first subdiagonal, diagonal, and first superdiagonal, -respectively. The result is of type `Tridiagonal` and provides efficient specialized linear +respectively. The result is of type `Tridiagonal` and provides efficient specialized linear solvers, but may be converted into a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). The lengths of `dl` and `du` must be one less than the length of `d`. # Examples ```jldoctest -julia> dl = [1; 2; 3] -3-element Array{Int64,1}: - 1 - 2 - 3 +julia> dl = [1, 2, 3]; -julia> du = [4; 5; 6] -3-element Array{Int64,1}: - 4 - 5 - 6 +julia> du = [4, 5, 6]; -julia> d = [7; 8; 9; 0] -4-element Array{Int64,1}: - 7 - 8 - 9 - 0 +julia> d = [7, 8, 9, 0]; julia> Tridiagonal(dl, d, du) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 7 4 ⋅ ⋅ 1 8 5 ⋅ ⋅ 2 9 6 ⋅ ⋅ 3 0 ``` """ -# Basic constructor takes in three dense vectors of same type -function Tridiagonal(dl::Vector{T}, d::Vector{T}, du::Vector{T}) where T - n = length(d) - if (length(dl) != n-1 || length(du) != n-1) - throw(ArgumentError("cannot make Tridiagonal from incompatible lengths of subdiagonal, diagonal and superdiagonal: ($(length(dl)), $(length(d)), $(length(du))")) - end - Tridiagonal(dl, d, du, zeros(T,n-2)) -end - -# Construct from diagonals of any abstract vector, any eltype -function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} - Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) -end +Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T}(dl, d, du) -# Provide a constructor Tridiagonal(A) similar to the triangulars, diagonal, symmetric """ Tridiagonal(A) -returns a `Tridiagonal` array based on (abstract) matrix `A`, using its first lower diagonal, -main diagonal, and first upper diagonal. +Construct a tridiagonal matrix from the first sub-diagonal, +diagonal and first super-diagonal of the matrix `A`. # Examples ```jldoctest @@ -475,16 +462,14 @@ julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4] 1 2 3 4 julia> Tridiagonal(A) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 1 2 ⋅ ⋅ 1 2 3 ⋅ ⋅ 2 3 4 ⋅ ⋅ 3 4 ``` """ -function Tridiagonal(A::AbstractMatrix) - return Tridiagonal(diag(A,-1), diag(A), diag(A,+1)) -end +Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1)) size(M::Tridiagonal) = (length(M.d), length(M.d)) function size(M::Tridiagonal, d::Integer) @@ -512,31 +497,31 @@ convert(::Type{Matrix}, M::Tridiagonal{T}) where {T} = convert(Matrix{T}, M) convert(::Type{Array}, M::Tridiagonal) = convert(Matrix, M) full(M::Tridiagonal) = convert(Array, M) function similar(M::Tridiagonal, ::Type{T}) where T - Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T), similar(M.du2, T)) + Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) end # Operations on Tridiagonal matrices -copy!(dest::Tridiagonal, src::Tridiagonal) = Tridiagonal(copy!(dest.dl, src.dl), copy!(dest.d, src.d), copy!(dest.du, src.du), copy!(dest.du2, src.du2)) +copy!(dest::Tridiagonal, src::Tridiagonal) = (copy!(dest.dl, src.dl); copy!(dest.d, src.d); copy!(dest.du, src.du); dest) #Elementary operations -broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du), abs.(M.du2)) -broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du), round.(M.du2)) -broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du), trunc.(M.du2)) -broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du), floor.(M.du2)) -broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du), ceil.(M.du2)) +broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du)) +broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du)) +broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du)) +broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du)) +broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du)) for func in (:conj, :copy, :real, :imag) @eval function ($func)(M::Tridiagonal) - Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du), ($func)(M.du2)) + Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du)) end end broadcast(::typeof(round), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du), round.(T, M.du2)) + Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du)) broadcast(::typeof(trunc), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du), trunc.(T, M.du2)) + Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du)) broadcast(::typeof(floor), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du), floor.(T, M.du2)) + Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du)) broadcast(::typeof(ceil), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du), ceil.(T, M.du2)) + Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du)) transpose(M::Tridiagonal) = Tridiagonal(M.du, M.d, M.dl) ctranspose(M::Tridiagonal) = conj(transpose(M)) @@ -646,7 +631,8 @@ end inv(A::Tridiagonal) = inv_usmani(A.dl, A.d, A.du) det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du) -convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = Tridiagonal(convert(Vector{T}, M.dl), convert(Vector{T}, M.d), convert(Vector{T}, M.du), convert(Vector{T}, M.du2)) +convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = + Tridiagonal(convert(AbstractVector{T}, M.dl), convert(AbstractVector{T}, M.d), convert(AbstractVector{T}, M.du)) convert(::Type{AbstractMatrix{T}},M::Tridiagonal) where {T} = convert(Tridiagonal{T}, M) convert(::Type{Tridiagonal{T}}, M::SymTridiagonal{T}) where {T} = Tridiagonal(M) function convert(::Type{SymTridiagonal{T}}, M::Tridiagonal) where T diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index 6027c7981fe9c..2e231a7ec337a 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -41,10 +41,17 @@ B = randn(n,2) @test ST == Matrix(ST) @test ST.dv === x @test ST.ev === y + TT = (Tridiagonal(y, x, y))::Tridiagonal{elty, typeof(x)} + @test TT == Matrix(TT) + @test TT.dl === y + @test TT.d === x + @test TT.du === y end # enable when deprecations for 0.7 are dropped # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) + # @test_throws MethodError Tridiagonal(GenericArray(ev), dv, GenericArray(ev)) + # @test_throws MethodError Tridiagonal(ev, GenericArray(dv), ev) end @testset "size and Array" begin diff --git a/test/show.jl b/test/show.jl index 0a17da25462ba..5d576dcd2920d 100644 --- a/test/show.jl +++ b/test/show.jl @@ -598,7 +598,7 @@ A = reshape(1:16,4,4) @test replstr(Bidiagonal(A,:U)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n ⋅ 6 10 ⋅\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(Bidiagonal(A,:L)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n ⋅ 7 11 ⋅\n ⋅ ⋅ 12 16" @test replstr(SymTridiagonal(A+A')) == "4×4 SymTridiagonal{$(Int),Array{$(Int),1}}:\n 2 7 ⋅ ⋅\n 7 12 17 ⋅\n ⋅ 17 22 27\n ⋅ ⋅ 27 32" -@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$Int}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" +@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" @test replstr(UpperTriangular(copy(A))) == "4×4 UpperTriangular{$Int,Array{$Int,2}}:\n 1 5 9 13\n ⋅ 6 10 14\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(LowerTriangular(copy(A))) == "4×4 LowerTriangular{$Int,Array{$Int,2}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n 3 7 11 ⋅\n 4 8 12 16"