diff --git a/test/bidiag.jl b/test/bidiag.jl index ea4cfcf..a39fa02 100644 --- a/test/bidiag.jl +++ b/test/bidiag.jl @@ -7,9 +7,6 @@ using LinearAlgebra: BlasReal, BlasFloat const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) using .Main.Quaternions @@ -354,36 +351,33 @@ Random.seed!(1) @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) end @testset "Specialized multiplication/division" begin - getval(x) = x - getval(x::Furlong) = x.val function _bidiagdivmultest(T, x, typemul=T.uplo == 'U' ? UpperTriangular : Matrix, typediv=T.uplo == 'U' ? UpperTriangular : Matrix, typediv2=T.uplo == 'U' ? UpperTriangular : Matrix) TM = Matrix(T) - @test map(getval, (T*x)::typemul) ≈ map(getval, TM*x) - @test map(getval, (x*T)::typemul) ≈ map(getval, x*TM) - @test map(getval, (x\T)::typediv) ≈ map(getval, x\TM) - @test map(getval, (T/x)::typediv) ≈ map(getval, TM/x) + @test (T*x)::typemul ≈ TM*x + @test (x*T)::typemul ≈ x*TM + @test (x\T)::typediv ≈ x\TM + @test (T/x)::typediv ≈ TM/x if !isa(x, Number) - @test map(getval, Array((T\x)::typediv2)) ≈ map(getval, Array(TM\x)) - @test map(getval, Array((x/T)::typediv2)) ≈ map(getval, Array(x/TM)) + @test Array((T\x)::typediv2) ≈ Array(TM\x) + @test Array((x/T)::typediv2) ≈ Array(x/TM) end return nothing end A = Matrix(T) - for t in (T, Furlong.(T)), (A, dv, ev) in ((A, dv, ev), (Furlong.(A), Furlong.(dv), Furlong.(ev))) - _bidiagdivmultest(t, 5, Bidiagonal, Bidiagonal) - _bidiagdivmultest(t, 5I, Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) - _bidiagdivmultest(t, Diagonal(dv), Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) - _bidiagdivmultest(t, UpperTriangular(A)) - _bidiagdivmultest(t, UnitUpperTriangular(A)) - _bidiagdivmultest(t, LowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) - _bidiagdivmultest(t, UnitLowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) - _bidiagdivmultest(t, Bidiagonal(dv, ev, :U), Matrix, Matrix, Matrix) - _bidiagdivmultest(t, Bidiagonal(dv, ev, :L), Matrix, Matrix, Matrix) - end + t = T + _bidiagdivmultest(t, 5, Bidiagonal, Bidiagonal) + _bidiagdivmultest(t, 5I, Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) + _bidiagdivmultest(t, Diagonal(dv), Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) + _bidiagdivmultest(t, UpperTriangular(A)) + _bidiagdivmultest(t, UnitUpperTriangular(A)) + _bidiagdivmultest(t, LowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) + _bidiagdivmultest(t, UnitLowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :U), Matrix, Matrix, Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :L), Matrix, Matrix, Matrix) end end diff --git a/test/diagonal.jl b/test/diagonal.jl index 1cd2e9d..d1f46f4 100644 --- a/test/diagonal.jl +++ b/test/diagonal.jl @@ -6,8 +6,6 @@ using Test, LinearAlgebra, Random using LinearAlgebra: BlasFloat, BlasComplex const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) using .Main.OffsetArrays @@ -470,23 +468,6 @@ Random.seed!(1) @test svdvals(D) == s @test svd(D).V == V end - - @testset "svd/eigen with Diagonal{Furlong}" begin - Du = Furlong.(D) - @test Du isa Diagonal{<:Furlong{1}} - F = svd(Du) - U, s, V = F - @test map(x -> x.val, Matrix(F)) ≈ map(x -> x.val, Du) - @test svdvals(Du) == s - @test U isa AbstractMatrix{<:Furlong{0}} - @test V isa AbstractMatrix{<:Furlong{0}} - @test s isa AbstractVector{<:Furlong{1}} - E = eigen(Du) - vals, vecs = E - @test Matrix(E) == Du - @test vals isa AbstractVector{<:Furlong{1}} - @test vecs isa AbstractMatrix{<:Furlong{0}} - end end @testset "axes" begin diff --git a/test/givens.jl b/test/givens.jl index 62d677c..3726afa 100644 --- a/test/givens.jl +++ b/test/givens.jl @@ -82,16 +82,6 @@ using LinearAlgebra: Givens, Rotation, givensAlgorithm end end -# 36430 -# dimensional correctness: -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - -@testset "testing dimensions with Furlongs" begin - @test_throws MethodError givens(Furlong(1.0), Furlong(2.0), 1, 2) -end - const TNumber = Union{Float64,ComplexF64} struct MockUnitful{T<:TNumber} <: Number data::T diff --git a/test/hessenberg.jl b/test/hessenberg.jl index 486d927..ebab3f2 100644 --- a/test/hessenberg.jl +++ b/test/hessenberg.jl @@ -5,9 +5,6 @@ module TestHessenberg using Test, LinearAlgebra, Random const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) using .Main.SizedArrays @@ -68,29 +65,20 @@ let n = 10 @test Array(Hc + H) == Array(Hc) + Array(H) @test Array(Hc - H) == Array(Hc) - Array(H) @testset "Preserve UpperHessenberg shape (issue #39388)" begin - for H = (UpperHessenberg(Areal), UpperHessenberg(Furlong.(Areal))) - if eltype(H) <: Furlong - A = Furlong.(rand(n,n)) - d = Furlong.(rand(n)) - dl = Furlong.(rand(n-1)) - du = Furlong.(rand(n-1)) - us = Furlong(1)*I - else - A = rand(n,n) - d = rand(n) - dl = rand(n-1) - du = rand(n-1) - us = 1*I - end - @testset "$op" for op = (+,-) - for x = (us, Diagonal(d), Bidiagonal(d,dl,:U), Bidiagonal(d,dl,:L), - Tridiagonal(dl,d,du), SymTridiagonal(d,dl), - UpperTriangular(A), UnitUpperTriangular(A)) - @test op(H,x) == op(Array(H),x) - @test op(x,H) == op(x,Array(H)) - @test op(H,x) isa UpperHessenberg - @test op(x,H) isa UpperHessenberg - end + H = UpperHessenberg(Areal) + A = rand(n,n) + d = rand(n) + dl = rand(n-1) + du = rand(n-1) + us = 1*I + @testset "$op" for op = (+,-) + for x = (us, Diagonal(d), Bidiagonal(d,dl,:U), Bidiagonal(d,dl,:L), + Tridiagonal(dl,d,du), SymTridiagonal(d,dl), + UpperTriangular(A), UnitUpperTriangular(A)) + @test op(H,x) == op(Array(H),x) + @test op(x,H) == op(x,Array(H)) + @test op(H,x) isa UpperHessenberg + @test op(x,H) isa UpperHessenberg end end H = UpperHessenberg(Areal) @@ -102,8 +90,8 @@ let n = 10 UpperTriangular(A), UnitUpperTriangular(A)) @test (H*x)::UpperHessenberg ≈ Array(H)*x @test (x*H)::UpperHessenberg ≈ x*Array(H) - @test H/x ≈ Array(H)/x# broken = eltype(H) <: Furlong && x isa UpperTriangular - @test x\H ≈ x\Array(H)# broken = eltype(H) <: Furlong && x isa UpperTriangular + @test H/x ≈ Array(H)/x + @test x\H ≈ x\Array(H) @test H/x isa UpperHessenberg @test x\H isa UpperHessenberg end @@ -113,23 +101,6 @@ let n = 10 @test H/x == Array(H)/x @test x\H == x\Array(H) end - H = UpperHessenberg(Furlong.(Areal)) - for A in (A, Furlong.(A)) - @testset "Multiplication/division Furlong" begin - for x = (5, 5I, Diagonal(d), Bidiagonal(d,dl,:U), - UpperTriangular(A), UnitUpperTriangular(A)) - @test map(x -> x.val, (H*x)::UpperHessenberg) ≈ map(x -> x.val, Array(H)*x) - @test map(x -> x.val, (x*H)::UpperHessenberg) ≈ map(x -> x.val, x*Array(H)) - @test map(x -> x.val, (H/x)::UpperHessenberg) ≈ map(x -> x.val, Array(H)/x) - @test map(x -> x.val, (x\H)::UpperHessenberg) ≈ map(x -> x.val, x\Array(H)) - end - x = Bidiagonal(d, dl, :L) - @test H*x == Array(H)*x - @test x*H == x*Array(H) - @test H/x == Array(H)/x - @test x\H == x\Array(H) - end - end end end diff --git a/test/lu.jl b/test/lu.jl index 56a402d..f5b4e24 100644 --- a/test/lu.jl +++ b/test/lu.jl @@ -338,22 +338,6 @@ include("trickyarithmetic.jl") @test B isa LinearAlgebra.LU{ElT,Matrix{ElT}} end -# dimensional correctness: -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - -@testset "lu factorization with dimension type" begin - n = 4 - A = Matrix(Furlong(1.0) * I, n, n) - F = lu(A).factors - @test Diagonal(F) == Diagonal(A) - # upper triangular part has a unit Furlong{1} - @test all(x -> typeof(x) == Furlong{1, Float64}, F[i,j] for j=1:n for i=1:j) - # lower triangular part is unitless Furlong{0} - @test all(x -> typeof(x) == Furlong{0, Float64}, F[i,j] for j=1:n for i=j+1:n) -end - @testset "Issue #30917. Determinant of integer matrix" begin @test det([1 1 0 0 1 0 0 0 1 0 1 0 0 1 0 0 diff --git a/test/special.jl b/test/special.jl index 4b91bcf..ac76279 100644 --- a/test/special.jl +++ b/test/special.jl @@ -376,11 +376,6 @@ end end end -# for testing types with a dimension -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - @testset "zero and one for structured matrices" begin for elty in (Int64, Float64, ComplexF64) D = Diagonal(rand(elty, 10)) @@ -440,27 +435,6 @@ using .Main.Furlongs @test one(T) isa Tridiagonal @test zero(S) isa SymTridiagonal @test one(S) isa SymTridiagonal - - # eltype with dimensions - D0 = Diagonal{Furlong{0, Int64}}([1, 2, 3, 4]) - Bu0 = Bidiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3], 'U') - Bl0 = Bidiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3], 'L') - T0 = Tridiagonal{Furlong{0, Int64}}([1, 2, 3], [1, 2, 3, 4], [1, 2, 3]) - S0 = SymTridiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3]) - F2 = Furlongs.Furlong{2}(1) - D2 = Diagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2) - Bu2 = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2, 'U') - Bl2 = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2, 'L') - T2 = Tridiagonal{Furlong{2, Int64}}([1, 2, 3].*F2, [1, 2, 3, 4].*F2, [1, 2, 3].*F2) - S2 = SymTridiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2) - mats = Any[D0, Bu0, Bl0, T0, S0, D2, Bu2, Bl2, T2, S2] - for A in mats - @test iszero(zero(A)) - @test isone(one(A)) - @test zero(A) == zero(Matrix(A)) - @test one(A) == one(Matrix(A)) - @test eltype(one(A)) == typeof(one(eltype(A))) - end end @testset "== for structured matrices" begin diff --git a/test/testgroups b/test/testgroups index a335b1c..406f8c0 100644 --- a/test/testgroups +++ b/test/testgroups @@ -1,31 +1,34 @@ -triangular -addmul +unitful +triangular2 +triangular3 bidiag -bitarray +special +addmul matmul dense symmetric diagonal -special -qr +lu +bunchkaufman cholesky +tridiag +triangular +qr blas -lu uniformscaling structuredbroadcast +generic hessenberg svd eigen -tridiag lapack lq adjtrans -generic schur -bunchkaufman givens pinv factorization abstractq ldlt symmetriceigen +bitarray diff --git a/test/testtriag.jl b/test/testtriag.jl new file mode 100644 index 0000000..7542d84 --- /dev/null +++ b/test/testtriag.jl @@ -0,0 +1,510 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# The following test block tries to call all methods in base/linalg/triangular.jl in order for a combination of input element types. Keep the ordering when adding code. +function test_triangular(elty1_types) + n = 9 + @testset for elty1 in elty1_types + # Begin loop for first Triangular matrix + @testset for (t1, uplo1) in ((UpperTriangular, :U), + (UnitUpperTriangular, :U), + (LowerTriangular, :L), + (UnitLowerTriangular, :L)) + + # Construct test matrix + A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> cholesky(t't).U |> t -> uplo1 === :U ? t : copy(t'))) + M1 = Matrix(A1) + @test t1(A1) === A1 + @test t1{elty1}(A1) === A1 + # test the ctor works for AbstractMatrix + symm = Symmetric(rand(Int8, n, n)) + t1s = t1{elty1}(symm) + @test typeof(t1s) == t1{elty1,Symmetric{elty1,Matrix{elty1}}} + t1t = t1{elty1}(t1(rand(Int8, n, n))) + @test typeof(t1t) == t1{elty1,Matrix{elty1}} + + # Convert + @test convert(AbstractMatrix{elty1}, A1) == A1 + @test convert(Matrix, A1) == A1 + @test t1{elty1}(convert(AbstractMatrix{elty1}, A1)) == A1 + + # full! + @test full!(copy(A1)) == A1 + + # similar + @test isa(similar(A1), t1) + @test eltype(similar(A1)) == elty1 + @test isa(similar(A1, Int), t1) + @test eltype(similar(A1, Int)) == Int + @test isa(similar(A1, (3, 2)), Matrix{elty1}) + @test isa(similar(A1, Int, (3, 2)), Matrix{Int}) + + #copyto! + simA1 = similar(A1) + copyto!(simA1, A1) + @test simA1 == A1 + + # getindex + let mA1 = M1 + # linear indexing + for i in 1:length(A1) + @test A1[i] == mA1[i] + end + # cartesian indexing + for i in 1:size(A1, 1), j in 1:size(A1, 2) + @test A1[i, j] == mA1[i, j] + end + end + @test isa(A1[2:4, 1], Vector) + + + # setindex! (and copy) + A1c = copy(A1) + for i = 1:size(A1, 1) + for j = 1:size(A1, 2) + if uplo1 === :U + if i > j + A1c[i, j] = 0 + @test_throws ArgumentError A1c[i, j] = 1 + elseif i == j && t1 == UnitUpperTriangular + A1c[i, j] = 1 + @test_throws ArgumentError A1c[i, j] = 0 + else + A1c[i, j] = 0 + @test A1c[i, j] == 0 + end + else + if i < j + A1c[i, j] = 0 + @test_throws ArgumentError A1c[i, j] = 1 + elseif i == j && t1 == UnitLowerTriangular + A1c[i, j] = 1 + @test_throws ArgumentError A1c[i, j] = 0 + else + A1c[i, j] = 0 + @test A1c[i, j] == 0 + end + end + end + end + + # istril/istriu + if uplo1 === :L + @test istril(A1) + @test !istriu(A1) + @test istriu(A1') + @test istriu(transpose(A1)) + @test !istril(A1') + @test !istril(transpose(A1)) + else + @test istriu(A1) + @test !istril(A1) + @test istril(A1') + @test istril(transpose(A1)) + @test !istriu(A1') + @test !istriu(transpose(A1)) + end + M = copy(parent(A1)) + for trans in (adjoint, transpose), k in -1:1 + triu!(M, k) + @test istril(trans(M), -k) == istril(copy(trans(M)), -k) == true + end + M = copy(parent(A1)) + for trans in (adjoint, transpose), k in 1:-1:-1 + tril!(M, k) + @test istriu(trans(M), -k) == istriu(copy(trans(M)), -k) == true + end + + #tril/triu + if uplo1 === :L + @test tril(A1, 0) == A1 + @test tril(A1, -1) == LowerTriangular(tril(M1, -1)) + @test tril(A1, 1) == t1(tril(tril(M1, 1))) + @test tril(A1, -n - 2) == zeros(size(A1)) + @test tril(A1, n) == A1 + @test triu(A1, 0) == t1(diagm(0 => diag(A1))) + @test triu(A1, -1) == t1(tril(triu(A1.data, -1))) + @test triu(A1, 1) == zeros(size(A1)) # or just @test iszero(triu(A1,1))? + @test triu(A1, -n) == A1 + @test triu(A1, n + 2) == zeros(size(A1)) + else + @test triu(A1, 0) == A1 + @test triu(A1, 1) == UpperTriangular(triu(M1, 1)) + @test triu(A1, -1) == t1(triu(triu(M1, -1))) + @test triu(A1, -n) == A1 + @test triu(A1, n + 2) == zeros(size(A1)) + @test tril(A1, 0) == t1(diagm(0 => diag(A1))) + @test tril(A1, 1) == t1(triu(tril(A1.data, 1))) + @test tril(A1, -1) == zeros(size(A1)) # or just @test iszero(tril(A1,-1))? + @test tril(A1, -n - 2) == zeros(size(A1)) + @test tril(A1, n) == A1 + end + + # factorize + @test factorize(A1) == A1 + + # [c]transpose[!] (test views as well, see issue #14317) + let vrange = 1:n-1, viewA1 = t1(view(A1.data, vrange, vrange)) + # transpose + @test copy(transpose(A1)) == transpose(M1) + @test copy(transpose(viewA1)) == transpose(Matrix(viewA1)) + # adjoint + @test copy(A1') == M1' + @test copy(viewA1') == Matrix(viewA1)' + # transpose! + @test transpose!(copy(A1)) == transpose(A1) + @test typeof(transpose!(copy(A1))).name == typeof(transpose(A1)).name + @test transpose!(t1(view(copy(A1).data, vrange, vrange))) == transpose(viewA1) + # adjoint! + @test adjoint!(copy(A1)) == adjoint(A1) + @test typeof(adjoint!(copy(A1))).name == typeof(adjoint(A1)).name + @test adjoint!(t1(view(copy(A1).data, vrange, vrange))) == adjoint(viewA1) + end + + # diag + @test diag(A1) == diag(M1) + + # tr + @test tr(A1)::elty1 == tr(M1) + + # real + @test real(A1) == real(M1) + @test imag(A1) == imag(M1) + @test abs.(A1) == abs.(M1) + + # zero + if A1 isa UpperTriangular || A1 isa LowerTriangular + @test zero(A1) == zero(parent(A1)) + end + + # Unary operations + @test -A1 == -M1 + + # copy and copyto! (test views as well, see issue #14317) + let vrange = 1:n-1, viewA1 = t1(view(A1.data, vrange, vrange)) + # copy + @test copy(A1) == copy(M1) + @test copy(viewA1) == copy(Matrix(viewA1)) + # copyto! + B = similar(A1) + copyto!(B, A1) + @test B == A1 + B = similar(copy(transpose(A1))) + copyto!(B, copy(transpose(A1))) + @test B == copy(transpose(A1)) + B = similar(viewA1) + copyto!(B, viewA1) + @test B == viewA1 + B = similar(copy(transpose(viewA1))) + copyto!(B, copy(transpose(viewA1))) + @test B == transpose(viewA1) + end + + #exp/log + if elty1 ∈ (Float32, Float64, ComplexF32, ComplexF64) + @test exp(Matrix(log(A1))) ≈ A1 + end + + # scale + if (t1 == UpperTriangular || t1 == LowerTriangular) + unitt = istriu(A1) ? UnitUpperTriangular : UnitLowerTriangular + if elty1 == Int + cr = 2 + else + cr = 0.5 + end + ci = cr * im + if elty1 <: Real + A1tmp = copy(A1) + rmul!(A1tmp, cr) + @test A1tmp == cr * A1 + A1tmp = copy(A1) + lmul!(cr, A1tmp) + @test A1tmp == cr * A1 + A1tmp = copy(A1) + A2tmp = unitt(A1) + mul!(A1tmp, A2tmp, cr) + @test A1tmp == cr * A2tmp + A1tmp = copy(A1) + A2tmp = unitt(A1) + mul!(A1tmp, cr, A2tmp) + @test A1tmp == cr * A2tmp + + A1tmp .= A1 + @test mul!(A1tmp, A2tmp, cr, 0, 2) == 2A1 + A1tmp .= A1 + @test mul!(A1tmp, cr, A2tmp, 0, 2) == 2A1 + else + A1tmp = copy(A1) + rmul!(A1tmp, ci) + @test A1tmp == ci * A1 + A1tmp = copy(A1) + lmul!(ci, A1tmp) + @test A1tmp == ci * A1 + A1tmp = copy(A1) + A2tmp = unitt(A1) + mul!(A1tmp, ci, A2tmp) + @test A1tmp == ci * A2tmp + A1tmp = copy(A1) + A2tmp = unitt(A1) + mul!(A1tmp, A2tmp, ci) + @test A1tmp == A2tmp * ci + end + end + + # generalized dot + for eltyb in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) + b1 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1) * fill(1.0, n)) + b2 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1) * randn(n)) + @test dot(b1, A1, b2) ≈ dot(A1'b1, b2) atol = sqrt(max(eps(real(float(one(elty1)))), eps(real(float(one(eltyb)))))) * n * n + end + + # Binary operations + @test A1 * 0.5 == M1 * 0.5 + @test 0.5 * A1 == 0.5 * M1 + @test A1 / 0.5 == M1 / 0.5 + @test 0.5 \ A1 == 0.5 \ M1 + + # inversion + @test inv(A1) ≈ inv(lu(M1)) + inv(M1) # issue #11298 + @test isa(inv(A1), t1) + # make sure the call to LAPACK works right + if elty1 <: BlasFloat + @test LinearAlgebra.inv!(copy(A1)) ≈ inv(lu(M1)) + end + + # Determinant + @test det(A1) ≈ det(lu(M1)) atol = sqrt(eps(real(float(one(elty1))))) * n * n + @test logdet(A1) ≈ logdet(lu(M1)) atol = sqrt(eps(real(float(one(elty1))))) * n * n + lada, ladb = logabsdet(A1) + flada, fladb = logabsdet(lu(M1)) + @test lada ≈ flada atol = sqrt(eps(real(float(one(elty1))))) * n * n + @test ladb ≈ fladb atol = sqrt(eps(real(float(one(elty1))))) * n * n + + # Matrix square root + @test sqrt(A1) |> (t -> (t * t)::typeof(t)) ≈ A1 + + # naivesub errors + @test_throws DimensionMismatch ldiv!(A1, Vector{elty1}(undef, n + 1)) + + # eigenproblems + if !(elty1 in (BigFloat, Complex{BigFloat})) # Not handled yet + vals, vecs = eigen(A1) + if (t1 == UpperTriangular || t1 == LowerTriangular) && elty1 != Int # Cannot really handle degenerate eigen space and Int matrices will probably have repeated eigenvalues. + @test vecs * diagm(0 => vals) / vecs ≈ A1 atol = sqrt(eps(float(real(one(vals[1]))))) * (opnorm(A1, Inf) * n)^2 + end + end + + # Condition number tests - can be VERY approximate + if elty1 <: BlasFloat + for p in (1.0, Inf) + @test cond(A1, p) ≈ cond(A1, p) atol = (cond(A1, p) + cond(A1, p)) + end + @test cond(A1, 2) == cond(M1, 2) + end + + if !(elty1 in (BigFloat, Complex{BigFloat})) # Not implemented yet + svd(A1) + elty1 <: BlasFloat && svd!(copy(A1)) + svdvals(A1) + end + + @test ((A1 * A1)::t1) ≈ M1 * M1 + @test ((A1 / A1)::t1) ≈ M1 / M1 + @test ((A1 \ A1)::t1) ≈ M1 \ M1 + + # Begin loop for second Triangular matrix + @testset for elty2 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) + # Only test methods for the same element type and a single combination of mixed element types + # to avoid too much compilation + if !(elty1 == elty2 || elty1 ∈ (ComplexF32, Int) || elty1 ∈ (ComplexF32, Int)) + continue + end + @testset for (t2, uplo2) in ((UpperTriangular, :U), + (UnitUpperTriangular, :U), + (LowerTriangular, :L), + (UnitLowerTriangular, :L)) + + A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> cholesky(t't).U |> t -> uplo2 === :U ? t : copy(t'))) + M2 = Matrix(A2) + # Convert + if elty1 <: Real && !(elty2 <: Integer) + @test convert(AbstractMatrix{elty2}, A1) == t1(convert(Matrix{elty2}, A1.data)) + elseif elty2 <: Real && !(elty1 <: Integer) + @test_throws InexactError convert(AbstractMatrix{elty2}, A1) == t1(convert(Matrix{elty2}, A1.data)) + end + + # Binary operations + @test A1 + A2 == M1 + M2 + @test A1 - A2 == M1 - M2 + @test kron(A1, A2) == kron(M1, M2) + + # Triangular-Triangular multiplication and division + @test A1 * A2 ≈ M1 * M2 + @test transpose(A1) * A2 ≈ transpose(M1) * M2 + @test transpose(A1) * adjoint(A2) ≈ transpose(M1) * adjoint(M2) + @test adjoint(A1) * transpose(A2) ≈ adjoint(M1) * transpose(M2) + @test A1'A2 ≈ M1'M2 + @test A1 * transpose(A2) ≈ M1 * transpose(M2) + @test A1 * A2' ≈ M1 * M2' + @test transpose(A1) * transpose(A2) ≈ transpose(M1) * transpose(M2) + @test A1'A2' ≈ M1'M2' + @test A1 / A2 ≈ M1 / M2 + @test A1 \ A2 ≈ M1 \ M2 + if uplo1 === :U && uplo2 === :U + if t1 === UnitUpperTriangular && t2 === UnitUpperTriangular + @test A1 * A2 isa UnitUpperTriangular + @test A1 / A2 isa UnitUpperTriangular + elty1 == Int && elty2 == Int && @test eltype(A1 / A2) == Int + @test A1 \ A2 isa UnitUpperTriangular + elty1 == Int && elty2 == Int && @test eltype(A1 \ A2) == Int + else + @test A1 * A2 isa UpperTriangular + @test A1 / A2 isa UpperTriangular + elty1 == Int && elty2 == Int && t2 === UnitUpperTriangular && @test eltype(A1 / A2) == Int + @test A1 \ A2 isa UpperTriangular + elty1 == Int && elty2 == Int && t1 === UnitUpperTriangular && @test eltype(A1 \ A2) == Int + end + elseif uplo1 === :L && uplo2 === :L + if t1 === UnitLowerTriangular && t2 === UnitLowerTriangular + @test A1 * A2 isa UnitLowerTriangular + @test A1 / A2 isa UnitLowerTriangular + elty1 == Int && elty2 == Int && @test eltype(A1 / A2) == Int + @test A1 \ A2 isa UnitLowerTriangular + elty1 == Int && elty2 == Int && @test eltype(A1 \ A2) == Int + else + @test A1 * A2 isa LowerTriangular + @test A1 / A2 isa LowerTriangular + elty1 == Int && elty2 == Int && t2 === UnitLowerTriangular && @test eltype(A1 / A2) == Int + @test A1 \ A2 isa LowerTriangular + elty1 == Int && elty2 == Int && t1 === UnitLowerTriangular && @test eltype(A1 \ A2) == Int + end + end + offsizeA = Matrix{Float64}(I, n + 1, n + 1) + @test_throws DimensionMismatch offsizeA / A2 + @test_throws DimensionMismatch offsizeA / transpose(A2) + @test_throws DimensionMismatch offsizeA / A2' + @test_throws DimensionMismatch offsizeA * A2 + @test_throws DimensionMismatch offsizeA * transpose(A2) + @test_throws DimensionMismatch offsizeA * A2' + @test_throws DimensionMismatch transpose(A2) * offsizeA + @test_throws DimensionMismatch A2' * offsizeA + @test_throws DimensionMismatch A2 * offsizeA + if (uplo1 == uplo2 && elty1 == elty2 != Int && t1 != UnitLowerTriangular && t1 != UnitUpperTriangular) + @test rdiv!(copy(A1), A2)::t1 ≈ A1 / A2 ≈ M1 / M2 + @test ldiv!(A2, copy(A1))::t1 ≈ A2 \ A1 ≈ M2 \ M1 + end + if (uplo1 != uplo2 && elty1 == elty2 != Int && t2 != UnitLowerTriangular && t2 != UnitUpperTriangular) + @test lmul!(adjoint(A1), copy(A2)) ≈ A1' * A2 ≈ M1' * M2 + @test lmul!(transpose(A1), copy(A2)) ≈ transpose(A1) * A2 ≈ transpose(M1) * M2 + @test ldiv!(adjoint(A1), copy(A2)) ≈ A1' \ A2 ≈ M1' \ M2 + @test ldiv!(transpose(A1), copy(A2)) ≈ transpose(A1) \ A2 ≈ transpose(M1) \ M2 + end + if (uplo1 != uplo2 && elty1 == elty2 != Int && t1 != UnitLowerTriangular && t1 != UnitUpperTriangular) + @test rmul!(copy(A1), adjoint(A2)) ≈ A1 * A2' ≈ M1 * M2' + @test rmul!(copy(A1), transpose(A2)) ≈ A1 * transpose(A2) ≈ M1 * transpose(M2) + @test rdiv!(copy(A1), adjoint(A2)) ≈ A1 / A2' ≈ M1 / M2' + @test rdiv!(copy(A1), transpose(A2)) ≈ A1 / transpose(A2) ≈ M1 / transpose(M2) + end + end + end + + for eltyB in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) + # Only test methods for the same element type and a single combination of mixed element types + # to avoid too much compilation + if !(elty1 == eltyB || elty1 ∈ (ComplexF32, Int) || eltyB ∈ (ComplexF32, Int)) + continue + end + + B = convert(Matrix{eltyB}, (elty1 <: Complex ? real(A1) : A1) * fill(1.0, n, n)) + + Tri = Tridiagonal(rand(eltyB, n - 1), rand(eltyB, n), rand(eltyB, n - 1)) + C = Matrix{promote_type(elty1, eltyB)}(undef, n, n) + mul!(C, Tri, A1) + @test C ≈ Tri * M1 + Tri = Tridiagonal(rand(eltyB, n - 1), rand(eltyB, n), rand(eltyB, n - 1)) + mul!(C, A1, Tri) + @test C ≈ M1 * Tri + + # Triangular-dense Matrix/vector multiplication + @test A1 * B[:, 1] ≈ M1 * B[:, 1] + @test A1 * B ≈ M1 * B + @test transpose(A1) * B[:, 1] ≈ transpose(M1) * B[:, 1] + @test A1'B[:, 1] ≈ M1'B[:, 1] + @test transpose(A1) * B ≈ transpose(M1) * B + @test A1'B ≈ M1'B + @test A1 * transpose(B) ≈ M1 * transpose(B) + @test adjoint(A1) * transpose(B) ≈ M1' * transpose(B) + @test transpose(A1) * adjoint(B) ≈ transpose(M1) * adjoint(B) + @test A1 * B' ≈ M1 * B' + @test B * A1 ≈ B * M1 + @test transpose(B[:, 1]) * A1 ≈ transpose(B[:, 1]) * M1 + @test B[:, 1]'A1 ≈ B[:, 1]'M1 + @test transpose(B) * A1 ≈ transpose(B) * M1 + @test transpose(B) * adjoint(A1) ≈ transpose(B) * M1' + @test adjoint(B) * transpose(A1) ≈ adjoint(B) * transpose(M1) + @test B'A1 ≈ B'M1 + @test B * transpose(A1) ≈ B * transpose(M1) + @test B * A1' ≈ B * M1' + @test transpose(B[:, 1]) * transpose(A1) ≈ transpose(B[:, 1]) * transpose(M1) + @test B[:, 1]'A1' ≈ B[:, 1]'M1' + @test transpose(B) * transpose(A1) ≈ transpose(B) * transpose(M1) + @test B'A1' ≈ B'M1' + + if eltyB == elty1 + @test mul!(similar(B), A1, B) ≈ M1 * B + @test mul!(similar(B), A1, adjoint(B)) ≈ M1 * B' + @test mul!(similar(B), A1, transpose(B)) ≈ M1 * transpose(B) + @test mul!(similar(B), adjoint(A1), adjoint(B)) ≈ M1' * B' + @test mul!(similar(B), transpose(A1), transpose(B)) ≈ transpose(M1) * transpose(B) + @test mul!(similar(B), transpose(A1), adjoint(B)) ≈ transpose(M1) * B' + @test mul!(similar(B), adjoint(A1), transpose(B)) ≈ M1' * transpose(B) + @test mul!(similar(B), adjoint(A1), B) ≈ M1' * B + @test mul!(similar(B), transpose(A1), B) ≈ transpose(M1) * B + # test also vector methods + B1 = vec(B[1, :]) + @test mul!(similar(B1), A1, B1) ≈ M1 * B1 + @test mul!(similar(B1), adjoint(A1), B1) ≈ M1' * B1 + @test mul!(similar(B1), transpose(A1), B1) ≈ transpose(M1) * B1 + end + #error handling + Ann, Bmm, bm = A1, Matrix{eltyB}(undef, n + 1, n + 1), Vector{eltyB}(undef, n + 1) + @test_throws DimensionMismatch lmul!(Ann, bm) + @test_throws DimensionMismatch rmul!(Bmm, Ann) + @test_throws DimensionMismatch lmul!(transpose(Ann), bm) + @test_throws DimensionMismatch lmul!(adjoint(Ann), bm) + @test_throws DimensionMismatch rmul!(Bmm, adjoint(Ann)) + @test_throws DimensionMismatch rmul!(Bmm, transpose(Ann)) + + # ... and division + @test A1 \ B[:, 1] ≈ M1 \ B[:, 1] + @test A1 \ B ≈ M1 \ B + @test transpose(A1) \ B[:, 1] ≈ transpose(M1) \ B[:, 1] + @test A1' \ B[:, 1] ≈ M1' \ B[:, 1] + @test transpose(A1) \ B ≈ transpose(M1) \ B + @test A1' \ B ≈ M1' \ B + @test A1 \ transpose(B) ≈ M1 \ transpose(B) + @test A1 \ B' ≈ M1 \ B' + @test transpose(A1) \ transpose(B) ≈ transpose(M1) \ transpose(B) + @test A1' \ B' ≈ M1' \ B' + Ann, bm = A1, Vector{elty1}(undef, n + 1) + @test_throws DimensionMismatch Ann \ bm + @test_throws DimensionMismatch Ann' \ bm + @test_throws DimensionMismatch transpose(Ann) \ bm + if t1 == UpperTriangular || t1 == LowerTriangular + @test_throws SingularException ldiv!(t1(zeros(elty1, n, n)), fill(eltyB(1), n)) + end + @test B / A1 ≈ B / M1 + @test B / transpose(A1) ≈ B / transpose(M1) + @test B / A1' ≈ B / M1' + @test transpose(B) / A1 ≈ transpose(B) / M1 + @test B' / A1 ≈ B' / M1 + @test transpose(B) / transpose(A1) ≈ transpose(B) / transpose(M1) + @test B' / A1' ≈ B' / M1' + + # Error bounds + !(elty1 in (BigFloat, Complex{BigFloat})) && !(eltyB in (BigFloat, Complex{BigFloat})) && errorbounds(A1, A1 \ B, B) + end + end + end +end diff --git a/test/triangular.jl b/test/triangular.jl index 499e052..a5fa45d 100644 --- a/test/triangular.jl +++ b/test/triangular.jl @@ -3,9 +3,7 @@ module TestTriangular using Test, LinearAlgebra, Random -using LinearAlgebra: BlasFloat, errorbounds, full!, transpose!, - UnitUpperTriangular, UnitLowerTriangular, - mul!, rdiv!, rmul!, lmul!, BandIndex +using LinearAlgebra: errorbounds, transpose!, BandIndex const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") @@ -18,522 +16,15 @@ using .Main.FillArrays n = 9 Random.seed!(123) -@testset "Test basic type functionality" begin - @test_throws DimensionMismatch LowerTriangular(randn(5, 4)) - @test LowerTriangular(randn(3, 3)) |> t -> [size(t, i) for i = 1:3] == [size(Matrix(t), i) for i = 1:3] -end - struct MyTriangular{T, A<:LinearAlgebra.AbstractTriangular{T}} <: LinearAlgebra.AbstractTriangular{T} data :: A end Base.size(A::MyTriangular) = size(A.data) Base.getindex(A::MyTriangular, i::Int, j::Int) = A.data[i,j] -# The following test block tries to call all methods in base/linalg/triangular.jl in order for a combination of input element types. Keep the ordering when adding code. -@testset for elty1 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) - # Begin loop for first Triangular matrix - @testset for (t1, uplo1) in ((UpperTriangular, :U), - (UnitUpperTriangular, :U), - (LowerTriangular, :L), - (UnitLowerTriangular, :L)) - - # Construct test matrix - A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> cholesky(t't).U |> t -> uplo1 === :U ? t : copy(t'))) - M1 = Matrix(A1) - @test t1(A1) === A1 - @test t1{elty1}(A1) === A1 - # test the ctor works for AbstractMatrix - symm = Symmetric(rand(Int8, n, n)) - t1s = t1{elty1}(symm) - @test typeof(t1s) == t1{elty1, Symmetric{elty1, Matrix{elty1}}} - t1t = t1{elty1}(t1(rand(Int8, n, n))) - @test typeof(t1t) == t1{elty1, Matrix{elty1}} - - # Convert - @test convert(AbstractMatrix{elty1}, A1) == A1 - @test convert(Matrix, A1) == A1 - @test t1{elty1}(convert(AbstractMatrix{elty1}, A1)) == A1 - - # full! - @test full!(copy(A1)) == A1 - - # similar - @test isa(similar(A1), t1) - @test eltype(similar(A1)) == elty1 - @test isa(similar(A1, Int), t1) - @test eltype(similar(A1, Int)) == Int - @test isa(similar(A1, (3,2)), Matrix{elty1}) - @test isa(similar(A1, Int, (3,2)), Matrix{Int}) - - #copyto! - simA1 = similar(A1) - copyto!(simA1, A1) - @test simA1 == A1 - - # getindex - let mA1 = M1 - # linear indexing - for i in 1:length(A1) - @test A1[i] == mA1[i] - end - # cartesian indexing - for i in 1:size(A1, 1), j in 1:size(A1, 2) - @test A1[i,j] == mA1[i,j] - end - end - @test isa(A1[2:4,1], Vector) - - - # setindex! (and copy) - A1c = copy(A1) - for i = 1:size(A1, 1) - for j = 1:size(A1, 2) - if uplo1 === :U - if i > j - A1c[i,j] = 0 - @test_throws ArgumentError A1c[i,j] = 1 - elseif i == j && t1 == UnitUpperTriangular - A1c[i,j] = 1 - @test_throws ArgumentError A1c[i,j] = 0 - else - A1c[i,j] = 0 - @test A1c[i,j] == 0 - end - else - if i < j - A1c[i,j] = 0 - @test_throws ArgumentError A1c[i,j] = 1 - elseif i == j && t1 == UnitLowerTriangular - A1c[i,j] = 1 - @test_throws ArgumentError A1c[i,j] = 0 - else - A1c[i,j] = 0 - @test A1c[i,j] == 0 - end - end - end - end - - # istril/istriu - if uplo1 === :L - @test istril(A1) - @test !istriu(A1) - @test istriu(A1') - @test istriu(transpose(A1)) - @test !istril(A1') - @test !istril(transpose(A1)) - else - @test istriu(A1) - @test !istril(A1) - @test istril(A1') - @test istril(transpose(A1)) - @test !istriu(A1') - @test !istriu(transpose(A1)) - end - M = copy(parent(A1)) - for trans in (adjoint, transpose), k in -1:1 - triu!(M, k) - @test istril(trans(M), -k) == istril(copy(trans(M)), -k) == true - end - M = copy(parent(A1)) - for trans in (adjoint, transpose), k in 1:-1:-1 - tril!(M, k) - @test istriu(trans(M), -k) == istriu(copy(trans(M)), -k) == true - end - - #tril/triu - if uplo1 === :L - @test tril(A1,0) == A1 - @test tril(A1,-1) == LowerTriangular(tril(M1, -1)) - @test tril(A1,1) == t1(tril(tril(M1, 1))) - @test tril(A1, -n - 2) == zeros(size(A1)) - @test tril(A1, n) == A1 - @test triu(A1,0) == t1(diagm(0 => diag(A1))) - @test triu(A1,-1) == t1(tril(triu(A1.data,-1))) - @test triu(A1,1) == zeros(size(A1)) # or just @test iszero(triu(A1,1))? - @test triu(A1, -n) == A1 - @test triu(A1, n + 2) == zeros(size(A1)) - else - @test triu(A1,0) == A1 - @test triu(A1,1) == UpperTriangular(triu(M1, 1)) - @test triu(A1,-1) == t1(triu(triu(M1, -1))) - @test triu(A1, -n) == A1 - @test triu(A1, n + 2) == zeros(size(A1)) - @test tril(A1,0) == t1(diagm(0 => diag(A1))) - @test tril(A1,1) == t1(triu(tril(A1.data,1))) - @test tril(A1,-1) == zeros(size(A1)) # or just @test iszero(tril(A1,-1))? - @test tril(A1, -n - 2) == zeros(size(A1)) - @test tril(A1, n) == A1 - end - - # factorize - @test factorize(A1) == A1 - - # [c]transpose[!] (test views as well, see issue #14317) - let vrange = 1:n-1, viewA1 = t1(view(A1.data, vrange, vrange)) - # transpose - @test copy(transpose(A1)) == transpose(M1) - @test copy(transpose(viewA1)) == transpose(Matrix(viewA1)) - # adjoint - @test copy(A1') == M1' - @test copy(viewA1') == Matrix(viewA1)' - # transpose! - @test transpose!(copy(A1)) == transpose(A1) - @test typeof(transpose!(copy(A1))).name == typeof(transpose(A1)).name - @test transpose!(t1(view(copy(A1).data, vrange, vrange))) == transpose(viewA1) - # adjoint! - @test adjoint!(copy(A1)) == adjoint(A1) - @test typeof(adjoint!(copy(A1))).name == typeof(adjoint(A1)).name - @test adjoint!(t1(view(copy(A1).data, vrange, vrange))) == adjoint(viewA1) - end - - # diag - @test diag(A1) == diag(M1) - - # tr - @test tr(A1)::elty1 == tr(M1) - - # real - @test real(A1) == real(M1) - @test imag(A1) == imag(M1) - @test abs.(A1) == abs.(M1) - - # zero - if A1 isa UpperTriangular || A1 isa LowerTriangular - @test zero(A1) == zero(parent(A1)) - end - - # Unary operations - @test -A1 == -M1 - - # copy and copyto! (test views as well, see issue #14317) - let vrange = 1:n-1, viewA1 = t1(view(A1.data, vrange, vrange)) - # copy - @test copy(A1) == copy(M1) - @test copy(viewA1) == copy(Matrix(viewA1)) - # copyto! - B = similar(A1) - copyto!(B, A1) - @test B == A1 - B = similar(copy(transpose(A1))) - copyto!(B, copy(transpose(A1))) - @test B == copy(transpose(A1)) - B = similar(viewA1) - copyto!(B, viewA1) - @test B == viewA1 - B = similar(copy(transpose(viewA1))) - copyto!(B, copy(transpose(viewA1))) - @test B == transpose(viewA1) - end - - #exp/log - if elty1 ∈ (Float32,Float64,ComplexF32,ComplexF64) - @test exp(Matrix(log(A1))) ≈ A1 - end - - # scale - if (t1 == UpperTriangular || t1 == LowerTriangular) - unitt = istriu(A1) ? UnitUpperTriangular : UnitLowerTriangular - if elty1 == Int - cr = 2 - else - cr = 0.5 - end - ci = cr * im - if elty1 <: Real - A1tmp = copy(A1) - rmul!(A1tmp, cr) - @test A1tmp == cr*A1 - A1tmp = copy(A1) - lmul!(cr, A1tmp) - @test A1tmp == cr*A1 - A1tmp = copy(A1) - A2tmp = unitt(A1) - mul!(A1tmp, A2tmp, cr) - @test A1tmp == cr * A2tmp - A1tmp = copy(A1) - A2tmp = unitt(A1) - mul!(A1tmp, cr, A2tmp) - @test A1tmp == cr * A2tmp - - A1tmp .= A1 - @test mul!(A1tmp, A2tmp, cr, 0, 2) == 2A1 - A1tmp .= A1 - @test mul!(A1tmp, cr, A2tmp, 0, 2) == 2A1 - else - A1tmp = copy(A1) - rmul!(A1tmp, ci) - @test A1tmp == ci*A1 - A1tmp = copy(A1) - lmul!(ci, A1tmp) - @test A1tmp == ci*A1 - A1tmp = copy(A1) - A2tmp = unitt(A1) - mul!(A1tmp, ci, A2tmp) - @test A1tmp == ci * A2tmp - A1tmp = copy(A1) - A2tmp = unitt(A1) - mul!(A1tmp, A2tmp, ci) - @test A1tmp == A2tmp*ci - end - end - - # generalized dot - for eltyb in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) - b1 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1)*fill(1., n)) - b2 = convert(Vector{eltyb}, (elty1 <: Complex ? real(A1) : A1)*randn(n)) - @test dot(b1, A1, b2) ≈ dot(A1'b1, b2) atol=sqrt(max(eps(real(float(one(elty1)))),eps(real(float(one(eltyb))))))*n*n - end - - # Binary operations - @test A1*0.5 == M1*0.5 - @test 0.5*A1 == 0.5*M1 - @test A1/0.5 == M1/0.5 - @test 0.5\A1 == 0.5\M1 - - # inversion - @test inv(A1) ≈ inv(lu(M1)) - inv(M1) # issue #11298 - @test isa(inv(A1), t1) - # make sure the call to LAPACK works right - if elty1 <: BlasFloat - @test LinearAlgebra.inv!(copy(A1)) ≈ inv(lu(M1)) - end - - # Determinant - @test det(A1) ≈ det(lu(M1)) atol=sqrt(eps(real(float(one(elty1)))))*n*n - @test logdet(A1) ≈ logdet(lu(M1)) atol=sqrt(eps(real(float(one(elty1)))))*n*n - lada, ladb = logabsdet(A1) - flada, fladb = logabsdet(lu(M1)) - @test lada ≈ flada atol=sqrt(eps(real(float(one(elty1)))))*n*n - @test ladb ≈ fladb atol=sqrt(eps(real(float(one(elty1)))))*n*n - - # Matrix square root - @test sqrt(A1) |> (t -> (t*t)::typeof(t)) ≈ A1 - - # naivesub errors - @test_throws DimensionMismatch ldiv!(A1, Vector{elty1}(undef, n+1)) - - # eigenproblems - if !(elty1 in (BigFloat, Complex{BigFloat})) # Not handled yet - vals, vecs = eigen(A1) - if (t1 == UpperTriangular || t1 == LowerTriangular) && elty1 != Int # Cannot really handle degenerate eigen space and Int matrices will probably have repeated eigenvalues. - @test vecs*diagm(0 => vals)/vecs ≈ A1 atol=sqrt(eps(float(real(one(vals[1])))))*(opnorm(A1,Inf)*n)^2 - end - end - - # Condition number tests - can be VERY approximate - if elty1 <:BlasFloat - for p in (1.0, Inf) - @test cond(A1,p) ≈ cond(A1,p) atol=(cond(A1,p)+cond(A1,p)) - end - @test cond(A1,2) == cond(M1,2) - end - - if !(elty1 in (BigFloat, Complex{BigFloat})) # Not implemented yet - svd(A1) - elty1 <: BlasFloat && svd!(copy(A1)) - svdvals(A1) - end - - @test ((A1*A1)::t1) ≈ M1 * M1 - @test ((A1/A1)::t1) ≈ M1 / M1 - @test ((A1\A1)::t1) ≈ M1 \ M1 - - # Begin loop for second Triangular matrix - @testset for elty2 in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) - # Only test methods for the same element type and a single combination of mixed element types - # to avoid too much compilation - if !(elty1 == elty2 || elty1 ∈ (ComplexF32, Int) || elty1 ∈ (ComplexF32, Int)) - continue - end - @testset for (t2, uplo2) in ((UpperTriangular, :U), - (UnitUpperTriangular, :U), - (LowerTriangular, :L), - (UnitLowerTriangular, :L)) - - A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> cholesky(t't).U |> t -> uplo2 === :U ? t : copy(t'))) - M2 = Matrix(A2) - # Convert - if elty1 <: Real && !(elty2 <: Integer) - @test convert(AbstractMatrix{elty2}, A1) == t1(convert(Matrix{elty2}, A1.data)) - elseif elty2 <: Real && !(elty1 <: Integer) - @test_throws InexactError convert(AbstractMatrix{elty2}, A1) == t1(convert(Matrix{elty2}, A1.data)) - end - - # Binary operations - @test A1 + A2 == M1 + M2 - @test A1 - A2 == M1 - M2 - @test kron(A1,A2) == kron(M1,M2) - - # Triangular-Triangular multiplication and division - @test A1*A2 ≈ M1*M2 - @test transpose(A1)*A2 ≈ transpose(M1)*M2 - @test transpose(A1)*adjoint(A2) ≈ transpose(M1)*adjoint(M2) - @test adjoint(A1)*transpose(A2) ≈ adjoint(M1)*transpose(M2) - @test A1'A2 ≈ M1'M2 - @test A1*transpose(A2) ≈ M1*transpose(M2) - @test A1*A2' ≈ M1*M2' - @test transpose(A1)*transpose(A2) ≈ transpose(M1)*transpose(M2) - @test A1'A2' ≈ M1'M2' - @test A1/A2 ≈ M1/M2 - @test A1\A2 ≈ M1\M2 - if uplo1 === :U && uplo2 === :U - if t1 === UnitUpperTriangular && t2 === UnitUpperTriangular - @test A1*A2 isa UnitUpperTriangular - @test A1/A2 isa UnitUpperTriangular - elty1 == Int && elty2 == Int && @test eltype(A1/A2) == Int - @test A1\A2 isa UnitUpperTriangular - elty1 == Int && elty2 == Int && @test eltype(A1\A2) == Int - else - @test A1*A2 isa UpperTriangular - @test A1/A2 isa UpperTriangular - elty1 == Int && elty2 == Int && t2 === UnitUpperTriangular && @test eltype(A1/A2) == Int - @test A1\A2 isa UpperTriangular - elty1 == Int && elty2 == Int && t1 === UnitUpperTriangular && @test eltype(A1\A2) == Int - end - elseif uplo1 === :L && uplo2 === :L - if t1 === UnitLowerTriangular && t2 === UnitLowerTriangular - @test A1*A2 isa UnitLowerTriangular - @test A1/A2 isa UnitLowerTriangular - elty1 == Int && elty2 == Int && @test eltype(A1/A2) == Int - @test A1\A2 isa UnitLowerTriangular - elty1 == Int && elty2 == Int && @test eltype(A1\A2) == Int - else - @test A1*A2 isa LowerTriangular - @test A1/A2 isa LowerTriangular - elty1 == Int && elty2 == Int && t2 === UnitLowerTriangular && @test eltype(A1/A2) == Int - @test A1\A2 isa LowerTriangular - elty1 == Int && elty2 == Int && t1 === UnitLowerTriangular && @test eltype(A1\A2) == Int - end - end - offsizeA = Matrix{Float64}(I, n+1, n+1) - @test_throws DimensionMismatch offsizeA / A2 - @test_throws DimensionMismatch offsizeA / transpose(A2) - @test_throws DimensionMismatch offsizeA / A2' - @test_throws DimensionMismatch offsizeA * A2 - @test_throws DimensionMismatch offsizeA * transpose(A2) - @test_throws DimensionMismatch offsizeA * A2' - @test_throws DimensionMismatch transpose(A2) * offsizeA - @test_throws DimensionMismatch A2' * offsizeA - @test_throws DimensionMismatch A2 * offsizeA - if (uplo1 == uplo2 && elty1 == elty2 != Int && t1 != UnitLowerTriangular && t1 != UnitUpperTriangular) - @test rdiv!(copy(A1), A2)::t1 ≈ A1/A2 ≈ M1/M2 - @test ldiv!(A2, copy(A1))::t1 ≈ A2\A1 ≈ M2\M1 - end - if (uplo1 != uplo2 && elty1 == elty2 != Int && t2 != UnitLowerTriangular && t2 != UnitUpperTriangular) - @test lmul!(adjoint(A1), copy(A2)) ≈ A1'*A2 ≈ M1'*M2 - @test lmul!(transpose(A1), copy(A2)) ≈ transpose(A1)*A2 ≈ transpose(M1)*M2 - @test ldiv!(adjoint(A1), copy(A2)) ≈ A1'\A2 ≈ M1'\M2 - @test ldiv!(transpose(A1), copy(A2)) ≈ transpose(A1)\A2 ≈ transpose(M1)\M2 - end - if (uplo1 != uplo2 && elty1 == elty2 != Int && t1 != UnitLowerTriangular && t1 != UnitUpperTriangular) - @test rmul!(copy(A1), adjoint(A2)) ≈ A1*A2' ≈ M1*M2' - @test rmul!(copy(A1), transpose(A2)) ≈ A1*transpose(A2) ≈ M1*transpose(M2) - @test rdiv!(copy(A1), adjoint(A2)) ≈ A1/A2' ≈ M1/M2' - @test rdiv!(copy(A1), transpose(A2)) ≈ A1/transpose(A2) ≈ M1/transpose(M2) - end - end - end - - for eltyB in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) - # Only test methods for the same element type and a single combination of mixed element types - # to avoid too much compilation - if !(elty1 == eltyB || elty1 ∈ (ComplexF32, Int) || eltyB ∈ (ComplexF32, Int)) - continue - end - - B = convert(Matrix{eltyB}, (elty1 <: Complex ? real(A1) : A1)*fill(1., n, n)) - - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - C = Matrix{promote_type(elty1,eltyB)}(undef, n, n) - mul!(C, Tri, A1) - @test C ≈ Tri*M1 - Tri = Tridiagonal(rand(eltyB,n-1),rand(eltyB,n),rand(eltyB,n-1)) - mul!(C, A1, Tri) - @test C ≈ M1*Tri - - # Triangular-dense Matrix/vector multiplication - @test A1*B[:,1] ≈ M1*B[:,1] - @test A1*B ≈ M1*B - @test transpose(A1)*B[:,1] ≈ transpose(M1)*B[:,1] - @test A1'B[:,1] ≈ M1'B[:,1] - @test transpose(A1)*B ≈ transpose(M1)*B - @test A1'B ≈ M1'B - @test A1*transpose(B) ≈ M1*transpose(B) - @test adjoint(A1)*transpose(B) ≈ M1'*transpose(B) - @test transpose(A1)*adjoint(B) ≈ transpose(M1)*adjoint(B) - @test A1*B' ≈ M1*B' - @test B*A1 ≈ B*M1 - @test transpose(B[:,1])*A1 ≈ transpose(B[:,1])*M1 - @test B[:,1]'A1 ≈ B[:,1]'M1 - @test transpose(B)*A1 ≈ transpose(B)*M1 - @test transpose(B)*adjoint(A1) ≈ transpose(B)*M1' - @test adjoint(B)*transpose(A1) ≈ adjoint(B)*transpose(M1) - @test B'A1 ≈ B'M1 - @test B*transpose(A1) ≈ B*transpose(M1) - @test B*A1' ≈ B*M1' - @test transpose(B[:,1])*transpose(A1) ≈ transpose(B[:,1])*transpose(M1) - @test B[:,1]'A1' ≈ B[:,1]'M1' - @test transpose(B)*transpose(A1) ≈ transpose(B)*transpose(M1) - @test B'A1' ≈ B'M1' - - if eltyB == elty1 - @test mul!(similar(B), A1, B) ≈ M1*B - @test mul!(similar(B), A1, adjoint(B)) ≈ M1*B' - @test mul!(similar(B), A1, transpose(B)) ≈ M1*transpose(B) - @test mul!(similar(B), adjoint(A1), adjoint(B)) ≈ M1'*B' - @test mul!(similar(B), transpose(A1), transpose(B)) ≈ transpose(M1)*transpose(B) - @test mul!(similar(B), transpose(A1), adjoint(B)) ≈ transpose(M1)*B' - @test mul!(similar(B), adjoint(A1), transpose(B)) ≈ M1'*transpose(B) - @test mul!(similar(B), adjoint(A1), B) ≈ M1'*B - @test mul!(similar(B), transpose(A1), B) ≈ transpose(M1)*B - # test also vector methods - B1 = vec(B[1,:]) - @test mul!(similar(B1), A1, B1) ≈ M1*B1 - @test mul!(similar(B1), adjoint(A1), B1) ≈ M1'*B1 - @test mul!(similar(B1), transpose(A1), B1) ≈ transpose(M1)*B1 - end - #error handling - Ann, Bmm, bm = A1, Matrix{eltyB}(undef, n+1, n+1), Vector{eltyB}(undef, n+1) - @test_throws DimensionMismatch lmul!(Ann, bm) - @test_throws DimensionMismatch rmul!(Bmm, Ann) - @test_throws DimensionMismatch lmul!(transpose(Ann), bm) - @test_throws DimensionMismatch lmul!(adjoint(Ann), bm) - @test_throws DimensionMismatch rmul!(Bmm, adjoint(Ann)) - @test_throws DimensionMismatch rmul!(Bmm, transpose(Ann)) - - # ... and division - @test A1\B[:,1] ≈ M1\B[:,1] - @test A1\B ≈ M1\B - @test transpose(A1)\B[:,1] ≈ transpose(M1)\B[:,1] - @test A1'\B[:,1] ≈ M1'\B[:,1] - @test transpose(A1)\B ≈ transpose(M1)\B - @test A1'\B ≈ M1'\B - @test A1\transpose(B) ≈ M1\transpose(B) - @test A1\B' ≈ M1\B' - @test transpose(A1)\transpose(B) ≈ transpose(M1)\transpose(B) - @test A1'\B' ≈ M1'\B' - Ann, bm = A1, Vector{elty1}(undef,n+1) - @test_throws DimensionMismatch Ann\bm - @test_throws DimensionMismatch Ann'\bm - @test_throws DimensionMismatch transpose(Ann)\bm - if t1 == UpperTriangular || t1 == LowerTriangular - @test_throws SingularException ldiv!(t1(zeros(elty1, n, n)), fill(eltyB(1), n)) - end - @test B/A1 ≈ B/M1 - @test B/transpose(A1) ≈ B/transpose(M1) - @test B/A1' ≈ B/M1' - @test transpose(B)/A1 ≈ transpose(B)/M1 - @test B'/A1 ≈ B'/M1 - @test transpose(B)/transpose(A1) ≈ transpose(B)/transpose(M1) - @test B'/A1' ≈ B'/M1' - - # Error bounds - !(elty1 in (BigFloat, Complex{BigFloat})) && !(eltyB in (BigFloat, Complex{BigFloat})) && errorbounds(A1, A1\B, B) - - end - end +@testset "Test basic type functionality" begin + @test_throws DimensionMismatch LowerTriangular(randn(5, 4)) + @test LowerTriangular(randn(3, 3)) |> t -> [size(t, i) for i = 1:3] == [size(Matrix(t), i) for i = 1:3] end @testset "non-strided arithmetic" begin @@ -553,21 +44,22 @@ end end end -# Matrix square root -Atn = UpperTriangular([-1 1 2; 0 -2 2; 0 0 -3]) -Atp = UpperTriangular([1 1 2; 0 2 2; 0 0 3]) -Atu = UnitUpperTriangular([1 1 2; 0 1 2; 0 0 1]) -@test sqrt(Atn) |> t->t*t ≈ Atn -@test sqrt(Atn) isa UpperTriangular -@test typeof(sqrt(Atn)[1,1]) <: Complex -@test sqrt(Atp) |> t->t*t ≈ Atp -@test sqrt(Atp) isa UpperTriangular -@test typeof(sqrt(Atp)[1,1]) <: Real -@test typeof(sqrt(complex(Atp))[1,1]) <: Complex -@test sqrt(Atu) |> t->t*t ≈ Atu -@test sqrt(Atu) isa UnitUpperTriangular -@test typeof(sqrt(Atu)[1,1]) <: Real -@test typeof(sqrt(complex(Atu))[1,1]) <: Complex +@testset "Matrix square root" begin + Atn = UpperTriangular([-1 1 2; 0 -2 2; 0 0 -3]) + Atp = UpperTriangular([1 1 2; 0 2 2; 0 0 3]) + Atu = UnitUpperTriangular([1 1 2; 0 1 2; 0 0 1]) + @test sqrt(Atn) |> t->t*t ≈ Atn + @test sqrt(Atn) isa UpperTriangular + @test typeof(sqrt(Atn)[1,1]) <: Complex + @test sqrt(Atp) |> t->t*t ≈ Atp + @test sqrt(Atp) isa UpperTriangular + @test typeof(sqrt(Atp)[1,1]) <: Real + @test typeof(sqrt(complex(Atp))[1,1]) <: Complex + @test sqrt(Atu) |> t->t*t ≈ Atu + @test sqrt(Atu) isa UnitUpperTriangular + @test typeof(sqrt(Atu)[1,1]) <: Real + @test typeof(sqrt(complex(Atu))[1,1]) <: Complex +end @testset "matrix square root quasi-triangular blockwise" begin @testset for T in (Float32, Float64, ComplexF32, ComplexF64) @@ -748,28 +240,6 @@ end # dimensional correctness: const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs -LinearAlgebra.sylvester(a::Furlong,b::Furlong,c::Furlong) = -c / (a + b) - -@testset "dimensional correctness" begin - A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) - @test sqrt(A)::UpperTriangular == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) - @test inv(A)::UpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) - B = UnitUpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) - @test sqrt(B)::UnitUpperTriangular == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) - @test inv(B)::UnitUpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) - b = [Furlong(5), Furlong(8)] - @test (A \ b)::Vector{<:Furlong{0}} == (B \ b)::Vector{<:Furlong{0}} == Furlong{0}.([-27, 8]) - C = LowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) - @test sqrt(C)::LowerTriangular == Furlong{1//2}.(LowerTriangular([1 0; 2 1])) - @test inv(C)::LowerTriangular == Furlong{-1}.(LowerTriangular([1 0; -4 1])) - D = UnitLowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) - @test sqrt(D)::UnitLowerTriangular == Furlong{1//2}.(UnitLowerTriangular([1 0; 2 1])) - @test inv(D)::UnitLowerTriangular == Furlong{-1}.(UnitLowerTriangular([1 0; -4 1])) - b = [Furlong(5), Furlong(8)] - @test (C \ b)::Vector{<:Furlong{0}} == (D \ b)::Vector{<:Furlong{0}} == Furlong{0}.([5, -12]) -end isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) using .Main.ImmutableArrays diff --git a/test/triangular2.jl b/test/triangular2.jl new file mode 100644 index 0000000..4cad137 --- /dev/null +++ b/test/triangular2.jl @@ -0,0 +1,14 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module TestTriangularReal + +using Test, LinearAlgebra, Random +using LinearAlgebra: BlasFloat, errorbounds, full!, transpose! + +Random.seed!(123) + +include("testtriag.jl") # test_approx_eq_modphase + +test_triangular((Float32, Float64, BigFloat, Int)) + +end # module TestTriangularReal diff --git a/test/triangular3.jl b/test/triangular3.jl new file mode 100644 index 0000000..82a2a14 --- /dev/null +++ b/test/triangular3.jl @@ -0,0 +1,14 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module TestTriangularComplex + +using Test, LinearAlgebra, Random +using LinearAlgebra: BlasFloat, errorbounds, full!, transpose! + +Random.seed!(123) + +include("testtriag.jl") # test_approx_eq_modphase + +test_triangular((ComplexF32, ComplexF64, Complex{BigFloat})) + +end # module TestTriangularComplex diff --git a/test/unitful.jl b/test/unitful.jl new file mode 100644 index 0000000..cd4ade5 --- /dev/null +++ b/test/unitful.jl @@ -0,0 +1,201 @@ +module TestUnitfulLinAlg + +using Test, LinearAlgebra, Random + +Random.seed!(1234321) + +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) +using .Main.Furlongs + +LinearAlgebra.sylvester(a::Furlong,b::Furlong,c::Furlong) = -c / (a + b) + +getval(x) = x +getval(x::Furlong) = x.val + +# specialized unitful multiplication/solves +@testset "unitful specialized mul/div" begin + n = 10 #Size of test matrix + function _bidiagdivmultest(T, + x, + typemul=T.uplo == 'U' ? UpperTriangular : Matrix, + typediv=T.uplo == 'U' ? UpperTriangular : Matrix, + typediv2=T.uplo == 'U' ? UpperTriangular : Matrix) + TM = Matrix(T) + @test map(getval, (T * x)::typemul) ≈ map(getval, TM * x) + @test map(getval, (x * T)::typemul) ≈ map(getval, x * TM) + @test map(getval, (x \ T)::typediv) ≈ map(getval, x \ TM) + @test map(getval, (T / x)::typediv) ≈ map(getval, TM / x) + if !isa(x, Number) + @test map(getval, Array((T \ x)::typediv2)) ≈ map(getval, Array(TM \ x)) + @test map(getval, Array((x / T)::typediv2)) ≈ map(getval, Array(x / TM)) + end + return nothing + end + @testset for relty in (Int, Float64, BigFloat), elty in (relty, Complex{relty}) + if relty <: AbstractFloat + dv = convert(Vector{elty}, randn(n)) + ev = convert(Vector{elty}, randn(n - 1)) + if (elty <: Complex) + dv += im * convert(Vector{elty}, randn(n)) + ev += im * convert(Vector{elty}, randn(n - 1)) + end + elseif relty <: Integer + dv = convert(Vector{elty}, rand(1:10, n)) + ev = convert(Vector{elty}, rand(1:10, n - 1)) + if (elty <: Complex) + dv += im * convert(Vector{elty}, rand(1:10, n)) + ev += im * convert(Vector{elty}, rand(1:10, n - 1)) + end + end + @testset for uplo in (:U, :L) + T = Bidiagonal(dv, ev, uplo) + A = Matrix(T) + for t in (T, Furlong.(T)), (A, dv, ev) in ((A, dv, ev), (Furlong.(A), Furlong.(dv), Furlong.(ev))) + any(x -> x <: Furlong, (eltype(t), eltype(A))) || continue + _bidiagdivmultest(t, 5, Bidiagonal, Bidiagonal) + _bidiagdivmultest(t, 5I, Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) + _bidiagdivmultest(t, Diagonal(dv), Bidiagonal, Bidiagonal, t.uplo == 'U' ? UpperTriangular : LowerTriangular) + _bidiagdivmultest(t, UpperTriangular(A)) + _bidiagdivmultest(t, UnitUpperTriangular(A)) + _bidiagdivmultest(t, LowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) + _bidiagdivmultest(t, UnitLowerTriangular(A), t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix, t.uplo == 'L' ? LowerTriangular : Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :U), Matrix, Matrix, Matrix) + _bidiagdivmultest(t, Bidiagonal(dv, ev, :L), Matrix, Matrix, Matrix) + end + end + end +end + +# diagonal +@testset for relty in (Float32, Float64, BigFloat), elty in (relty, Complex{relty}) + n = 12 + dd = convert(Vector{elty}, randn(n)) + D = Diagonal(dd) + @testset "svd/eigen with Diagonal{Furlong}" begin + Du = Furlong.(D) + @test Du isa Diagonal{<:Furlong{1}} + F = svd(Du) + U, s, V = F + @test map(getval, Matrix(F)) ≈ map(getval, Du) + @test svdvals(Du) == s + @test U isa AbstractMatrix{<:Furlong{0}} + @test V isa AbstractMatrix{<:Furlong{0}} + @test s isa AbstractVector{<:Furlong{1}} + E = eigen(Du) + vals, vecs = E + @test Matrix(E) == Du + @test vals isa AbstractVector{<:Furlong{1}} + @test vecs isa AbstractMatrix{<:Furlong{0}} + end +end + +# givens +@testset "testing dimensions with Furlongs #36430" begin + @test_throws MethodError givens(Furlong(1.0), Furlong(2.0), 1, 2) +end + +# hessenberg +@testset "dimensional Hessenberg" begin + n = 10 + Random.seed!(1234321) + + Areal = randn(n, n) / 2 + @testset "Preserve UpperHessenberg shape (issue #39388)" begin + H = UpperHessenberg(Furlong.(Areal)) + A = Furlong.(rand(n, n)) + d = Furlong.(rand(n)) + dl = Furlong.(rand(n - 1)) + du = Furlong.(rand(n - 1)) + us = Furlong(1) * I + + @testset "$op" for op = (+, -) + for x = (us, Diagonal(d), Bidiagonal(d, dl, :U), Bidiagonal(d, dl, :L), + Tridiagonal(dl, d, du), SymTridiagonal(d, dl), + UpperTriangular(A), UnitUpperTriangular(A)) + @test op(H, x) == op(Array(H), x) + @test op(x, H) == op(x, Array(H)) + @test op(H, x) isa UpperHessenberg + @test op(x, H) isa UpperHessenberg + end + end + end + H = UpperHessenberg(Furlong.(Areal)) + A = randn(n, n) + d = randn(n) + dl = randn(n - 1) + for A in (A, Furlong.(A)) + @testset "Multiplication/division Furlong" begin + for x = (5, 5I, Diagonal(d), Bidiagonal(d, dl, :U), + UpperTriangular(A), UnitUpperTriangular(A)) + @test map(getval, (H * x)::UpperHessenberg) ≈ map(getval, Array(H) * x) + @test map(getval, (x * H)::UpperHessenberg) ≈ map(getval, x * Array(H)) + @test map(getval, (H / x)::UpperHessenberg) ≈ map(getval, Array(H) / x) + @test map(getval, (x \ H)::UpperHessenberg) ≈ map(getval, x \ Array(H)) + end + x = Bidiagonal(d, dl, :L) + @test H * x == Array(H) * x + @test x * H == x * Array(H) + @test H / x == Array(H) / x + @test x \ H == x \ Array(H) + end + end +end + +# lu +@testset "lu factorization with dimension type" begin + n = 4 + A = Matrix(Furlong(1.0) * I, n, n) + F = lu(A).factors + @test Diagonal(F) == Diagonal(A) + # upper triangular part has a unit Furlong{1} + @test all(x -> typeof(x) == Furlong{1,Float64}, F[i, j] for j = 1:n for i = 1:j) + # lower triangular part is unitless Furlong{0} + @test all(x -> typeof(x) == Furlong{0,Float64}, F[i, j] for j = 1:n for i = j+1:n) +end + +# special +@testset "zero and one for unitful structured matrices" begin + # eltype with dimensions + D0 = Diagonal{Furlong{0,Int64}}([1, 2, 3, 4]) + Bu0 = Bidiagonal{Furlong{0,Int64}}([1, 2, 3, 4], [1, 2, 3], 'U') + Bl0 = Bidiagonal{Furlong{0,Int64}}([1, 2, 3, 4], [1, 2, 3], 'L') + T0 = Tridiagonal{Furlong{0,Int64}}([1, 2, 3], [1, 2, 3, 4], [1, 2, 3]) + S0 = SymTridiagonal{Furlong{0,Int64}}([1, 2, 3, 4], [1, 2, 3]) + F2 = Furlongs.Furlong{2}(1) + D2 = Diagonal{Furlong{2,Int64}}([1, 2, 3, 4] .* F2) + Bu2 = Bidiagonal{Furlong{2,Int64}}([1, 2, 3, 4] .* F2, [1, 2, 3] .* F2, 'U') + Bl2 = Bidiagonal{Furlong{2,Int64}}([1, 2, 3, 4] .* F2, [1, 2, 3] .* F2, 'L') + T2 = Tridiagonal{Furlong{2,Int64}}([1, 2, 3] .* F2, [1, 2, 3, 4] .* F2, [1, 2, 3] .* F2) + S2 = SymTridiagonal{Furlong{2,Int64}}([1, 2, 3, 4] .* F2, [1, 2, 3] .* F2) + mats = Any[D0, Bu0, Bl0, T0, S0, D2, Bu2, Bl2, T2, S2] + for A in mats + @test iszero(zero(A)) + @test isone(one(A)) + @test zero(A) == zero(Matrix(A)) + @test one(A) == one(Matrix(A)) + @test eltype(one(A)) == typeof(one(eltype(A))) + end +end + +# triangular +@testset "triangular: dimensional correctness" begin + A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) + @test sqrt(A)::UpperTriangular == Furlong{1 // 2}.(UpperTriangular([1 2; 0 1])) + @test inv(A)::UpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) + B = UnitUpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) + @test sqrt(B)::UnitUpperTriangular == Furlong{1 // 2}.(UpperTriangular([1 2; 0 1])) + @test inv(B)::UnitUpperTriangular == Furlong{-1}.(UpperTriangular([1 -4; 0 1])) + b = [Furlong(5), Furlong(8)] + @test (A \ b)::Vector{<:Furlong{0}} == (B \ b)::Vector{<:Furlong{0}} == Furlong{0}.([-27, 8]) + C = LowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) + @test sqrt(C)::LowerTriangular == Furlong{1 // 2}.(LowerTriangular([1 0; 2 1])) + @test inv(C)::LowerTriangular == Furlong{-1}.(LowerTriangular([1 0; -4 1])) + D = UnitLowerTriangular([Furlong(1) Furlong(0); Furlong(4) Furlong(1)]) + @test sqrt(D)::UnitLowerTriangular == Furlong{1 // 2}.(UnitLowerTriangular([1 0; 2 1])) + @test inv(D)::UnitLowerTriangular == Furlong{-1}.(UnitLowerTriangular([1 0; -4 1])) + b = [Furlong(5), Furlong(8)] + @test (C \ b)::Vector{<:Furlong{0}} == (D \ b)::Vector{<:Furlong{0}} == Furlong{0}.([5, -12]) +end + +end # module TestUnitfulLinAlg