From e5e7be25b07252fb7a9eba42b05ad8a49753339a Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 24 Nov 2024 19:44:57 +0100 Subject: [PATCH] move out LinearAlgebra into its own repository (#56637) This moves out LinearAlgebra into its own repo https://github.com/JuliaLang/LinearAlgebra.jl. This repo is still a bit bare (README needs to be added) but it has CI set up to run on buildkite (https://buildkite.com/julialang/linearalgebra-dot-jl/builds/18) and doc building on GHA. The external repo has all commits up to https://github.com/JuliaLang/julia/commit/4709b6c48e79f6226e6dbee1b49bf7e563058ff7 included in it. The reason for the move is to be able to focus issues and PRs and development regarding LinearAlgebra in one place. --- .../md5 | 1 + .../sha512 | 1 + julia.spdx.json | 12 + stdlib/.gitignore | 2 + stdlib/LinearAlgebra.version | 4 + stdlib/LinearAlgebra/Project.toml | 15 - stdlib/LinearAlgebra/docs/src/index.md | 903 --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 843 -- stdlib/LinearAlgebra/src/abstractq.jl | 642 -- stdlib/LinearAlgebra/src/adjtrans.jl | 524 -- stdlib/LinearAlgebra/src/bidiag.jl | 1489 ---- stdlib/LinearAlgebra/src/bitarray.jl | 272 - stdlib/LinearAlgebra/src/blas.jl | 2258 ------ stdlib/LinearAlgebra/src/bunchkaufman.jl | 1601 ---- stdlib/LinearAlgebra/src/cholesky.jl | 1038 --- stdlib/LinearAlgebra/src/dense.jl | 1885 ----- stdlib/LinearAlgebra/src/deprecated.jl | 7 - stdlib/LinearAlgebra/src/diagonal.jl | 1148 --- stdlib/LinearAlgebra/src/eigen.jl | 682 -- stdlib/LinearAlgebra/src/exceptions.jl | 76 - stdlib/LinearAlgebra/src/factorization.jl | 202 - stdlib/LinearAlgebra/src/generic.jl | 2093 ----- stdlib/LinearAlgebra/src/givens.jl | 429 - stdlib/LinearAlgebra/src/hessenberg.jl | 624 -- stdlib/LinearAlgebra/src/lapack.jl | 7218 ----------------- stdlib/LinearAlgebra/src/lbt.jl | 348 - stdlib/LinearAlgebra/src/ldlt.jl | 224 - stdlib/LinearAlgebra/src/lq.jl | 203 - stdlib/LinearAlgebra/src/lu.jl | 834 -- stdlib/LinearAlgebra/src/matmul.jl | 1339 --- stdlib/LinearAlgebra/src/qr.jl | 769 -- stdlib/LinearAlgebra/src/schur.jl | 449 - stdlib/LinearAlgebra/src/special.jl | 595 -- .../LinearAlgebra/src/structuredbroadcast.jl | 297 - stdlib/LinearAlgebra/src/svd.jl | 578 -- stdlib/LinearAlgebra/src/symmetric.jl | 1064 --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 410 - stdlib/LinearAlgebra/src/transpose.jl | 257 - stdlib/LinearAlgebra/src/triangular.jl | 2990 ------- stdlib/LinearAlgebra/src/tridiag.jl | 1099 --- stdlib/LinearAlgebra/src/uniformscaling.jl | 448 - stdlib/LinearAlgebra/test/abstractq.jl | 156 - stdlib/LinearAlgebra/test/addmul.jl | 273 - stdlib/LinearAlgebra/test/adjtrans.jl | 721 -- stdlib/LinearAlgebra/test/ambiguous_exec.jl | 21 - stdlib/LinearAlgebra/test/bidiag.jl | 1141 --- stdlib/LinearAlgebra/test/blas.jl | 783 -- stdlib/LinearAlgebra/test/bunchkaufman.jl | 260 - stdlib/LinearAlgebra/test/cholesky.jl | 661 -- stdlib/LinearAlgebra/test/dense.jl | 1331 --- stdlib/LinearAlgebra/test/diagonal.jl | 1455 ---- stdlib/LinearAlgebra/test/eigen.jl | 282 - stdlib/LinearAlgebra/test/factorization.jl | 94 - stdlib/LinearAlgebra/test/generic.jl | 840 -- stdlib/LinearAlgebra/test/givens.jl | 124 - stdlib/LinearAlgebra/test/hessenberg.jl | 308 - stdlib/LinearAlgebra/test/lapack.jl | 902 -- stdlib/LinearAlgebra/test/ldlt.jl | 41 - stdlib/LinearAlgebra/test/lq.jl | 237 - stdlib/LinearAlgebra/test/lu.jl | 502 -- stdlib/LinearAlgebra/test/matmul.jl | 1151 --- stdlib/LinearAlgebra/test/pinv.jl | 186 - stdlib/LinearAlgebra/test/qr.jl | 543 -- stdlib/LinearAlgebra/test/runtests.jl | 10 - stdlib/LinearAlgebra/test/schur.jl | 221 - stdlib/LinearAlgebra/test/special.jl | 862 -- .../LinearAlgebra/test/structuredbroadcast.jl | 379 - stdlib/LinearAlgebra/test/svd.jl | 297 - stdlib/LinearAlgebra/test/symmetric.jl | 1181 --- stdlib/LinearAlgebra/test/symmetriceigen.jl | 187 - stdlib/LinearAlgebra/test/testgroups | 30 - stdlib/LinearAlgebra/test/testutils.jl | 27 - stdlib/LinearAlgebra/test/triangular.jl | 1419 ---- stdlib/LinearAlgebra/test/trickyarithmetic.jl | 66 - stdlib/LinearAlgebra/test/tridiag.jl | 1078 --- stdlib/LinearAlgebra/test/uniformscaling.jl | 577 -- stdlib/Makefile | 4 +- 77 files changed, 22 insertions(+), 54201 deletions(-) create mode 100644 deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/md5 create mode 100644 deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/sha512 create mode 100644 stdlib/LinearAlgebra.version delete mode 100644 stdlib/LinearAlgebra/Project.toml delete mode 100644 stdlib/LinearAlgebra/docs/src/index.md delete mode 100644 stdlib/LinearAlgebra/src/LinearAlgebra.jl delete mode 100644 stdlib/LinearAlgebra/src/abstractq.jl delete mode 100644 stdlib/LinearAlgebra/src/adjtrans.jl delete mode 100644 stdlib/LinearAlgebra/src/bidiag.jl delete mode 100644 stdlib/LinearAlgebra/src/bitarray.jl delete mode 100644 stdlib/LinearAlgebra/src/blas.jl delete mode 100644 stdlib/LinearAlgebra/src/bunchkaufman.jl delete mode 100644 stdlib/LinearAlgebra/src/cholesky.jl delete mode 100644 stdlib/LinearAlgebra/src/dense.jl delete mode 100644 stdlib/LinearAlgebra/src/deprecated.jl delete mode 100644 stdlib/LinearAlgebra/src/diagonal.jl delete mode 100644 stdlib/LinearAlgebra/src/eigen.jl delete mode 100644 stdlib/LinearAlgebra/src/exceptions.jl delete mode 100644 stdlib/LinearAlgebra/src/factorization.jl delete mode 100644 stdlib/LinearAlgebra/src/generic.jl delete mode 100644 stdlib/LinearAlgebra/src/givens.jl delete mode 100644 stdlib/LinearAlgebra/src/hessenberg.jl delete mode 100644 stdlib/LinearAlgebra/src/lapack.jl delete mode 100644 stdlib/LinearAlgebra/src/lbt.jl delete mode 100644 stdlib/LinearAlgebra/src/ldlt.jl delete mode 100644 stdlib/LinearAlgebra/src/lq.jl delete mode 100644 stdlib/LinearAlgebra/src/lu.jl delete mode 100644 stdlib/LinearAlgebra/src/matmul.jl delete mode 100644 stdlib/LinearAlgebra/src/qr.jl delete mode 100644 stdlib/LinearAlgebra/src/schur.jl delete mode 100644 stdlib/LinearAlgebra/src/special.jl delete mode 100644 stdlib/LinearAlgebra/src/structuredbroadcast.jl delete mode 100644 stdlib/LinearAlgebra/src/svd.jl delete mode 100644 stdlib/LinearAlgebra/src/symmetric.jl delete mode 100644 stdlib/LinearAlgebra/src/symmetriceigen.jl delete mode 100644 stdlib/LinearAlgebra/src/transpose.jl delete mode 100644 stdlib/LinearAlgebra/src/triangular.jl delete mode 100644 stdlib/LinearAlgebra/src/tridiag.jl delete mode 100644 stdlib/LinearAlgebra/src/uniformscaling.jl delete mode 100644 stdlib/LinearAlgebra/test/abstractq.jl delete mode 100644 stdlib/LinearAlgebra/test/addmul.jl delete mode 100644 stdlib/LinearAlgebra/test/adjtrans.jl delete mode 100644 stdlib/LinearAlgebra/test/ambiguous_exec.jl delete mode 100644 stdlib/LinearAlgebra/test/bidiag.jl delete mode 100644 stdlib/LinearAlgebra/test/blas.jl delete mode 100644 stdlib/LinearAlgebra/test/bunchkaufman.jl delete mode 100644 stdlib/LinearAlgebra/test/cholesky.jl delete mode 100644 stdlib/LinearAlgebra/test/dense.jl delete mode 100644 stdlib/LinearAlgebra/test/diagonal.jl delete mode 100644 stdlib/LinearAlgebra/test/eigen.jl delete mode 100644 stdlib/LinearAlgebra/test/factorization.jl delete mode 100644 stdlib/LinearAlgebra/test/generic.jl delete mode 100644 stdlib/LinearAlgebra/test/givens.jl delete mode 100644 stdlib/LinearAlgebra/test/hessenberg.jl delete mode 100644 stdlib/LinearAlgebra/test/lapack.jl delete mode 100644 stdlib/LinearAlgebra/test/ldlt.jl delete mode 100644 stdlib/LinearAlgebra/test/lq.jl delete mode 100644 stdlib/LinearAlgebra/test/lu.jl delete mode 100644 stdlib/LinearAlgebra/test/matmul.jl delete mode 100644 stdlib/LinearAlgebra/test/pinv.jl delete mode 100644 stdlib/LinearAlgebra/test/qr.jl delete mode 100644 stdlib/LinearAlgebra/test/runtests.jl delete mode 100644 stdlib/LinearAlgebra/test/schur.jl delete mode 100644 stdlib/LinearAlgebra/test/special.jl delete mode 100644 stdlib/LinearAlgebra/test/structuredbroadcast.jl delete mode 100644 stdlib/LinearAlgebra/test/svd.jl delete mode 100644 stdlib/LinearAlgebra/test/symmetric.jl delete mode 100644 stdlib/LinearAlgebra/test/symmetriceigen.jl delete mode 100644 stdlib/LinearAlgebra/test/testgroups delete mode 100644 stdlib/LinearAlgebra/test/testutils.jl delete mode 100644 stdlib/LinearAlgebra/test/triangular.jl delete mode 100644 stdlib/LinearAlgebra/test/trickyarithmetic.jl delete mode 100644 stdlib/LinearAlgebra/test/tridiag.jl delete mode 100644 stdlib/LinearAlgebra/test/uniformscaling.jl diff --git a/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/md5 b/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/md5 new file mode 100644 index 0000000000000..e240a1083833c --- /dev/null +++ b/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/md5 @@ -0,0 +1 @@ +00198e6d92d033fb33e75cf4eac34dca diff --git a/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/sha512 b/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/sha512 new file mode 100644 index 0000000000000..5eeceaf1dbed3 --- /dev/null +++ b/deps/checksums/LinearAlgebra-56d561c22e1ab8e0421160edbdd42f3f194ecfa8.tar.gz/sha512 @@ -0,0 +1 @@ +ba4b390d99644c31d64594352da888e9ef18021cc9b7700c37a6cdb0c1ff2532eb208ecaccf93217e3183e1db8e6c089456fa5d93633b8ff037e8796199934e7 diff --git a/julia.spdx.json b/julia.spdx.json index 63683dd302a39..0e0067f00efb1 100644 --- a/julia.spdx.json +++ b/julia.spdx.json @@ -86,6 +86,18 @@ "copyrightText": "Copyright (c) 2020 Stefan Karpinski and contributors", "summary": "ArgTools provides tools for creating consistent, flexible APIs that work with various kinds of function arguments." }, + { + "name": "LinearAlgebra.jl", + "SPDXID": "SPDXRef-JuliaLinearAlgebra", + "downloadLocation": "git+https://github.com/JuliaLang/LinearAlgebra.jl.git", + "filesAnalyzed": false, + "homepage": "https://juliastats.org", + "sourceInfo": "The git hash of the version in use can be found in the file stdlib/LinearAlgebra.version", + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "copyrightText": "Copyright (c) 2009-2024: Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors: https://github.com/JuliaLang/julia/contributors", + "summary": "Development repository for the LinearAlgebra standard library (stdlib) that ships with Julia." + }, { "name": "Tar.jl", "SPDXID": "SPDXRef-JuliaTar", diff --git a/stdlib/.gitignore b/stdlib/.gitignore index 93668857189af..5996091c5a0ef 100644 --- a/stdlib/.gitignore +++ b/stdlib/.gitignore @@ -29,6 +29,8 @@ /StyledStrings /JuliaSyntaxHighlighting-* /JuliaSyntaxHighlighting +/LinearAlgebra-* +/LinearAlgebra /*_jll/StdlibArtifacts.toml /*/Manifest.toml /*.image diff --git a/stdlib/LinearAlgebra.version b/stdlib/LinearAlgebra.version new file mode 100644 index 0000000000000..d6a33ea421adf --- /dev/null +++ b/stdlib/LinearAlgebra.version @@ -0,0 +1,4 @@ +LINEARALGEBRA_BRANCH = master +LINEARALGEBRA_SHA1 = 56d561c22e1ab8e0421160edbdd42f3f194ecfa8 +LINEARALGEBRA_GIT_URL := https://github.com/JuliaLang/LinearAlgebra.jl.git +LINEARALGEBRA_TAR_URL = https://api.github.com/repos/JuliaLang/LinearAlgebra.jl/tarball/$1 diff --git a/stdlib/LinearAlgebra/Project.toml b/stdlib/LinearAlgebra/Project.toml deleted file mode 100644 index 892de0397c219..0000000000000 --- a/stdlib/LinearAlgebra/Project.toml +++ /dev/null @@ -1,15 +0,0 @@ -name = "LinearAlgebra" -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[deps] -Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -libblastrampoline_jll = "8e850b90-86db-534c-a0d3-1478176c7d93" -OpenBLAS_jll = "4536629a-c528-5b80-bd46-f80d51c5b363" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[targets] -test = ["Test", "Random"] diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md deleted file mode 100644 index 3e18a45752aeb..0000000000000 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ /dev/null @@ -1,903 +0,0 @@ -```@meta -EditURL = "https://github.com/JuliaLang/julia/blob/master/stdlib/LinearAlgebra/docs/src/index.md" -``` - -# [Linear Algebra](@id man-linalg) - -```@meta -DocTestSetup = :(using LinearAlgebra) -``` - -In addition to (and as part of) its support for multi-dimensional arrays, Julia provides native implementations -of many common and useful linear algebra operations which can be loaded with `using LinearAlgebra`. Basic operations, such as [`tr`](@ref), [`det`](@ref), -and [`inv`](@ref) are all supported: - -```jldoctest -julia> A = [1 2 3; 4 1 6; 7 8 1] -3×3 Matrix{Int64}: - 1 2 3 - 4 1 6 - 7 8 1 - -julia> tr(A) -3 - -julia> det(A) -104.0 - -julia> inv(A) -3×3 Matrix{Float64}: - -0.451923 0.211538 0.0865385 - 0.365385 -0.192308 0.0576923 - 0.240385 0.0576923 -0.0673077 -``` - -As well as other useful operations, such as finding eigenvalues or eigenvectors: - -```jldoctest -julia> A = [-4. -17.; 2. 2.] -2×2 Matrix{Float64}: - -4.0 -17.0 - 2.0 2.0 - -julia> eigvals(A) -2-element Vector{ComplexF64}: - -1.0 - 5.0im - -1.0 + 5.0im - -julia> eigvecs(A) -2×2 Matrix{ComplexF64}: - 0.945905-0.0im 0.945905+0.0im - -0.166924+0.278207im -0.166924-0.278207im -``` - -In addition, Julia provides many [factorizations](@ref man-linalg-factorizations) which can be used to -speed up problems such as linear solve or matrix exponentiation by pre-factorizing a matrix into a form -more amenable (for performance or memory reasons) to the problem. See the documentation on [`factorize`](@ref) -for more information. As an example: - -```jldoctest -julia> A = [1.5 2 -4; 3 -1 -6; -10 2.3 4] -3×3 Matrix{Float64}: - 1.5 2.0 -4.0 - 3.0 -1.0 -6.0 - -10.0 2.3 4.0 - -julia> factorize(A) -LU{Float64, Matrix{Float64}, Vector{Int64}} -L factor: -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - -0.15 1.0 0.0 - -0.3 -0.132196 1.0 -U factor: -3×3 Matrix{Float64}: - -10.0 2.3 4.0 - 0.0 2.345 -3.4 - 0.0 0.0 -5.24947 -``` - -Since `A` is not Hermitian, symmetric, triangular, tridiagonal, or bidiagonal, an LU factorization may be the -best we can do. Compare with: - -```jldoctest -julia> B = [1.5 2 -4; 2 -1 -3; -4 -3 5] -3×3 Matrix{Float64}: - 1.5 2.0 -4.0 - 2.0 -1.0 -3.0 - -4.0 -3.0 5.0 - -julia> factorize(B) -BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} -D factor: -3×3 Tridiagonal{Float64, Vector{Float64}}: - -1.64286 0.0 ⋅ - 0.0 -2.8 0.0 - ⋅ 0.0 5.0 -U factor: -3×3 UnitUpperTriangular{Float64, Matrix{Float64}}: - 1.0 0.142857 -0.8 - ⋅ 1.0 -0.6 - ⋅ ⋅ 1.0 -permutation: -3-element Vector{Int64}: - 1 - 2 - 3 -``` - -Here, Julia was able to detect that `B` is in fact symmetric, and used a more appropriate factorization. -Often it's possible to write more efficient code for a matrix that is known to have certain properties e.g. -it is symmetric, or tridiagonal. Julia provides some special types so that you can "tag" matrices as having -these properties. For instance: - -```jldoctest -julia> B = [1.5 2 -4; 2 -1 -3; -4 -3 5] -3×3 Matrix{Float64}: - 1.5 2.0 -4.0 - 2.0 -1.0 -3.0 - -4.0 -3.0 5.0 - -julia> sB = Symmetric(B) -3×3 Symmetric{Float64, Matrix{Float64}}: - 1.5 2.0 -4.0 - 2.0 -1.0 -3.0 - -4.0 -3.0 5.0 -``` - -`sB` has been tagged as a matrix that's (real) symmetric, so for later operations we might perform on it, -such as eigenfactorization or computing matrix-vector products, efficiencies can be found by only referencing -half of it. For example: - -```jldoctest -julia> B = [1.5 2 -4; 2 -1 -3; -4 -3 5] -3×3 Matrix{Float64}: - 1.5 2.0 -4.0 - 2.0 -1.0 -3.0 - -4.0 -3.0 5.0 - -julia> sB = Symmetric(B) -3×3 Symmetric{Float64, Matrix{Float64}}: - 1.5 2.0 -4.0 - 2.0 -1.0 -3.0 - -4.0 -3.0 5.0 - -julia> x = [1; 2; 3] -3-element Vector{Int64}: - 1 - 2 - 3 - -julia> sB\x -3-element Vector{Float64}: - -1.7391304347826084 - -1.1086956521739126 - -1.4565217391304346 -``` - -The `\` operation here performs the linear solution. The left-division operator is pretty -powerful and it's easy to write compact, readable code that is flexible enough to solve all -sorts of systems of linear equations. - -## Special matrices - -[Matrices with special symmetries and structures](https://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=3274) -arise often in linear algebra and are frequently associated with various matrix factorizations. -Julia features a rich collection of special matrix types, which allow for fast computation with -specialized routines that are specially developed for particular matrix types. - -The following tables summarize the types of special matrices that have been implemented in Julia, -as well as whether hooks to various optimized methods for them in LAPACK are available. - -| Type | Description | -|:----------------------------- |:--------------------------------------------------------------------------------------------- | -| [`Symmetric`](@ref) | [Symmetric matrix](https://en.wikipedia.org/wiki/Symmetric_matrix) | -| [`Hermitian`](@ref) | [Hermitian matrix](https://en.wikipedia.org/wiki/Hermitian_matrix) | -| [`UpperTriangular`](@ref) | Upper [triangular matrix](https://en.wikipedia.org/wiki/Triangular_matrix) | -| [`UnitUpperTriangular`](@ref) | Upper [triangular matrix](https://en.wikipedia.org/wiki/Triangular_matrix) with unit diagonal | -| [`LowerTriangular`](@ref) | Lower [triangular matrix](https://en.wikipedia.org/wiki/Triangular_matrix) | | -| [`UnitLowerTriangular`](@ref) | Lower [triangular matrix](https://en.wikipedia.org/wiki/Triangular_matrix) with unit diagonal | -| [`UpperHessenberg`](@ref) | Upper [Hessenberg matrix](https://en.wikipedia.org/wiki/Hessenberg_matrix) -| [`Tridiagonal`](@ref) | [Tridiagonal matrix](https://en.wikipedia.org/wiki/Tridiagonal_matrix) | -| [`SymTridiagonal`](@ref) | Symmetric tridiagonal matrix | -| [`Bidiagonal`](@ref) | Upper/lower [bidiagonal matrix](https://en.wikipedia.org/wiki/Bidiagonal_matrix) | -| [`Diagonal`](@ref) | [Diagonal matrix](https://en.wikipedia.org/wiki/Diagonal_matrix) | -| [`UniformScaling`](@ref) | [Uniform scaling operator](https://en.wikipedia.org/wiki/Uniform_scaling) | - -### Elementary operations - -| Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | -|:----------------------------- |:--- |:--- |:--- |:--- |:----------------------------------------------------------- | -| [`Symmetric`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`cbrt`](@ref), [`exp`](@ref) | -| [`Hermitian`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`cbrt`](@ref), [`exp`](@ref) | -| [`UpperTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) | -| [`UnitUpperTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) | -| [`LowerTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) | -| [`UnitLowerTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) | -| [`UpperHessenberg`](@ref) | | | | MM | [`inv`](@ref), [`det`](@ref) | -| [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax`](@ref), [`eigmin`](@ref) | -| [`Tridiagonal`](@ref) | M | M | MS | MV | | -| [`Bidiagonal`](@ref) | M | M | MS | MV | | -| [`Diagonal`](@ref) | M | M | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref), [`/`](@ref) | -| [`UniformScaling`](@ref) | M | M | MVS | MVS | [`/`](@ref) | - -Legend: - -| Key | Description | -|:---------- |:------------------------------------------------------------- | -| M (matrix) | An optimized method for matrix-matrix operations is available | -| V (vector) | An optimized method for matrix-vector operations is available | -| S (scalar) | An optimized method for matrix-scalar operations is available | - -### Matrix factorizations - -| Matrix type | LAPACK | [`eigen`](@ref) | [`eigvals`](@ref) | [`eigvecs`](@ref) | [`svd`](@ref) | [`svdvals`](@ref) | -|:----------------------------- |:------ |:------------- |:----------------- |:----------------- |:------------- |:----------------- | -| [`Symmetric`](@ref) | SY | | ARI | | | | -| [`Hermitian`](@ref) | HE | | ARI | | | | -| [`UpperTriangular`](@ref) | TR | A | A | A | | | -| [`UnitUpperTriangular`](@ref) | TR | A | A | A | | | -| [`LowerTriangular`](@ref) | TR | A | A | A | | | -| [`UnitLowerTriangular`](@ref) | TR | A | A | A | | | -| [`SymTridiagonal`](@ref) | ST | A | ARI | AV | | | -| [`Tridiagonal`](@ref) | GT | | | | | | -| [`Bidiagonal`](@ref) | BD | | | | A | A | -| [`Diagonal`](@ref) | DI | | A | | | | - -Legend: - -| Key | Description | Example | -|:------------ |:------------------------------------------------------------------------------------------------------------------------------- |:-------------------- | -| A (all) | An optimized method to find all the characteristic values and/or vectors is available | e.g. `eigvals(M)` | -| R (range) | An optimized method to find the `il`th through the `ih`th characteristic values are available | `eigvals(M, il, ih)` | -| I (interval) | An optimized method to find the characteristic values in the interval [`vl`, `vh`] is available | `eigvals(M, vl, vh)` | -| V (vectors) | An optimized method to find the characteristic vectors corresponding to the characteristic values `x=[x1, x2,...]` is available | `eigvecs(M, x)` | - -### The uniform scaling operator - -A [`UniformScaling`](@ref) operator represents a scalar times the identity operator, `λ*I`. The identity -operator `I` is defined as a constant and is an instance of `UniformScaling`. The size of these -operators are generic and match the other matrix in the binary operations [`+`](@ref), [`-`](@ref), -[`*`](@ref) and [`\`](@ref). For `A+I` and `A-I` this means that `A` must be square. Multiplication -with the identity operator `I` is a noop (except for checking that the scaling factor is one) -and therefore almost without overhead. - -To see the `UniformScaling` operator in action: - -```jldoctest -julia> U = UniformScaling(2); - -julia> a = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> a + U -2×2 Matrix{Int64}: - 3 2 - 3 6 - -julia> a * U -2×2 Matrix{Int64}: - 2 4 - 6 8 - -julia> [a U] -2×4 Matrix{Int64}: - 1 2 2 0 - 3 4 0 2 - -julia> b = [1 2 3; 4 5 6] -2×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - -julia> b - U -ERROR: DimensionMismatch: matrix is not square: dimensions are (2, 3) -Stacktrace: -[...] -``` - -If you need to solve many systems of the form `(A+μI)x = b` for the same `A` and different `μ`, it might be beneficial -to first compute the Hessenberg factorization `F` of `A` via the [`hessenberg`](@ref) function. -Given `F`, Julia employs an efficient algorithm for `(F+μ*I) \ b` (equivalent to `(A+μ*I)x \ b`) and related -operations like determinants. - -## [Matrix factorizations](@id man-linalg-factorizations) - -[Matrix factorizations (a.k.a. matrix decompositions)](https://en.wikipedia.org/wiki/Matrix_decomposition) -compute the factorization of a matrix into a product of matrices, and are one of the central concepts -in (numerical) linear algebra. - -The following table summarizes the types of matrix factorizations that have been implemented in -Julia. Details of their associated methods can be found in the [Standard functions](@ref) section -of the Linear Algebra documentation. - -| Type | Description | -|:------------------ |:-------------------------------------------------------------------------------------------------------------- | -| `BunchKaufman` | Bunch-Kaufman factorization | -| `Cholesky` | [Cholesky factorization](https://en.wikipedia.org/wiki/Cholesky_decomposition) | -| `CholeskyPivoted` | [Pivoted](https://en.wikipedia.org/wiki/Pivot_element) Cholesky factorization | -| `LDLt` | [LDL(T) factorization](https://en.wikipedia.org/wiki/Cholesky_decomposition#LDL_decomposition) | -| `LU` | [LU factorization](https://en.wikipedia.org/wiki/LU_decomposition) | -| `QR` | [QR factorization](https://en.wikipedia.org/wiki/QR_decomposition) | -| `QRCompactWY` | Compact WY form of the QR factorization | -| `QRPivoted` | Pivoted [QR factorization](https://en.wikipedia.org/wiki/QR_decomposition) | -| `LQ` | [QR factorization](https://en.wikipedia.org/wiki/QR_decomposition) of `transpose(A)` | -| `Hessenberg` | [Hessenberg decomposition](https://mathworld.wolfram.com/HessenbergDecomposition.html) | -| `Eigen` | [Spectral decomposition](https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix) | -| `GeneralizedEigen` | [Generalized spectral decomposition](https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix#Generalized_eigenvalue_problem) | -| `SVD` | [Singular value decomposition](https://en.wikipedia.org/wiki/Singular_value_decomposition) | -| `GeneralizedSVD` | [Generalized SVD](https://en.wikipedia.org/wiki/Generalized_singular_value_decomposition#Higher_order_version) | -| `Schur` | [Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition) | -| `GeneralizedSchur` | [Generalized Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition#Generalized_Schur_decomposition) | - -Adjoints and transposes of [`Factorization`](@ref) objects are lazily wrapped in -`AdjointFactorization` and `TransposeFactorization` objects, respectively. Generically, -transpose of real `Factorization`s are wrapped as `AdjointFactorization`. - -## [Orthogonal matrices (`AbstractQ`)](@id man-linalg-abstractq) - -Some matrix factorizations generate orthogonal/unitary "matrix" factors. These -factorizations include QR-related factorizations obtained from calls to [`qr`](@ref), i.e., -`QR`, `QRCompactWY` and `QRPivoted`, the Hessenberg factorization obtained from calls to -[`hessenberg`](@ref), and the LQ factorization obtained from [`lq`](@ref). While these -orthogonal/unitary factors admit a matrix representation, their internal representation -is, for performance and memory reasons, different. Hence, they should be rather viewed as -matrix-backed, function-based linear operators. In particular, reading, for instance, a -column of its matrix representation requires running "matrix"-vector multiplication code, -rather than simply reading out data from memory (possibly filling parts of the vector with -structural zeros). Another clear distinction from other, non-triangular matrix types is -that the underlying multiplication code allows for in-place modification during multiplication. -Furthermore, objects of specific `AbstractQ` subtypes as those created via [`qr`](@ref), -[`hessenberg`](@ref) and [`lq`](@ref) can behave like a square or a rectangular matrix -depending on context: - -```julia -julia> using LinearAlgebra - -julia> Q = qr(rand(3,2)).Q -3×3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} - -julia> Matrix(Q) -3×2 Matrix{Float64}: - -0.320597 0.865734 - -0.765834 -0.475694 - -0.557419 0.155628 - -julia> Q*I -3×3 Matrix{Float64}: - -0.320597 0.865734 -0.384346 - -0.765834 -0.475694 -0.432683 - -0.557419 0.155628 0.815514 - -julia> Q*ones(2) -3-element Vector{Float64}: - 0.5451367118802273 - -1.241527373086654 - -0.40179067589600226 - -julia> Q*ones(3) -3-element Vector{Float64}: - 0.16079054743832022 - -1.674209978965636 - 0.41372375588835797 - -julia> ones(1,2) * Q' -1×3 Matrix{Float64}: - 0.545137 -1.24153 -0.401791 - -julia> ones(1,3) * Q' -1×3 Matrix{Float64}: - 0.160791 -1.67421 0.413724 -``` - -Due to this distinction from dense or structured matrices, the abstract `AbstractQ` type -does not subtype `AbstractMatrix`, but instead has its own type hierarchy. Custom types -that subtype `AbstractQ` can rely on generic fallbacks if the following interface is satisfied. -For example, for - -```julia -struct MyQ{T} <: LinearAlgebra.AbstractQ{T} - # required fields -end -``` - -provide overloads for - -```julia -Base.size(Q::MyQ) # size of corresponding square matrix representation -Base.convert(::Type{AbstractQ{T}}, Q::MyQ) # eltype promotion [optional] -LinearAlgebra.lmul!(Q::MyQ, x::AbstractVecOrMat) # left-multiplication -LinearAlgebra.rmul!(A::AbstractMatrix, Q::MyQ) # right-multiplication -``` - -If `eltype` promotion is not of interest, the `convert` method is unnecessary, since by -default `convert(::Type{AbstractQ{T}}, Q::AbstractQ{T})` returns `Q` itself. -Adjoints of `AbstractQ`-typed objects are lazily wrapped in an `AdjointQ` wrapper type, -which requires its own `LinearAlgebra.lmul!` and `LinearAlgebra.rmul!` methods. Given this -set of methods, any `Q::MyQ` can be used like a matrix, preferably in a multiplicative -context: multiplication via `*` with scalars, vectors and matrices from left and right, -obtaining a matrix representation of `Q` via `Matrix(Q)` (or `Q*I`) and indexing into the -matrix representation all work. In contrast, addition and subtraction as well as more -generally broadcasting over elements in the matrix representation fail because that would -be highly inefficient. For such use cases, consider computing the matrix representation -up front and cache it for future reuse. - -## [Pivoting Strategies](@id man-linalg-pivoting-strategies) - -Several of Julia's [matrix factorizations](@ref man-linalg-factorizations) support -[pivoting](https://en.wikipedia.org/wiki/Pivot_element), which can be used to improve their -numerical stability. In fact, some matrix factorizations, such as the LU -factorization, may fail without pivoting. - -In pivoting, first, a [pivot element](https://en.wikipedia.org/wiki/Pivot_element) -with good numerical properties is chosen based on a pivoting strategy. Next, the rows and -columns of the original matrix are permuted to bring the chosen element in place for -subsequent computation. Furthermore, the process is repeated for each stage of the factorization. - -Consequently, besides the conventional matrix factors, the outputs of -pivoted factorization schemes also include permutation matrices. - -In the following, the pivoting strategies implemented in Julia are briefly described. Note -that not all matrix factorizations may support them. Consult the documentation of the -respective [matrix factorization](@ref man-linalg-factorizations) for details on the -supported pivoting strategies. - -See also [`LinearAlgebra.ZeroPivotException`](@ref). - -```@docs -LinearAlgebra.NoPivot -LinearAlgebra.RowNonZero -LinearAlgebra.RowMaximum -LinearAlgebra.ColumnNorm -``` - -## Standard functions - -Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](https://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) -Base.:*(::AbstractMatrix, ::AbstractMatrix, ::AbstractVector) -Base.:\(::AbstractMatrix, ::AbstractVecOrMat) -Base.:/(::AbstractVecOrMat, ::AbstractVecOrMat) -LinearAlgebra.SingularException -LinearAlgebra.PosDefException -LinearAlgebra.ZeroPivotException -LinearAlgebra.RankDeficientException -LinearAlgebra.LAPACKException -LinearAlgebra.dot -LinearAlgebra.dot(::Any, ::Any, ::Any) -LinearAlgebra.cross -LinearAlgebra.axpy! -LinearAlgebra.axpby! -LinearAlgebra.rotate! -LinearAlgebra.reflect! -LinearAlgebra.factorize -LinearAlgebra.Diagonal -LinearAlgebra.Bidiagonal -LinearAlgebra.SymTridiagonal -LinearAlgebra.Tridiagonal -LinearAlgebra.Symmetric -LinearAlgebra.Hermitian -LinearAlgebra.LowerTriangular -LinearAlgebra.UpperTriangular -LinearAlgebra.UnitLowerTriangular -LinearAlgebra.UnitUpperTriangular -LinearAlgebra.UpperHessenberg -LinearAlgebra.UniformScaling -LinearAlgebra.I -LinearAlgebra.UniformScaling(::Integer) -LinearAlgebra.Factorization -LinearAlgebra.LU -LinearAlgebra.lu -LinearAlgebra.lu! -LinearAlgebra.Cholesky -LinearAlgebra.CholeskyPivoted -LinearAlgebra.cholesky -LinearAlgebra.cholesky! -LinearAlgebra.lowrankupdate -LinearAlgebra.lowrankdowndate -LinearAlgebra.lowrankupdate! -LinearAlgebra.lowrankdowndate! -LinearAlgebra.LDLt -LinearAlgebra.ldlt -LinearAlgebra.ldlt! -LinearAlgebra.QR -LinearAlgebra.QRCompactWY -LinearAlgebra.QRPivoted -LinearAlgebra.qr -LinearAlgebra.qr! -LinearAlgebra.LQ -LinearAlgebra.lq -LinearAlgebra.lq! -LinearAlgebra.BunchKaufman -LinearAlgebra.bunchkaufman -LinearAlgebra.bunchkaufman! -LinearAlgebra.Eigen -LinearAlgebra.GeneralizedEigen -LinearAlgebra.eigvals -LinearAlgebra.eigvals! -LinearAlgebra.eigmax -LinearAlgebra.eigmin -LinearAlgebra.eigvecs -LinearAlgebra.eigen -LinearAlgebra.eigen! -LinearAlgebra.Hessenberg -LinearAlgebra.hessenberg -LinearAlgebra.hessenberg! -LinearAlgebra.Schur -LinearAlgebra.GeneralizedSchur -LinearAlgebra.schur -LinearAlgebra.schur! -LinearAlgebra.ordschur -LinearAlgebra.ordschur! -LinearAlgebra.SVD -LinearAlgebra.GeneralizedSVD -LinearAlgebra.svd -LinearAlgebra.svd! -LinearAlgebra.svdvals -LinearAlgebra.svdvals! -LinearAlgebra.Givens -LinearAlgebra.givens -LinearAlgebra.triu -LinearAlgebra.triu! -LinearAlgebra.tril -LinearAlgebra.tril! -LinearAlgebra.diagind -LinearAlgebra.diag -LinearAlgebra.diagm -LinearAlgebra.rank -LinearAlgebra.norm -LinearAlgebra.opnorm -LinearAlgebra.normalize! -LinearAlgebra.normalize -LinearAlgebra.cond -LinearAlgebra.condskeel -LinearAlgebra.tr -LinearAlgebra.det -LinearAlgebra.logdet -LinearAlgebra.logabsdet -Base.inv(::AbstractMatrix) -LinearAlgebra.pinv -LinearAlgebra.nullspace -Base.kron -Base.kron! -LinearAlgebra.exp(::StridedMatrix{<:LinearAlgebra.BlasFloat}) -Base.cis(::AbstractMatrix) -Base.:^(::AbstractMatrix, ::Number) -Base.:^(::Number, ::AbstractMatrix) -LinearAlgebra.log(::StridedMatrix) -LinearAlgebra.sqrt(::StridedMatrix) -LinearAlgebra.cbrt(::AbstractMatrix{<:Real}) -LinearAlgebra.cos(::StridedMatrix{<:Real}) -LinearAlgebra.sin(::StridedMatrix{<:Real}) -LinearAlgebra.sincos(::StridedMatrix{<:Real}) -LinearAlgebra.tan(::StridedMatrix{<:Real}) -LinearAlgebra.sec(::StridedMatrix) -LinearAlgebra.csc(::StridedMatrix) -LinearAlgebra.cot(::StridedMatrix) -LinearAlgebra.cosh(::StridedMatrix) -LinearAlgebra.sinh(::StridedMatrix) -LinearAlgebra.tanh(::StridedMatrix) -LinearAlgebra.sech(::StridedMatrix) -LinearAlgebra.csch(::StridedMatrix) -LinearAlgebra.coth(::StridedMatrix) -LinearAlgebra.acos(::StridedMatrix) -LinearAlgebra.asin(::StridedMatrix) -LinearAlgebra.atan(::StridedMatrix) -LinearAlgebra.asec(::StridedMatrix) -LinearAlgebra.acsc(::StridedMatrix) -LinearAlgebra.acot(::StridedMatrix) -LinearAlgebra.acosh(::StridedMatrix) -LinearAlgebra.asinh(::StridedMatrix) -LinearAlgebra.atanh(::StridedMatrix) -LinearAlgebra.asech(::StridedMatrix) -LinearAlgebra.acsch(::StridedMatrix) -LinearAlgebra.acoth(::StridedMatrix) -LinearAlgebra.lyap -LinearAlgebra.sylvester -LinearAlgebra.issuccess -LinearAlgebra.issymmetric -LinearAlgebra.isposdef -LinearAlgebra.isposdef! -LinearAlgebra.istril -LinearAlgebra.istriu -LinearAlgebra.isdiag -LinearAlgebra.ishermitian -Base.transpose -LinearAlgebra.transpose! -LinearAlgebra.Transpose -LinearAlgebra.TransposeFactorization -Base.adjoint -LinearAlgebra.adjoint! -LinearAlgebra.Adjoint -LinearAlgebra.AdjointFactorization -Base.copy(::Union{Transpose,Adjoint}) -LinearAlgebra.stride1 -LinearAlgebra.checksquare -LinearAlgebra.peakflops -LinearAlgebra.hermitianpart -LinearAlgebra.hermitianpart! -LinearAlgebra.copy_adjoint! -LinearAlgebra.copy_transpose! -``` - -## Low-level matrix operations - -In many cases there are in-place versions of matrix operations that allow you to supply -a pre-allocated output vector or matrix. This is useful when optimizing critical code in order -to avoid the overhead of repeated allocations. These in-place operations are suffixed with `!` -below (e.g. `mul!`) according to the usual Julia convention. - -```@docs -LinearAlgebra.mul! -LinearAlgebra.lmul! -LinearAlgebra.rmul! -LinearAlgebra.ldiv! -LinearAlgebra.rdiv! -``` - -## BLAS functions - -In Julia (as in much of scientific computation), dense linear-algebra operations are based on -the [LAPACK library](https://www.netlib.org/lapack/), which in turn is built on top of basic linear-algebra -building-blocks known as the [BLAS](https://www.netlib.org/blas/). There are highly optimized -implementations of BLAS available for every computer architecture, and sometimes in high-performance -linear algebra routines it is useful to call the BLAS functions directly. - -`LinearAlgebra.BLAS` provides wrappers for some of the BLAS functions. Those BLAS functions -that overwrite one of the input arrays have names ending in `'!'`. Usually, a BLAS function has -four methods defined, for [`Float32`](@ref), [`Float64`](@ref), [`ComplexF32`](@ref Complex), -and [`ComplexF64`](@ref Complex) arrays. - -### [BLAS character arguments](@id stdlib-blas-chars) - -Many BLAS functions accept arguments that determine whether to transpose an argument (`trans`), -which triangle of a matrix to reference (`uplo` or `ul`), -whether the diagonal of a triangular matrix can be assumed to -be all ones (`dA`) or which side of a matrix multiplication -the input argument belongs on (`side`). The possibilities are: - -#### [Multiplication order](@id stdlib-blas-side) - -| `side` | Meaning | -|:-------|:--------------------------------------------------------------------| -| `'L'` | The argument goes on the *left* side of a matrix-matrix operation. | -| `'R'` | The argument goes on the *right* side of a matrix-matrix operation. | - -#### [Triangle referencing](@id stdlib-blas-uplo) - -| `uplo`/`ul` | Meaning | -|:------------|:------------------------------------------------------| -| `'U'` | Only the *upper* triangle of the matrix will be used. | -| `'L'` | Only the *lower* triangle of the matrix will be used. | - -#### [Transposition operation](@id stdlib-blas-trans) - -| `trans`/`tX` | Meaning | -|:-------------|:--------------------------------------------------------| -| `'N'` | The input matrix `X` is not transposed or conjugated. | -| `'T'` | The input matrix `X` will be transposed. | -| `'C'` | The input matrix `X` will be conjugated and transposed. | - -#### [Unit diagonal](@id stdlib-blas-diag) - -| `diag`/`dX` | Meaning | -|:------------|:----------------------------------------------------------| -| `'N'` | The diagonal values of the matrix `X` will be read. | -| `'U'` | The diagonal of the matrix `X` is assumed to be all ones. | - -```@docs -LinearAlgebra.BLAS -LinearAlgebra.BLAS.set_num_threads -LinearAlgebra.BLAS.get_num_threads -``` - -BLAS functions can be divided into three groups, also called three levels, -depending on when they were first proposed, the type of input parameters, -and the complexity of the operation. - -### Level 1 BLAS functions - -The level 1 BLAS functions were first proposed in ([Lawson, 1979](https://dl.acm.org/doi/10.1145/355841.355847)) and -define operations between scalars and vectors. - -```@docs -# xROTG -# xROTMG -LinearAlgebra.BLAS.rot! -# xROTM -# xSWAP -LinearAlgebra.BLAS.scal! -LinearAlgebra.BLAS.scal -LinearAlgebra.BLAS.blascopy! -# xAXPY! -# xAXPBY! -LinearAlgebra.BLAS.dot -LinearAlgebra.BLAS.dotu -LinearAlgebra.BLAS.dotc -# xxDOT -LinearAlgebra.BLAS.nrm2 -LinearAlgebra.BLAS.asum -LinearAlgebra.BLAS.iamax -``` - -### Level 2 BLAS functions - -The level 2 BLAS functions were published in ([Dongarra, 1988](https://dl.acm.org/doi/10.1145/42288.42291)) -and define matrix-vector operations. - -**return a vector** - -```@docs -LinearAlgebra.BLAS.gemv! -LinearAlgebra.BLAS.gemv(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.gemv(::Any, ::Any, ::Any) -LinearAlgebra.BLAS.gbmv! -LinearAlgebra.BLAS.gbmv -LinearAlgebra.BLAS.hemv! -LinearAlgebra.BLAS.hemv(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.hemv(::Any, ::Any, ::Any) -# hbmv!, hbmv -LinearAlgebra.BLAS.hpmv! -LinearAlgebra.BLAS.symv! -LinearAlgebra.BLAS.symv(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.symv(::Any, ::Any, ::Any) -LinearAlgebra.BLAS.sbmv! -LinearAlgebra.BLAS.sbmv(::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.sbmv(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.spmv! -LinearAlgebra.BLAS.trmv! -LinearAlgebra.BLAS.trmv -# xTBMV -# xTPMV -LinearAlgebra.BLAS.trsv! -LinearAlgebra.BLAS.trsv -# xTBSV -# xTPSV -``` - -**return a matrix** - -```@docs -LinearAlgebra.BLAS.ger! -# xGERU -# xGERC -LinearAlgebra.BLAS.her! -# xHPR -# xHER2 -# xHPR2 -LinearAlgebra.BLAS.syr! -LinearAlgebra.BLAS.spr! -# xSYR2 -# xSPR2 -``` - -### Level 3 BLAS functions - -The level 3 BLAS functions were published in ([Dongarra, 1990](https://dl.acm.org/doi/10.1145/77626.79170)) -and define matrix-matrix operations. - -```@docs -LinearAlgebra.BLAS.gemmt! -LinearAlgebra.BLAS.gemmt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.gemmt(::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.gemm! -LinearAlgebra.BLAS.gemm(::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.gemm(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.symm! -LinearAlgebra.BLAS.symm(::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.symm(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.hemm! -LinearAlgebra.BLAS.hemm(::Any, ::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.hemm(::Any, ::Any, ::Any, ::Any) -LinearAlgebra.BLAS.syrk! -LinearAlgebra.BLAS.syrk -LinearAlgebra.BLAS.herk! -LinearAlgebra.BLAS.herk -LinearAlgebra.BLAS.syr2k! -LinearAlgebra.BLAS.syr2k -LinearAlgebra.BLAS.her2k! -LinearAlgebra.BLAS.her2k -LinearAlgebra.BLAS.trmm! -LinearAlgebra.BLAS.trmm -LinearAlgebra.BLAS.trsm! -LinearAlgebra.BLAS.trsm -``` - -## [LAPACK functions](@id man-linalg-lapack-functions) - -`LinearAlgebra.LAPACK` provides wrappers for some of the LAPACK functions for linear algebra. - Those functions that overwrite one of the input arrays have names ending in `'!'`. - -Usually a function has 4 methods defined, one each for [`Float64`](@ref), [`Float32`](@ref), -`ComplexF64` and `ComplexF32` arrays. - -Note that the LAPACK API provided by Julia can and will change in the future. Since this API is -not user-facing, there is no commitment to support/deprecate this specific set of functions in -future releases. - -```@docs -LinearAlgebra.LAPACK -LinearAlgebra.LAPACK.gbtrf! -LinearAlgebra.LAPACK.gbtrs! -LinearAlgebra.LAPACK.gebal! -LinearAlgebra.LAPACK.gebak! -LinearAlgebra.LAPACK.gebrd! -LinearAlgebra.LAPACK.gelqf! -LinearAlgebra.LAPACK.geqlf! -LinearAlgebra.LAPACK.geqrf! -LinearAlgebra.LAPACK.geqp3! -LinearAlgebra.LAPACK.gerqf! -LinearAlgebra.LAPACK.geqrt! -LinearAlgebra.LAPACK.geqrt3! -LinearAlgebra.LAPACK.getrf! -LinearAlgebra.LAPACK.tzrzf! -LinearAlgebra.LAPACK.ormrz! -LinearAlgebra.LAPACK.gels! -LinearAlgebra.LAPACK.gesv! -LinearAlgebra.LAPACK.getrs! -LinearAlgebra.LAPACK.getri! -LinearAlgebra.LAPACK.gesvx! -LinearAlgebra.LAPACK.gelsd! -LinearAlgebra.LAPACK.gelsy! -LinearAlgebra.LAPACK.gglse! -LinearAlgebra.LAPACK.geev! -LinearAlgebra.LAPACK.gesdd! -LinearAlgebra.LAPACK.gesvd! -LinearAlgebra.LAPACK.ggsvd! -LinearAlgebra.LAPACK.ggsvd3! -LinearAlgebra.LAPACK.geevx! -LinearAlgebra.LAPACK.ggev! -LinearAlgebra.LAPACK.ggev3! -LinearAlgebra.LAPACK.gtsv! -LinearAlgebra.LAPACK.gttrf! -LinearAlgebra.LAPACK.gttrs! -LinearAlgebra.LAPACK.orglq! -LinearAlgebra.LAPACK.orgqr! -LinearAlgebra.LAPACK.orgql! -LinearAlgebra.LAPACK.orgrq! -LinearAlgebra.LAPACK.ormlq! -LinearAlgebra.LAPACK.ormqr! -LinearAlgebra.LAPACK.ormql! -LinearAlgebra.LAPACK.ormrq! -LinearAlgebra.LAPACK.gemqrt! -LinearAlgebra.LAPACK.posv! -LinearAlgebra.LAPACK.potrf! -LinearAlgebra.LAPACK.potri! -LinearAlgebra.LAPACK.potrs! -LinearAlgebra.LAPACK.pstrf! -LinearAlgebra.LAPACK.ptsv! -LinearAlgebra.LAPACK.pttrf! -LinearAlgebra.LAPACK.pttrs! -LinearAlgebra.LAPACK.trtri! -LinearAlgebra.LAPACK.trtrs! -LinearAlgebra.LAPACK.trcon! -LinearAlgebra.LAPACK.trevc! -LinearAlgebra.LAPACK.trrfs! -LinearAlgebra.LAPACK.stev! -LinearAlgebra.LAPACK.stebz! -LinearAlgebra.LAPACK.stegr! -LinearAlgebra.LAPACK.stein! -LinearAlgebra.LAPACK.syconv! -LinearAlgebra.LAPACK.sysv! -LinearAlgebra.LAPACK.sytrf! -LinearAlgebra.LAPACK.sytri! -LinearAlgebra.LAPACK.sytrs! -LinearAlgebra.LAPACK.hesv! -LinearAlgebra.LAPACK.hetrf! -LinearAlgebra.LAPACK.hetri! -LinearAlgebra.LAPACK.hetrs! -LinearAlgebra.LAPACK.syev! -LinearAlgebra.LAPACK.syevr! -LinearAlgebra.LAPACK.syevd! -LinearAlgebra.LAPACK.sygvd! -LinearAlgebra.LAPACK.bdsqr! -LinearAlgebra.LAPACK.bdsdc! -LinearAlgebra.LAPACK.gecon! -LinearAlgebra.LAPACK.gehrd! -LinearAlgebra.LAPACK.orghr! -LinearAlgebra.LAPACK.gees! -LinearAlgebra.LAPACK.gges! -LinearAlgebra.LAPACK.gges3! -LinearAlgebra.LAPACK.trexc! -LinearAlgebra.LAPACK.trsen! -LinearAlgebra.LAPACK.tgsen! -LinearAlgebra.LAPACK.trsyl! -LinearAlgebra.LAPACK.hseqr! -``` - -## Developer Documentation - -```@docs -LinearAlgebra.matprod_dest -LinearAlgebra.haszero -``` - -```@meta -DocTestSetup = nothing -``` diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl deleted file mode 100644 index fc1081e007da2..0000000000000 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ /dev/null @@ -1,843 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" -Linear algebra module. Provides array arithmetic, -matrix factorizations and other linear algebra related -functionality. -""" -module LinearAlgebra - -import Base: \, /, //, *, ^, +, -, == -import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, asec, asech, - asin, asinh, atan, atanh, axes, big, broadcast, cbrt, ceil, cis, collect, conj, convert, - copy, copyto!, copymutable, cos, cosh, cot, coth, csc, csch, eltype, exp, fill!, floor, - getindex, hcat, getproperty, imag, inv, invpermuterows!, isapprox, isequal, isone, iszero, - IndexStyle, kron, kron!, length, log, map, ndims, one, oneunit, parent, permutecols!, - permutedims, permuterows!, power_by_squaring, promote_rule, real, sec, sech, setindex!, - show, similar, sin, sincos, sinh, size, sqrt, strides, stride, tan, tanh, transpose, trunc, - typed_hcat, vec, view, zero -using Base: IndexLinear, promote_eltype, promote_op, print_matrix, - @propagate_inbounds, reduce, typed_hvcat, typed_vcat, require_one_based_indexing, - splat, BitInteger -using Base.Broadcast: Broadcasted, broadcasted -using Base.PermutedDimsArrays: CommutativeOps -using OpenBLAS_jll -using libblastrampoline_jll -import Libdl - -export -# Modules - BLAS, - LAPACK, - -# Types - Adjoint, - Bidiagonal, - BunchKaufman, - Cholesky, - CholeskyPivoted, - ColumnNorm, - Diagonal, - Eigen, - Factorization, - GeneralizedEigen, - GeneralizedSVD, - GeneralizedSchur, - Hermitian, - Hessenberg, - LDLt, - LQ, - LU, - LowerTriangular, - NoPivot, - QR, - QRPivoted, - RowMaximum, - RowNonZero, - SVD, - Schur, - SymTridiagonal, - Symmetric, - Transpose, - Tridiagonal, - UniformScaling, - UnitLowerTriangular, - UnitUpperTriangular, - UpperHessenberg, - UpperTriangular, - - -# Functions - adjoint!, - adjoint, - axpby!, - axpy!, - bunchkaufman!, - bunchkaufman, - cholesky!, - cholesky, - cond, - condskeel, - copy_adjoint!, - copy_transpose!, - copyto!, - copytrito!, - cross, - det, - diag, - diagind, - diagm, - diagview, - dot, - eigen!, - eigen, - eigmax, - eigmin, - eigvals!, - eigvals, - eigvecs, - factorize, - givens, - hermitianpart!, - hermitianpart, - hessenberg!, - hessenberg, - isdiag, - ishermitian, - isposdef!, - isposdef, - issuccess, - issymmetric, - istril, - istriu, - kron!, - kron, - ldiv!, - ldlt!, - ldlt, - lmul!, - logabsdet, - logdet, - lowrankdowndate!, - lowrankdowndate, - lowrankupdate!, - lowrankupdate, - lq!, - lq, - lu!, - lu, - lyap, - mul!, - norm, - normalize!, - normalize, - nullspace, - opnorm, - ordschur!, - ordschur, - pinv, - qr!, - qr, - rank, - rdiv!, - reflect!, - rmul!, - rotate!, - schur!, - schur, - svd!, - svd, - svdvals!, - svdvals, - sylvester, - tr, - transpose!, - transpose, - tril!, - tril, - triu!, - triu, - - -# Operators - \, - /, - -# Constants - I - -# not exported, but public names -public AbstractTriangular, - Givens, - checksquare, - haszero, - hermitian, - hermitian_type, - isbanded, - peakflops, - symmetric, - symmetric_type, - zeroslike, - matprod_dest - -const BlasFloat = Union{Float64,Float32,ComplexF64,ComplexF32} -const BlasReal = Union{Float64,Float32} -const BlasComplex = Union{ComplexF64,ComplexF32} - -if USE_BLAS64 - const BlasInt = Int64 -else - const BlasInt = Int32 -end - - -abstract type Algorithm end -struct DivideAndConquer <: Algorithm end -struct QRIteration <: Algorithm end -struct RobustRepresentations <: Algorithm end - -# Pivoting strategies for matrix factorization algorithms. -abstract type PivotingStrategy end - -""" - NoPivot - -Pivoting is not performed. This is the default strategy for [`cholesky`](@ref) and -[`qr`](@ref) factorizations. Note, however, that other matrix factorizations such as the LU -factorization may fail without pivoting, and may also be numerically unstable for -floating-point matrices in the face of roundoff error. In such cases, this pivot strategy -is mainly useful for pedagogical purposes. -""" -struct NoPivot <: PivotingStrategy end - -""" - RowNonZero - -First non-zero element in the remaining rows is chosen as the pivot element. - -Beware that for floating-point matrices, the resulting LU algorithm is numerically unstable -— this strategy is mainly useful for comparison to hand calculations (which typically use -this strategy) or for other algebraic types (e.g. rational numbers) not susceptible to -roundoff errors. Otherwise, the default `RowMaximum` pivoting strategy should be generally -preferred in Gaussian elimination. - -Note that the [element type](@ref eltype) of the matrix must admit an [`iszero`](@ref) -method. -""" -struct RowNonZero <: PivotingStrategy end - -""" - RowMaximum - -A row (and potentially also column) pivot is chosen based on a maximum property. -This is the default strategy for LU factorization and for pivoted Cholesky factorization -(though [`NoPivot`] is the default for [`cholesky`](@ref)). - -In the LU case, the maximum-magnitude element within the current column in the remaining -rows is chosen as the pivot element. This is sometimes referred to as the "partial -pivoting" algorithm. In this case, the [element type](@ref eltype) of the matrix must admit -an [`abs`](@ref) method, whose result type must admit a [`<`](@ref) method. - -In the Cholesky case, the maximal element among the remaining diagonal elements is -chosen as the pivot element. This is sometimes referred to as the "diagonal pivoting" -algorithm, and leads to _complete pivoting_ (i.e., of both rows and columns by the same -permutation). In this case, the (real part of the) [element type](@ref eltype) of the -matrix must admit a [`<`](@ref) method. -""" -struct RowMaximum <: PivotingStrategy end - -""" - ColumnNorm - -The column with the maximum norm is used for subsequent computation. This is used for -pivoted QR factorization. - -Note that the [element type](@ref eltype) of the matrix must admit [`norm`](@ref) and -[`abs`](@ref) methods, whose respective result types must admit a [`<`](@ref) method. -""" -struct ColumnNorm <: PivotingStrategy end - -using Base: DimOrInd - -# Check that stride of matrix/vector is 1 -# Writing like this to avoid splatting penalty when called with multiple arguments, -# see PR 16416 -""" - stride1(A) -> Int - -Return the distance between successive array elements -in dimension 1 in units of element size. - -# Examples -```jldoctest -julia> A = [1,2,3,4] -4-element Vector{Int64}: - 1 - 2 - 3 - 4 - -julia> LinearAlgebra.stride1(A) -1 - -julia> B = view(A, 2:2:4) -2-element view(::Vector{Int64}, 2:2:4) with eltype Int64: - 2 - 4 - -julia> LinearAlgebra.stride1(B) -2 -``` -""" -stride1(x) = stride(x,1) -stride1(x::Array) = 1 -stride1(x::DenseArray) = stride(x, 1)::Int - -@inline chkstride1(A...) = _chkstride1(true, A...) -@noinline _chkstride1(ok::Bool) = ok || error("matrix does not have contiguous columns") -@inline _chkstride1(ok::Bool, A, B...) = _chkstride1(ok & (stride1(A) == 1), B...) - -# Subtypes of StridedArrays that satisfy certain properties on their strides -# Similar to Base.RangeIndex, but only include range types where the step is statically known to be non-zero -const IncreasingRangeIndex = Union{BitInteger, AbstractUnitRange{<:BitInteger}} -const NonConstRangeIndex = Union{IncreasingRangeIndex, StepRange{<:BitInteger, <:BitInteger}} -# StridedArray subtypes for which _fullstride2(::T) === true is known from the type -DenseOrStridedReshapedReinterpreted{T,N} = - Union{DenseArray{T,N}, Base.StridedReshapedArray{T,N}, Base.StridedReinterpretArray{T,N}} -# Similar to Base.StridedSubArray, except with a NonConstRangeIndex instead of a RangeIndex -StridedSubArrayStandard{T,N,A<:DenseOrStridedReshapedReinterpreted, - I<:Tuple{Vararg{Union{NonConstRangeIndex, Base.ReshapedUnitRange, Base.AbstractCartesianIndex}}}} = Base.StridedSubArray{T,N,A,I} -StridedArrayStdSubArray{T,N} = Union{DenseOrStridedReshapedReinterpreted{T,N},StridedSubArrayStandard{T,N}} -# Similar to Base.StridedSubArray, except with a IncreasingRangeIndex instead of a RangeIndex -StridedSubArrayIncr{T,N,A<:DenseOrStridedReshapedReinterpreted, - I<:Tuple{Vararg{Union{IncreasingRangeIndex, Base.ReshapedUnitRange, Base.AbstractCartesianIndex}}}} = Base.StridedSubArray{T,N,A,I} -StridedArrayStdSubArrayIncr{T,N} = Union{DenseOrStridedReshapedReinterpreted{T,N},StridedSubArrayIncr{T,N}} -# These subarrays have a stride of 1 along the first dimension -StridedSubArrayAUR{T,N,A<:DenseOrStridedReshapedReinterpreted, - I<:Tuple{AbstractUnitRange{<:BitInteger}}} = Base.StridedSubArray{T,N,A,I} -StridedArrayStride1{T,N} = Union{DenseOrStridedReshapedReinterpreted{T,N},StridedSubArrayIncr{T,N}} -# StridedMatrixStride1 may typically be forwarded to LAPACK methods -StridedMatrixStride1{T} = StridedArrayStride1{T,2} - -""" - LinearAlgebra.checksquare(A) - -Check that a matrix is square, then return its common dimension. -For multiple arguments, return a vector. - -# Examples -```jldoctest -julia> A = fill(1, (4,4)); B = fill(1, (5,5)); - -julia> LinearAlgebra.checksquare(A, B) -2-element Vector{Int64}: - 4 - 5 -``` -""" -function checksquare(A) - m,n = size(A) - m == n || throw(DimensionMismatch(lazy"matrix is not square: dimensions are $(size(A))")) - m -end - -function checksquare(A...) - sizes = Int[] - for a in A - size(a,1)==size(a,2) || throw(DimensionMismatch(lazy"matrix is not square: dimensions are $(size(a))")) - push!(sizes, size(a,1)) - end - return sizes -end - -function char_uplo(uplo::Symbol) - if uplo === :U - return 'U' - elseif uplo === :L - return 'L' - else - throw_uplo() - end -end - -function sym_uplo(uplo::Char) - if uplo == 'U' - return :U - elseif uplo == 'L' - return :L - else - throw_uplo() - end -end - -@noinline throw_uplo() = throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)")) - -""" - ldiv!(Y, A, B) -> Y - -Compute `A \\ B` in-place and store the result in `Y`, returning the result. - -The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a -factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). -The reason for this is that factorization itself is both expensive and typically allocates memory -(although it can also be done in-place via, e.g., [`lu!`](@ref)), -and performance-critical situations requiring `ldiv!` usually also require fine-grained -control over the factorization of `A`. - -!!! note - Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as - these are already in a factorized form - -# Examples -```jldoctest -julia> A = [1 2.2 4; 3.1 0.2 3; 4 1 2]; - -julia> X = [1; 2.5; 3]; - -julia> Y = zero(X); - -julia> ldiv!(Y, qr(A), X); - -julia> Y ≈ A\\X -true -``` -""" -ldiv!(Y, A, B) - -""" - ldiv!(A, B) - -Compute `A \\ B` in-place and overwriting `B` to store the result. - -The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a -factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). -The reason for this is that factorization itself is both expensive and typically allocates memory -(although it can also be done in-place via, e.g., [`lu!`](@ref)), -and performance-critical situations requiring `ldiv!` usually also require fine-grained -control over the factorization of `A`. - -!!! note - Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as - these are already in a factorized form - -# Examples -```jldoctest -julia> A = [1 2.2 4; 3.1 0.2 3; 4 1 2]; - -julia> X = [1; 2.5; 3]; - -julia> Y = copy(X); - -julia> ldiv!(qr(A), X); - -julia> X ≈ A\\Y -true -``` -""" -ldiv!(A, B) - - -""" - rdiv!(A, B) - -Compute `A / B` in-place and overwriting `A` to store the result. - -The argument `B` should *not* be a matrix. Rather, instead of matrices it should be a -factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). -The reason for this is that factorization itself is both expensive and typically allocates memory -(although it can also be done in-place via, e.g., [`lu!`](@ref)), -and performance-critical situations requiring `rdiv!` usually also require fine-grained -control over the factorization of `B`. - -!!! note - Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as - these are already in a factorized form -""" -rdiv!(A, B) - -""" - copy_oftype(A, T) - -Creates a copy of `A` with eltype `T`. No assertions about mutability of the result are -made. When `eltype(A) == T`, then this calls `copy(A)` which may be overloaded for custom -array types. Otherwise, this calls `convert(AbstractArray{T}, A)`. -""" -copy_oftype(A::AbstractArray{T}, ::Type{T}) where {T} = copy(A) -copy_oftype(A::AbstractArray{T,N}, ::Type{S}) where {T,N,S} = convert(AbstractArray{S,N}, A) - -""" - copymutable_oftype(A, T) - -Copy `A` to a mutable array with eltype `T` based on `similar(A, T)`. - -The resulting matrix typically has similar algebraic structure as `A`. For -example, supplying a tridiagonal matrix results in another tridiagonal matrix. -In general, the type of the output corresponds to that of `similar(A, T)`. - -In LinearAlgebra, mutable copies (of some desired eltype) are created to be passed -to in-place algorithms (such as `ldiv!`, `rdiv!`, `lu!` and so on). If the specific -algorithm is known to preserve the algebraic structure, use `copymutable_oftype`. -If the algorithm is known to return a dense matrix (or some wrapper backed by a dense -matrix), then use `copy_similar`. - -See also: `Base.copymutable`, `copy_similar`. -""" -copymutable_oftype(A::AbstractArray, ::Type{S}) where {S} = copyto!(similar(A, S), A) - -""" - copy_similar(A, T) - -Copy `A` to a mutable array with eltype `T` based on `similar(A, T, size(A))`. - -Compared to `copymutable_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(A))`. - -See also: `copymutable_oftype`. -""" -copy_similar(A::AbstractArray, ::Type{T}) where {T} = copyto!(similar(A, T, size(A)), A) - -""" - BandIndex(band, index) - -Represent a Cartesian index as a linear index along a band. -This type is primarily meant to index into a specific band without branches, -so, for best performance, `band` should be a compile-time constant. -""" -struct BandIndex - band :: Int - index :: Int -end -function _cartinds(b::BandIndex) - (; band, index) = b - bandg0 = max(band,0) - row = index - band + bandg0 - col = index + bandg0 - CartesianIndex(row, col) -end -function Base.to_indices(A, inds, t::Tuple{BandIndex, Vararg{Any}}) - to_indices(A, inds, (_cartinds(first(t)), Base.tail(t)...)) -end -function Base.checkbounds(::Type{Bool}, A::AbstractMatrix, b::BandIndex) - checkbounds(Bool, A, _cartinds(b)) -end -function Base.checkbounds(A::Broadcasted, b::BandIndex) - checkbounds(A, _cartinds(b)) -end - -include("adjtrans.jl") -include("transpose.jl") - -include("exceptions.jl") -include("generic.jl") - -include("blas.jl") -include("matmul.jl") -include("lapack.jl") - -include("dense.jl") -include("tridiag.jl") -include("triangular.jl") - -include("factorization.jl") -include("eigen.jl") -include("svd.jl") -include("symmetric.jl") -include("cholesky.jl") -include("lu.jl") -include("bunchkaufman.jl") -include("diagonal.jl") -include("symmetriceigen.jl") -include("bidiag.jl") -include("uniformscaling.jl") -include("qr.jl") -include("lq.jl") -include("hessenberg.jl") -include("abstractq.jl") -include("givens.jl") -include("special.jl") -include("bitarray.jl") -include("ldlt.jl") -include("schur.jl") -include("structuredbroadcast.jl") -include("deprecated.jl") - -const ⋅ = dot -const × = cross -export ⋅, × - -# Separate the char corresponding to the wrapper from that corresponding to the uplo -# In most cases, the former may be constant-propagated, while the latter usually can't be. -# This improves type-inference in wrap for Symmetric/Hermitian matrices -# A WrapperChar is equivalent to `isuppertri ? uppercase(wrapperchar) : lowercase(wrapperchar)` -struct WrapperChar <: AbstractChar - wrapperchar :: Char - isuppertri :: Bool -end -function Base.Char(w::WrapperChar) - T = w.wrapperchar - if T ∈ ('N', 'T', 'C') # known cases where isuppertri is true - T - else - _isuppertri(w) ? uppercase(T) : lowercase(T) - end -end -Base.codepoint(w::WrapperChar) = codepoint(Char(w)) -WrapperChar(n::UInt32) = WrapperChar(Char(n)) -WrapperChar(c::Char) = WrapperChar(c, isuppercase(c)) -# We extract the wrapperchar so that the result may be constant-propagated -# This doesn't return a value of the same type on purpose -Base.uppercase(w::WrapperChar) = uppercase(w.wrapperchar) -Base.lowercase(w::WrapperChar) = lowercase(w.wrapperchar) -_isuppertri(w::WrapperChar) = w.isuppertri -_isuppertri(x::AbstractChar) = isuppercase(x) # compatibility with earlier Char-based implementation -_uplosym(x) = _isuppertri(x) ? (:U) : (:L) - -wrapper_char(::AbstractArray) = 'N' -wrapper_char(::Adjoint) = 'C' -wrapper_char(::Adjoint{<:Real}) = 'T' -wrapper_char(::Transpose) = 'T' -wrapper_char(A::Hermitian) = WrapperChar('H', A.uplo == 'U') -wrapper_char(A::Hermitian{<:Real}) = WrapperChar('S', A.uplo == 'U') -wrapper_char(A::Symmetric) = WrapperChar('S', A.uplo == 'U') - -wrapper_char_NTC(A::AbstractArray) = uppercase(wrapper_char(A)) == 'N' -wrapper_char_NTC(A::Union{StridedArray, Adjoint, Transpose}) = true -wrapper_char_NTC(A::Union{Symmetric, Hermitian}) = false - -Base.@constprop :aggressive function wrap(A::AbstractVecOrMat, tA::AbstractChar) - # merge the result of this before return, so that we can type-assert the return such - # that even if the tmerge is inaccurate, inference can still identify that the - # `_generic_matmatmul` signature still matches and doesn't require missing backedges - tA_uc = uppercase(tA) - B = if tA_uc == 'N' - A - elseif tA_uc == 'T' - transpose(A) - elseif tA_uc == 'C' - adjoint(A) - elseif tA_uc == 'H' - Hermitian(A, _uplosym(tA)) - elseif tA_uc == 'S' - Symmetric(A, _uplosym(tA)) - end - return B::AbstractVecOrMat -end - -_unwrap(A::AbstractVecOrMat) = A - -## convenience methods -## return only the solution of a least squares problem while avoiding promoting -## vectors to matrices. -_cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x -_cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X - -# SymTridiagonal ev can be the same length as dv, but the last element is -# ignored. However, some methods can fail if they read the entire ev -# rather than just the meaningful elements. This is a helper function -# for getting only the meaningful elements of ev. See #41089 -_evview(S::SymTridiagonal) = @view S.ev[begin:begin + length(S.dv) - 2] - -## append right hand side with zeros if necessary -_zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n)) -_zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2)) - -# append a zero element / drop the last element -_pushzero(A) = (B = similar(A, length(A)+1); @inbounds B[begin:end-1] .= A; @inbounds B[end] = zero(eltype(B)); B) -_droplast!(A) = deleteat!(A, lastindex(A)) - -# destination type for matmul -matprod_dest(A::StructuredMatrix, B::StructuredMatrix, TS) = similar(B, TS, size(B)) -matprod_dest(A, B::StructuredMatrix, TS) = similar(A, TS, size(A)) -matprod_dest(A::StructuredMatrix, B, TS) = similar(B, TS, size(B)) -# diagonal is special, as it does not change the structure of the other matrix -# we call similar without a size to preserve the type of the matrix wherever possible -# reroute through _matprod_dest_diag to allow speicalizing on the type of the StructuredMatrix -# without defining methods for both the orderings -matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = _matprod_dest_diag(A, TS) -matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = _matprod_dest_diag(B, TS) -matprod_dest(A::Diagonal, B::Diagonal, TS) = _matprod_dest_diag(B, TS) -_matprod_dest_diag(A, TS) = similar(A, TS) -_matprod_dest_diag(A::UnitUpperTriangular, TS) = UpperTriangular(similar(parent(A), TS)) -_matprod_dest_diag(A::UnitLowerTriangular, TS) = LowerTriangular(similar(parent(A), TS)) -function _matprod_dest_diag(A::SymTridiagonal, TS) - n = size(A, 1) - ev = similar(A, TS, max(0, n-1)) - dv = similar(A, TS, n) - Tridiagonal(ev, dv, similar(ev)) -end - -# Special handling for adj/trans vec -matprod_dest(A::Diagonal, B::AdjOrTransAbsVec, TS) = similar(B, TS) - -# General fallback definition for handling under- and overdetermined system as well as square problems -# While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs -# which is required by LAPACK but not SuiteSparse which allows real-complex solves in some cases. Hence, -# we restrict this method to only the LAPACK factorizations in LinearAlgebra. -# The definition is put here since it explicitly references all the Factorization structs so it has -# to be located after all the files that define the structs. -const LAPACKFactorizations{T,S} = Union{ - BunchKaufman{T,S}, - Cholesky{T,S}, - LQ{T,S}, - LU{T,S}, - QR{T,S}, - QRCompactWY{T,S}, - QRPivoted{T,S}, - SVD{T,<:Real,S}} - -(\)(F::LAPACKFactorizations, B::AbstractVecOrMat) = ldiv(F, B) -(\)(F::AdjointFactorization{<:Any,<:LAPACKFactorizations}, B::AbstractVecOrMat) = ldiv(F, B) -(\)(F::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) = ldiv(F, B) - -function ldiv(F::Factorization, B::AbstractVecOrMat) - require_one_based_indexing(B) - m, n = size(F) - if m != size(B, 1) - throw(DimensionMismatch("arguments must have the same number of rows")) - end - - TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) - FF = Factorization{TFB}(F) - - # For wide problem we (often) compute a minimum norm solution. The solution - # is larger than the right hand side so we use size(F, 2). - BB = _zeros(TFB, B, n) - - if n > size(B, 1) - # Underdetermined - copyto!(view(BB, 1:m, :), B) - else - copyto!(BB, B) - end - - ldiv!(FF, BB) - - # For tall problems, we compute a least squares solution so only part - # of the rhs should be returned from \ while ldiv! uses (and returns) - # the complete rhs - return _cut_B(BB, 1:n) -end -# disambiguate -(\)(F::LAPACKFactorizations{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - @invoke \(F::Factorization{T}, B::VecOrMat{Complex{T}}) -(\)(F::AdjointFactorization{T,<:LAPACKFactorizations}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - ldiv(F, B) -(\)(F::TransposeFactorization{T,<:LU}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - ldiv(F, B) - -""" - LinearAlgebra.peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) - -`peakflops` computes the peak flop rate of the computer by using double precision -[`gemm!`](@ref LinearAlgebra.BLAS.gemm!). By default, if no arguments are specified, it -multiplies two `Float64` matrices of size `n x n`, where `n = 4096`. If the underlying BLAS is using -multiple threads, higher flop rates are realized. The number of BLAS threads can be set with -[`BLAS.set_num_threads(n)`](@ref). - -If the keyword argument `eltype` is provided, `peakflops` will construct matrices with elements -of type `eltype` for calculating the peak flop rate. - -By default, `peakflops` will use the best timing from 3 trials. If the `ntrials` keyword argument -is provided, `peakflops` will use those many trials for picking the best timing. - -If the keyword argument `parallel` is set to `true`, `peakflops` is run in parallel on all -the worker processors. The flop rate of the entire parallel computer is returned. When -running in parallel, only 1 BLAS thread is used. The argument `n` still refers to the size -of the problem that is solved on each processor. - -!!! compat "Julia 1.1" - This function requires at least Julia 1.1. In Julia 1.0 it is available from - the standard library `InteractiveUtils`. -""" -function peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) - t = zeros(Float64, ntrials) - for i=1:ntrials - a = ones(eltype,n,n) - t[i] = @elapsed a2 = a*a - @assert a2[1,1] == n - end - - if parallel - let Distributed = Base.require(Base.PkgId( - Base.UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) - nworkers = @invokelatest Distributed.nworkers() - results = @invokelatest Distributed.pmap(peakflops, fill(n, nworkers)) - return sum(results) - end - else - return 2*Float64(n)^3 / minimum(t) - end -end - - -function versioninfo(io::IO=stdout) - indent = " " - config = BLAS.get_config() - build_flags = join(string.(config.build_flags), ", ") - println(io, "BLAS: ", BLAS.libblastrampoline, " (", build_flags, ")") - for lib in config.loaded_libs - interface = uppercase(string(lib.interface)) - println(io, indent, "--> ", lib.libname, " (", interface, ")") - end - println(io, "Threading:") - println(io, indent, "Threads.threadpoolsize() = ", Threads.threadpoolsize()) - println(io, indent, "Threads.maxthreadid() = ", Base.Threads.maxthreadid()) - println(io, indent, "LinearAlgebra.BLAS.get_num_threads() = ", BLAS.get_num_threads()) - println(io, "Relevant environment variables:") - env_var_names = [ - "JULIA_NUM_THREADS", - "MKL_DYNAMIC", - "MKL_NUM_THREADS", - # OpenBLAS has a hierarchy of environment variables for setting the - # number of threads, see - # https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables - ("OPENBLAS_NUM_THREADS", "GOTO_NUM_THREADS", "OMP_NUM_THREADS"), - ] - printed_at_least_one_env_var = false - print_var(io, indent, name) = println(io, indent, name, " = ", ENV[name]) - for name in env_var_names - if name isa Tuple - # If `name` is a Tuple, then find the first environment which is - # defined, and disregard the following ones. - for nm in name - if haskey(ENV, nm) - print_var(io, indent, nm) - printed_at_least_one_env_var = true - break - end - end - else - if haskey(ENV, name) - print_var(io, indent, name) - printed_at_least_one_env_var = true - end - end - end - if !printed_at_least_one_env_var - println(io, indent, "[none]") - end - return nothing -end - -function __init__() - try - verbose = parse(Bool, get(ENV, "LBT_VERBOSE", "false")) - BLAS.lbt_forward(OpenBLAS_jll.libopenblas_path; clear=true, verbose) - BLAS.check() - catch ex - Base.showerror_nostdio(ex, "WARNING: Error during initialization of module LinearAlgebra") - end - # register a hook to disable BLAS threading - Base.at_disable_library_threading(() -> BLAS.set_num_threads(1)) - - # https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables - if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "GOTO_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS") - @static if Sys.isapple() && Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "aarch64" - BLAS.set_num_threads(max(1, @ccall(jl_effective_threads()::Cint))) - else - BLAS.set_num_threads(max(1, @ccall(jl_effective_threads()::Cint) ÷ 2)) - end - end -end - -end # module LinearAlgebra diff --git a/stdlib/LinearAlgebra/src/abstractq.jl b/stdlib/LinearAlgebra/src/abstractq.jl deleted file mode 100644 index 0fa2233b89593..0000000000000 --- a/stdlib/LinearAlgebra/src/abstractq.jl +++ /dev/null @@ -1,642 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -abstract type AbstractQ{T} end - -struct AdjointQ{T,S<:AbstractQ{T}} <: AbstractQ{T} - Q::S -end - -parent(adjQ::AdjointQ) = adjQ.Q -eltype(::Type{<:AbstractQ{T}}) where {T} = T -Base.eltypeof(Q::AbstractQ) = eltype(Q) -ndims(::AbstractQ) = 2 - -# inversion/adjoint/transpose -inv(Q::AbstractQ) = Q' -adjoint(Q::AbstractQ) = AdjointQ(Q) -transpose(Q::AbstractQ{<:Real}) = AdjointQ(Q) -transpose(Q::AbstractQ) = error("transpose not implemented for $(typeof(Q)). Consider using adjoint instead of transpose.") -adjoint(adjQ::AdjointQ) = adjQ.Q - -(^)(Q::AbstractQ, p::Integer) = p < 0 ? power_by_squaring(inv(Q), -p) : power_by_squaring(Q, p) -@inline Base.literal_pow(::typeof(^), Q::AbstractQ, ::Val{1}) = Q -@inline Base.literal_pow(::typeof(^), Q::AbstractQ, ::Val{-1}) = inv(Q) - -# promotion with AbstractMatrix, at least for equal eltypes -promote_rule(::Type{<:AbstractMatrix{T}}, ::Type{<:AbstractQ{T}}) where {T} = - (@inline; Union{AbstractMatrix{T},AbstractQ{T}}) - -# conversion -# the following eltype promotion should be defined for each subtype `QType` -# convert(::Type{AbstractQ{T}}, Q::QType) where {T} = QType{T}(Q) -# and then care has to be taken that -# QType{T}(Q::QType{T}) where T = ... -# is implemented as a no-op - -# the following conversion method ensures functionality when the above method is not defined -# (as for HessenbergQ), but no eltype conversion is required either (say, in multiplication) -convert(::Type{AbstractQ{T}}, Q::AbstractQ{T}) where {T} = Q -convert(::Type{AbstractQ{T}}, adjQ::AdjointQ{T}) where {T} = adjQ -convert(::Type{AbstractQ{T}}, adjQ::AdjointQ) where {T} = convert(AbstractQ{T}, adjQ.Q)' - -# ... to matrix -collect(Q::AbstractQ) = copyto!(Matrix{eltype(Q)}(undef, size(Q)), Q) -Matrix{T}(Q::AbstractQ) where {T} = convert(Matrix{T}, Q*I) # generic fallback, yields square matrix -Matrix{T}(adjQ::AdjointQ{S}) where {T,S} = convert(Matrix{T}, lmul!(adjQ, Matrix{S}(I, size(adjQ)))) -Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) -Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) -Array(Q::AbstractQ) = Matrix(Q) -convert(::Type{T}, Q::AbstractQ) where {T<:AbstractArray} = T(Q) -# legacy -@deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T}, - convert(LinearAlgebra.AbstractQ{T}, Q)) - -function size(Q::AbstractQ, dim::Integer) - if dim < 1 - throw(BoundsError()) - elseif dim <= 2 # && 1 <= dim - return size(Q)[dim] - else # 2 < dim - return 1 - end -end -size(adjQ::AdjointQ) = reverse(size(adjQ.Q)) - -# comparison -(==)(Q::AbstractQ, A::AbstractMatrix) = lmul!(Q, Matrix{eltype(Q)}(I, size(A))) == A -(==)(A::AbstractMatrix, Q::AbstractQ) = Q == A -(==)(Q::AbstractQ, P::AbstractQ) = Matrix(Q) == Matrix(P) -isapprox(Q::AbstractQ, A::AbstractMatrix; kwargs...) = - isapprox(lmul!(Q, Matrix{eltype(Q)}(I, size(A))), A, kwargs...) -isapprox(A::AbstractMatrix, Q::AbstractQ; kwargs...) = isapprox(Q, A, kwargs...) -isapprox(Q::AbstractQ, P::AbstractQ; kwargs...) = isapprox(Matrix(Q), Matrix(P), kwargs...) - -# pseudo-array behaviour, required for indexing with `begin` or `end` -axes(Q::AbstractQ) = map(Base.oneto, size(Q)) -axes(Q::AbstractQ, d::Integer) = d in (1, 2) ? axes(Q)[d] : Base.OneTo(1) - -copymutable(Q::AbstractQ{T}) where {T} = lmul!(Q, Matrix{T}(I, size(Q))) -copy(Q::AbstractQ) = copymutable(Q) - -# legacy compatibility -similar(Q::AbstractQ) = similar(Q, eltype(Q), size(Q)) -similar(Q::AbstractQ, ::Type{T}) where {T} = similar(Q, T, size(Q)) -similar(Q::AbstractQ, size::DimOrInd...) = similar(Q, eltype(Q), size...) -similar(Q::AbstractQ, ::Type{T}, size::DimOrInd...) where {T} = similar(Q, T, Base.to_shape(size)) -similar(Q::AbstractQ, size::Tuple{Vararg{DimOrInd}}) = similar(Q, eltype(Q), Base.to_shape(size)) -similar(Q::AbstractQ, ::Type{T}, size::NTuple{N,Integer}) where {T,N} = Array{T,N}(undef, size) - -# getindex -@inline function getindex(Q::AbstractQ, inds...) - @boundscheck Base.checkbounds_indices(Bool, axes(Q), inds) || Base.throw_boundserror(Q, inds) - return _getindex(Q, inds...) -end -@inline getindex(Q::AbstractQ, ::Colon) = copymutable(Q)[:] -@inline getindex(Q::AbstractQ, ::Colon, ::Colon) = copy(Q) - -@inline _getindex(Q::AbstractQ, inds...) = @inbounds copymutable(Q)[inds...] -@inline function _getindex(Q::AbstractQ, ::Colon, J::AbstractVector{<:Integer}) - Y = zeros(eltype(Q), size(Q, 2), length(J)) - @inbounds for (i,j) in enumerate(J) - Y[j,i] = oneunit(eltype(Q)) - end - lmul!(Q, Y) -end -@inline _getindex(Q::AbstractQ, I::AbstractVector{Int}, J::AbstractVector{Int}) = @inbounds Q[:,J][I,:] -@inline function _getindex(Q::AbstractQ, ::Colon, j::Int) - y = zeros(eltype(Q), size(Q, 2)) - y[j] = oneunit(eltype(Q)) - lmul!(Q, y) -end -@inline _getindex(Q::AbstractQ, i::Int, j::Int) = @inbounds Q[:,j][i] - -# needed because AbstractQ does not subtype AbstractMatrix -qr(Q::AbstractQ{T}, arg...; kwargs...) where {T} = qr!(Matrix{_qreltype(T)}(Q), arg...; kwargs...) -lq(Q::AbstractQ{T}, arg...; kwargs...) where {T} = lq!(Matrix{lq_eltype(T)}(Q), arg...; kwargs...) -hessenberg(Q::AbstractQ{T}) where {T} = hessenberg!(Matrix{eigtype(T)}(Q)) - -# needed when used interchangeably with AbstractMatrix (analogous to views of ranges) -view(A::AbstractQ, I...) = getindex(A, I...) - -# specialization avoiding the fallback using slow `getindex` -function copyto!(dest::AbstractMatrix, src::AbstractQ) - copyto!(dest, I) - lmul!(src, dest) -end -# needed to resolve method ambiguities -function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,perm} - if perm == (1, 2) - copyto!(parent(dest), src) - else - @assert perm == (2, 1) # there are no other permutations of two indices - if T <: Real - copyto!(parent(dest), I) - lmul!(src', parent(dest)) - else - # LAPACK does not offer inplace lmul!(transpose(Q), B) for complex Q - tmp = similar(parent(dest)) - copyto!(tmp, I) - rmul!(tmp, src) - permutedims!(parent(dest), tmp, (2, 1)) - end - end - return dest -end -# used in concatenations: Base.__cat_offset1! -Base._copy_or_fill!(A, inds, Q::AbstractQ) = (A[inds...] = collect(Q)) -# overloads of helper functions -Base.cat_size(A::AbstractQ) = size(A) -Base.cat_size(A::AbstractQ, d) = size(A, d) -Base.cat_length(a::AbstractQ) = prod(size(a)) -Base.cat_ndims(a::AbstractQ) = ndims(a) -Base.cat_indices(A::AbstractQ, d) = axes(A, d) -Base.cat_similar(A::AbstractQ, T::Type, shape::Tuple) = Array{T}(undef, shape) -Base.cat_similar(A::AbstractQ, T::Type, shape::Vector) = Array{T}(undef, shape...) - -function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ) - print(io, Base.dims2string(size(Q)), ' ', summary(Q)) -end - -# multiplication -# generically, treat AbstractQ like a matrix with its definite size -qsize_check(Q::AbstractQ, B::AbstractVecOrMat) = - size(Q, 2) == size(B, 1) || - throw(DimensionMismatch(lazy"second dimension of Q, $(size(Q,2)), must coincide with first dimension of B, $(size(B,1))")) -qsize_check(A::AbstractVecOrMat, Q::AbstractQ) = - size(A, 2) == size(Q, 1) || - throw(DimensionMismatch(lazy"second dimension of A, $(size(A,2)), must coincide with first dimension of Q, $(size(Q,1))")) -qsize_check(Q::AbstractQ, P::AbstractQ) = - size(Q, 2) == size(P, 1) || - throw(DimensionMismatch(lazy"second dimension of A, $(size(Q,2)), must coincide with first dimension of B, $(size(P,1))")) - -# mimic the AbstractArray fallback -*(Q::AbstractQ{<:Number}) = Q - -(*)(Q::AbstractQ, J::UniformScaling) = Q*J.λ -function (*)(Q::AbstractQ, b::Number) - T = promote_type(eltype(Q), typeof(b)) - lmul!(convert(AbstractQ{T}, Q), Matrix{T}(b*I, size(Q))) -end -function (*)(Q::AbstractQ, B::AbstractVector) - T = promote_type(eltype(Q), eltype(B)) - qsize_check(Q, B) - mul!(similar(B, T, size(Q, 1)), convert(AbstractQ{T}, Q), B) -end -function (*)(Q::AbstractQ, B::AbstractMatrix) - T = promote_type(eltype(Q), eltype(B)) - qsize_check(Q, B) - mul!(similar(B, T, (size(Q, 1), size(B, 2))), convert(AbstractQ{T}, Q), B) -end - -(*)(J::UniformScaling, Q::AbstractQ) = J.λ*Q -function (*)(a::Number, Q::AbstractQ) - T = promote_type(typeof(a), eltype(Q)) - rmul!(Matrix{T}(a*I, size(Q)), convert(AbstractQ{T}, Q)) -end -function (*)(A::AbstractVector, Q::AbstractQ) - T = promote_type(eltype(A), eltype(Q)) - qsize_check(A, Q) - return mul!(similar(A, T, length(A)), A, convert(AbstractQ{T}, Q)) -end -function (*)(A::AbstractMatrix, Q::AbstractQ) - T = promote_type(eltype(A), eltype(Q)) - qsize_check(A, Q) - return mul!(similar(A, T, (size(A, 1), size(Q, 2))), A, convert(AbstractQ{T}, Q)) -end -(*)(u::AdjointAbsVec, Q::AbstractQ) = (Q'u')' - -### Q*Q (including adjoints) -(*)(Q::AbstractQ, P::AbstractQ) = Q * (P*I) - -### mul! -function mul!(C::AbstractVecOrMat{T}, Q::AbstractQ{T}, B::Union{AbstractVecOrMat,AbstractQ}) where {T} - require_one_based_indexing(C, B) - mB, nB = size(B, 1), size(B, 2) - mC, nC = size(C, 1), size(C, 2) - qsize_check(Q, B) - nB != nC && throw(DimensionMismatch()) - if mB < mC - inds = CartesianIndices(axes(B)) - copyto!(view(C, inds), B) - C[CartesianIndices((mB+1:mC, axes(C, 2)))] .= zero(T) - return lmul!(Q, C) - else - return lmul!(Q, copyto!(C, B)) - end -end -function mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat, Q::AbstractQ{T}) where {T} - require_one_based_indexing(C, A) - mA, nA = size(A, 1), size(A, 2) - mC, nC = size(C, 1), size(C, 2) - mA != mC && throw(DimensionMismatch()) - qsize_check(A, Q) - if nA < nC - inds = CartesianIndices(axes(A)) - copyto!(view(C, inds), A) - C[CartesianIndices((axes(C, 1), nA+1:nC))] .= zero(T) - return rmul!(C, Q) - else - return rmul!(copyto!(C, A), Q) - end -end - -### division -\(Q::AbstractQ, A::AbstractVecOrMat) = Q'*A -/(A::AbstractVecOrMat, Q::AbstractQ) = A*Q' -/(Q::AbstractQ, A::AbstractVecOrMat) = Matrix(Q) / A -ldiv!(Q::AbstractQ, A::AbstractVecOrMat) = lmul!(Q', A) -ldiv!(C::AbstractVecOrMat, Q::AbstractQ, A::AbstractVecOrMat) = mul!(C, Q', A) -rdiv!(A::AbstractVecOrMat, Q::AbstractQ) = rmul!(A, Q') - -logabsdet(Q::AbstractQ) = (d = det(Q); return log(abs(d)), sign(d)) -function logdet(A::AbstractQ) - d, s = logabsdet(A) - return d + log(s) -end - -########################################################### -################ Q from QR decompositions ################# -########################################################### - -""" - QRPackedQ <: LinearAlgebra.AbstractQ - -The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or -[`QRPivoted`](@ref) format. -""" -struct QRPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} - factors::S - τ::C - - function QRPackedQ{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} - require_one_based_indexing(factors, τ) - new{T,S,C}(factors, τ) - end -end -QRPackedQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = - QRPackedQ{T,typeof(factors),typeof(τ)}(factors, τ) -QRPackedQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} = - QRPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S}, - QRPackedQ{T,S,typeof(τ)}(factors, τ), false) - -""" - QRCompactWYQ <: LinearAlgebra.AbstractQ - -The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref) -format. -""" -struct QRCompactWYQ{S, M<:AbstractMatrix{S}, C<:AbstractMatrix{S}} <: AbstractQ{S} - factors::M - T::C - - function QRCompactWYQ{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} - require_one_based_indexing(factors, T) - new{S,M,C}(factors, T) - end -end -QRCompactWYQ(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = - QRCompactWYQ{S,typeof(factors),typeof(T)}(factors, T) -QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = - QRCompactWYQ(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, - QRCompactWYQ{S,M,typeof(T)}(factors, T), false) - -QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ)) -QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) - -# override generic square fallback -Matrix{T}(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {T,S} = - convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...)))) -Matrix(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {S} = Matrix{S}(Q) - -convert(::Type{AbstractQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ{T}(Q) -convert(::Type{AbstractQ{T}}, Q::QRCompactWYQ) where {T} = QRCompactWYQ{T}(Q) - -size(Q::Union{QRCompactWYQ,QRPackedQ}, dim::Integer) = - size(Q.factors, dim == 2 ? 1 : dim) -size(Q::Union{QRCompactWYQ,QRPackedQ}) = (n = size(Q.factors, 1); (n, n)) - -## Multiplication -### QB -lmul!(A::QRCompactWYQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.gemqrt!('L', 'N', A.factors, A.T, B) -lmul!(A::QRPackedQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.ormqr!('L', 'N', A.factors, A.τ, B) -function lmul!(A::QRPackedQ, B::AbstractVecOrMat) - require_one_based_indexing(B) - mA, nA = size(A.factors) - mB, nB = size(B,1), size(B,2) - if mA != mB - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) - end - Afactors = A.factors - @inbounds begin - for k = min(mA,nA):-1:1 - for j = 1:nB - vBj = B[k,j] - for i = k+1:mB - vBj += conj(Afactors[i,k])*B[i,j] - end - vBj = A.τ[k]*vBj - B[k,j] -= vBj - for i = k+1:mB - B[i,j] -= Afactors[i,k]*vBj - end - end - end - end - B -end - -### QcB -lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = - (Q = adjQ.Q; LAPACK.gemqrt!('L', 'T', Q.factors, Q.T, B)) -lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - (Q = adjQ.Q; LAPACK.gemqrt!('L', 'C', Q.factors, Q.T, B)) -lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = - (Q = adjQ.Q; LAPACK.ormqr!('L', 'T', Q.factors, Q.τ, B)) -lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - (Q = adjQ.Q; LAPACK.ormqr!('L', 'C', Q.factors, Q.τ, B)) -function lmul!(adjA::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractVecOrMat) - require_one_based_indexing(B) - A = adjA.Q - mA, nA = size(A.factors) - mB, nB = size(B,1), size(B,2) - if mA != mB - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) - end - Afactors = A.factors - @inbounds begin - for k = 1:min(mA,nA) - for j = 1:nB - vBj = B[k,j] - for i = k+1:mB - vBj += conj(Afactors[i,k])*B[i,j] - end - vBj = conj(A.τ[k])*vBj - B[k,j] -= vBj - for i = k+1:mB - B[i,j] -= Afactors[i,k]*vBj - end - end - end - end - B -end - -### AQ -rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,<:StridedMatrix}) where {T<:BlasFloat} = - LAPACK.gemqrt!('R', 'N', B.factors, B.T, A) -rmul!(A::StridedVecOrMat{T}, B::QRPackedQ{T,<:StridedMatrix}) where {T<:BlasFloat} = - LAPACK.ormqr!('R', 'N', B.factors, B.τ, A) -function rmul!(A::AbstractVecOrMat, Q::QRPackedQ) - require_one_based_indexing(A) - mQ, nQ = size(Q.factors) - mA, nA = size(A,1), size(A,2) - if nA != mQ - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) - end - Qfactors = Q.factors - @inbounds begin - for k = 1:min(mQ,nQ) - for i = 1:mA - vAi = A[i,k] - for j = k+1:mQ - vAi += A[i,j]*Qfactors[j,k] - end - vAi = vAi*Q.τ[k] - A[i,k] -= vAi - for j = k+1:nA - A[i,j] -= vAi*conj(Qfactors[j,k]) - end - end - end - end - A -end - -### AQc -rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} = - (Q = adjQ.Q; LAPACK.gemqrt!('R', 'T', Q.factors, Q.T, A)) -rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasComplex} = - (Q = adjQ.Q; LAPACK.gemqrt!('R', 'C', Q.factors, Q.T, A)) -rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasReal} = - (Q = adjQ.Q; LAPACK.ormqr!('R', 'T', Q.factors, Q.τ, A)) -rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasComplex} = - (Q = adjQ.Q; LAPACK.ormqr!('R', 'C', Q.factors, Q.τ, A)) -function rmul!(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:QRPackedQ}) - require_one_based_indexing(A) - Q = adjQ.Q - mQ, nQ = size(Q.factors) - mA, nA = size(A,1), size(A,2) - if nA != mQ - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) - end - Qfactors = Q.factors - @inbounds begin - for k = min(mQ,nQ):-1:1 - for i = 1:mA - vAi = A[i,k] - for j = k+1:mQ - vAi += A[i,j]*Qfactors[j,k] - end - vAi = vAi*conj(Q.τ[k]) - A[i,k] -= vAi - for j = k+1:nA - A[i,j] -= vAi*conj(Qfactors[j,k]) - end - end - end - end - A -end - -det(Q::QRPackedQ) = _det_tau(Q.τ) -det(Q::QRCompactWYQ) = - prod(i -> _det_tau(diagview(Q.T[:, i:min(i + size(Q.T, 1), size(Q.T, 2))])), - 1:size(Q.T, 1):size(Q.T, 2)) - -# Compute `det` from the number of Householder reflections. Handle -# the case `Q.τ` contains zeros. -_det_tau(τs::AbstractVector{<:Real}) = - isodd(count(!iszero, τs)) ? -one(eltype(τs)) : one(eltype(τs)) - -# In complex case, we need to compute the non-unit eigenvalue `λ = 1 - c*τ` -# (where `c = v'v`) of each Householder reflector. As we know that the -# reflector must have the determinant of 1, it must satisfy `abs2(λ) == 1`. -# Combining this with the constraint `c > 0`, it turns out that the eigenvalue -# (hence the determinant) can be computed as `λ = -sign(τ)^2`. -# See: https://github.com/JuliaLang/julia/pull/32887#issuecomment-521935716 -_det_tau(τs) = prod(τ -> iszero(τ) ? one(τ) : -sign(τ)^2, τs) - -########################################################### -######## Q from Hessenberg decomposition ################## -########################################################### - -""" - HessenbergQ <: AbstractQ - -Given a [`Hessenberg`](@ref) factorization object `F`, `F.Q` returns -a `HessenbergQ` object, which is an implicit representation of the unitary -matrix `Q` in the Hessenberg factorization `QHQ'` represented by `F`. -This `F.Q` object can be efficiently multiplied by matrices or vectors, -and can be converted to an ordinary matrix type with `Matrix(F.Q)`. -""" -struct HessenbergQ{T,S<:AbstractMatrix,W<:AbstractVector,sym} <: AbstractQ{T} - uplo::Char - factors::S - τ::W - function HessenbergQ{T,S,W,sym}(uplo::AbstractChar, factors, τ) where {T,S<:AbstractMatrix,W<:AbstractVector,sym} - new(uplo, factors, τ) - end -end -HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,false}(F.uplo, F.factors, F.τ) -HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.τ) - -size(Q::HessenbergQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim) -size(Q::HessenbergQ) = size(Q, 1), size(Q, 2) - -# HessenbergQ from LAPACK/BLAS (as opposed to Julia libraries like GenericLinearAlgebra) -const BlasHessenbergQ{T,sym} = HessenbergQ{T,<:StridedMatrix{T},<:StridedVector{T},sym} where {T<:BlasFloat,sym} - -## reconstruct the original matrix -Matrix{T}(Q::BlasHessenbergQ{<:Any,false}) where {T} = convert(Matrix{T}, LAPACK.orghr!(1, size(Q.factors, 1), copy(Q.factors), Q.τ)) -Matrix{T}(Q::BlasHessenbergQ{<:Any,true}) where {T} = convert(Matrix{T}, LAPACK.orgtr!(Q.uplo, copy(Q.factors), Q.τ)) - -lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) -rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} = - LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X) -lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - (Q = adjQ.Q; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) -rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} = - (Q = adjQ.Q; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)) - -lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.τ, X) -rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} = - LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.τ, X) -lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = - (Q = adjQ.Q; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) -rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} = - (Q = adjQ.Q; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X)) - -lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')' -rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')' -lmul!(adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', adjQ')' -rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')' - -# division by a matrix -function /(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, B::AbstractVecOrMat) - size(B, 2) in size(Q.factors) || - throw(DimensionMismatch(lazy"second dimension of B, $(size(B,2)), must equal one of the dimensions of Q, $(size(Q.factors))")) - if size(B, 2) == size(Q.factors, 2) - return Matrix(Q) / B - else - return collect(Q) / B - end -end -function \(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) - Q = adjQ.Q - size(A, 1) in size(Q.factors) || - throw(DimensionMismatch(lazy"first dimension of A, $(size(A,1)), must equal one of the dimensions of Q, $(size(Q.factors))")) - if size(A, 1) == size(Q.factors, 2) - return A \ Matrix(Q)' - else - return A \ collect(Q)' - end -end - -# flexible left-multiplication (and adjoint right-multiplication) -qsize_check(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, B::AbstractVecOrMat) = - size(B, 1) in size(Q.factors) || - throw(DimensionMismatch(lazy"first dimension of B, $(size(B,1)), must equal one of the dimensions of Q, $(size(Q.factors))")) -qsize_check(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) = - (Q = adjQ.Q; size(A, 2) in size(Q.factors) || - throw(DimensionMismatch(lazy"second dimension of A, $(size(A,2)), must equal one of the dimensions of Q, $(size(Q.factors))"))) - -det(Q::HessenbergQ) = _det_tau(Q.τ) - -########################################################### -################ Q from LQ decomposition ################## -########################################################### - -struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} - factors::S - τ::C -end - -LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ)) -@deprecate(AbstractMatrix{T}(Q::LQPackedQ) where {T}, - convert(AbstractQ{T}, Q), - false) -Matrix{T}(A::LQPackedQ) where {T} = convert(Matrix{T}, LAPACK.orglq!(copy(A.factors), A.τ)) -convert(::Type{AbstractQ{T}}, Q::LQPackedQ) where {T} = LQPackedQ{T}(Q) - -# size(Q::LQPackedQ) yields the shape of Q's square form -size(Q::LQPackedQ) = (n = size(Q.factors, 2); return n, n) - -## Multiplication -# out-of-place right application of LQPackedQs -# -# these methods: (1) check whether the applied-to matrix's (A's) appropriate dimension -# (columns for A_*, rows for Ac_*) matches the number of columns (nQ) of the LQPackedQ (Q), -# and if so effectively apply Q's square form to A without additional shenanigans; and -# (2) if the preceding dimensions do not match, check whether the appropriate dimension of -# A instead matches the number of rows of the matrix of which Q is a factor (i.e. -# size(Q.factors, 1)), and if so implicitly apply Q's truncated form to A by zero extending -# A as necessary for check (1) to pass (if possible) and then applying Q's square form - -qsize_check(adjQ::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractVecOrMat) = - size(B, 1) in size(adjQ.Q.factors) || - throw(DimensionMismatch(lazy"first dimension of B, $(size(B,1)), must equal one of the dimensions of Q, $(size(adjQ.Q.factors))")) -qsize_check(A::AbstractVecOrMat, Q::LQPackedQ) = - size(A, 2) in size(Q.factors) || - throw(DimensionMismatch(lazy"second dimension of A, $(size(A,2)), must equal one of the dimensions of Q, $(size(Q.factors))")) - -# in-place right-application of LQPackedQs -# these methods require that the applied-to matrix's (A's) number of columns -# match the number of columns (nQ) of the LQPackedQ (Q) (necessary for in-place -# operation, and the underlying LAPACK routine (ormlq) treats the implicit Q -# as its (nQ-by-nQ) square form) -rmul!(A::StridedVecOrMat{T}, B::LQPackedQ{T}) where {T<:BlasFloat} = - LAPACK.ormlq!('R', 'N', B.factors, B.τ, A) -rmul!(A::StridedVecOrMat{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasReal} = - (B = adjB.Q; LAPACK.ormlq!('R', 'T', B.factors, B.τ, A)) -rmul!(A::StridedVecOrMat{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasComplex} = - (B = adjB.Q; LAPACK.ormlq!('R', 'C', B.factors, B.τ, A)) - -### QB / QcB -lmul!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.τ,B) -lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = - (A = adjA.Q; LAPACK.ormlq!('L', 'T', A.factors, A.τ, B)) -lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - (A = adjA.Q; LAPACK.ormlq!('L', 'C', A.factors, A.τ, B)) - -# division by a matrix -function /(adjQ::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractVecOrMat) - Q = adjQ.Q - size(B, 2) in size(Q.factors) || - throw(DimensionMismatch(lazy"second dimension of B, $(size(B,2)), must equal one of the dimensions of Q, $(size(Q.factors))")) - if size(B, 2) == size(Q.factors, 1) - return Matrix(Q)' / B - else - return collect(Q)' / B - end -end -function \(A::AbstractVecOrMat, Q::LQPackedQ) - size(A, 1) in size(Q.factors) || - throw(DimensionMismatch(lazy"first dimension of A, $(size(A,1)), must equal one of the dimensions of Q, $(size(Q.factors))")) - if size(A, 1) == size(Q.factors, 1) - return A \ Matrix(Q) - else - return A \ collect(Q) - end -end - -# In LQ factorization, `Q` is expressed as the product of the adjoint of the -# reflectors. Thus, `det` has to be conjugated. -det(Q::LQPackedQ) = conj(_det_tau(Q.τ)) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl deleted file mode 100644 index b722e49bb2c3d..0000000000000 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ /dev/null @@ -1,524 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -### basic definitions (types, aliases, constructors, abstractarray interface, sundry similar) - -# note that Adjoint and Transpose must be able to wrap not only vectors and matrices -# but also factorizations, rotations, and other linear algebra objects, including -# user-defined such objects. so do not restrict the wrapped type. -""" - Adjoint - -Lazy wrapper type for an adjoint view of the underlying linear algebra object, -usually an `AbstractVector`/`AbstractMatrix`. -Usually, the `Adjoint` constructor should not be called directly, use [`adjoint`](@ref) -instead. To materialize the view use [`copy`](@ref). - -This type is intended for linear algebra usage - for general data manipulation see -[`permutedims`](@ref Base.permutedims). - -# Examples -```jldoctest -julia> A = [3+2im 9+2im; 0 0] -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 0+0im 0+0im - -julia> Adjoint(A) -2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: - 3-2im 0+0im - 9-2im 0+0im -``` -""" -struct Adjoint{T,S} <: AbstractMatrix{T} - parent::S -end -""" - Transpose - -Lazy wrapper type for a transpose view of the underlying linear algebra object, -usually an `AbstractVector`/`AbstractMatrix`. -Usually, the `Transpose` constructor should not be called directly, use [`transpose`](@ref) -instead. To materialize the view use [`copy`](@ref). - -This type is intended for linear algebra usage - for general data manipulation see -[`permutedims`](@ref Base.permutedims). - -# Examples -```jldoctest -julia> A = [2 3; 0 0] -2×2 Matrix{Int64}: - 2 3 - 0 0 - -julia> Transpose(A) -2×2 transpose(::Matrix{Int64}) with eltype Int64: - 2 0 - 3 0 -``` -""" -struct Transpose{T,S} <: AbstractMatrix{T} - parent::S -end - -# basic outer constructors -Adjoint(A) = Adjoint{Base.promote_op(adjoint,eltype(A)),typeof(A)}(A) -Transpose(A) = Transpose{Base.promote_op(transpose,eltype(A)),typeof(A)}(A) - -""" - inplace_adj_or_trans(::AbstractArray) -> adjoint!|transpose!|copyto! - inplace_adj_or_trans(::Type{<:AbstractArray}) -> adjoint!|transpose!|copyto! - -Return [`adjoint!`](@ref) from an `Adjoint` type or object and -[`transpose!`](@ref) from a `Transpose` type or object. Otherwise, -return [`copyto!`](@ref). Note that `Adjoint` and `Transpose` have -to be the outer-most wrapper object for a non-`identity` function to be -returned. -""" -inplace_adj_or_trans(::T) where {T <: AbstractArray} = inplace_adj_or_trans(T) -inplace_adj_or_trans(::Type{<:AbstractArray}) = copyto! -inplace_adj_or_trans(::Type{<:Adjoint}) = adjoint! -inplace_adj_or_trans(::Type{<:Transpose}) = transpose! - -# unwraps Adjoint, Transpose, Symmetric, Hermitian -_unwrap(A::Adjoint) = parent(A) -_unwrap(A::Transpose) = parent(A) - -# unwraps Adjoint and Transpose only -_unwrap_at(A) = A -_unwrap_at(A::Adjoint) = parent(A) -_unwrap_at(A::Transpose) = parent(A) - -Base.dataids(A::Union{Adjoint, Transpose}) = Base.dataids(A.parent) -Base.unaliascopy(A::Union{Adjoint,Transpose}) = typeof(A)(Base.unaliascopy(A.parent)) - -# wrapping lowercase quasi-constructors -""" - A' - adjoint(A) - -Lazy adjoint (conjugate transposition). Note that `adjoint` is applied recursively to -elements. - -For number types, `adjoint` returns the complex conjugate, and therefore it is equivalent to -the identity function for real numbers. - -This operation is intended for linear algebra usage - for general data manipulation see -[`permutedims`](@ref Base.permutedims). - -# Examples -```jldoctest -julia> A = [3+2im 9+2im; 0 0] -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 0+0im 0+0im - -julia> B = A' # equivalently adjoint(A) -2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: - 3-2im 0+0im - 9-2im 0+0im - -julia> B isa Adjoint -true - -julia> adjoint(B) === A # the adjoint of an adjoint unwraps the parent -true - -julia> Adjoint(B) # however, the constructor always wraps its argument -2×2 adjoint(adjoint(::Matrix{Complex{Int64}})) with eltype Complex{Int64}: - 3+2im 9+2im - 0+0im 0+0im - -julia> B[1,2] = 4 + 5im; # modifying B will modify A automatically - -julia> A -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 4-5im 0+0im -``` - -For real matrices, the `adjoint` operation is equivalent to a `transpose`. - -```jldoctest -julia> A = reshape([x for x in 1:4], 2, 2) -2×2 Matrix{Int64}: - 1 3 - 2 4 - -julia> A' -2×2 adjoint(::Matrix{Int64}) with eltype Int64: - 1 2 - 3 4 - -julia> adjoint(A) == transpose(A) -true -``` - -The adjoint of an `AbstractVector` is a row-vector: -```jldoctest -julia> x = [3, 4im] -2-element Vector{Complex{Int64}}: - 3 + 0im - 0 + 4im - -julia> x' -1×2 adjoint(::Vector{Complex{Int64}}) with eltype Complex{Int64}: - 3+0im 0-4im - -julia> x'x # compute the dot product, equivalently x' * x -25 + 0im -``` - -For a matrix of matrices, the individual blocks are recursively operated on: -```jldoctest -julia> A = reshape([x + im*x for x in 1:4], 2, 2) -2×2 Matrix{Complex{Int64}}: - 1+1im 3+3im - 2+2im 4+4im - -julia> C = reshape([A, 2A, 3A, 4A], 2, 2) -2×2 Matrix{Matrix{Complex{Int64}}}: - [1+1im 3+3im; 2+2im 4+4im] [3+3im 9+9im; 6+6im 12+12im] - [2+2im 6+6im; 4+4im 8+8im] [4+4im 12+12im; 8+8im 16+16im] - -julia> C' -2×2 adjoint(::Matrix{Matrix{Complex{Int64}}}) with eltype Adjoint{Complex{Int64}, Matrix{Complex{Int64}}}: - [1-1im 2-2im; 3-3im 4-4im] [2-2im 4-4im; 6-6im 8-8im] - [3-3im 6-6im; 9-9im 12-12im] [4-4im 8-8im; 12-12im 16-16im] -``` -""" -adjoint(A::AbstractVecOrMat) = Adjoint(A) - -""" - transpose(A) - -Lazy transpose. Mutating the returned object should appropriately mutate `A`. Often, -but not always, yields `Transpose(A)`, where `Transpose` is a lazy transpose wrapper. Note -that this operation is recursive. - -This operation is intended for linear algebra usage - for general data manipulation see -[`permutedims`](@ref Base.permutedims), which is non-recursive. - -# Examples -```jldoctest -julia> A = [3 2; 0 0] -2×2 Matrix{Int64}: - 3 2 - 0 0 - -julia> B = transpose(A) -2×2 transpose(::Matrix{Int64}) with eltype Int64: - 3 0 - 2 0 - -julia> B isa Transpose -true - -julia> transpose(B) === A # the transpose of a transpose unwraps the parent -true - -julia> Transpose(B) # however, the constructor always wraps its argument -2×2 transpose(transpose(::Matrix{Int64})) with eltype Int64: - 3 2 - 0 0 - -julia> B[1,2] = 4; # modifying B will modify A automatically - -julia> A -2×2 Matrix{Int64}: - 3 2 - 4 0 -``` - -For complex matrices, the `adjoint` operation is equivalent to a conjugate-transpose. -```jldoctest -julia> A = reshape([Complex(x, x) for x in 1:4], 2, 2) -2×2 Matrix{Complex{Int64}}: - 1+1im 3+3im - 2+2im 4+4im - -julia> adjoint(A) == conj(transpose(A)) -true -``` - -The `transpose` of an `AbstractVector` is a row-vector: -```jldoctest -julia> v = [1,2,3] -3-element Vector{Int64}: - 1 - 2 - 3 - -julia> transpose(v) # returns a row-vector -1×3 transpose(::Vector{Int64}) with eltype Int64: - 1 2 3 - -julia> transpose(v) * v # compute the dot product -14 -``` - -For a matrix of matrices, the individual blocks are recursively operated on: -```jldoctest -julia> C = [1 3; 2 4] -2×2 Matrix{Int64}: - 1 3 - 2 4 - -julia> D = reshape([C, 2C, 3C, 4C], 2, 2) # construct a block matrix -2×2 Matrix{Matrix{Int64}}: - [1 3; 2 4] [3 9; 6 12] - [2 6; 4 8] [4 12; 8 16] - -julia> transpose(D) # blocks are recursively transposed -2×2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}: - [1 2; 3 4] [2 4; 6 8] - [3 6; 9 12] [4 8; 12 16] -``` -""" -transpose(A::AbstractVecOrMat) = Transpose(A) - -# unwrapping lowercase quasi-constructors -adjoint(A::Adjoint) = A.parent -transpose(A::Transpose) = A.parent -adjoint(A::Transpose{<:Real}) = A.parent -transpose(A::Adjoint{<:Real}) = A.parent -adjoint(A::Transpose{<:Any,<:Adjoint}) = transpose(A.parent.parent) -transpose(A::Adjoint{<:Any,<:Transpose}) = adjoint(A.parent.parent) -# disambiguation -adjoint(A::Transpose{<:Real,<:Adjoint}) = transpose(A.parent.parent) -transpose(A::Adjoint{<:Real,<:Transpose}) = A.parent - -# printing -function Base.showarg(io::IO, v::Adjoint, toplevel) - print(io, "adjoint(") - Base.showarg(io, parent(v), false) - print(io, ')') - toplevel && print(io, " with eltype ", eltype(v)) - return nothing -end -function Base.showarg(io::IO, v::Transpose, toplevel) - print(io, "transpose(") - Base.showarg(io, parent(v), false) - print(io, ')') - toplevel && print(io, " with eltype ", eltype(v)) - return nothing -end -function Base.show(io::IO, v::Adjoint{<:Real, <:AbstractVector}) - print(io, "adjoint(") - show(io, parent(v)) - print(io, ")") -end -function Base.show(io::IO, v::Transpose{<:Number, <:AbstractVector}) - print(io, "transpose(") - show(io, parent(v)) - print(io, ")") -end - -# some aliases for internal convenience use -const AdjOrTrans{T,S} = Union{Adjoint{T,S},Transpose{T,S}} where {T,S} -const AdjointAbsVec{T} = Adjoint{T,<:AbstractVector} -const AdjointAbsMat{T} = Adjoint{T,<:AbstractMatrix} -const TransposeAbsVec{T} = Transpose{T,<:AbstractVector} -const TransposeAbsMat{T} = Transpose{T,<:AbstractMatrix} -const AdjOrTransAbsVec{T} = AdjOrTrans{T,<:AbstractVector} -const AdjOrTransAbsMat{T} = AdjOrTrans{T,<:AbstractMatrix} - -# for internal use below -wrapperop(_) = identity -wrapperop(::Adjoint) = adjoint -wrapperop(::Transpose) = transpose - -# the following fallbacks can be removed if Adjoint/Transpose are restricted to AbstractVecOrMat -size(A::AdjOrTrans) = reverse(size(A.parent)) -axes(A::AdjOrTrans) = reverse(axes(A.parent)) -# AbstractArray interface, basic definitions -length(A::AdjOrTrans) = length(A.parent) -size(v::AdjOrTransAbsVec) = (1, length(v.parent)) -size(A::AdjOrTransAbsMat) = reverse(size(A.parent)) -axes(v::AdjOrTransAbsVec) = (axes(v.parent,2), axes(v.parent)...) -axes(A::AdjOrTransAbsMat) = reverse(axes(A.parent)) -IndexStyle(::Type{<:AdjOrTransAbsVec}) = IndexLinear() -@propagate_inbounds Base.isassigned(v::AdjOrTransAbsVec, i::Int) = isassigned(v.parent, i-1+first(axes(v.parent)[1])) -@propagate_inbounds Base.isassigned(v::AdjOrTransAbsMat, i::Int, j::Int) = isassigned(v.parent, j, i) -@propagate_inbounds getindex(v::AdjOrTransAbsVec{T}, i::Int) where {T} = wrapperop(v)(v.parent[i-1+first(axes(v.parent)[1])])::T -@propagate_inbounds getindex(A::AdjOrTransAbsMat{T}, i::Int, j::Int) where {T} = wrapperop(A)(A.parent[j, i])::T -@propagate_inbounds setindex!(v::AdjOrTransAbsVec, x, i::Int) = (setindex!(v.parent, wrapperop(v)(x), i-1+first(axes(v.parent)[1])); v) -@propagate_inbounds setindex!(A::AdjOrTransAbsMat, x, i::Int, j::Int) = (setindex!(A.parent, wrapperop(A)(x), j, i); A) -# AbstractArray interface, additional definitions to retain wrapper over vectors where appropriate -@propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, is::AbstractArray{Int}) = wrapperop(v)(v.parent[is]) -@propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, ::Colon) = wrapperop(v)(v.parent[:]) - -# conversion of underlying storage -convert(::Type{Adjoint{T,S}}, A::Adjoint) where {T,S} = Adjoint{T,S}(convert(S, A.parent))::Adjoint{T,S} -convert(::Type{Transpose{T,S}}, A::Transpose) where {T,S} = Transpose{T,S}(convert(S, A.parent))::Transpose{T,S} - -# Strides and pointer for transposed strided arrays — but only if the elements are actually stored in memory -Base.strides(A::Adjoint{<:Real, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1)) -Base.strides(A::Transpose{<:Any, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1)) -# For matrices it's slightly faster to use reverse and avoid calling stride twice -Base.strides(A::Adjoint{<:Real, <:AbstractMatrix}) = reverse(strides(A.parent)) -Base.strides(A::Transpose{<:Any, <:AbstractMatrix}) = reverse(strides(A.parent)) - -Base.cconvert(::Type{Ptr{T}}, A::Adjoint{<:Real, <:AbstractVecOrMat}) where {T} = Base.cconvert(Ptr{T}, A.parent) -Base.cconvert(::Type{Ptr{T}}, A::Transpose{<:Any, <:AbstractVecOrMat}) where {T} = Base.cconvert(Ptr{T}, A.parent) - -Base.elsize(::Type{<:Adjoint{<:Real, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P) -Base.elsize(::Type{<:Transpose{<:Any, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P) - -# for vectors, the semantics of the wrapped and unwrapped types differ -# so attempt to maintain both the parent and wrapper type insofar as possible -similar(A::AdjOrTransAbsVec) = wrapperop(A)(similar(A.parent)) -similar(A::AdjOrTransAbsVec, ::Type{T}) where {T} = wrapperop(A)(similar(A.parent, Base.promote_op(wrapperop(A), T))) -# for matrices, the semantics of the wrapped and unwrapped types are generally the same -# and as you are allocating with similar anyway, you might as well get something unwrapped -similar(A::AdjOrTrans) = similar(A.parent, eltype(A), axes(A)) -similar(A::AdjOrTrans, ::Type{T}) where {T} = similar(A.parent, T, axes(A)) -similar(A::AdjOrTrans, ::Type{T}, dims::Dims{N}) where {T,N} = similar(A.parent, T, dims) - -# AbstractMatrix{T} constructor for adjtrans vector: preserve wrapped type -AbstractMatrix{T}(A::AdjOrTransAbsVec) where {T} = wrapperop(A)(AbstractVector{T}(A.parent)) - -# sundry basic definitions -parent(A::AdjOrTrans) = A.parent -vec(v::TransposeAbsVec{<:Number}) = parent(v) -vec(v::AdjointAbsVec{<:Real}) = parent(v) - -### concatenation -# preserve Adjoint/Transpose wrapper around vectors -# to retain the associated semantics post-concatenation -hcat(avs::Union{Number,AdjointAbsVec}...) = _adjoint_hcat(avs...) -hcat(tvs::Union{Number,TransposeAbsVec}...) = _transpose_hcat(tvs...) -_adjoint_hcat(avs::Union{Number,AdjointAbsVec}...) = adjoint(vcat(map(adjoint, avs)...)) -_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 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...) -hcat(tvs::Transpose{T,Vector{T}}...) where {T} = _transpose_hcat(tvs...) -# TODO unify and allow mixed combinations - - -### higher order functions -# preserve Adjoint/Transpose wrapper around vectors -# to retain the associated semantics post-map/broadcast -# -# note that the caller's operation f operates in the domain of the wrapped vectors' entries. -# hence the adjoint->f->adjoint shenanigans applied to the parent vectors' entries. -function map(f, av::AdjointAbsVec, avs::AdjointAbsVec...) - s = (av, avs...) - adjoint(map((xs...) -> adjoint(f(adjoint.(xs)...)), parent.(s)...)) -end -function map(f, tv::TransposeAbsVec, tvs::TransposeAbsVec...) - s = (tv, tvs...) - transpose(map((xs...) -> transpose(f(transpose.(xs)...)), parent.(s)...)) -end -quasiparentt(x) = parent(x); quasiparentt(x::Number) = x # to handle numbers in the defs below -quasiparenta(x) = parent(x); quasiparenta(x::Number) = conj(x) # to handle numbers in the defs below -quasiparentc(x) = parent(parent(x)); quasiparentc(x::Number) = conj(x) # to handle numbers in the defs below -broadcast(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) -broadcast(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) -# Hack to preserve behavior after #32122; this needs to be done with a broadcast style instead to support dotted fusion -Broadcast.broadcast_preserving_zero_d(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) -Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) -Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Transpose{<:Any,<:AdjointAbsVec}}...) = - transpose(adjoint(broadcast((xs...) -> adjoint(transpose(f(conj.(xs)...))), quasiparentc.(tvs)...))) -Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,Adjoint{<:Any,<:TransposeAbsVec}}...) = - adjoint(transpose(broadcast((xs...) -> transpose(adjoint(f(conj.(xs)...))), quasiparentc.(tvs)...))) -# TODO unify and allow mixed combinations with a broadcast style - - -### reductions -# faster to sum the Array than to work through the wrapper (but only in commutative reduction ops as in Base/permuteddimsarray.jl) -Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Transpose, dims::Colon) = - Base._mapreduce_dim(f∘transpose, op, init, parent(A), dims) -Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Adjoint, dims::Colon) = - Base._mapreduce_dim(f∘adjoint, op, init, parent(A), dims) -# in prod, use fast path only in the commutative case to avoid surprises -Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Transpose{<:Union{Real,Complex}}, dims::Colon) = - Base._mapreduce_dim(f∘transpose, op, init, parent(A), dims) -Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Adjoint{<:Union{Real,Complex}}, dims::Colon) = - Base._mapreduce_dim(f∘adjoint, op, init, parent(A), dims) -# count allows for optimization only if the parent array has Bool eltype -Base._count(::typeof(identity), A::Transpose{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init) -Base._count(::typeof(identity), A::Adjoint{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init) -Base._any(f, A::Transpose, ::Colon) = Base._any(f∘transpose, parent(A), :) -Base._any(f, A::Adjoint, ::Colon) = Base._any(f∘adjoint, parent(A), :) -Base._all(f, A::Transpose, ::Colon) = Base._all(f∘transpose, parent(A), :) -Base._all(f, A::Adjoint, ::Colon) = Base._all(f∘adjoint, parent(A), :) -# sum(A'; dims) -Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::TransposeAbsMat) = - (Base.mapreducedim!(f∘transpose, op, switch_dim12(B), parent(A)); B) -Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::AdjointAbsMat) = - (Base.mapreducedim!(f∘adjoint, op, switch_dim12(B), parent(A)); B) -Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::TransposeAbsMat{<:Union{Real,Complex}}) = - (Base.mapreducedim!(f∘transpose, op, switch_dim12(B), parent(A)); B) -Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::AdjointAbsMat{<:Union{Real,Complex}}) = - (Base.mapreducedim!(f∘adjoint, op, switch_dim12(B), parent(A)); B) - -switch_dim12(B::AbstractVector) = permutedims(B) -switch_dim12(B::AbstractVector{<:Number}) = transpose(B) # avoid allocs due to permutedims -switch_dim12(B::AbstractArray{<:Any,0}) = B -switch_dim12(B::AbstractArray) = PermutedDimsArray(B, (2, 1, ntuple(Base.Fix1(+,2), ndims(B) - 2)...)) - -### linear algebra - -(-)(A::Adjoint) = Adjoint( -A.parent) -(-)(A::Transpose) = Transpose(-A.parent) - -tr(A::Adjoint) = adjoint(tr(parent(A))) -tr(A::Transpose) = transpose(tr(parent(A))) - -## multiplication * - -function _dot_nonrecursive(u, v) - lu = length(u) - if lu != length(v) - throw(DimensionMismatch(lazy"first array has length $(lu) which does not match the length of the second, $(length(v)).")) - end - if lu == 0 - zero(eltype(u)) * zero(eltype(v)) - else - sum(uu*vv for (uu, vv) in zip(u, v)) - end -end - -# Adjoint/Transpose-vector * vector -*(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v) -*(u::TransposeAbsVec{T}, v::AbstractVector{T}) where {T<:Real} = dot(u.parent, v) -*(u::AdjOrTransAbsVec, v::AbstractVector) = _dot_nonrecursive(u, v) - - -# vector * Adjoint/Transpose-vector -*(u::AbstractVector, v::AdjOrTransAbsVec) = broadcast(*, u, v) - -# AdjOrTransAbsVec{<:Any,<:AdjOrTransAbsVec} is a lazy conj vectors -# We need to expand the combinations to avoid ambiguities -(*)(u::TransposeAbsVec, v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v) -(*)(u::AdjointAbsVec, v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v) -(*)(u::TransposeAbsVec, v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v) -(*)(u::AdjointAbsVec, v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v) - -## pseudoinversion -pinv(v::AdjointAbsVec, tol::Real = 0) = pinv(v.parent, tol).parent -pinv(v::TransposeAbsVec, tol::Real = 0) = pinv(conj(v.parent)).parent - - -## left-division \ -\(u::AdjOrTransAbsVec, v::AdjOrTransAbsVec) = pinv(u) * v - - -## right-division / -/(u::AdjointAbsVec, A::AbstractMatrix) = adjoint(adjoint(A) \ u.parent) -/(u::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A) \ u.parent) -/(u::AdjointAbsVec, A::TransposeAbsMat) = adjoint(conj(A.parent) \ u.parent) # technically should be adjoint(copy(adjoint(copy(A))) \ u.parent) -/(u::TransposeAbsVec, A::AdjointAbsMat) = transpose(conj(A.parent) \ u.parent) # technically should be transpose(copy(transpose(copy(A))) \ u.parent) - -## complex conjugate -conj(A::Transpose) = adjoint(A.parent) -conj(A::Adjoint) = transpose(A.parent) - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::AdjOrTrans,i::Integer,j::Integer,s::AbstractString) - Base.replace_in_print_matrix(parent(A), j, i, s) -end diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl deleted file mode 100644 index 5b7264558f9ae..0000000000000 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ /dev/null @@ -1,1489 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Bidiagonal matrices -struct Bidiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} - dv::V # diagonal - ev::V # sub/super diagonal - uplo::Char # upper bidiagonal ('U') or lower ('L') - function Bidiagonal{T,V}(dv, ev, uplo::AbstractChar) where {T,V<:AbstractVector{T}} - require_one_based_indexing(dv, ev) - if length(ev) != max(length(dv)-1, 0) - throw(DimensionMismatch(lazy"length of diagonal vector is $(length(dv)), length of off-diagonal vector is $(length(ev))")) - end - (uplo != 'U' && uplo != 'L') && throw_uplo() - new{T,V}(dv, ev, uplo) - end -end -function Bidiagonal{T,V}(dv, ev, uplo::Symbol) where {T,V<:AbstractVector{T}} - Bidiagonal{T,V}(dv, ev, char_uplo(uplo)) -end -function Bidiagonal{T}(dv::AbstractVector, ev::AbstractVector, uplo::Union{Symbol,AbstractChar}) where {T} - Bidiagonal(convert(AbstractVector{T}, dv)::AbstractVector{T}, - convert(AbstractVector{T}, ev)::AbstractVector{T}, - uplo) -end -function Bidiagonal{T,V}(A::Bidiagonal) where {T,V<:AbstractVector{T}} - Bidiagonal{T,V}(A.dv, A.ev, A.uplo) -end - -""" - Bidiagonal(dv::V, ev::V, uplo::Symbol) where V <: AbstractVector - -Constructs an upper (`uplo=:U`) or lower (`uplo=:L`) bidiagonal matrix using the -given diagonal (`dv`) and off-diagonal (`ev`) vectors. The result is of type `Bidiagonal` -and provides efficient specialized linear solvers, but may be converted into a regular -matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). The length of `ev` -must be one less than the length of `dv`. - -# Examples -```jldoctest -julia> dv = [1, 2, 3, 4] -4-element Vector{Int64}: - 1 - 2 - 3 - 4 - -julia> ev = [7, 8, 9] -3-element Vector{Int64}: - 7 - 8 - 9 - -julia> Bu = Bidiagonal(dv, ev, :U) # ev is on the first superdiagonal -4×4 Bidiagonal{Int64, Vector{Int64}}: - 1 7 ⋅ ⋅ - ⋅ 2 8 ⋅ - ⋅ ⋅ 3 9 - ⋅ ⋅ ⋅ 4 - -julia> Bl = Bidiagonal(dv, ev, :L) # ev is on the first subdiagonal -4×4 Bidiagonal{Int64, Vector{Int64}}: - 1 ⋅ ⋅ ⋅ - 7 2 ⋅ ⋅ - ⋅ 8 3 ⋅ - ⋅ ⋅ 9 4 -``` -""" -function Bidiagonal(dv::V, ev::V, uplo::Symbol) where {T,V<:AbstractVector{T}} - Bidiagonal{T,V}(dv, ev, uplo) -end -function Bidiagonal(dv::V, ev::V, uplo::AbstractChar) where {T,V<:AbstractVector{T}} - Bidiagonal{T,V}(dv, ev, uplo) -end - -#To allow Bidiagonal's where the "dv" is Vector{T} and "ev" Vector{S}, -#where T and S can be promoted -function Bidiagonal(dv::Vector{T}, ev::Vector{S}, uplo::Symbol) where {T,S} - TS = promote_type(T,S) - return Bidiagonal{TS,Vector{TS}}(dv, ev, uplo) -end - -""" - Bidiagonal(A, uplo::Symbol) - -Construct a `Bidiagonal` matrix from the main diagonal of `A` and -its first super- (if `uplo=:U`) or sub-diagonal (if `uplo=:L`). - -# Examples -```jldoctest -julia> A = [1 1 1 1; 2 2 2 2; 3 3 3 3; 4 4 4 4] -4×4 Matrix{Int64}: - 1 1 1 1 - 2 2 2 2 - 3 3 3 3 - 4 4 4 4 - -julia> Bidiagonal(A, :U) # contains the main diagonal and first superdiagonal of A -4×4 Bidiagonal{Int64, Vector{Int64}}: - 1 1 ⋅ ⋅ - ⋅ 2 2 ⋅ - ⋅ ⋅ 3 3 - ⋅ ⋅ ⋅ 4 - -julia> Bidiagonal(A, :L) # contains the main diagonal and first subdiagonal of A -4×4 Bidiagonal{Int64, Vector{Int64}}: - 1 ⋅ ⋅ ⋅ - 2 2 ⋅ ⋅ - ⋅ 3 3 ⋅ - ⋅ ⋅ 4 4 -``` -""" -function Bidiagonal(A::AbstractMatrix, uplo::Symbol) - Bidiagonal(diag(A, 0), diag(A, uplo === :U ? 1 : -1), uplo) -end - - -Bidiagonal(A::Bidiagonal) = A -Bidiagonal{T}(A::Bidiagonal{T}) where {T} = A -Bidiagonal{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A.dv, A.ev, A.uplo) - -_offdiagind(uplo) = uplo == 'U' ? 1 : -1 - -@inline function Base.isassigned(A::Bidiagonal, i::Int, j::Int) - @boundscheck checkbounds(Bool, A, i, j) || return false - if i == j - return @inbounds isassigned(A.dv, i) - elseif i == j - _offdiagind(A.uplo) - return @inbounds isassigned(A.ev, A.uplo == 'U' ? i : j) - else - return true - end -end - -@inline function Base.isstored(A::Bidiagonal, i::Int, j::Int) - @boundscheck checkbounds(A, i, j) - if i == j - return @inbounds Base.isstored(A.dv, i) - elseif i == j - _offdiagind(A.uplo) - return @inbounds Base.isstored(A.ev, A.uplo == 'U' ? i : j) - else - return false - end -end - -@inline function getindex(A::Bidiagonal{T}, i::Int, j::Int) where T - @boundscheck checkbounds(A, i, j) - if i == j - return @inbounds A.dv[i] - elseif i == j - _offdiagind(A.uplo) - return @inbounds A.ev[A.uplo == 'U' ? i : j] - else - return diagzero(A, i, j) - end -end - -@inline function getindex(A::Bidiagonal{T}, b::BandIndex) where T - @boundscheck checkbounds(A, b) - if b.band == 0 - return @inbounds A.dv[b.index] - elseif b.band ∈ (-1,1) && b.band == _offdiagind(A.uplo) - # we explicitly compare the possible bands as b.band may be constant-propagated - return @inbounds A.ev[b.index] - else - return diagzero(A, Tuple(_cartinds(b))...) - end -end - -@inline function setindex!(A::Bidiagonal, x, i::Integer, j::Integer) - @boundscheck checkbounds(A, i, j) - if i == j - @inbounds A.dv[i] = x - elseif i == j - _offdiagind(A.uplo) - @inbounds A.ev[A.uplo == 'U' ? i : j] = x - elseif !iszero(x) - throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off the ", - A.uplo == 'U' ? "upper" : "lower", " bidiagonal band to a nonzero value ", x))) - end - return A -end - -Base._reverse(A::Bidiagonal, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::Bidiagonal, ::Colon) = Bidiagonal(reverse(A.dv), reverse(A.ev), A.uplo == 'U' ? :L : :U) - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::Bidiagonal,i::Integer,j::Integer,s::AbstractString) - i==j || i==j-_offdiagind(A.uplo) ? s : Base.replace_with_centered_mark(s) -end - -#Converting from Bidiagonal to dense Matrix -function Matrix{T}(A::Bidiagonal) where T - B = Matrix{T}(undef, size(A)) - if haszero(T) # optimized path for types with zero(T) defined - size(B,1) > 1 && fill!(B, zero(T)) - copyto!(diagview(B), A.dv) - copyto!(diagview(B, _offdiagind(A.uplo)), A.ev) - else - copyto!(B, A) - end - return B -end -Matrix(A::Bidiagonal{T}) where {T} = Matrix{promote_type(T, typeof(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 -promote_rule(::Type{<:Matrix}, ::Type{<:Bidiagonal}) = Matrix - -#Converting from Bidiagonal to Tridiagonal -function Tridiagonal{T}(A::Bidiagonal) where T - dv = convert(AbstractVector{T}, A.dv) - ev = convert(AbstractVector{T}, A.ev) - # ensure that the types are identical, even if zero returns a different type - z = oftype(ev, zero(ev)) - A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z) -end -promote_rule(::Type{<:Tridiagonal{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = - @isdefined(T) && @isdefined(S) ? Tridiagonal{promote_type(T,S)} : Tridiagonal -promote_rule(::Type{<:Tridiagonal}, ::Type{<:Bidiagonal}) = Tridiagonal - -# When asked to convert Bidiagonal to AbstractMatrix{T}, preserve structure by converting to Bidiagonal{T} <: AbstractMatrix{T} -AbstractMatrix{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A) -AbstractMatrix{T}(A::Bidiagonal{T}) where {T} = copy(A) - -convert(::Type{T}, m::AbstractMatrix) where {T<:Bidiagonal} = m isa T ? m : T(m)::T - -similar(B::Bidiagonal, ::Type{T}) where {T} = Bidiagonal(similar(B.dv, T), similar(B.ev, T), B.uplo) -similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(B.dv, T, dims) - -tr(B::Bidiagonal) = sum(B.dv) - -function kron(A::Diagonal, B::Bidiagonal) - # `_droplast!` is only guaranteed to work with `Vector` - kdv = convert(Vector, kron(diag(A), B.dv)) - kev = _droplast!(convert(Vector, kron(diag(A), _pushzero(B.ev)))) - Bidiagonal(kdv, kev, B.uplo) -end - -################### -# LAPACK routines # -################### - -#Singular values -svdvals!(M::Bidiagonal{<:BlasReal}) = LAPACK.bdsdc!(M.uplo, 'N', M.dv, M.ev)[1] -function svd!(M::Bidiagonal{<:BlasReal}; full::Bool = false) - d, e, U, Vt, Q, iQ = LAPACK.bdsdc!(M.uplo, 'I', M.dv, M.ev) - SVD(U, d, Vt) -end -function svd(M::Bidiagonal; kw...) - svd!(copy(M), kw...) -end - -#################### -# Generic routines # -#################### - -function show(io::IO, M::Bidiagonal) - print(io, "Bidiagonal(") - show(io, M.dv) - print(io, ", ") - show(io, M.ev) - print(io, ", ") - show(io, sym_uplo(M.uplo)) - print(io, ")") -end - -size(M::Bidiagonal) = (n = length(M.dv); (n, n)) -axes(M::Bidiagonal) = (ax = axes(M.dv, 1); (ax, ax)) - -#Elementary operations -for func in (:conj, :copy, :real, :imag) - @eval ($func)(M::Bidiagonal) = Bidiagonal(($func)(M.dv), ($func)(M.ev), M.uplo) -end - -adjoint(B::Bidiagonal{<:Number}) = Bidiagonal(vec(adjoint(B.dv)), vec(adjoint(B.ev)), B.uplo == 'U' ? :L : :U) -adjoint(B::Bidiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = - Bidiagonal(adjoint(parent(B.dv)), adjoint(parent(B.ev)), B.uplo == 'U' ? :L : :U) -transpose(B::Bidiagonal{<:Number}) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? :L : :U) -permutedims(B::Bidiagonal) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? 'L' : 'U') -function permutedims(B::Bidiagonal, perm) - Base.checkdims_perm(axes(B), axes(B), perm) - NTuple{2}(perm) == (2, 1) ? permutedims(B) : B -end -function Base.copy(aB::Adjoint{<:Any,<:Bidiagonal}) - B = aB.parent - return Bidiagonal(map(x -> copy.(adjoint.(x)), (B.dv, B.ev))..., B.uplo == 'U' ? :L : :U) -end -function Base.copy(tB::Transpose{<:Any,<:Bidiagonal}) - B = tB.parent - return Bidiagonal(map(x -> copy.(transpose.(x)), (B.dv, B.ev))..., B.uplo == 'U' ? :L : :U) -end - -@noinline function throw_zeroband_error(A) - uplo = A.uplo - zeroband = uplo == 'U' ? "lower" : "upper" - throw(ArgumentError(LazyString("cannot set the ", - zeroband, " bidiagonal band to a nonzero value for uplo=:", uplo))) -end - -# copyto! for matching axes -function _copyto_banded!(A::Bidiagonal, B::Bidiagonal) - A.dv .= B.dv - if A.uplo == B.uplo - A.ev .= B.ev - elseif iszero(B.ev) # diagonal source - A.ev .= B.ev - else - throw_zeroband_error(A) - end - return A -end - -iszero(M::Bidiagonal) = iszero(M.dv) && iszero(M.ev) -isone(M::Bidiagonal) = all(isone, M.dv) && iszero(M.ev) -Base.@constprop :aggressive function istriu(M::Bidiagonal, k::Integer=0) - if M.uplo == 'U' - if k <= 0 - return true - elseif k == 1 - return iszero(M.dv) - else # k >= 2 - return iszero(M.dv) && iszero(M.ev) - end - else # M.uplo == 'L' - if k <= -1 - return true - elseif k == 0 - return iszero(M.ev) - else # k >= 1 - return iszero(M.ev) && iszero(M.dv) - end - end -end -Base.@constprop :aggressive function istril(M::Bidiagonal, k::Integer=0) - if M.uplo == 'U' - if k >= 1 - return true - elseif k == 0 - return iszero(M.ev) - else # k <= -1 - return iszero(M.ev) && iszero(M.dv) - end - else # M.uplo == 'L' - if k >= 0 - return true - elseif k == -1 - return iszero(M.dv) - else # k <= -2 - return iszero(M.dv) && iszero(M.ev) - end - end -end -isdiag(M::Bidiagonal) = iszero(M.ev) -issymmetric(M::Bidiagonal) = isdiag(M) && all(issymmetric, M.dv) -ishermitian(M::Bidiagonal) = isdiag(M) && all(ishermitian, M.dv) - -function tril!(M::Bidiagonal{T}, k::Integer=0) where T - n = length(M.dv) - if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) - elseif M.uplo == 'U' && k < 0 - fill!(M.dv, zero(T)) - fill!(M.ev, zero(T)) - elseif k < -1 - fill!(M.dv, zero(T)) - fill!(M.ev, zero(T)) - elseif M.uplo == 'U' && k == 0 - fill!(M.ev, zero(T)) - elseif M.uplo == 'L' && k == -1 - fill!(M.dv, zero(T)) - end - return M -end - -function triu!(M::Bidiagonal{T}, k::Integer=0) where T - n = length(M.dv) - if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least", - lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) - elseif M.uplo == 'L' && k > 0 - fill!(M.dv, zero(T)) - fill!(M.ev, zero(T)) - elseif k > 1 - fill!(M.dv, zero(T)) - fill!(M.ev, zero(T)) - elseif M.uplo == 'L' && k == 0 - fill!(M.ev, zero(T)) - elseif M.uplo == 'U' && k == 1 - fill!(M.dv, zero(T)) - end - return M -end - -function diag(M::Bidiagonal, n::Integer=0) - # every branch call similar(..., ::Int) to make sure the - # same vector type is returned independent of n - v = similar(M.dv, max(0, length(M.dv)-abs(n))) - if n == 0 - copyto!(v, M.dv) - elseif (n == 1 && M.uplo == 'U') || (n == -1 && M.uplo == 'L') - copyto!(v, M.ev) - elseif -size(M,1) <= n <= size(M,1) - for i in eachindex(v) - v[i] = M[BandIndex(n,i)] - end - end - return v -end - -function +(A::Bidiagonal, B::Bidiagonal) - if A.uplo == B.uplo || length(A.dv) == 0 - Bidiagonal(A.dv+B.dv, A.ev+B.ev, A.uplo) - else - newdv = A.dv+B.dv - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(B.ev), newdv, typeof(newdv)(A.ev)) : (typeof(newdv)(A.ev), newdv, typeof(newdv)(B.ev)))...) - end -end - -function -(A::Bidiagonal, B::Bidiagonal) - if A.uplo == B.uplo || length(A.dv) == 0 - Bidiagonal(A.dv-B.dv, A.ev-B.ev, A.uplo) - else - newdv = A.dv-B.dv - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-B.ev), newdv, typeof(newdv)(A.ev)) : (typeof(newdv)(A.ev), newdv, typeof(newdv)(-B.ev)))...) - end -end - --(A::Bidiagonal)=Bidiagonal(-A.dv,-A.ev,A.uplo) -*(A::Bidiagonal, B::Number) = Bidiagonal(A.dv*B, A.ev*B, A.uplo) -*(B::Number, A::Bidiagonal) = Bidiagonal(B*A.dv, B*A.ev, A.uplo) -function rmul!(B::Bidiagonal, x::Number) - if size(B,1) > 1 - isupper = B.uplo == 'U' - row, col = 1 + isupper, 1 + !isupper - # ensure that zeros are preserved on scaling - y = B[row,col] * x - iszero(y) || throw(ArgumentError(LazyString(lazy"cannot set index ($row, $col) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. B.dv *= x - @. B.ev *= x - return B -end -function lmul!(x::Number, B::Bidiagonal) - if size(B,1) > 1 - isupper = B.uplo == 'U' - row, col = 1 + isupper, 1 + !isupper - # ensure that zeros are preserved on scaling - y = x * B[row,col] - iszero(y) || throw(ArgumentError(LazyString(lazy"cannot set index ($row, $col) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. B.dv = x * B.dv - @. B.ev = x * B.ev - return B -end -/(A::Bidiagonal, B::Number) = Bidiagonal(A.dv/B, A.ev/B, A.uplo) -\(B::Number, A::Bidiagonal) = Bidiagonal(B\A.dv, B\A.ev, A.uplo) - -function ==(A::Bidiagonal, B::Bidiagonal) - if A.uplo == B.uplo - return A.dv == B.dv && A.ev == B.ev - else - return iszero(A.ev) && iszero(B.ev) && A.dv == B.dv - end -end - -const BandedMatrix = Union{Bidiagonal,Diagonal,Tridiagonal,SymTridiagonal} # or BiDiTriSym -const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal} -const TriSym = Union{Tridiagonal,SymTridiagonal} -const BiTri = Union{Bidiagonal,Tridiagonal} -@inline _mul!(C::AbstractVector, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) -@inline _mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) -for T in (:AbstractMatrix, :Diagonal) - @eval begin - @inline _mul!(C::AbstractMatrix, A::BandedMatrix, B::$T, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) - @inline _mul!(C::AbstractMatrix, A::$T, B::BandedMatrix, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) - end -end -@inline _mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) - -# B .= A * B -function lmul!(A::Bidiagonal, B::AbstractVecOrMat) - _muldiag_size_check(size(A), size(B)) - (; dv, ev) = A - if A.uplo == 'U' - for k in axes(B,2) - for i in axes(ev,1) - B[i,k] = dv[i] * B[i,k] + ev[i] * B[i+1,k] - end - B[end,k] = dv[end] * B[end,k] - end - else - for k in axes(B,2) - for i in reverse(axes(dv,1)[2:end]) - B[i,k] = dv[i] * B[i,k] + ev[i-1] * B[i-1,k] - end - B[1,k] = dv[1] * B[1,k] - end - end - return B -end -# B .= D * B -function lmul!(D::Diagonal, B::Bidiagonal) - _muldiag_size_check(size(D), size(B)) - (; dv, ev) = B - isL = B.uplo == 'L' - dv[1] = D.diag[1] * dv[1] - for i in axes(ev,1) - ev[i] = D.diag[i + isL] * ev[i] - dv[i+1] = D.diag[i+1] * dv[i+1] - end - return B -end -# B .= B * A -function rmul!(B::AbstractMatrix, A::Bidiagonal) - _muldiag_size_check(size(A), size(B)) - (; dv, ev) = A - if A.uplo == 'U' - for k in reverse(axes(dv,1)[2:end]) - for i in axes(B,1) - B[i,k] = B[i,k] * dv[k] + B[i,k-1] * ev[k-1] - end - end - for i in axes(B,1) - B[i,1] *= dv[1] - end - else - for k in axes(ev,1) - for i in axes(B,1) - B[i,k] = B[i,k] * dv[k] + B[i,k+1] * ev[k] - end - end - for i in axes(B,1) - B[i,end] *= dv[end] - end - end - return B -end -# B .= B * D -function rmul!(B::Bidiagonal, D::Diagonal) - _muldiag_size_check(size(B), size(D)) - (; dv, ev) = B - isU = B.uplo == 'U' - dv[1] *= D.diag[1] - for i in axes(ev,1) - ev[i] *= D.diag[i + isU] - dv[i+1] *= D.diag[i+1] - end - return B -end - -@noinline function check_A_mul_B!_sizes((mC, nC)::NTuple{2,Integer}, (mA, nA)::NTuple{2,Integer}, (mB, nB)::NTuple{2,Integer}) - # check for matching sizes in one column of B and C - check_A_mul_B!_sizes((mC,), (mA, nA), (mB,)) - # ensure that the number of columns in B and C match - if nB != nC - throw(DimensionMismatch(lazy"second dimension of output C, $nC, and second dimension of B, $nB, must match")) - end -end -@noinline function check_A_mul_B!_sizes((mC,)::Tuple{Integer}, (mA, nA)::NTuple{2,Integer}, (mB,)::Tuple{Integer}) - if mA != mC - throw(DimensionMismatch(lazy"first dimension of A, $mA, and first dimension of output C, $mC, must match")) - elseif nA != mB - throw(DimensionMismatch(lazy"second dimension of A, $nA, and first dimension of B, $mB, must match")) - end -end - -# function to get the internally stored vectors for Bidiagonal and [Sym]Tridiagonal -# to avoid allocations in _mul! below (#24324, #24578) -_diag(A::Tridiagonal, k) = k == -1 ? A.dl : k == 0 ? A.d : A.du -_diag(A::SymTridiagonal{<:Number}, k) = k == 0 ? A.dv : A.ev -_diag(A::SymTridiagonal, k) = diagview(A,k) -function _diag(A::Bidiagonal, k) - if k == 0 - return A.dv - elseif k == _offdiagind(A.uplo) - return A.ev - else - return diag(A, k) - end -end - -_mul!(C::AbstractMatrix, A::BiTriSym, B::TriSym, _add::MulAddMul) = - _bibimul!(C, A, B, _add) -_mul!(C::AbstractMatrix, A::BiTriSym, B::Bidiagonal, _add::MulAddMul) = - _bibimul!(C, A, B, _add) -function _bibimul!(C, A, B, _add) - require_one_based_indexing(C) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - # We use `_rmul_or_fill!` instead of `_modify!` here since using - # `_modify!` in the following loop will not update the - # off-diagonal elements for non-zero beta. - _rmul_or_fill!(C, _add.beta) - iszero(_add.alpha) && return C - if n <= 3 - # naive multiplication - for I in CartesianIndices(C) - C[I] += _add(sum(A[I[1], k] * B[k, I[2]] for k in axes(A,2))) - end - return C - end - @inbounds begin - # first column of C - C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2,1]) - C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) - C[3,1] += _add(A[3,2]*B[2,1]) - # second column of C - C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) - C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) - C[3,2] += _add(A[3,2]*B[2,2] + A[3,3]*B[3,2]) - C[4,2] += _add(A[4,3]*B[3,2]) - end # inbounds - # middle columns - __bibimul!(C, A, B, _add) - @inbounds begin - C[n-3,n-1] += _add(A[n-3,n-2]*B[n-2,n-1]) - C[n-2,n-1] += _add(A[n-2,n-2]*B[n-2,n-1] + A[n-2,n-1]*B[n-1,n-1]) - C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) - C[n, n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) - # last column of C - C[n-2, n] += _add(A[n-2,n-1]*B[n-1,n]) - C[n-1, n] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1,n]*B[n,n ]) - C[n, n] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) - end # inbounds - C -end -function __bibimul!(C, A, B, _add) - n = size(A,1) - Al = _diag(A, -1) - Ad = _diag(A, 0) - Au = _diag(A, 1) - Bl = _diag(B, -1) - Bd = _diag(B, 0) - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - C -end -function __bibimul!(C, A, B::Bidiagonal, _add) - n = size(A,1) - Al = _diag(A, -1) - Ad = _diag(A, 0) - Au = _diag(A, 1) - Bd = _diag(B, 0) - if B.uplo == 'U' - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj) - end - end - else # B.uplo == 'L' - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end - C -end -function __bibimul!(C, A::Bidiagonal, B, _add) - n = size(A,1) - Bl = _diag(B, -1) - Bd = _diag(B, 0) - Bu = _diag(B, 1) - Ad = _diag(A, 0) - if A.uplo == 'U' - Au = _diag(A, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) - end - end - else # A.uplo == 'L' - Al = _diag(A, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end - C -end -function __bibimul!(C, A::Bidiagonal, B::Bidiagonal, _add) - n = size(A,1) - Ad = _diag(A, 0) - Bd = _diag(B, 0) - if A.uplo == 'U' && B.uplo == 'U' - Au = _diag(A, 1) - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋2j₋1 = Au[j-2] - Aj₋1j = Au[j-1] - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-2, j] += _add(Aj₋2j₋1*Bj₋1j) - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j + Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj) - end - end - elseif A.uplo == 'U' && B.uplo == 'L' - Au = _diag(A, 1) - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Aj₋1j = Au[j-1] - Ajj₊1 = Au[j] - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j-1, j] += _add(Aj₋1j*Bjj) - C[j, j] += _add(Ajj*Bjj + Ajj₊1*Bj₊1j) - C[j+1, j] += _add(Aj₊1j₊1*Bj₊1j) - end - end - elseif A.uplo == 'L' && B.uplo == 'U' - Al = _diag(A, -1) - Bu = _diag(B, 1) - @inbounds begin - for j in 3:n-2 - Aj₋1j₋1 = Ad[j-1] - Ajj = Ad[j] - Ajj₋1 = Al[j-1] - Aj₊1j = Al[j] - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - - C[j-1, j] += _add(Aj₋1j₋1*Bj₋1j) - C[j, j] += _add(Ajj₋1*Bj₋1j + Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj) - end - end - else # A.uplo == 'L' && B.uplo == 'L' - Al = _diag(A, -1) - Bl = _diag(B, -1) - @inbounds begin - for j in 3:n-2 - Ajj = Ad[j] - Aj₊1j₊1 = Ad[j+1] - Aj₊1j = Al[j] - Aj₊2j₊1 = Al[j+1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - - C[j, j] += _add(Ajj*Bjj) - C[j+1, j] += _add(Aj₊1j*Bjj + Aj₊1j₊1*Bj₊1j) - C[j+2, j] += _add(Aj₊2j₊1*Bj₊1j) - end - end - end - C -end - -_mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) -function _mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, _add::MulAddMul) - require_one_based_indexing(C) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - _rmul_or_fill!(C, _add.beta) # see the same use above - iszero(_add.alpha) && return C - Al = _diag(A, -1) - Ad = _diag(A, 0) - Au = _diag(A, 1) - Bd = B.diag - @inbounds begin - # first row of C - for j in 1:min(2, n) - C[1,j] += _add(A[1,j]*B[j,j]) - end - # second row of C - if n > 1 - for j in 1:min(3, n) - C[2,j] += _add(A[2,j]*B[j,j]) - end - end - for j in 3:n-2 - C[j, j-1] += _add(Al[j-1]*Bd[j-1]) - C[j, j ] += _add(Ad[j ]*Bd[j ]) - C[j, j+1] += _add(Au[j ]*Bd[j+1]) - end - if n > 3 - # row before last of C - for j in n-2:n - C[n-1,j] += _add(A[n-1,j]*B[j,j]) - end - end - # last row of C - if n > 2 - for j in n-1:n - C[n,j] += _add(A[n,j]*B[j,j]) - end - end - end # inbounds - C -end - -function _mul!(C::AbstractMatrix, A::Bidiagonal, B::Diagonal, _add::MulAddMul) - require_one_based_indexing(C) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - _rmul_or_fill!(C, _add.beta) # see the same use above - iszero(_add.alpha) && return C - (; dv, ev) = A - Bd = B.diag - rowshift = A.uplo == 'U' ? -1 : 1 - evshift = Int(A.uplo == 'U') - @inbounds begin - # first row of C - C[1,1] += _add(dv[1]*Bd[1]) - if n > 1 - if A.uplo == 'L' - C[2,1] += _add(ev[1]*Bd[1]) - end - for col in 2:n-1 - C[col+rowshift, col] += _add(ev[col - evshift]*Bd[col]) - C[col, col] += _add(dv[col]*Bd[col]) - end - if A.uplo == 'U' - C[n-1,n] += _add(ev[n-1]*Bd[n]) - end - C[n, n] += _add(dv[n]*Bd[n]) - end - end # inbounds - C -end - -function _mul!(C::Bidiagonal, A::Bidiagonal, B::Diagonal, _add::MulAddMul) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - Adv, Aev = A.dv, A.ev - Cdv, Cev = C.dv, C.ev - Bd = B.diag - shift = Int(A.uplo == 'U') - if C.uplo == A.uplo - @inbounds begin - _modify!(_add, Adv[1]*Bd[1], Cdv, 1) - for j in eachindex(IndexLinear(), Aev, Cev) - _modify!(_add, Aev[j]*Bd[j+shift], Cev, j) - _modify!(_add, Adv[j+1]*Bd[j+1], Cdv, j+1) - end - end # inbounds - else - @inbounds begin - _modify!(_add, Adv[1]*Bd[1], Cdv, 1) - for j in eachindex(IndexLinear(), Aev, Cev) - _modify!(_add, Adv[j+1]*Bd[j+1], Cdv, j+1) - # this branch will error unless the value is zero - _modify!(_add, Aev[j]*Bd[j+shift], C, (j+1-shift, j+shift)) - # zeros of the correct type - _modify!(_add, A[j+shift, j+1-shift]*Bd[j+1-shift], Cev, j) - end - end - end - C -end - -function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulAddMul) - require_one_based_indexing(C, B) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - nA = size(A,1) - nB = size(B,2) - (iszero(nA) || iszero(nB)) && return C - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - if nA <= 3 - # naive multiplication - for I in CartesianIndices(C) - col = Base.tail(Tuple(I)) - _modify!(_add, sum(A[I[1], k] * B[k, col...] for k in axes(A,2)), C, I) - end - return C - end - _mul_bitrisym!(C, A, B, _add) -end -function _mul_bitrisym!(C::AbstractVecOrMat, A::Bidiagonal, B::AbstractVecOrMat, _add::MulAddMul) - nA = size(A,1) - nB = size(B,2) - d = A.dv - if A.uplo == 'U' - u = A.ev - @inbounds begin - for j = 1:nB - b₀, b₊ = B[1, j], B[2, j] - _modify!(_add, d[1]*b₀ + u[1]*b₊, C, (1, j)) - for i = 2:nA - 1 - b₀, b₊ = b₊, B[i + 1, j] - _modify!(_add, d[i]*b₀ + u[i]*b₊, C, (i, j)) - end - _modify!(_add, d[nA]*b₊, C, (nA, j)) - end - end - else - l = A.ev - @inbounds begin - for j = 1:nB - b₀, b₊ = B[1, j], B[2, j] - _modify!(_add, d[1]*b₀, C, (1, j)) - for i = 2:nA - 1 - b₋, b₀, b₊ = b₀, b₊, B[i + 1, j] - _modify!(_add, l[i - 1]*b₋ + d[i]*b₀, C, (i, j)) - end - _modify!(_add, l[nA - 1]*b₀ + d[nA]*b₊, C, (nA, j)) - end - end - end - C -end -function _mul_bitrisym!(C::AbstractVecOrMat, A::TriSym, B::AbstractVecOrMat, _add::MulAddMul) - nA = size(A,1) - nB = size(B,2) - l = _diag(A, -1) - d = _diag(A, 0) - u = _diag(A, 1) - @inbounds begin - for j = 1:nB - b₀, b₊ = B[1, j], B[2, j] - _modify!(_add, d[1]*b₀ + u[1]*b₊, C, (1, j)) - for i = 2:nA - 1 - b₋, b₀, b₊ = b₀, b₊, B[i + 1, j] - _modify!(_add, l[i - 1]*b₋ + d[i]*b₀ + u[i]*b₊, C, (i, j)) - end - _modify!(_add, l[nA - 1]*b₀ + d[nA]*b₊, C, (nA, j)) - end - end - C -end - -function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::TriSym, _add::MulAddMul) - require_one_based_indexing(C, A) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - m = size(B,2) - (iszero(_add.alpha) || iszero(m)) && return _rmul_or_fill!(C, _add.beta) - if m == 1 - B11 = B[1,1] - return mul!(C, A, B11, _add.alpha, _add.beta) - end - Bl = _diag(B, -1) - Bd = _diag(B, 0) - Bu = _diag(B, 1) - @inbounds begin - # first and last column of C - B11 = Bd[1] - B21 = Bl[1] - Bmm = Bd[m] - Bm₋1m = Bu[m-1] - for i in 1:n - _modify!(_add, A[i,1] * B11 + A[i, 2] * B21, C, (i, 1)) - _modify!(_add, A[i, m-1] * Bm₋1m + A[i, m] * Bmm, C, (i, m)) - end - # middle columns of C - for j = 2:m-1 - Bj₋1j = Bu[j-1] - Bjj = Bd[j] - Bj₊1j = Bl[j] - for i = 1:n - _modify!(_add, A[i, j-1] * Bj₋1j + A[i, j]*Bjj + A[i, j+1] * Bj₊1j, C, (i, j)) - end - end - end # inbounds - C -end - -function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal, _add::MulAddMul) - require_one_based_indexing(C, A) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - m, n = size(A) - (iszero(m) || iszero(n)) && return C - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - @inbounds if B.uplo == 'U' - for j in n:-1:2, i in 1:m - _modify!(_add, A[i,j] * B.dv[j] + A[i,j-1] * B.ev[j-1], C, (i, j)) - end - for i in 1:m - _modify!(_add, A[i,1] * B.dv[1], C, (i, 1)) - end - else # uplo == 'L' - for j in 1:n-1, i in 1:m - _modify!(_add, A[i,j] * B.dv[j] + A[i,j+1] * B.ev[j], C, (i, j)) - end - for i in 1:m - _modify!(_add, A[i,n] * B.dv[n], C, (i, n)) - end - end - C -end - -_mul!(C::AbstractMatrix, A::Diagonal, B::BiTriSym, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) -_mul!(C::AbstractMatrix, A::Diagonal, B::Bidiagonal, _add::MulAddMul) = - _dibimul!(C, A, B, _add) -_mul!(C::AbstractMatrix, A::Diagonal, B::TriSym, _add::MulAddMul) = - _dibimul!(C, A, B, _add) -function _dibimul!(C, A, B, _add) - require_one_based_indexing(C) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - # ensure that we fill off-band elements in the destination - _rmul_or_fill!(C, _add.beta) - iszero(_add.alpha) && return C - if n <= 3 - # For simplicity, use a naive multiplication for small matrices - # that loops over all elements. - for I in CartesianIndices(C) - C[I] += _add(A.diag[I[1]] * B[I[1], I[2]]) - end - return C - end - Ad = A.diag - Bl = _diag(B, -1) - Bd = _diag(B, 0) - Bu = _diag(B, 1) - @inbounds begin - # first row of C - C[1,1] += _add(A[1,1]*B[1,1]) - C[1,2] += _add(A[1,1]*B[1,2]) - # second row of C - C[2,1] += _add(A[2,2]*B[2,1]) - C[2,2] += _add(A[2,2]*B[2,2]) - C[2,3] += _add(A[2,2]*B[2,3]) - for j in 3:n-2 - Ajj = Ad[j] - C[j, j-1] += _add(Ajj*Bl[j-1]) - C[j, j ] += _add(Ajj*Bd[j]) - C[j, j+1] += _add(Ajj*Bu[j]) - end - # row before last of C - C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2]) - C[n-1,n-1] += _add(A[n-1,n-1]*B[n-1,n-1]) - C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ]) - # last row of C - C[n,n-1] += _add(A[n,n]*B[n,n-1]) - C[n,n ] += _add(A[n,n]*B[n,n ]) - end # inbounds - C -end -function _dibimul!(C::AbstractMatrix, A::Diagonal, B::Bidiagonal, _add) - require_one_based_indexing(C) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - iszero(n) && return C - # ensure that we fill off-band elements in the destination - _rmul_or_fill!(C, _add.beta) - iszero(_add.alpha) && return C - Ad = A.diag - Bdv, Bev = B.dv, B.ev - rowshift = B.uplo == 'U' ? -1 : 1 - evshift = Int(B.uplo == 'U') - @inbounds begin - # first row of C - C[1,1] += _add(Ad[1]*Bdv[1]) - if n > 1 - if B.uplo == 'L' - C[2,1] += _add(Ad[2]*Bev[1]) - end - for col in 2:n-1 - evrow = col+rowshift - C[evrow, col] += _add(Ad[evrow]*Bev[col - evshift]) - C[col, col] += _add(Ad[col]*Bdv[col]) - end - if B.uplo == 'U' - C[n-1,n] += _add(Ad[n-1]*Bev[n-1]) - end - C[n, n] += _add(Ad[n]*Bdv[n]) - end - end # inbounds - C -end -function _dibimul!(C::Bidiagonal, A::Diagonal, B::Bidiagonal, _add) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - n = size(A,1) - n == 0 && return C - iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) - Ad = A.diag - Bdv, Bev = B.dv, B.ev - Cdv, Cev = C.dv, C.ev - shift = Int(B.uplo == 'L') - if C.uplo == B.uplo - @inbounds begin - _modify!(_add, Ad[1]*Bdv[1], Cdv, 1) - for j in eachindex(IndexLinear(), Bev, Cev) - _modify!(_add, Ad[j+shift]*Bev[j], Cev, j) - _modify!(_add, Ad[j+1]*Bdv[j+1], Cdv, j+1) - end - end # inbounds - else - @inbounds begin - _modify!(_add, Ad[1]*Bdv[1], Cdv, 1) - for j in eachindex(IndexLinear(), Bev, Cev) - _modify!(_add, Ad[j+1]*Bdv[j+1], Cdv, j+1) - # this branch will error unless the value is zero - _modify!(_add, Ad[j+shift]*Bev[j], C, (j+shift, j+1-shift)) - # zeros of the correct type - _modify!(_add, Ad[j+1-shift]*B[j+1-shift,j+shift], Cev, j) - end - end - end - C -end - -function *(A::UpperOrUnitUpperTriangular, B::Bidiagonal) - TS = promote_op(matprod, eltype(A), eltype(B)) - C = mul!(similar(A, TS, size(A)), A, B) - return B.uplo == 'U' ? UpperTriangular(C) : C -end - -function *(A::LowerOrUnitLowerTriangular, B::Bidiagonal) - TS = promote_op(matprod, eltype(A), eltype(B)) - C = mul!(similar(A, TS, size(A)), A, B) - return B.uplo == 'L' ? LowerTriangular(C) : C -end - -function *(A::Bidiagonal, B::UpperOrUnitUpperTriangular) - TS = promote_op(matprod, eltype(A), eltype(B)) - C = mul!(similar(B, TS, size(B)), A, B) - return A.uplo == 'U' ? UpperTriangular(C) : C -end - -function *(A::Bidiagonal, B::LowerOrUnitLowerTriangular) - TS = promote_op(matprod, eltype(A), eltype(B)) - C = mul!(similar(B, TS, size(B)), A, B) - return A.uplo == 'L' ? LowerTriangular(C) : C -end - -function dot(x::AbstractVector, B::Bidiagonal, y::AbstractVector) - require_one_based_indexing(x, y) - nx, ny = length(x), length(y) - (nx == size(B, 1) == ny) || throw(DimensionMismatch()) - if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(B)), zero(eltype(y))) - return dot(x[1], B.dv[1], y[1]) - end - ev, dv = B.ev, B.dv - @inbounds if B.uplo == 'U' - x₀ = x[1] - r = dot(x[1], dv[1], y[1]) - for j in 2:nx-1 - x₋, x₀ = x₀, x[j] - r += dot(adjoint(ev[j-1])*x₋ + adjoint(dv[j])*x₀, y[j]) - end - r += dot(adjoint(ev[nx-1])*x₀ + adjoint(dv[nx])*x[nx], y[nx]) - return r - else # B.uplo == 'L' - x₀ = x[1] - x₊ = x[2] - r = dot(adjoint(dv[1])*x₀ + adjoint(ev[1])*x₊, y[1]) - for j in 2:nx-1 - x₀, x₊ = x₊, x[j+1] - r += dot(adjoint(dv[j])*x₀ + adjoint(ev[j])*x₊, y[j]) - end - r += dot(x₊, dv[nx], y[nx]) - return r - end -end - -#Linear solvers -#Generic solver using naive substitution -ldiv!(A::Bidiagonal, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) -function ldiv!(c::AbstractVecOrMat, A::Bidiagonal, b::AbstractVecOrMat) - require_one_based_indexing(c, A, b) - N = size(A, 2) - mb, nb = size(b, 1), size(b, 2) - if N != mb - throw(DimensionMismatch(lazy"second dimension of A, $N, does not match first dimension of b, $mb")) - end - mc, nc = size(c, 1), size(c, 2) - if mc != mb || nc != nb - throw(DimensionMismatch(lazy"size of result, ($mc, $nc), does not match the size of b, ($mb, $nb)")) - end - - if N == 0 - return copyto!(c, b) - end - - zi = findfirst(iszero, A.dv) - isnothing(zi) || throw(SingularException(zi)) - - @inbounds for j in 1:nb - if A.uplo == 'L' #do colwise forward substitution - c[1,j] = bi1 = A.dv[1] \ b[1,j] - for i in 2:N - c[i,j] = bi1 = A.dv[i] \ (b[i,j] - A.ev[i - 1] * bi1) - end - else #do colwise backward substitution - c[N,j] = bi1 = A.dv[N] \ b[N,j] - for i in (N - 1):-1:1 - c[i,j] = bi1 = A.dv[i] \ (b[i,j] - A.ev[i] * bi1) - end - end - end - return c -end -ldiv!(A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) -ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = - (t = wrapperop(A); _rdiv!(t(c), t(b), t(A)); return c) - -### Generic promotion methods and fallbacks -\(A::Bidiagonal, B::AbstractVecOrMat) = - ldiv!(matprod_dest(A, B, promote_op(\, eltype(A), eltype(B))), A, B) -\(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B - -### Triangular specializations -for tri in (:UpperTriangular, :UnitUpperTriangular) - @eval function \(B::Bidiagonal, U::$tri) - A = ldiv!(matprod_dest(B, U, promote_op(\, eltype(B), eltype(U))), B, U) - return B.uplo == 'U' ? UpperTriangular(A) : A - end - @eval function \(U::$tri, B::Bidiagonal) - A = ldiv!(matprod_dest(U, B, promote_op(\, eltype(U), eltype(B))), U, B) - return B.uplo == 'U' ? UpperTriangular(A) : A - end -end -for tri in (:LowerTriangular, :UnitLowerTriangular) - @eval function \(B::Bidiagonal, L::$tri) - A = ldiv!(matprod_dest(B, L, promote_op(\, eltype(B), eltype(L))), B, L) - return B.uplo == 'L' ? LowerTriangular(A) : A - end - @eval function \(L::$tri, B::Bidiagonal) - A = ldiv!(matprod_dest(L, B, promote_op(\, eltype(L), eltype(B))), L, B) - return B.uplo == 'L' ? LowerTriangular(A) : A - end -end - -### Diagonal specialization -function \(B::Bidiagonal, D::Diagonal) - A = ldiv!(similar(D, promote_op(\, eltype(B), eltype(D)), size(D)), B, D) - return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) -end - -function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal) - require_one_based_indexing(C, A, B) - m, n = size(A) - if size(B, 1) != n - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - mc, nc = size(C) - if mc != m || nc != n - throw(DimensionMismatch(lazy"expect output to have size ($m, $n), but got ($mc, $nc)")) - end - - zi = findfirst(iszero, B.dv) - isnothing(zi) || throw(SingularException(zi)) - - if B.uplo == 'L' - diagB = B.dv[n] - for i in 1:m - C[i,n] = A[i,n] / diagB - end - for j in n-1:-1:1 - diagB = B.dv[j] - offdiagB = B.ev[j] - for i in 1:m - C[i,j] = (A[i,j] - C[i,j+1]*offdiagB)/diagB - end - end - else - diagB = B.dv[1] - for i in 1:m - C[i,1] = A[i,1] / diagB - end - for j in 2:n - diagB = B.dv[j] - offdiagB = B.ev[j-1] - for i = 1:m - C[i,j] = (A[i,j] - C[i,j-1]*offdiagB)/diagB - end - end - end - C -end -rdiv!(A::AbstractMatrix, B::Bidiagonal) = @inline _rdiv!(A, A, B) -rdiv!(A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, A, B) -_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = - (t = wrapperop(B); ldiv!(t(C), t(B), t(A)); return C) - -/(A::AbstractMatrix, B::Bidiagonal) = - _rdiv!(similar(A, promote_op(/, eltype(A), eltype(B)), size(A)), A, B) - -### Triangular specializations -for tri in (:UpperTriangular, :UnitUpperTriangular) - @eval function /(U::$tri, B::Bidiagonal) - A = _rdiv!(matprod_dest(U, B, promote_op(/, eltype(U), eltype(B))), U, B) - return B.uplo == 'U' ? UpperTriangular(A) : A - end - @eval function /(B::Bidiagonal, U::$tri) - A = _rdiv!(matprod_dest(B, U, promote_op(/, eltype(B), eltype(U))), B, U) - return B.uplo == 'U' ? UpperTriangular(A) : A - end -end -for tri in (:LowerTriangular, :UnitLowerTriangular) - @eval function /(L::$tri, B::Bidiagonal) - A = _rdiv!(matprod_dest(L, B, promote_op(/, eltype(L), eltype(B))), L, B) - return B.uplo == 'L' ? LowerTriangular(A) : A - end - @eval function /(B::Bidiagonal, L::$tri) - A = _rdiv!(matprod_dest(B, L, promote_op(/, eltype(B), eltype(L))), B, L) - return B.uplo == 'L' ? LowerTriangular(A) : A - end -end - -### Diagonal specialization -function /(D::Diagonal, B::Bidiagonal) - A = _rdiv!(similar(D, promote_op(/, eltype(D), eltype(B)), size(D)), D, B) - return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) -end - -/(A::AbstractMatrix, B::Transpose{<:Any,<:Bidiagonal}) = A / copy(B) -/(A::AbstractMatrix, B::Adjoint{<:Any,<:Bidiagonal}) = A / copy(B) -# disambiguation -/(A::AdjointAbsVec, B::Bidiagonal) = adjoint(adjoint(B) \ parent(A)) -/(A::TransposeAbsVec, B::Bidiagonal) = transpose(transpose(B) \ parent(A)) -/(A::AdjointAbsVec, B::Transpose{<:Any,<:Bidiagonal}) = adjoint(adjoint(B) \ parent(A)) -/(A::TransposeAbsVec, B::Transpose{<:Any,<:Bidiagonal}) = transpose(transpose(B) \ parent(A)) -/(A::AdjointAbsVec, B::Adjoint{<:Any,<:Bidiagonal}) = adjoint(adjoint(B) \ parent(A)) -/(A::TransposeAbsVec, B::Adjoint{<:Any,<:Bidiagonal}) = transpose(transpose(B) \ parent(A)) - -factorize(A::Bidiagonal) = A -function inv(B::Bidiagonal{T}) where T - n = size(B, 1) - dest = zeros(typeof(inv(oneunit(T))), (n, n)) - ldiv!(dest, B, Diagonal{typeof(one(T)/one(T))}(I, n)) - return B.uplo == 'U' ? UpperTriangular(dest) : LowerTriangular(dest) -end - -# Eigensystems -eigvals(M::Bidiagonal) = copy(M.dv) -function eigvecs(M::Bidiagonal{T}) where T - n = length(M.dv) - Q = Matrix{T}(undef, n,n) - blks = [0; findall(iszero, M.ev); n] - v = zeros(T, n) - if M.uplo == 'U' - for idx_block = 1:length(blks) - 1, i = blks[idx_block] + 1:blks[idx_block + 1] #index of eigenvector - fill!(v, zero(T)) - v[blks[idx_block] + 1] = one(T) - for j = blks[idx_block] + 1:i - 1 #Starting from j=i, eigenvector elements will be 0 - v[j+1] = (M.dv[i] - M.dv[j])/M.ev[j] * v[j] - end - c = norm(v) - for j = 1:n - Q[j, i] = v[j] / c - end - end - else - for idx_block = 1:length(blks) - 1, i = blks[idx_block + 1]:-1:blks[idx_block] + 1 #index of eigenvector - fill!(v, zero(T)) - v[blks[idx_block+1]] = one(T) - for j = (blks[idx_block+1] - 1):-1:max(1, (i - 1)) #Starting from j=i, eigenvector elements will be 0 - v[j] = (M.dv[i] - M.dv[j+1])/M.ev[j] * v[j+1] - end - c = norm(v) - for j = 1:n - Q[j, i] = v[j] / c - end - end - end - Q #Actually Triangular -end -eigen(M::Bidiagonal) = Eigen(eigvals(M), eigvecs(M)) - -Base._sum(A::Bidiagonal, ::Colon) = sum(A.dv) + sum(A.ev) -function Base._sum(A::Bidiagonal, dims::Integer) - res = Base.reducedim_initarray(A, dims, zero(eltype(A))) - n = length(A.dv) - if n == 0 - # Just to be sure. This shouldn't happen since there is a check whether - # length(A.dv) == length(A.ev) + 1 in the constructor. - return res - elseif n == 1 - res[1] = A.dv[1] - return res - end - @inbounds begin - if (dims == 1 && A.uplo == 'U') || (dims == 2 && A.uplo == 'L') - res[1] = A.dv[1] - for i = 2:length(A.dv) - res[i] = A.ev[i-1] + A.dv[i] - end - elseif (dims == 1 && A.uplo == 'L') || (dims == 2 && A.uplo == 'U') - for i = 1:length(A.dv)-1 - res[i] = A.ev[i] + A.dv[i] - end - res[end] = A.dv[end] - elseif dims >= 3 - if A.uplo == 'U' - for i = 1:length(A.dv)-1 - res[i,i] = A.dv[i] - res[i,i+1] = A.ev[i] - end - else - for i = 1:length(A.dv)-1 - res[i,i] = A.dv[i] - res[i+1,i] = A.ev[i] - end - end - res[end,end] = A.dv[end] - end - end - res -end diff --git a/stdlib/LinearAlgebra/src/bitarray.jl b/stdlib/LinearAlgebra/src/bitarray.jl deleted file mode 100644 index ccc9138d227a3..0000000000000 --- a/stdlib/LinearAlgebra/src/bitarray.jl +++ /dev/null @@ -1,272 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -function dot(x::BitVector, y::BitVector) - # simplest way to mimic Array dot behavior - length(x) == length(y) || throw(DimensionMismatch()) - s = 0 - xc = x.chunks - yc = y.chunks - @inbounds for i = 1:length(xc) - s += count_ones(xc[i] & yc[i]) - end - s -end - -## slower than the unpacked version, which is MUCH slower -# than blas'd (this one saves storage though, keeping it commented -# just in case) -#function aTb(A::BitMatrix, B::BitMatrix) - #(mA, nA) = size(A) - #(mB, nB) = size(B) - #C = falses(nA, nB) - #if mA != mB; throw(DimensionMismatch()) end - #if mA == 0; return C; end - #col_ch = num_bit_chunks(mA) - ## TODO: avoid using aux chunks and copy (?) - #aux_chunksA = zeros(UInt64, col_ch) - #aux_chunksB = [zeros(UInt64, col_ch) for j=1:nB] - #for j = 1:nB - #Base.copy_chunks!(aux_chunksB[j], 1, B.chunks, (j-1)*mA+1, mA) - #end - #for i = 1:nA - #Base.copy_chunks!(aux_chunksA, 1, A.chunks, (i-1)*mA+1, mA) - #for j = 1:nB - #for k = 1:col_ch - ## TODO: improve - #C[i, j] += count_ones(aux_chunksA[k] & aux_chunksB[j][k]) - #end - #end - #end - #C -#end - -#aCb(A::BitMatrix{T}, B::BitMatrix{S}) where {T,S} = aTb(A, B) - -function triu(B::BitMatrix, k::Integer=0) - m,n = size(B) - if !(-m + 1 <= k <= n + 1) - throw(ArgumentError(string("the requested diagonal, $k, must be at least", - "$(-m + 1) and at most $(n + 1) in an $m-by-$n matrix"))) - end - A = falses(m,n) - Ac = A.chunks - Bc = B.chunks - for i = max(k+1,1):n - j = clamp((i - 1) * m + 1, 1, i * m) - Base.copy_chunks!(Ac, j, Bc, j, min(i-k, m)) - end - A -end - -function tril(B::BitMatrix, k::Integer=0) - m,n = size(B) - if !(-m - 1 <= k <= n - 1) - throw(ArgumentError(string("the requested diagonal, $k, must be at least ", - "$(-m - 1) and at most $(n - 1) in an $m-by-$n matrix"))) - end - A = falses(m, n) - Ac = A.chunks - Bc = B.chunks - for i = 1:min(n, m+k) - j = clamp((i - 1) * m + i - k, 1, i * m) - Base.copy_chunks!(Ac, j, Bc, j, max(m-i+k+1, 0)) - end - A -end - -## diag - -function diag(B::BitMatrix) - n = minimum(size(B)) - v = similar(B, n) - for i = 1:n - v[i] = B[i,i] - end - v -end - -## norm and rank - -svd(A::BitMatrix) = svd(float(A)) -qr(A::BitMatrix) = qr(float(A)) - -## kron - -@inline function kron!(R::BitVector, a::BitVector, b::BitVector) - m = length(a) - n = length(b) - @boundscheck length(R) == n*m || throw(DimensionMismatch()) - Rc = R.chunks - bc = b.chunks - for j = 1:m - a[j] && Base.copy_chunks!(Rc, (j-1)*n+1, bc, 1, n) - end - return R -end - -function kron(a::BitVector, b::BitVector) - m = length(a) - n = length(b) - R = falses(n * m) - return @inbounds kron!(R, a, b) -end - -function kron!(R::BitMatrix, a::BitMatrix, b::BitMatrix) - mA,nA = size(a) - mB,nB = size(b) - @boundscheck size(R) == (mA*mB, nA*nB) || throw(DimensionMismatch()) - - for i = 1:mA - ri = (1:mB) .+ ((i-1)*mB) - for j = 1:nA - if a[i,j] - rj = (1:nB) .+ ((j-1)*nB) - R[ri,rj] = b - end - end - end - return R -end - -function kron(a::BitMatrix, b::BitMatrix) - mA,nA = size(a) - mB,nB = size(b) - R = falses(mA*mB, nA*nB) - return @inbounds kron!(R, a, b) -end - -## Structure query functions - -issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && count(!iszero, A - copy(A'))==0 -ishermitian(A::BitMatrix) = issymmetric(A) - -function nonzero_chunks(chunks::Vector{UInt64}, pos0::Int, pos1::Int) - k0, l0 = Base.get_chunks_id(pos0) - k1, l1 = Base.get_chunks_id(pos1) - - delta_k = k1 - k0 - - z = UInt64(0) - u = ~z - if delta_k == 0 - msk_0 = (u << l0) & ~(u << l1 << 1) - else - msk_0 = (u << l0) - msk_1 = ~(u << l1 << 1) - end - - @inbounds begin - (chunks[k0] & msk_0) == z || return true - delta_k == 0 && return false - for i = k0 + 1 : k1 - 1 - chunks[i] == z || return true - end - (chunks[k1] & msk_1)==z || return true - end - return false -end - -function istriu(A::BitMatrix) - m, n = size(A) - for j = 1:min(n,m-1) - stride = (j-1) * m - nonzero_chunks(A.chunks, stride+j+1, stride+m) && return false - end - return true -end - -function istril(A::BitMatrix) - m, n = size(A) - (m == 0 || n == 0) && return true - for j = 2:n - stride = (j-1) * m - nonzero_chunks(A.chunks, stride+1, stride+min(j-1,m)) && return false - end - return true -end - -# fast 8x8 bit transpose from Henry S. Warrens's "Hacker's Delight" -# https://www.hackersdelight.org/hdcodetxt/transpose8.c.txt -function transpose8x8(x::UInt64) - y = x - t = xor(y, y >>> 7) & 0x00aa00aa00aa00aa - y = xor(y, t, t << 7) - t = xor(y, y >>> 14) & 0x0000cccc0000cccc - y = xor(y, t, t << 14) - t = xor(y, y >>> 28) & 0x00000000f0f0f0f0 - return xor(y, t, t << 28) -end - -function form_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) - x = UInt64(0) - - k, l = Base.get_chunks_id(i1 + (i2 - 1) * m) - r = 0 - for j = 1:8 - k > nc && break - x |= ((Bc[k] >>> l) & msk8) << r - if l + 8 >= 64 && nc > k - r0 = 8 - Base._mod64(l + 8) - x |= (Bc[k + 1] & (msk8 >>> r0)) << (r + r0) - end - k += cgap + (l + cinc >= 64 ? 1 : 0) - l = Base._mod64(l + cinc) - r += 8 - end - return x -end - -# note: assumes B is filled with 0's -function put_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, x::UInt64, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) - k, l = Base.get_chunks_id(i1 + (i2 - 1) * m) - r = 0 - for j = 1:8 - k > nc && break - Bc[k] |= ((x >>> r) & msk8) << l - if l + 8 >= 64 && nc > k - r0 = 8 - Base._mod64(l + 8) - Bc[k + 1] |= ((x >>> (r + r0)) & (msk8 >>> r0)) - end - k += cgap + (l + cinc >= 64 ? 1 : 0) - l = Base._mod64(l + cinc) - r += 8 - end - return -end - -adjoint(B::Union{BitVector,BitMatrix}) = Adjoint(B) -transpose(B::Union{BitVector,BitMatrix}) = Transpose(B) -Base.copy(B::Adjoint{Bool,BitMatrix}) = transpose!(falses(size(B)), B.parent) -Base.copy(B::Transpose{Bool,BitMatrix}) = transpose!(falses(size(B)), B.parent) -function transpose!(C::BitMatrix, B::BitMatrix) - @boundscheck size(C) == reverse(size(B)) || throw(DimensionMismatch()) - l1, l2 = size(B) - - cgap1, cinc1 = Base._div64(l1), Base._mod64(l1) - cgap2, cinc2 = Base._div64(l2), Base._mod64(l2) - - Bc = B.chunks - Cc = C.chunks - - nc = length(Bc) - - for i = 1:8:l1 - msk8_1 = UInt64(0xff) - if (l1 < i + 7) - msk8_1 >>>= i + 7 - l1 - end - - for j = 1:8:l2 - x = form_8x8_chunk(Bc, i, j, l1, cgap1, cinc1, nc, msk8_1) - x = transpose8x8(x) - - msk8_2 = UInt64(0xff) - if (l2 < j + 7) - msk8_2 >>>= j + 7 - l2 - end - - put_8x8_chunk(Cc, j, i, x, l2, cgap2, cinc2, nc, msk8_2) - end - end - return C -end diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl deleted file mode 100644 index 3c15630091162..0000000000000 --- a/stdlib/LinearAlgebra/src/blas.jl +++ /dev/null @@ -1,2258 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" -Interface to BLAS subroutines. -""" -module BLAS - -using Base: require_one_based_indexing, USE_BLAS64 - -export -# Note: `xFUNC_NAME` is a placeholder for not exported BLAS functions -# ref: https://www.netlib.org/blas/blasqr.pdf -# Level 1 - # xROTG - # xROTMG - rot!, - # xROTM - # xSWAP - scal!, - scal, - blascopy!, - # xAXPY!, - # xAXPBY!, - # xDOT - dotc, - dotu, - # xxDOT - nrm2, - asum, - iamax, -# Level 2 - gemv!, - gemv, - gbmv!, - gbmv, - hemv!, - hemv, - # xHBMV - hpmv!, - symv!, - symv, - sbmv!, - sbmv, - spmv!, - trmv!, - trmv, - # xTBMV - # xTPMV - trsv!, - trsv, - # xTBSV - # xTPSV - ger!, - geru!, - # xGERU - # xGERC - her!, - # xHPR - # xHER2 - # xHPR2 - syr!, - spr!, - # xSYR2 - # xSPR2 -# Level 3 - gemmt!, - gemmt, - gemm!, - gemm, - symm!, - symm, - hemm!, - hemm, - syrk!, - syrk, - herk!, - herk, - syr2k!, - syr2k, - her2k!, - her2k, - trmm!, - trmm, - trsm!, - trsm - -using ..LinearAlgebra: libblastrampoline, BlasReal, BlasComplex, BlasFloat, BlasInt, DimensionMismatch, checksquare, chkstride1 - -include("lbt.jl") - -# Legacy bindings that some packages (such as NNlib.jl) use. -# We maintain these for backwards-compatibility but new packages -# should not look at these, instead preferring to parse the output -# of BLAS.get_config() -const libblas = libblastrampoline -const liblapack = libblastrampoline - -vendor() = :lbt - -""" - get_config() - -Return an object representing the current `libblastrampoline` configuration. - -!!! compat "Julia 1.7" - `get_config()` requires at least Julia 1.7. -""" -get_config() = lbt_get_config() - -if USE_BLAS64 - macro blasfunc(x) - return Expr(:quote, Symbol(x, "64_")) - end -else - macro blasfunc(x) - return Expr(:quote, x) - end -end - -_tryparse_env_int(key) = tryparse(Int, get(ENV, key, "")) - - -""" - set_num_threads(n::Integer) - set_num_threads(::Nothing) - -Set the number of threads the BLAS library should use equal to `n::Integer`. - -Also accepts `nothing`, in which case julia tries to guess the default number of threads. -Passing `nothing` is discouraged and mainly exists for historical reasons. -""" -set_num_threads(nt::Integer)::Nothing = lbt_set_num_threads(Int32(nt)) -function set_num_threads(::Nothing) - nt = something( - _tryparse_env_int("OPENBLAS_NUM_THREADS"), - _tryparse_env_int("OMP_NUM_THREADS"), - _tryparse_env_int("VECLIB_MAXIMUM_THREADS"), - max(1, Sys.CPU_THREADS ÷ 2), - ) - return set_num_threads(nt) -end - -""" - get_num_threads() - -Get the number of threads the BLAS library is using. - -!!! compat "Julia 1.6" - `get_num_threads` requires at least Julia 1.6. -""" -get_num_threads()::Int = lbt_get_num_threads() - -function check() - # TODO: once we have bitfields of the BLAS functions that are actually forwarded, - # ensure that we have a complete set here (warning on an incomplete BLAS implementation) - config = get_config() - - # Ensure that one of our loaded libraries satisfies our interface requirement - interface = USE_BLAS64 ? :ilp64 : :lp64 - if !any(lib.interface == interface for lib in config.loaded_libs) - interfacestr = uppercase(string(interface)) - println(Core.stderr, "No loaded BLAS libraries were built with $interfacestr support.") - exit(1) - end -end - -"Check that upper/lower (for special matrices) is correctly specified" -function chkuplo(uplo::AbstractChar) - if !(uplo == 'U' || uplo == 'L') - throw(ArgumentError(lazy"uplo argument must be 'U' (upper) or 'L' (lower), got '$uplo'")) - end - uplo -end - -# Level 1 -# A help function to pick the pointer and inc for 1d like inputs. -@inline function vec_pointer_stride(x::AbstractArray, stride0check = nothing) - Base._checkcontiguous(Bool, x) && return pointer(x), 1 # simplify runtime check when possible - st, ptr = checkedstride(x), pointer(x) - isnothing(stride0check) || (st == 0 && throw(stride0check)) - ptr += min(st, 0) * sizeof(eltype(x)) * (length(x) - 1) - ptr, st -end -function checkedstride(x::AbstractArray) - szs::Dims = size(x) - sts::Dims = strides(x) - _, st, n = Base.merge_adjacent_dim(szs, sts) - n === ndims(x) && return st - throw(ArgumentError("only support vector like inputs")) -end -## copy - -""" - blascopy!(n, X, incx, Y, incy) - -Copy `n` elements of array `X` with stride `incx` to array `Y` with stride `incy`. Returns `Y`. -""" -function blascopy! end - -for (fname, elty) in ((:dcopy_,:Float64), - (:scopy_,:Float32), - (:zcopy_,:ComplexF64), - (:ccopy_,:ComplexF32)) - @eval begin - # SUBROUTINE DCOPY(N,DX,INCX,DY,INCY) - function blascopy!(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - n, DX, incx, DY, incy) - DY - end - end -end - - -## rot - -""" - rot!(n, X, incx, Y, incy, c, s) - -Overwrite `X` with `c*X + s*Y` and `Y` with `-conj(s)*X + c*Y` for the first `n` elements of array `X` with stride `incx` and -first `n` elements of array `Y` with stride `incy`. Returns `X` and `Y`. - -!!! compat "Julia 1.5" - `rot!` requires at least Julia 1.5. -""" -function rot! end - -for (fname, elty, cty, sty, lib) in ((:drot_, :Float64, :Float64, :Float64, libblastrampoline), - (:srot_, :Float32, :Float32, :Float32, libblastrampoline), - (:zdrot_, :ComplexF64, :Float64, :Float64, libblastrampoline), - (:csrot_, :ComplexF32, :Float32, :Float32, libblastrampoline), - (:zrot_, :ComplexF64, :Float64, :ComplexF64, libblastrampoline), - (:crot_, :ComplexF32, :Float32, :ComplexF32, libblastrampoline)) - @eval begin - # SUBROUTINE DROT(N,DX,INCX,DY,INCY,C,S) - function rot!(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer, C::$cty, S::$sty) - ccall((@blasfunc($fname), $lib), Cvoid, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$cty}, Ref{$sty}), - n, DX, incx, DY, incy, C, S) - DX, DY - end - end -end - -## scal - -""" - scal!(n, a, X, incx) - scal!(a, X) - -Overwrite `X` with `a*X` for the first `n` elements of array `X` with stride `incx`. Returns `X`. - -If `n` and `incx` are not provided, `length(X)` and `stride(X,1)` are used. -""" -function scal! end - -""" - scal(n, a, X, incx) - scal(a, X) - -Return `X` scaled by `a` for the first `n` elements of array `X` with stride `incx`. - -If `n` and `incx` are not provided, `length(X)` and `stride(X,1)` are used. -""" -function scal end - -for (fname, elty) in ((:dscal_,:Float64), - (:sscal_,:Float32), - (:zscal_,:ComplexF64), - (:cscal_,:ComplexF32)) - @eval begin - # SUBROUTINE DSCAL(N,DA,DX,INCX) - function scal!(n::Integer, DA::$elty, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), - n, DA, DX, incx) - DX - end - - function scal!(DA::$elty, DX::AbstractArray{$elty}) - p, st = vec_pointer_stride(DX, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve DX scal!(length(DX), DA, p, abs(st)) - DX - end - end -end -scal(n, DA, DX, incx) = scal!(n, DA, copy(DX), incx) -scal(DA, DX) = scal!(DA, copy(DX)) - -## dot - -""" - dot(n, X, incx, Y, incy) - -Dot product of two vectors consisting of `n` elements of array `X` with stride `incx` and -`n` elements of array `Y` with stride `incy`. - -# Examples -```jldoctest -julia> BLAS.dot(10, fill(1.0, 10), 1, fill(1.0, 20), 2) -10.0 -``` -""" -function dot end - -""" - dotc(n, X, incx, U, incy) - -Dot function for two complex vectors, consisting of `n` elements of array `X` -with stride `incx` and `n` elements of array `U` with stride `incy`, -conjugating the first vector. - -# Examples -```jldoctest -julia> BLAS.dotc(10, fill(1.0im, 10), 1, fill(1.0+im, 20), 2) -10.0 - 10.0im -``` -""" -function dotc end - -""" - dotu(n, X, incx, Y, incy) - -Dot function for two complex vectors consisting of `n` elements of array `X` -with stride `incx` and `n` elements of array `Y` with stride `incy`. - -# Examples -```jldoctest -julia> BLAS.dotu(10, fill(1.0im, 10), 1, fill(1.0+im, 20), 2) --10.0 + 10.0im -``` -""" -function dotu end - -for (fname, elty) in ((:cblas_ddot,:Float64), - (:cblas_sdot,:Float32)) - @eval begin - # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) - # * .. Scalar Arguments .. - # INTEGER INCX,INCY,N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION DX(*),DY(*) - function dot(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) - ccall((@blasfunc($fname), libblastrampoline), $elty, - (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt), - n, DX, incx, DY, incy) - end - end -end -for (fname, elty) in ((:cblas_zdotc_sub,:ComplexF64), - (:cblas_cdotc_sub,:ComplexF32)) - @eval begin - # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) - # * .. Scalar Arguments .. - # INTEGER INCX,INCY,N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION DX(*),DY(*) - function dotc(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) - result = Ref{$elty}() - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), - n, DX, incx, DY, incy, result) - result[] - end - end -end -for (fname, elty) in ((:cblas_zdotu_sub,:ComplexF64), - (:cblas_cdotu_sub,:ComplexF32)) - @eval begin - # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) - # * .. Scalar Arguments .. - # INTEGER INCX,INCY,N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION DX(*),DY(*) - function dotu(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) - result = Ref{$elty}() - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), - n, DX, incx, DY, incy, result) - result[] - end - end -end - -for (elty, f) in ((Float32, :dot), (Float64, :dot), - (ComplexF32, :dotc), (ComplexF64, :dotc), - (ComplexF32, :dotu), (ComplexF64, :dotu)) - @eval begin - function $f(x::AbstractArray{$elty}, y::AbstractArray{$elty}) - n, m = length(x), length(y) - n == m || throw(DimensionMismatch(lazy"dot product arguments have lengths $n and $m")) - GC.@preserve x y $f(n, vec_pointer_stride(x)..., vec_pointer_stride(y)...) - end - end -end - -## nrm2 - -""" - nrm2(n, X, incx) - -2-norm of a vector consisting of `n` elements of array `X` with stride `incx`. - -# Examples -```jldoctest -julia> BLAS.nrm2(4, fill(1.0, 8), 2) -2.0 - -julia> BLAS.nrm2(1, fill(1.0, 8), 2) -1.0 -``` -""" -function nrm2 end - -for (fname, elty, ret_type) in ((:dnrm2_,:Float64,:Float64), - (:snrm2_,:Float32,:Float32), - (:dznrm2_,:ComplexF64,:Float64), - (:scnrm2_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DNRM2(N,X,INCX) - function nrm2(n::Integer, X::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) - ccall((@blasfunc($fname), libblastrampoline), $ret_type, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - n, X, incx) - end - end -end -# openblas returns 0 for negative stride -function nrm2(x::AbstractArray) - p, st = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x nrm2(length(x), p, abs(st)) -end - -## asum - -""" - asum(n, X, incx) - -Sum of the magnitudes of the first `n` elements of array `X` with stride `incx`. - -For a real array, the magnitude is the absolute value. For a complex array, the -magnitude is the sum of the absolute value of the real part and the absolute value -of the imaginary part. - -# Examples -```jldoctest -julia> BLAS.asum(5, fill(1.0im, 10), 2) -5.0 - -julia> BLAS.asum(2, fill(1.0im, 10), 5) -2.0 -``` -""" -function asum end - -for (fname, elty, ret_type) in ((:dasum_,:Float64,:Float64), - (:sasum_,:Float32,:Float32), - (:dzasum_,:ComplexF64,:Float64), - (:scasum_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ASUM(N, X, INCX) - function asum(n::Integer, X::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) - ccall((@blasfunc($fname), libblastrampoline), $ret_type, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - n, X, incx) - end - end -end -function asum(x::AbstractArray) - p, st = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x asum(length(x), p, abs(st)) -end - -## axpy - -""" - axpy!(a, X, Y) - -Overwrite `Y` with `X*a + Y`, where `a` is a scalar. Return `Y`. - -# Examples -```jldoctest -julia> x = [1.; 2; 3]; - -julia> y = [4. ;; 5 ;; 6]; - -julia> BLAS.axpy!(2, x, y) -1×3 Matrix{Float64}: - 6.0 9.0 12.0 -``` -""" -function axpy! end - -for (fname, elty) in ((:daxpy_,:Float64), - (:saxpy_,:Float32), - (:zaxpy_,:ComplexF64), - (:caxpy_,:ComplexF32)) - @eval begin - # SUBROUTINE DAXPY(N,DA,DX,INCX,DY,INCY) - # DY <- DA*DX + DY - #* .. Scalar Arguments .. - # DOUBLE PRECISION DA - # INTEGER INCX,INCY,N - #* .. Array Arguments .. - # DOUBLE PRECISION DX(*),DY(*) - function axpy!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, dy::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - n, alpha, dx, incx, dy, incy) - dy - end - end -end - -function axpy!(alpha::Number, x::AbstractArray{T}, y::AbstractArray{T}) where T<:BlasFloat - if length(x) != length(y) - throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) - end - GC.@preserve x y axpy!(length(x), T(alpha), vec_pointer_stride(x)..., - vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed"))...) - y -end - -function axpy!(alpha::Number, x::Array{T}, rx::AbstractRange{Ti}, - y::Array{T}, ry::AbstractRange{Ti}) where {T<:BlasFloat,Ti<:Integer} - if length(rx) != length(ry) - throw(DimensionMismatch("ranges of differing lengths")) - end - if minimum(rx) < 1 || maximum(rx) > length(x) - throw(ArgumentError(lazy"range out of bounds for x, of length $(length(x))")) - end - if minimum(ry) < 1 || maximum(ry) > length(y) - throw(ArgumentError(lazy"range out of bounds for y, of length $(length(y))")) - end - GC.@preserve x y axpy!( - length(rx), - T(alpha), - pointer(x, minimum(rx)), - step(rx), - pointer(y, minimum(ry)), - step(ry)) - - return y -end - -""" - axpby!(a, X, b, Y) - -Overwrite `Y` with `X*a + Y*b`, where `a` and `b` are scalars. Return `Y`. - -# Examples -```jldoctest -julia> x = [1., 2, 3]; - -julia> y = [4., 5, 6]; - -julia> BLAS.axpby!(2., x, 3., y) -3-element Vector{Float64}: - 14.0 - 19.0 - 24.0 -``` -""" -function axpby! end - -for (fname, elty) in ((:daxpby_,:Float64), (:saxpby_,:Float32), - (:zaxpby_,:ComplexF64), (:caxpby_,:ComplexF32)) - @eval begin - # SUBROUTINE DAXPBY(N,DA,DX,INCX,DB,DY,INCY) - # DY <- DA*DX + DB*DY - #* .. Scalar Arguments .. - # DOUBLE PRECISION DA,DB - # INTEGER INCX,INCY,N - #* .. Array Arguments .. - # DOUBLE PRECISION DX(*),DY(*) - function axpby!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, - AbstractArray{$elty}}, incx::Integer, beta::($elty), - dy::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), - n, alpha, dx, incx, beta, dy, incy) - dy - end - end -end - -function axpby!(alpha::Number, x::AbstractArray{T}, beta::Number, y::AbstractArray{T}) where T<:BlasFloat - require_one_based_indexing(x, y) - if length(x) != length(y) - throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) - end - GC.@preserve x y axpby!(length(x), T(alpha), vec_pointer_stride(x)..., T(beta), - vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed"))...) - y -end - -## iamax -for (fname, elty) in ((:idamax_,:Float64), - (:isamax_,:Float32), - (:izamax_,:ComplexF64), - (:icamax_,:ComplexF32)) - @eval begin - function iamax(n::Integer, dx::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer) - ccall((@blasfunc($fname), libblastrampoline),BlasInt, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - n, dx, incx) - end - end -end -function iamax(dx::AbstractArray) - p, st = vec_pointer_stride(dx) - st <= 0 && return BlasInt(0) - iamax(length(dx), p, st) -end - -""" - iamax(n, dx, incx) - iamax(dx) - -Find the index of the element of `dx` with the maximum absolute value. `n` is the length of `dx`, and `incx` is the -stride. If `n` and `incx` are not provided, they assume default values of `n=length(dx)` and `incx=stride1(dx)`. -""" -iamax - -# Level 2 -## mv -### gemv -for (fname, elty) in ((:dgemv_,:Float64), - (:sgemv_,:Float32), - (:zgemv_,:ComplexF64), - (:cgemv_,:ComplexF32)) - @eval begin - #SUBROUTINE DGEMV(TRANS,M,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - #* .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,LDA,M,N - # CHARACTER TRANS - #* .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function gemv!(trans::AbstractChar, alpha::Union{($elty), Bool}, - A::AbstractVecOrMat{$elty}, X::AbstractVector{$elty}, - beta::Union{($elty), Bool}, Y::AbstractVector{$elty}) - require_one_based_indexing(A, X, Y) - m,n = size(A,1),size(A,2) - if trans == 'N' && (length(X) != n || length(Y) != m) - throw(DimensionMismatch(lazy"A has dimensions $(size(A)), X has length $(length(X)) and Y has length $(length(Y))")) - elseif trans == 'C' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch(lazy"the adjoint of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) - elseif trans == 'T' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch(lazy"the transpose of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) - end - chkstride1(A) - lda = stride(A,2) - pX, sX = vec_pointer_stride(X, ArgumentError("input vector with 0 stride is not allowed")) - pY, sY = vec_pointer_stride(Y, ArgumentError("dest vector with 0 stride is not allowed")) - pA = pointer(A) - if lda < 0 - pA += (size(A, 2) - 1) * lda * sizeof($elty) - lda = -lda - trans == 'N' ? (sX = -sX) : (sY = -sY) - end - lda >= size(A,1) || size(A,2) <= 1 || error("when `size(A,2) > 1`, `abs(stride(A,2))` must be at least `size(A,1)`") - lda = max(1, size(A,1), lda) - GC.@preserve A X Y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), - trans, size(A,1), size(A,2), alpha, - pA, lda, pX, sX, - beta, pY, sY, 1) - Y - end - function gemv(trans::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, X::AbstractVector{$elty}) - gemv!(trans, alpha, A, X, zero($elty), similar(X, $elty, size(A, (trans == 'N' ? 1 : 2)))) - end - function gemv(trans::AbstractChar, A::AbstractMatrix{$elty}, X::AbstractVector{$elty}) - gemv!(trans, one($elty), A, X, zero($elty), similar(X, $elty, size(A, (trans == 'N' ? 1 : 2)))) - end - end -end - -""" - gemv!(tA, alpha, A, x, beta, y) - -Update the vector `y` as `alpha*A*x + beta*y` or `alpha*A'x + beta*y` -according to [`tA`](@ref stdlib-blas-trans). -`alpha` and `beta` are scalars. Return the updated `y`. -""" -gemv! - -""" - gemv(tA, alpha, A, x) - -Return `alpha*A*x` or `alpha*A'x` according to [`tA`](@ref stdlib-blas-trans). -`alpha` is a scalar. -""" -gemv(tA, alpha, A, x) - -""" - gemv(tA, A, x) - -Return `A*x` or `A'x` according to [`tA`](@ref stdlib-blas-trans). -""" -gemv(tA, A, x) - -### (GB) general banded matrix-vector multiplication - -""" - gbmv!(trans, m, kl, ku, alpha, A, x, beta, y) - -Update vector `y` as `alpha*A*x + beta*y` or `alpha*A'*x + beta*y` according to [`trans`](@ref stdlib-blas-trans). -The matrix `A` is a general band matrix of dimension `m` by `size(A,2)` with `kl` -sub-diagonals and `ku` super-diagonals. `alpha` and `beta` are scalars. Return the updated `y`. -""" -function gbmv! end - -""" - gbmv(trans, m, kl, ku, alpha, A, x) - -Return `alpha*A*x` or `alpha*A'*x` according to [`trans`](@ref stdlib-blas-trans). -The matrix `A` is a general band matrix of dimension `m` by `size(A,2)` with `kl` sub-diagonals and `ku` -super-diagonals, and `alpha` is a scalar. -""" -function gbmv end - -for (fname, elty) in ((:dgbmv_,:Float64), - (:sgbmv_,:Float32), - (:zgbmv_,:ComplexF64), - (:cgbmv_,:ComplexF32)) - @eval begin - # SUBROUTINE DGBMV(TRANS,M,N,KL,KU,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,KL,KU,LDA,M,N - # CHARACTER TRANS - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function gbmv!(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, - alpha::Union{($elty), Bool}, A::AbstractMatrix{$elty}, - x::AbstractVector{$elty}, beta::Union{($elty), Bool}, - y::AbstractVector{$elty}) - require_one_based_indexing(A, x, y) - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Clong), - trans, m, size(A,2), kl, - ku, alpha, A, max(1,stride(A,2)), - px, stx, beta, py, sty, 1) - y - end - function gbmv(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - n = size(A,2) - leny = trans == 'N' ? m : n - gbmv!(trans, m, kl, ku, alpha, A, x, zero($elty), similar(x, $elty, leny)) - end - function gbmv(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - gbmv(trans, m, kl, ku, one($elty), A, x) - end - end -end - -### symv - -""" - symv!(ul, alpha, A, x, beta, y) - -Update the vector `y` as `alpha*A*x + beta*y`. `A` is assumed to be symmetric. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -`alpha` and `beta` are scalars. Return the updated `y`. -""" -function symv! end - -for (fname, elty, lib) in ((:dsymv_,:Float64,libblastrampoline), - (:ssymv_,:Float32,libblastrampoline), - (:zsymv_,:ComplexF64,libblastrampoline), - (:csymv_,:ComplexF32,libblastrampoline)) - # Note that the complex symv are not BLAS but auiliary functions in LAPACK - @eval begin - # SUBROUTINE DSYMV(UPLO,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - # .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,LDA,N - # CHARACTER UPLO - # .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function symv!(uplo::AbstractChar, alpha::Union{($elty), Bool}, - A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, - beta::Union{($elty), Bool}, y::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x, y) - m, n = size(A) - if m != n - throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) - end - if n != length(x) - throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) - end - if m != length(y) - throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) - end - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), $lib), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, alpha, A, - max(1,stride(A,2)), px, stx, beta, - py, sty, 1) - y - end - function symv(uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - symv!(uplo, alpha, A, x, zero($elty), similar(x)) - end - function symv(uplo::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - symv(uplo, one($elty), A, x) - end - end -end - -""" - symv(ul, alpha, A, x) - -Return `alpha*A*x`. `A` is assumed to be symmetric. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -`alpha` is a scalar. -""" -symv(ul, alpha, A, x) - -""" - symv(ul, A, x) - -Return `A*x`. `A` is assumed to be symmetric. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -symv(ul, A, x) - -### hemv -""" - hemv!(ul, alpha, A, x, beta, y) - -Update the vector `y` as `alpha*A*x + beta*y`. `A` is assumed to be Hermitian. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -`alpha` and `beta` are scalars. Return the updated `y`. -""" -function hemv! end - -for (fname, elty) in ((:zhemv_,:ComplexF64), - (:chemv_,:ComplexF32)) - @eval begin - function hemv!(uplo::AbstractChar, α::Union{$elty, Bool}, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, β::Union{$elty, Bool}, y::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x, y) - m, n = size(A) - if m != n - throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) - end - if n != length(x) - throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) - end - if m != length(y) - throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) - end - chkstride1(A) - lda = max(1, stride(A, 2)) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, α, A, - lda, px, stx, β, - py, sty, 1) - y - end - function hemv(uplo::AbstractChar, α::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - hemv!(uplo, α, A, x, zero($elty), similar(x)) - end - function hemv(uplo::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - hemv(uplo, one($elty), A, x) - end - end -end - -""" - hemv(ul, alpha, A, x) - -Return `alpha*A*x`. `A` is assumed to be Hermitian. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -`alpha` is a scalar. -""" -hemv(ul, alpha, A, x) - -""" - hemv(ul, A, x) - -Return `A*x`. `A` is assumed to be Hermitian. -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -hemv(ul, A, x) - -### hpmv!, (HP) Hermitian packed matrix-vector operation defined as y := alpha*A*x + beta*y. -for (fname, elty) in ((:zhpmv_, :ComplexF64), - (:chpmv_, :ComplexF32)) - @eval begin - # SUBROUTINE ZHPMV(UPLO,N,ALPHA,AP,X,INCX,BETA,Y,INCY) - # Y <- ALPHA*AP*X + BETA*Y - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,N - # CHARACTER UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(N,N),X(N),Y(N) - function hpmv!(uplo::AbstractChar, - n::Integer, - α::$elty, - AP::Union{Ptr{$elty}, AbstractArray{$elty}}, - x::Union{Ptr{$elty}, AbstractArray{$elty}}, - incx::Integer, - β::$elty, - y::Union{Ptr{$elty}, AbstractArray{$elty}}, - incy::Integer) - - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, # uplo, - Ref{BlasInt}, # n, - Ref{$elty}, # α, - Ptr{$elty}, # AP, - Ptr{$elty}, # x, - Ref{BlasInt}, # incx, - Ref{$elty}, # β, - Ptr{$elty}, # y, output - Ref{BlasInt}, # incy - Clong), # length of uplo - uplo, - n, - α, - AP, - x, - incx, - β, - y, - incy, - 1) - return y - end - end -end - -function hpmv!(uplo::AbstractChar, - α::Number, AP::AbstractArray{T}, x::AbstractArray{T}, - β::Number, y::AbstractArray{T}) where {T <: BlasComplex} - require_one_based_indexing(AP, x, y) - N = length(x) - if N != length(y) - throw(DimensionMismatch(lazy"x has length $(N), but y has length $(length(y))")) - end - if 2*length(AP) < N*(N + 1) - throw(DimensionMismatch(lazy"Packed hermitian matrix A has size smaller than length(x) = $(N).")) - end - chkstride1(AP) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y hpmv!(uplo, N, T(α), AP, px, stx, T(β), py, sty) - y -end - -""" - hpmv!(uplo, α, AP, x, β, y) - -Update vector `y` as `α*A*x + β*y`, where `A` is a Hermitian matrix provided -in packed format `AP`. - -With `uplo = 'U'`, the array AP must contain the upper triangular part of the -Hermitian matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` -respectively, and so on. - -With `uplo = 'L'`, the array AP must contain the lower triangular part of the -Hermitian matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` -respectively, and so on. - -The scalar inputs `α` and `β` must be complex or real numbers. - -The array inputs `x`, `y` and `AP` must all be of `ComplexF32` or `ComplexF64` type. - -Return the updated `y`. - -!!! compat "Julia 1.5" - `hpmv!` requires at least Julia 1.5. -""" -hpmv! - -### sbmv, (SB) symmetric banded matrix-vector multiplication -for (fname, elty) in ((:dsbmv_,:Float64), - (:ssbmv_,:Float32)) - @eval begin - # SUBROUTINE DSBMV(UPLO,N,K,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,K,LDA,N - # CHARACTER UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function sbmv!(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::($elty), y::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x, y) - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, size(A,2), k, alpha, - A, max(1,stride(A,2)), px, stx, - beta, py, sty, 1) - y - end - function sbmv(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - n = size(A,2) - sbmv!(uplo, k, alpha, A, x, zero($elty), similar(x, $elty, n)) - end - function sbmv(uplo::AbstractChar, k::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - sbmv(uplo, k, one($elty), A, x) - end - end -end - -""" - sbmv(uplo, k, alpha, A, x) - -Return `alpha*A*x` where `A` is a symmetric band matrix of order `size(A,2)` with `k` -super-diagonals stored in the argument `A`. -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -sbmv(uplo, k, alpha, A, x) - -""" - sbmv(uplo, k, A, x) - -Return `A*x` where `A` is a symmetric band matrix of order `size(A,2)` with `k` -super-diagonals stored in the argument `A`. -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -sbmv(uplo, k, A, x) - -""" - sbmv!(uplo, k, alpha, A, x, beta, y) - -Update vector `y` as `alpha*A*x + beta*y` where `A` is a symmetric band matrix of order -`size(A,2)` with `k` super-diagonals stored in the argument `A`. The storage layout for `A` -is described the reference BLAS module, level-2 BLAS at -. -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. - -Return the updated `y`. -""" -sbmv! - -### spmv!, (SP) symmetric packed matrix-vector operation defined as y := alpha*A*x + beta*y. -for (fname, elty) in ((:dspmv_, :Float64), - (:sspmv_, :Float32)) - @eval begin - # SUBROUTINE DSPMV(UPLO,N,ALPHA,AP,X,INCX,BETA,Y,INCY) - # Y <- ALPHA*AP*X + BETA*Y - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,N - # CHARACTER UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(N,N),X(N),Y(N) - function spmv!(uplo::AbstractChar, - n::Integer, - α::$elty, - AP::Union{Ptr{$elty}, AbstractArray{$elty}}, - x::Union{Ptr{$elty}, AbstractArray{$elty}}, - incx::Integer, - β::$elty, - y::Union{Ptr{$elty}, AbstractArray{$elty}}, - incy::Integer) - - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, # uplo, - Ref{BlasInt}, # n, - Ref{$elty}, # α, - Ptr{$elty}, # AP, - Ptr{$elty}, # x, - Ref{BlasInt}, # incx, - Ref{$elty}, # β, - Ptr{$elty}, # y, out - Ref{BlasInt}, # incy - Clong), # length of uplo - uplo, - n, - α, - AP, - x, - incx, - β, - y, - incy, - 1) - return y - end - end -end - -function spmv!(uplo::AbstractChar, - α::Real, AP::AbstractArray{T}, x::AbstractArray{T}, - β::Real, y::AbstractArray{T}) where {T <: BlasReal} - require_one_based_indexing(AP, x, y) - N = length(x) - if N != length(y) - throw(DimensionMismatch(lazy"x has length $(N), but y has length $(length(y))")) - end - if 2*length(AP) < N*(N + 1) - throw(DimensionMismatch(lazy"Packed symmetric matrix A has size smaller than length(x) = $(N).")) - end - chkstride1(AP) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y spmv!(uplo, N, T(α), AP, px, stx, T(β), py, sty) - y -end - -""" - spmv!(uplo, α, AP, x, β, y) - -Update vector `y` as `α*A*x + β*y`, where `A` is a symmetric matrix provided -in packed format `AP`. - -With `uplo = 'U'`, the array AP must contain the upper triangular part of the -symmetric matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` -respectively, and so on. - -With `uplo = 'L'`, the array AP must contain the lower triangular part of the -symmetric matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` -respectively, and so on. - -The scalar inputs `α` and `β` must be real. - -The array inputs `x`, `y` and `AP` must all be of `Float32` or `Float64` type. - -Return the updated `y`. - -!!! compat "Julia 1.5" - `spmv!` requires at least Julia 1.5. -""" -spmv! - -### spr!, (SP) symmetric packed matrix-vector operation defined as A := alpha*x*x' + A -for (fname, elty) in ((:dspr_, :Float64), - (:sspr_, :Float32)) - @eval begin - function spr!(uplo::AbstractChar, - n::Integer, - α::$elty, - x::Union{Ptr{$elty}, AbstractArray{$elty}}, - incx::Integer, - AP::Union{Ptr{$elty}, AbstractArray{$elty}}) - - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, # uplo, - Ref{BlasInt}, # n, - Ref{$elty}, # α, - Ptr{$elty}, # x, - Ref{BlasInt}, # incx, - Ptr{$elty}, # AP, - Clong), # length of uplo - uplo, - n, - α, - x, - incx, - AP, - 1) - return AP - end - end -end - -function spr!(uplo::AbstractChar, - α::Real, x::AbstractArray{T}, - AP::AbstractArray{T}) where {T <: BlasReal} - chkuplo(uplo) - require_one_based_indexing(AP, x) - N = length(x) - if 2*length(AP) < N*(N + 1) - throw(DimensionMismatch(lazy"Packed symmetric matrix A has size smaller than length(x) = $(N).")) - end - chkstride1(AP) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - return GC.@preserve x spr!(uplo, N, T(α), px, stx , AP) -end - -""" - spr!(uplo, α, x, AP) - -Update matrix `A` as `A+α*x*x'`, where `A` is a symmetric matrix provided -in packed format `AP` and `x` is a vector. - -With `uplo = 'U'`, the array AP must contain the upper triangular part of the -symmetric matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` -respectively, and so on. - -With `uplo = 'L'`, the array AP must contain the lower triangular part of the -symmetric matrix packed sequentially, column by column, so that `AP[1]` -contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` -respectively, and so on. - -The scalar input `α` must be real. - -The array inputs `x` and `AP` must all be of `Float32` or `Float64` type. -Return the updated `AP`. - -!!! compat "Julia 1.8" - `spr!` requires at least Julia 1.8. -""" -spr! - -### hbmv, (HB) Hermitian banded matrix-vector multiplication -for (fname, elty) in ((:zhbmv_,:ComplexF64), - (:chbmv_,:ComplexF32)) - @eval begin - # SUBROUTINE ZHBMV(UPLO,N,K,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,K,LDA,N - # CHARACTER UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function hbmv!(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::($elty), y::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x, y) - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, size(A,2), k, alpha, - A, max(1,stride(A,2)), px, stx, - beta, py, sty, 1) - y - end - function hbmv(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - n = size(A,2) - hbmv!(uplo, k, alpha, A, x, zero($elty), similar(x, $elty, n)) - end - function hbmv(uplo::AbstractChar, k::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - hbmv(uplo, k, one($elty), A, x) - end - end -end - -### trmv, Triangular matrix-vector multiplication - -""" - trmv(ul, tA, dA, A, b) - -Return `op(A)*b`, where `op` is determined by [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -""" -function trmv end - -""" - trmv!(ul, tA, dA, A, b) - -Return `op(A)*b`, where `op` is determined by [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -The multiplication occurs in-place on `b`. -""" -function trmv! end - -for (fname, elty) in ((:dtrmv_,:Float64), - (:strmv_,:Float32), - (:ztrmv_,:ComplexF64), - (:ctrmv_,:ComplexF32)) - @eval begin - # SUBROUTINE DTRMV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) - # * .. Scalar Arguments .. - # INTEGER INCX,LDA,N - # CHARACTER DIAG,TRANS,UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*) - function trmv!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x) - n = checksquare(A) - if n != length(x) - throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) - end - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong, Clong), - uplo, trans, diag, n, - A, max(1,stride(A,2)), px, stx, 1, 1, 1) - x - end - function trmv(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - trmv!(uplo, trans, diag, A, copy(x)) - end - end -end - -### trsv, Triangular matrix-vector solve - -""" - trsv!(ul, tA, dA, A, b) - -Overwrite `b` with the solution to `A*x = b` or one of the other two variants determined by -[`tA`](@ref stdlib-blas-trans) and [`ul`](@ref stdlib-blas-uplo). -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -Return the updated `b`. -""" -function trsv! end - -""" - trsv(ul, tA, dA, A, b) - -Return the solution to `A*x = b` or one of the other two variants determined by -[`tA`](@ref stdlib-blas-trans) and [`ul`](@ref stdlib-blas-uplo). -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -""" -function trsv end - -for (fname, elty) in ((:dtrsv_,:Float64), - (:strsv_,:Float32), - (:ztrsv_,:ComplexF64), - (:ctrsv_,:ComplexF32)) - @eval begin - # SUBROUTINE DTRSV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) - # .. Scalar Arguments .. - # INTEGER INCX,LDA,N - # CHARACTER DIAG,TRANS,UPLO - # .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*) - function trsv!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x) - n = checksquare(A) - if n != length(x) - throw(DimensionMismatch(lazy"size of A is $n != length(x) = $(length(x))")) - end - chkstride1(A) - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong, Clong), - uplo, trans, diag, n, - A, max(1,stride(A,2)), px, stx, 1, 1, 1) - x - end - function trsv(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) - trsv!(uplo, trans, diag, A, copy(x)) - end - end -end - -### ger - -""" - ger!(alpha, x, y, A) - -Rank-1 update of the matrix `A` with vectors `x` and `y` as `alpha*x*y' + A`. -""" -function ger! end - -for (fname, elty) in ((:dger_,:Float64), - (:sger_,:Float32), - (:zgerc_,:ComplexF64), - (:cgerc_,:ComplexF32)) - @eval begin - function ger!(α::$elty, x::AbstractVector{$elty}, y::AbstractVector{$elty}, A::AbstractMatrix{$elty}) - require_one_based_indexing(A, x, y) - m, n = size(A) - if m != length(x) || n != length(y) - throw(DimensionMismatch(lazy"A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) - end - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}), - m, n, α, px, stx, py, sty, A, max(1,stride(A,2))) - A - end - end -end - -### geru - -""" - geru!(alpha, x, y, A) - -Rank-1 update of the matrix `A` with vectors `x` and `y` as `alpha*x*transpose(y) + A`. -""" -function geru! end - -for (fname, elty) in ((:zgeru_,:ComplexF64), (:cgeru_,:ComplexF32)) - @eval begin - function geru!(α::$elty, x::AbstractVector{$elty}, y::AbstractVector{$elty}, A::AbstractMatrix{$elty}) - require_one_based_indexing(A, x, y) - m, n = size(A) - if m != length(x) || n != length(y) - throw(DimensionMismatch(lazy"A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) - end - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - py, sty = vec_pointer_stride(y, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}), - m, n, α, px, stx, py, sty, A, max(1,stride(A,2))) - A - end - end -end -for elty in (:Float64, :Float32) - @eval begin - geru!(α::$elty, x::AbstractVector{$elty}, y::AbstractVector{$elty}, A::AbstractMatrix{$elty}) = - ger!(α, x, y, A) - end -end - -### syr - -""" - syr!(uplo, alpha, x, A) - -Rank-1 update of the symmetric matrix `A` with vector `x` as `alpha*x*transpose(x) + A`. -[`uplo`](@ref stdlib-blas-uplo) controls which triangle of `A` is updated. Returns `A`. -""" -function syr! end - -for (fname, elty, lib) in ((:dsyr_,:Float64,libblastrampoline), - (:ssyr_,:Float32,libblastrampoline), - (:zsyr_,:ComplexF64,libblastrampoline), - (:csyr_,:ComplexF32,libblastrampoline)) - @eval begin - function syr!(uplo::AbstractChar, α::$elty, x::AbstractVector{$elty}, A::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x) - n = checksquare(A) - if length(x) != n - throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) - end - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x ccall((@blasfunc($fname), $lib), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), - uplo, n, α, px, stx, A, max(1,stride(A, 2))) - A - end - end -end - -### her - -""" - her!(uplo, alpha, x, A) - -Methods for complex arrays only. Rank-1 update of the Hermitian matrix `A` with vector `x` -as `alpha*x*x' + A`. -[`uplo`](@ref stdlib-blas-uplo) controls which triangle of `A` is updated. Returns `A`. -""" -function her! end - -for (fname, elty, relty) in ((:zher_,:ComplexF64, :Float64), - (:cher_,:ComplexF32, :Float32)) - @eval begin - function her!(uplo::AbstractChar, α::$relty, x::AbstractVector{$elty}, A::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, x) - n = checksquare(A) - if length(x) != n - throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) - end - px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) - GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, α, px, stx, A, max(1,stride(A,2)), 1) - A - end - end -end - -# Level 3 -## (GE) general matrix-matrix multiplication - -""" - gemmt!(uplo, tA, tB, alpha, A, B, beta, C) - -Update the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `C` as -`alpha*A*B + beta*C` or the other variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. -Return the updated `C`. - -!!! compat "Julia 1.11" - `gemmt!` requires at least Julia 1.11. -""" -function gemmt! end - -for (gemmt, elty) in - ((:dgemmt_,:Float64), - (:sgemmt_,:Float32), - (:zgemmt_,:ComplexF64), - (:cgemmt_,:ComplexF32)) - @eval begin - # SUBROUTINE DGEMMT(UPLO,TRANSA,TRANSB,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER K,LDA,LDB,LDC,N - # CHARACTER UPLO,TRANSA,TRANSB - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function gemmt!(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, - alpha::Union{($elty), Bool}, - A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, - beta::Union{($elty), Bool}, - C::AbstractVecOrMat{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B, C) - m = size(A, transA == 'N' ? 1 : 2) - ka = size(A, transA == 'N' ? 2 : 1) - kb = size(B, transB == 'N' ? 1 : 2) - n = size(B, transB == 'N' ? 2 : 1) - if ka != kb || m != n || m != size(C,1) || n != size(C,2) - throw(DimensionMismatch(lazy"A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) - end - chkstride1(A) - chkstride1(B) - chkstride1(C) - ccall((@blasfunc($gemmt), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Clong, Clong, Clong), - uplo, transA, transB, n, - ka, alpha, A, max(1,stride(A,2)), - B, max(1,stride(B,2)), beta, C, - max(1,stride(C,2)), 1, 1, 1) - C - end - function gemmt(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - gemmt!(uplo, transA, transB, alpha, A, B, zero($elty), similar(B, $elty, (size(A, transA == 'N' ? 1 : 2), size(B, transB == 'N' ? 2 : 1)))) - end - function gemmt(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - gemmt(uplo, transA, transB, one($elty), A, B) - end - end -end - -""" - gemmt(uplo, tA, tB, alpha, A, B) - -Return the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. - -!!! compat "Julia 1.11" - `gemmt` requires at least Julia 1.11. -""" -gemmt(uplo, tA, tB, alpha, A, B) - -""" - gemmt(uplo, tA, tB, A, B) - -Return the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. - -!!! compat "Julia 1.11" - `gemmt` requires at least Julia 1.11. -""" -gemmt(uplo, tA, tB, A, B) - -""" - gemm!(tA, tB, alpha, A, B, beta, C) - -Update `C` as `alpha*A*B + beta*C` or the other three variants according to -[`tA`](@ref stdlib-blas-trans) and `tB`. Return the updated `C`. -""" -function gemm! end - -for (gemm, elty) in - ((:dgemm_,:Float64), - (:sgemm_,:Float32), - (:zgemm_,:ComplexF64), - (:cgemm_,:ComplexF32)) - @eval begin - # SUBROUTINE DGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER K,LDA,LDB,LDC,M,N - # CHARACTER TRANSA,TRANSB - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function gemm!(transA::AbstractChar, transB::AbstractChar, - alpha::Union{($elty), Bool}, - A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, - beta::Union{($elty), Bool}, - C::AbstractVecOrMat{$elty}) -# if any([stride(A,1), stride(B,1), stride(C,1)] .!= 1) -# error("gemm!: BLAS module requires contiguous matrix columns") -# end # should this be checked on every call? - require_one_based_indexing(A, B, C) - m = size(A, transA == 'N' ? 1 : 2) - ka = size(A, transA == 'N' ? 2 : 1) - kb = size(B, transB == 'N' ? 1 : 2) - n = size(B, transB == 'N' ? 2 : 1) - if ka != kb || m != size(C,1) || n != size(C,2) - throw(DimensionMismatch(lazy"A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) - end - chkstride1(A) - chkstride1(B) - chkstride1(C) - ccall((@blasfunc($gemm), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Clong, Clong), - transA, transB, m, n, - ka, alpha, A, max(1,stride(A,2)), - B, max(1,stride(B,2)), beta, C, - max(1,stride(C,2)), 1, 1) - C - end - function gemm(transA::AbstractChar, transB::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - gemm!(transA, transB, alpha, A, B, zero($elty), similar(B, $elty, (size(A, transA == 'N' ? 1 : 2), size(B, transB == 'N' ? 2 : 1)))) - end - function gemm(transA::AbstractChar, transB::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - gemm(transA, transB, one($elty), A, B) - end - end -end - -""" - gemm(tA, tB, alpha, A, B) - -Return `alpha*A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. -""" -gemm(tA, tB, alpha, A, B) - -""" - gemm(tA, tB, A, B) - -Return `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. -""" -gemm(tA, tB, A, B) - - -## (SY) symmetric matrix-matrix and matrix-vector multiplication -for (mfname, elty) in ((:dsymm_,:Float64), - (:ssymm_,:Float32), - (:zsymm_,:ComplexF64), - (:csymm_,:ComplexF32)) - @eval begin - # SUBROUTINE DSYMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER LDA,LDB,LDC,M,N - # CHARACTER SIDE,UPLO - # .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function symm!(side::AbstractChar, uplo::AbstractChar, alpha::Union{($elty), Bool}, - A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, - beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B, C) - m, n = size(C) - j = checksquare(A) - M, N = size(B) - if side == 'L' - if j != m - throw(DimensionMismatch(lazy"A has first dimension $j but needs to match first dimension of C, $m")) - end - if N != n - throw(DimensionMismatch(lazy"B has second dimension $N but needs to match second dimension of C, $n")) - end - if j != M - throw(DimensionMismatch(lazy"A has second dimension $j but needs to match first dimension of B, $M")) - end - else - if j != n - throw(DimensionMismatch(lazy"B has second dimension $j but needs to match second dimension of C, $n")) - end - if N != j - throw(DimensionMismatch(lazy"A has second dimension $N but needs to match first dimension of B, $j")) - end - if M != m - throw(DimensionMismatch(lazy"A has first dimension $M but needs to match first dimension of C, $m")) - end - end - chkstride1(A) - chkstride1(B) - chkstride1(C) - ccall((@blasfunc($mfname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong), - side, uplo, m, n, - alpha, A, max(1,stride(A,2)), B, - max(1,stride(B,2)), beta, C, max(1,stride(C,2)), - 1, 1) - C - end - function symm(side::AbstractChar, uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - symm!(side, uplo, alpha, A, B, zero($elty), similar(B)) - end - function symm(side::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - symm(side, uplo, one($elty), A, B) - end - end -end - -""" - symm(side, ul, alpha, A, B) - -Return `alpha*A*B` or `alpha*B*A` according to [`side`](@ref stdlib-blas-side). -`A` is assumed to be symmetric. Only -the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -symm(side, ul, alpha, A, B) - -""" - symm(side, ul, A, B) - -Return `A*B` or `B*A` according to [`side`](@ref stdlib-blas-side). -`A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) -triangle of `A` is used. -""" -symm(side, ul, A, B) - -""" - symm!(side, ul, alpha, A, B, beta, C) - -Update `C` as `alpha*A*B + beta*C` or `alpha*B*A + beta*C` according to [`side`](@ref stdlib-blas-side). -`A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of -`A` is used. Return the updated `C`. -""" -symm! - -## (HE) Hermitian matrix-matrix and matrix-vector multiplication -for (mfname, elty) in ((:zhemm_,:ComplexF64), - (:chemm_,:ComplexF32)) - @eval begin - # SUBROUTINE DHEMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER LDA,LDB,LDC,M,N - # CHARACTER SIDE,UPLO - # .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function hemm!(side::AbstractChar, uplo::AbstractChar, alpha::Union{($elty), Bool}, - A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, - beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B, C) - m, n = size(C) - j = checksquare(A) - M, N = size(B) - if side == 'L' - if j != m - throw(DimensionMismatch(lazy"A has first dimension $j but needs to match first dimension of C, $m")) - end - if N != n - throw(DimensionMismatch(lazy"B has second dimension $N but needs to match second dimension of C, $n")) - end - if j != M - throw(DimensionMismatch(lazy"A has second dimension $j but needs to match first dimension of B, $M")) - end - else - if j != n - throw(DimensionMismatch(lazy"B has second dimension $j but needs to match second dimension of C, $n")) - end - if N != j - throw(DimensionMismatch(lazy"A has second dimension $N but needs to match first dimension of B, $j")) - end - if M != m - throw(DimensionMismatch(lazy"A has first dimension $M but needs to match first dimension of C, $m")) - end - end - chkstride1(A) - chkstride1(B) - chkstride1(C) - ccall((@blasfunc($mfname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong), - side, uplo, m, n, - alpha, A, max(1,stride(A,2)), B, - max(1,stride(B,2)), beta, C, max(1,stride(C,2)), - 1, 1) - C - end - function hemm(side::AbstractChar, uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - hemm!(side, uplo, alpha, A, B, zero($elty), similar(B)) - end - function hemm(side::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - hemm(side, uplo, one($elty), A, B) - end - end -end - -""" - hemm(side, ul, alpha, A, B) - -Return `alpha*A*B` or `alpha*B*A` according to [`side`](@ref stdlib-blas-side). -`A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle -of `A` is used. -""" -hemm(side, ul, alpha, A, B) - -""" - hemm(side, ul, A, B) - -Return `A*B` or `B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed -to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -""" -hemm(side, ul, A, B) - -""" - hemm!(side, ul, alpha, A, B, beta, C) - -Update `C` as `alpha*A*B + beta*C` or `alpha*B*A + beta*C` according to -[`side`](@ref stdlib-blas-side). `A` is assumed to be Hermitian. Only the -[`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. Return the updated `C`. -""" -hemm! - -## syrk - -""" - syrk!(uplo, trans, alpha, A, beta, C) - -Rank-k update of the symmetric matrix `C` as `alpha*A*transpose(A) + beta*C` or -`alpha*transpose(A)*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Return `C`. -""" -function syrk! end - -""" - syrk(uplo, trans, alpha, A) - -Return either the upper triangle or the lower triangle of `A`, -according to [`uplo`](@ref stdlib-blas-uplo), -of `alpha*A*transpose(A)` or `alpha*transpose(A)*A`, -according to [`trans`](@ref stdlib-blas-trans). -""" -function syrk end - -for (fname, elty) in ((:dsyrk_,:Float64), - (:ssyrk_,:Float32), - (:zsyrk_,:ComplexF64), - (:csyrk_,:ComplexF32)) - @eval begin - # SUBROUTINE DSYRK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) - # * .. Scalar Arguments .. - # REAL ALPHA,BETA - # INTEGER K,LDA,LDC,N - # CHARACTER TRANS,UPLO - # * .. Array Arguments .. - # REAL A(LDA,*),C(LDC,*) - function syrk!(uplo::AbstractChar, trans::AbstractChar, - alpha::Union{($elty), Bool}, A::AbstractVecOrMat{$elty}, - beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, C) - n = checksquare(C) - nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end - k = size(A, trans == 'N' ? 2 : 1) - chkstride1(A) - chkstride1(C) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - uplo, trans, n, k, - alpha, A, max(1,stride(A,2)), beta, - C, max(1,stride(C,2)), 1, 1) - C - end - end -end -function syrk(uplo::AbstractChar, trans::AbstractChar, alpha::Number, A::AbstractVecOrMat) - T = eltype(A) - n = size(A, trans == 'N' ? 1 : 2) - syrk!(uplo, trans, convert(T,alpha), A, zero(T), similar(A, T, (n, n))) -end -syrk(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat) = syrk(uplo, trans, one(eltype(A)), A) - -""" - herk!(uplo, trans, alpha, A, beta, C) - -Methods for complex arrays only. Rank-k update of the Hermitian matrix `C` as -`alpha*A*A' + beta*C` or `alpha*A'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is updated. Returns `C`. -""" -function herk! end - -""" - herk(uplo, trans, alpha, A) - -Methods for complex arrays only. Returns the [`uplo`](@ref stdlib-blas-uplo) -triangle of `alpha*A*A'` or `alpha*A'*A`, according to [`trans`](@ref stdlib-blas-trans). -""" -function herk end - -for (fname, elty, relty) in ((:zherk_, :ComplexF64, :Float64), - (:cherk_, :ComplexF32, :Float32)) - @eval begin - # SUBROUTINE CHERK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) - # * .. Scalar Arguments .. - # REAL ALPHA,BETA - # INTEGER K,LDA,LDC,N - # CHARACTER TRANS,UPLO - # * .. - # * .. Array Arguments .. - # COMPLEX A(LDA,*),C(LDC,*) - function herk!(uplo::AbstractChar, trans::AbstractChar, - α::Union{$relty, Bool}, A::AbstractVecOrMat{$elty}, - β::Union{$relty, Bool}, C::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, C) - n = checksquare(C) - nn = size(A, trans == 'N' ? 1 : 2) - if nn != n - throw(DimensionMismatch(lazy"the matrix to update has dimension $n but the implied dimension of the update is $(size(A, trans == 'N' ? 1 : 2))")) - end - chkstride1(A) - chkstride1(C) - k = size(A, trans == 'N' ? 2 : 1) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, - Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - uplo, trans, n, k, - α, A, max(1,stride(A,2)), β, - C, max(1,stride(C,2)), 1, 1) - C - end - function herk(uplo::AbstractChar, trans::AbstractChar, α::$relty, A::AbstractVecOrMat{$elty}) - n = size(A, trans == 'N' ? 1 : 2) - herk!(uplo, trans, α, A, zero($relty), similar(A, (n,n))) - end - herk(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat{$elty}) = herk(uplo, trans, one($relty), A) - end -end - -## syr2k -for (fname, elty) in ((:dsyr2k_,:Float64), - (:ssyr2k_,:Float32), - (:zsyr2k_,:ComplexF64), - (:csyr2k_,:ComplexF32)) - @eval begin - # SUBROUTINE DSYR2K(UPLO,TRANS,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # - # .. Scalar Arguments .. - # REAL PRECISION ALPHA,BETA - # INTEGER K,LDA,LDB,LDC,N - # CHARACTER TRANS,UPLO - # .. - # .. Array Arguments .. - # REAL PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function syr2k!(uplo::AbstractChar, trans::AbstractChar, - alpha::($elty), A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, - beta::($elty), C::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B, C) - n = checksquare(C) - nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end - k = size(A, trans == 'N' ? 2 : 1) - chkstride1(A) - chkstride1(B) - chkstride1(C) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - uplo, trans, n, k, - alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, - C, max(1,stride(C,2)), 1, 1) - C - end - end -end - -""" - syr2k!(uplo, trans, alpha, A, B, beta, C) - -Rank-2k update of the symmetric matrix `C` as -`alpha*A*transpose(B) + alpha*B*transpose(A) + beta*C` or -`alpha*transpose(A)*B + alpha*transpose(B)*A + beta*C` -according to [`trans`](@ref stdlib-blas-trans). -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Returns `C`. -""" -function syr2k! end - -""" - syr2k(uplo, trans, alpha, A, B) - -Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of -`alpha*A*transpose(B) + alpha*B*transpose(A)` or -`alpha*transpose(A)*B + alpha*transpose(B)*A`, -according to [`trans`](@ref stdlib-blas-trans). -""" -function syr2k(uplo::AbstractChar, trans::AbstractChar, alpha::Number, A::AbstractVecOrMat, B::AbstractVecOrMat) - T = eltype(A) - n = size(A, trans == 'N' ? 1 : 2) - syr2k!(uplo, trans, convert(T,alpha), A, B, zero(T), similar(A, T, (n, n))) -end -""" - syr2k(uplo, trans, A, B) - -Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*transpose(B) + B*transpose(A)` -or `transpose(A)*B + transpose(B)*A`, according to [`trans`](@ref stdlib-blas-trans). -""" -syr2k(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat, B::AbstractVecOrMat) = syr2k(uplo, trans, one(eltype(A)), A, B) - -for (fname, elty1, elty2) in ((:zher2k_,:ComplexF64,:Float64), (:cher2k_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE CHER2K(UPLO,TRANS,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # - # .. Scalar Arguments .. - # COMPLEX ALPHA - # REAL BETA - # INTEGER K,LDA,LDB,LDC,N - # CHARACTER TRANS,UPLO - # .. - # .. Array Arguments .. - # COMPLEX A(LDA,*),B(LDB,*),C(LDC,*) - function her2k!(uplo::AbstractChar, trans::AbstractChar, alpha::($elty1), - A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}, - beta::($elty2), C::AbstractMatrix{$elty1}) - chkuplo(uplo) - require_one_based_indexing(A, B, C) - n = checksquare(C) - nn = size(A, trans == 'N' ? 1 : 2) - if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end - chkstride1(A) - chkstride1(B) - chkstride1(C) - k = size(A, trans == 'N' ? 2 : 1) - ccall((@blasfunc($fname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty1}, Ptr{$elty1}, Ref{BlasInt}, Ptr{$elty1}, Ref{BlasInt}, - Ref{$elty2}, Ptr{$elty1}, Ref{BlasInt}, Clong, Clong), - uplo, trans, n, k, - alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), - beta, C, max(1,stride(C,2)), 1, 1) - C - end - function her2k(uplo::AbstractChar, trans::AbstractChar, alpha::($elty1), A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}) - n = size(A, trans == 'N' ? 1 : 2) - her2k!(uplo, trans, alpha, A, B, zero($elty2), similar(A, $elty1, (n,n))) - end - her2k(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}) = - her2k(uplo, trans, one($elty1), A, B) - end -end - -""" - her2k!(uplo, trans, alpha, A, B, beta, C) - -Rank-2k update of the Hermitian matrix `C` as -`alpha*A*B' + alpha*B*A' + beta*C` or `alpha*A'*B + alpha*B'*A + beta*C` -according to [`trans`](@ref stdlib-blas-trans). The scalar `beta` has to be real. -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Return `C`. -""" -function her2k! end - -""" - her2k(uplo, trans, alpha, A, B) - -Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*B' + alpha*B*A'` -or `alpha*A'*B + alpha*B'*A`, according to [`trans`](@ref stdlib-blas-trans). -""" -her2k(uplo, trans, alpha, A, B) - -""" - her2k(uplo, trans, A, B) - -Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*B' + B*A'` -or `A'*B + B'*A`, according to [`trans`](@ref stdlib-blas-trans). -""" -her2k(uplo, trans, A, B) - -## (TR) Triangular matrix and vector multiplication and solution - -""" - trmm!(side, ul, tA, dA, alpha, A, B) - -Update `B` as `alpha*A*B` or one of the other three variants determined by -[`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -Return the updated `B`. -""" -function trmm! end - -""" - trmm(side, ul, tA, dA, alpha, A, B) - -Return `alpha*A*B` or one of the other three variants determined by -[`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -""" -function trmm end - -""" - trsm!(side, ul, tA, dA, alpha, A, B) - -Overwrite `B` with the solution to `A*X = alpha*B` or one of the other three variants -determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -Returns the updated `B`. -""" -function trsm! end - -""" - trsm(side, ul, tA, dA, alpha, A, B) - -Return the solution to `A*X = alpha*B` or one of the other three variants determined by -determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). -Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. -[`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or -are assumed to be all ones. -""" -function trsm end - -for (mmname, smname, elty) in - ((:dtrmm_,:dtrsm_,:Float64), - (:strmm_,:strsm_,:Float32), - (:ztrmm_,:ztrsm_,:ComplexF64), - (:ctrmm_,:ctrsm_,:ComplexF32)) - @eval begin - # SUBROUTINE DTRMM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA - # INTEGER LDA,LDB,M,N - # CHARACTER DIAG,SIDE,TRANSA,UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*) - function trmm!(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::Number, - A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B) - m, n = size(B) - nA = checksquare(A) - if nA != (side == 'L' ? m : n) - throw(DimensionMismatch(lazy"size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) - end - chkstride1(A) - chkstride1(B) - ccall((@blasfunc($mmname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong, Clong, Clong), - side, uplo, transa, diag, m, n, - alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), - 1, 1, 1, 1) - B - end - function trmm(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, - alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - trmm!(side, uplo, transa, diag, alpha, A, copy(B)) - end - # SUBROUTINE DTRSM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA - # INTEGER LDA,LDB,M,N - # CHARACTER DIAG,SIDE,TRANSA,UPLO - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*) - function trsm!(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, - alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - chkuplo(uplo) - require_one_based_indexing(A, B) - m, n = size(B) - k = checksquare(A) - if k != (side == 'L' ? m : n) - throw(DimensionMismatch(lazy"size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) - end - chkstride1(A) - chkstride1(B) - ccall((@blasfunc($smname), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, - Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Clong, Clong, Clong, Clong), - side, uplo, transa, diag, - m, n, alpha, A, - max(1,stride(A,2)), B, max(1,stride(B,2)), - 1, 1, 1, 1) - B - end - function trsm(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - trsm!(side, uplo, transa, diag, alpha, A, copy(B)) - end - end -end - -end # module - -function copyto!(dest::Array{T}, rdest::AbstractRange{Ti}, - src::Array{T}, rsrc::AbstractRange{Ti}) where {T<:BlasFloat,Ti<:Integer} - if minimum(rdest) < 1 || maximum(rdest) > length(dest) - throw(ArgumentError(lazy"range out of bounds for dest, of length $(length(dest))")) - end - if minimum(rsrc) < 1 || maximum(rsrc) > length(src) - throw(ArgumentError(lazy"range out of bounds for src, of length $(length(src))")) - end - if length(rdest) != length(rsrc) - throw(DimensionMismatch(lazy"ranges must be of the same length")) - end - GC.@preserve src dest BLAS.blascopy!( - length(rsrc), - pointer(src, minimum(rsrc)), - step(rsrc), - pointer(dest, minimum(rdest)), - step(rdest)) - - return dest -end diff --git a/stdlib/LinearAlgebra/src/bunchkaufman.jl b/stdlib/LinearAlgebra/src/bunchkaufman.jl deleted file mode 100644 index a44f1a1c99094..0000000000000 --- a/stdlib/LinearAlgebra/src/bunchkaufman.jl +++ /dev/null @@ -1,1601 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## Create an extractor that extracts the modified original matrix, e.g. -## LD for BunchKaufman, UL for CholeskyDense, LU for LUDense and -## define size methods for Factorization types using it. - -##----------- Type utilities for generic Bunch-Kaufman implementation ------------ -# Generic real type. Any real number type should able to approximate -# real numbers, and thus be closed under arithmetic operations. -# Therefore so Int, Complex{Int}, etc. are excluded. -ClosedReal = T where T <: Union{AbstractFloat, Rational} -# Similarly, we also use a closed scalar type -ClosedScalar = Union{T, Complex{T}} where T <: ClosedReal -##-------------------------------------------------------------------------------- - -""" - BunchKaufman <: Factorization - -Matrix factorization type of the Bunch-Kaufman factorization of a symmetric or -Hermitian matrix `A` as `P'UDU'P` or `P'LDL'P`, depending on whether the upper -(the default) or the lower triangle is stored in `A`. If `A` is complex symmetric -then `U'` and `L'` denote the unconjugated transposes, i.e. `transpose(U)` and -`transpose(L)`, respectively. This is the return type of [`bunchkaufman`](@ref), -the corresponding matrix factorization function. - -If `S::BunchKaufman` is the factorization object, the components can be obtained -via `S.D`, `S.U` or `S.L` as appropriate given `S.uplo`, and `S.p`. - -Iterating the decomposition produces the components `S.D`, `S.U` or `S.L` -as appropriate given `S.uplo`, and `S.p`. - -# Examples -```jldoctest -julia> A = Float64.([1 2; 2 3]) -2×2 Matrix{Float64}: - 1.0 2.0 - 2.0 3.0 - -julia> S = bunchkaufman(A) # A gets wrapped internally by Symmetric(A) -BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} -D factor: -2×2 Tridiagonal{Float64, Vector{Float64}}: - -0.333333 0.0 - 0.0 3.0 -U factor: -2×2 UnitUpperTriangular{Float64, Matrix{Float64}}: - 1.0 0.666667 - ⋅ 1.0 -permutation: -2-element Vector{Int64}: - 1 - 2 - -julia> d, u, p = S; # destructuring via iteration - -julia> d == S.D && u == S.U && p == S.p -true - -julia> S = bunchkaufman(Symmetric(A, :L)) -BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} -D factor: -2×2 Tridiagonal{Float64, Vector{Float64}}: - 3.0 0.0 - 0.0 -0.333333 -L factor: -2×2 UnitLowerTriangular{Float64, Matrix{Float64}}: - 1.0 ⋅ - 0.666667 1.0 -permutation: -2-element Vector{Int64}: - 2 - 1 -``` -""" -struct BunchKaufman{T,S<:AbstractMatrix,P<:AbstractVector{<:Integer}} <: Factorization{T} - LD::S - ipiv::P - uplo::Char - symmetric::Bool - rook::Bool - info::BlasInt - - function BunchKaufman{T,S,P}(LD, ipiv, uplo, symmetric, rook, info) where {T,S<:AbstractMatrix,P<:AbstractVector} - require_one_based_indexing(LD) - new{T,S,P}(LD, ipiv, uplo, symmetric, rook, info) - end -end -BunchKaufman(A::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, uplo::AbstractChar, - symmetric::Bool, rook::Bool, info::BlasInt) where {T} = - BunchKaufman{T,typeof(A),typeof(ipiv)}(A, ipiv, uplo, symmetric, rook, info) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(BunchKaufman{T,S}(LD, ipiv, uplo, symmetric, rook, info) where {T,S}, - BunchKaufman{T,S,typeof(ipiv)}(LD, ipiv, uplo, symmetric, rook, info), false) - -# iteration for destructuring into components -Base.iterate(S::BunchKaufman) = (S.D, Val(:UL)) -Base.iterate(S::BunchKaufman, ::Val{:UL}) = (S.uplo == 'L' ? S.L : S.U, Val(:p)) -Base.iterate(S::BunchKaufman, ::Val{:p}) = (S.p, Val(:done)) -Base.iterate(S::BunchKaufman, ::Val{:done}) = nothing -copy(S::BunchKaufman) = BunchKaufman(copy(S.LD), copy(S.ipiv), S.uplo, S.symmetric, S.rook, S.info) - -""" - bunchkaufman!(A, rook::Bool=false; check = true) -> BunchKaufman - -`bunchkaufman!` is the same as [`bunchkaufman`](@ref), but saves space by overwriting the -input `A`, instead of creating a copy. -""" -function bunchkaufman!(A::RealHermSymComplexSym{<:BlasReal,<:StridedMatrix}, - rook::Bool = false; check::Bool = true) - LD, ipiv, info = rook ? LAPACK.sytrf_rook!(A.uplo, A.data) : LAPACK.sytrf!(A.uplo, A.data) - check && checknonsingular(info) - BunchKaufman(LD, ipiv, A.uplo, true, rook, info) -end -function bunchkaufman!(A::Hermitian{<:BlasComplex,<:StridedMatrix}, - rook::Bool = false; check::Bool = true) - LD, ipiv, info = rook ? LAPACK.hetrf_rook!(A.uplo, A.data) : LAPACK.hetrf!(A.uplo, A.data) - check && checknonsingular(info) - BunchKaufman(LD, ipiv, A.uplo, false, rook, info) -end -function bunchkaufman!(A::StridedMatrix{<:BlasFloat}, rook::Bool = false; check::Bool = true) - if ishermitian(A) - return bunchkaufman!(Hermitian(A), rook; check = check) - elseif issymmetric(A) - return bunchkaufman!(Symmetric(A), rook; check = check) - else - throw(ArgumentError("Bunch-Kaufman decomposition is only valid for symmetric or Hermitian matrices")) - end -end - -bkcopy_oftype(A, S) = eigencopy_oftype(A, S) -bkcopy_oftype(A::Symmetric{<:Complex}, S) = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) - -""" - bunchkaufman(A, rook::Bool=false; check = true) -> S::BunchKaufman - -Compute the Bunch-Kaufman [^Bunch1977] factorization of a symmetric or -Hermitian matrix `A` as `P'*U*D*U'*P` or `P'*L*D*L'*P`, depending on -which triangle is stored in `A`, and return a [`BunchKaufman`](@ref) object. -Note that if `A` is complex symmetric then `U'` and `L'` denote -the unconjugated transposes, i.e. `transpose(U)` and `transpose(L)`. - -Iterating the decomposition produces the components `S.D`, `S.U` or `S.L` -as appropriate given `S.uplo`, and `S.p`. - -If `rook` is `true`, rook pivoting is used. If `rook` is false, -rook pivoting is not used. - -When `check = true`, an error is thrown if the decomposition fails. -When `check = false`, responsibility for checking the decomposition's -validity (via [`issuccess`](@ref)) lies with the user. - -The following functions are available for `BunchKaufman` objects: -[`size`](@ref), `\\`, [`inv`](@ref), [`issymmetric`](@ref), -[`ishermitian`](@ref), [`getindex`](@ref). - -[^Bunch1977]: J R Bunch and L Kaufman, Some stable methods for calculating inertia and solving symmetric linear systems, Mathematics of Computation 31:137 (1977), 163-179. [url](https://www.ams.org/journals/mcom/1977-31-137/S0025-5718-1977-0428694-0/). - -# Examples -```jldoctest -julia> A = Float64.([1 2; 2 3]) -2×2 Matrix{Float64}: - 1.0 2.0 - 2.0 3.0 - -julia> S = bunchkaufman(A) # A gets wrapped internally by Symmetric(A) -BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} -D factor: -2×2 Tridiagonal{Float64, Vector{Float64}}: - -0.333333 0.0 - 0.0 3.0 -U factor: -2×2 UnitUpperTriangular{Float64, Matrix{Float64}}: - 1.0 0.666667 - ⋅ 1.0 -permutation: -2-element Vector{Int64}: - 1 - 2 - -julia> d, u, p = S; # destructuring via iteration - -julia> d == S.D && u == S.U && p == S.p -true - -julia> S.U*S.D*S.U' - S.P*A*S.P' -2×2 Matrix{Float64}: - 0.0 0.0 - 0.0 0.0 - -julia> S = bunchkaufman(Symmetric(A, :L)) -BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} -D factor: -2×2 Tridiagonal{Float64, Vector{Float64}}: - 3.0 0.0 - 0.0 -0.333333 -L factor: -2×2 UnitLowerTriangular{Float64, Matrix{Float64}}: - 1.0 ⋅ - 0.666667 1.0 -permutation: -2-element Vector{Int64}: - 2 - 1 - -julia> S.L*S.D*S.L' - A[S.p, S.p] -2×2 Matrix{Float64}: - 0.0 0.0 - 0.0 0.0 -``` -""" -bunchkaufman(A::AbstractMatrix{T}, rook::Bool=false; check::Bool = true) where {T} = - bunchkaufman!(bkcopy_oftype(A, typeof(sqrt(oneunit(T)))), rook; check = check) - -BunchKaufman{T}(B::BunchKaufman) where {T} = - BunchKaufman(convert(Matrix{T}, B.LD), B.ipiv, B.uplo, B.symmetric, B.rook, B.info) -Factorization{T}(B::BunchKaufman) where {T} = BunchKaufman{T}(B) - -size(B::BunchKaufman) = size(getfield(B, :LD)) -size(B::BunchKaufman, d::Integer) = size(getfield(B, :LD), d) -issymmetric(B::BunchKaufman) = B.symmetric -ishermitian(B::BunchKaufman{T}) where T = T<:Real || !B.symmetric - -function _ipiv2perm_bk(v::AbstractVector{T}, maxi::Integer, uplo::AbstractChar, rook::Bool) where T - require_one_based_indexing(v) - p = T[1:maxi;] - uploL = uplo == 'L' - i = uploL ? 1 : maxi - # if uplo == 'U' we construct the permutation backwards - @inbounds while 1 <= i <= length(v) - vi = v[i] - if vi > 0 # the 1x1 blocks - p[i], p[vi] = p[vi], p[i] - i += uploL ? 1 : -1 - else # the 2x2 blocks - if rook - p[i], p[-vi] = p[-vi], p[i] - end - if uploL - vp = rook ? -v[i+1] : -vi - p[i + 1], p[vp] = p[vp], p[i + 1] - i += 2 - else # 'U' - vp = rook ? -v[i-1] : -vi - p[i - 1], p[vp] = p[vp], p[i - 1] - i -= 2 - end - end - end - return p -end - -function getproperty(B::BunchKaufman{TS}, - d::Symbol) where TS <: ClosedScalar{TR} where TR <: ClosedReal - n = size(B, 1) - if d === :p - return _ipiv2perm_bk(getfield(B, :ipiv), n, getfield(B, :uplo), B.rook) - elseif d === :P - return Matrix{TS}(I, n, n)[:,invperm(B.p)] - elseif d === :L || d === :U || d === :D - if d === :D - _, od, md = generic_syconv(B, false) - elseif typeof(B) <: BunchKaufman{T,<:StridedMatrix} where {T<:BlasFloat} - # We use LAPACK whenever we can - if getfield(B, :rook) - LUD, _ = LAPACK.syconvf_rook!(getfield(B, :uplo), 'C', - copy(getfield(B, :LD)), getfield(B, :ipiv)) - else - LUD, _ = LAPACK.syconv!(getfield(B, :uplo), copy(getfield(B, :LD)), - getfield(B, :ipiv)) - end - else - LUD, _ = generic_syconv(B) - end - if d === :D - if getfield(B, :uplo) == 'L' - odl = od[1:n - 1] - return Tridiagonal(odl, md, getfield(B, :symmetric) ? odl : conj.(odl)) - else # 'U' - odu = od[2:n] - return Tridiagonal(getfield(B, :symmetric) ? odu : conj.(odu), md, odu) - end - elseif d === :L - if getfield(B, :uplo) == 'L' - return UnitLowerTriangular(LUD) - else - throw(ArgumentError("factorization is U*D*U' but you requested L")) - end - else # :U - if B.uplo == 'U' - return UnitUpperTriangular(LUD) - else - throw(ArgumentError("factorization is L*D*L' but you requested U")) - end - end - else - getfield(B, d) - end -end - -Base.propertynames(B::BunchKaufman, private::Bool=false) = - (:p, :P, :L, :U, :D, (private ? fieldnames(typeof(B)) : ())...) - -function Base.:(==)(B1::BunchKaufman, B2::BunchKaufman) - # check for the equality between properties instead of fields - B1.p == B2.p || return false - if B1.uplo == 'L' - B1.L == B2.L || return false - else - B1.U == B2.U || return false - end - return (B1.D == B2.D) -end - -function getproperties!(B::BunchKaufman{T,<:StridedMatrix}) where {T<:BlasFloat} - # NOTE: Unlike in the 'getproperty' function, in this function L/U and D are computed in place. - if B.rook - LUD, od = LAPACK.syconvf_rook!(B.uplo, 'C', B.LD, B.ipiv) - else - LUD, od = LAPACK.syconv!(B.uplo, B.LD, B.ipiv) - end - if B.uplo == 'U' - M = UnitUpperTriangular(LUD) - du = od[2:end] - # Avoid aliasing dl and du. - dl = B.symmetric ? du : conj.(du) - else - M = UnitLowerTriangular(LUD) - dl = od[1:end-1] - # Avoid aliasing dl and du. - du = B.symmetric ? dl : conj.(dl) - end - return (M, Tridiagonal(dl, diag(LUD), du), B.p) -end - -issuccess(B::BunchKaufman) = B.info == 0 - -function adjoint(B::BunchKaufman) - if ishermitian(B) - return B - else - throw(ArgumentError("adjoint not implemented for complex symmetric matrices")) - end -end - -function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, B::BunchKaufman) - if issuccess(B) - summary(io, B); println(io) - println(io, "D factor:") - show(io, mime, B.D) - println(io, "\n$(B.uplo) factor:") - show(io, mime, B.uplo == 'L' ? B.L : B.U) - println(io, "\npermutation:") - show(io, mime, B.p) - else - print(io, "Failed factorization of type $(typeof(B))") - end -end - -function inv(B::BunchKaufman{<:BlasReal,<:StridedMatrix}) - if B.rook - copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) - else - copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) - end -end - -function inv(B::BunchKaufman{<:BlasComplex,<:StridedMatrix}) - if issymmetric(B) - if B.rook - copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo) - else - copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo) - end - else - if B.rook - copytri!(LAPACK.hetri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) - else - copytri!(LAPACK.hetri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) - end - end -end - -function ldiv!(B::BunchKaufman{T,<:StridedMatrix}, R::StridedVecOrMat{T}) where {T<:BlasReal} - if B.rook - LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R) - else - LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R) - end -end -function ldiv!(B::BunchKaufman{T,<:StridedMatrix}, R::StridedVecOrMat{T}) where {T<:BlasComplex} - if B.rook - if issymmetric(B) - LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R) - else - LAPACK.hetrs_rook!(B.uplo, B.LD, B.ipiv, R) - end - else - if issymmetric(B) - LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R) - else - LAPACK.hetrs!(B.uplo, B.LD, B.ipiv, R) - end - end -end - -function logabsdet(F::BunchKaufman) - M = F.LD - p = F.ipiv - n = size(F.LD, 1) - - if !issuccess(F) - return eltype(F)(-Inf), zero(eltype(F)) - end - s = one(real(eltype(F))) - i = 1 - abs_det = zero(real(eltype(F))) - while i <= n - if p[i] > 0 - elm = M[i,i] - s *= sign(elm) - abs_det += log(abs(elm)) - i += 1 - else - # 2x2 pivot case. Make sure not to square before the subtraction by scaling - # with the off-diagonal element. This is safe because the off diagonal is - # always large for 2x2 pivots. - if F.uplo == 'U' - elm = M[i, i + 1]*(M[i,i]/M[i, i + 1]*M[i + 1, i + 1] - - (issymmetric(F) ? M[i, i + 1] : conj(M[i, i + 1]))) - s *= sign(elm) - abs_det += log(abs(elm)) - else - elm = M[i + 1,i]*(M[i, i]/M[i + 1, i]*M[i + 1, i + 1] - - (issymmetric(F) ? M[i + 1, i] : conj(M[i + 1, i]))) - s *= sign(elm) - abs_det += log(abs(elm)) - end - i += 2 - end - end - return abs_det, s -end - -## reconstruct the original matrix -## TODO: understand the procedure described at -## https://www.nag.com/numeric/FL/nagdoc_fl22/pdf/F07/f07mdf.pdf - - -##-------------------------------------------------------------------------- -##------------- Start of generic Bunch-Kaufman Implementation -------------- -##-------------------------------------------------------------------------- - -export inertia - -function arg_illegal(fun_name::AbstractString, - info::Integer, - waer::AbstractChar) - if waer == 'W' - @warn " ** On entry to '$(fun_name)' parameter number " * - "$(info) had an illegal value" - else - error(" ** On entry to '$(fun_name)' parameter number " * - "$(info) had an illegal value") - end -end - - -function cabs1(z::T) where T <: Complex - return abs(real(z)) + abs(imag(z)) -end - - -function cabsr(z::T) where T <: Complex - return abs(real(z)) -end - - -""" -generic_adr1!(uplo, alpha, x, y, A, syhe) -> nothing - -`generic_adr1!` performs the following adjoint (symmetric or Hermitian) -rank 1 operation - -`A[1:K,1:L] = alpha*x*y' + A[1:K,1:L]` - -in-place, where `alpha` is a scalar, `x` is a K element vector, `y` -is an L element vector and `A` is an `NxM` matrix. Note that `y'` can -denote either the transpose, i.e. `transpose(y)` or the conjugate -transpose , i.e. `adjoint(y)`. - -`uplo` is a character, either `'U'`, `'L'` or `'F'`, indicating whether -the matrix is stored in the upper triangular part (`uplo=='U'`), the -lower triangular part (`uplo=='L'`), or the full storage space is used -(`uplo=='F'`). If `uplo!='F'` then only the corresponding triangular -part is updated. The values `'U'` or `'L'` can only be used when A is -square (`N==M`). - -`syhe` is a character, either `'S'` or `'H'`, indicating whether the -symmetric adjoint (`syhe=='S'`, and `y'==transpose(y)`) or the hermitian -adjoint (`syhe=='H'`, and `y'==adjoint(y)`) must be used. -""" -function generic_adr1!(uplo::AbstractChar, - alpha::ClosedScalar{TR}, - x::AbstractVector{TS}, - y::AbstractVector{TS}, - A::AbstractMatrix{TS}, - syhe::AbstractChar - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Inputs must be 1-indexed; bounds may not be checked. - Base.require_one_based_indexing(x, A) - - # Check argument validity - K = length(x) - L = length(y) - N, M = size(A) - info = 0::BlasInt - if (uplo != 'U' && uplo != 'L' && uplo != 'F') || (uplo != 'F' && N != M) - info = (-1)::BlasInt - elseif K > N - info = (-3)::BlasInt - elseif L > M - info = (-4)::BlasInt - elseif syhe != 'S' && syhe != 'H' - info = (-6)::BlasInt - end - if info < 0 - arg_illegal("generic_sadr1!", -info, 'E') - end - - # Load the requested adjoining operator - adj_op = syhe == 'S' ? identity : conj - - # Define loop range function according to the type of storage - # TODO: can we adjust the range without anonymous functions, - # but without having to write the same code thrice? - i_range = uplo == 'F' ? _ -> (1:K) : uplo == 'U' ? j -> (1:min(j,K)) : j -> (j:K) - - # Compute rank update of A - for j in 1:L; @inbounds begin - if y[j] != 0 - temp = alpha * adj_op(y[j]) - for i in i_range(j) - A[i,j] += x[i] * temp - end - end - end; end - return -end - - -""" -generic_mvpv!(trans, alpha, A, x, beta, y) -> nothing - -`generic_mvpv!` performs the following matrix-vector operation: - -`y[1:K] = alpha*A'*x[1:L] + beta*y[1:K]` - -in-place, where `alpha` and `beta` are scalars, `x` is a vector with at -least L elements, `y` is a vector with at least K elements, and `A` is -an `NxM` matrix. `A'` can denote the transpose, i.e. `transpose(A)` or -the conjugate transpose, i.e. `adjoint(A)`, and then `M==K && N==L`. -`A'` can also denote no adjoining at all, i.e. `A'==A`, and then -`N==K && M==L`. - -`trans` is a character, either `'T'`, `'C'` or `'N'`, indicating whether -`A'=transpose(A)`, `A'=adjoint(A)` or `A'=A`, respectively. -""" -function generic_mvpv!(trans::AbstractChar, - alpha::ClosedScalar{TR}, - A::AbstractMatrix{TS}, - x::AbstractVector{TS}, - beta::ClosedScalar{TR}, - y::AbstractVector{TS}, - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Inputs must be 1-indexed; bounds may not be checked. - Base.require_one_based_indexing(A, x, y) - - # Check argument validity - M, N = size(A) - K = trans == 'N' ? M : N - L = trans == 'N' ? N : M - info = 0::BlasInt - if trans != 'T' && trans != 'C' && trans != 'N' - info = (-1)::BlasInt - elseif length(y) < K - info = (-3)::BlasInt - elseif length(x) < L - info = (-4)::BlasInt - end - if info < 0 - arg_illegal("generic_sadr1!", -info, 'E') - end - - # Quick return if possible. - if K == 0 || (alpha == 0 && beta == 1); return; end - - # Start the operations. In this version the elements of A are - # accessed sequentially with one pass through A. - # First form y := beta*y. - @inbounds begin - if beta != 1 - if beta == 0 - # Way less allocations and way faster for BigFloat. - # For Float64 there is some (acceptable IMO) performance loss. - y[1:K] .= 0 - else - for i in 1:K; y[i] *= beta; end - end - end - if alpha == 0 || L == 0; return; end - - if trans == 'N' - # Form y := alpha*A*x + y. - for j in 1:L - # Faster than a loop - axpy!(alpha*x[j], view(A, 1:K, j), view(y, 1:K)) - end - else - # Form y := alpha*A**T*x + y or y := alpha*A**H*x + y. - noconj = (trans == 'T') - for i = 1:K - temp = 0 - if noconj - for j = 1:L - temp = temp + A[j,i]*x[j] - end - else - for j = 1:L - temp = temp + conj(A[j,i])*x[j] - end - end - y[i] += alpha*temp - end - end - end - return -end - - -""" -bk_rowcol_swap!(A, k, kp, kstep, upper, herm) -> did_swap::Bool - -Performs the row and column interchange of the Bunch-Kaufman factorization. -If `upper==true` then the rows and columns `kp` of `A[1:k,1:k]` are -interchanged with either rows and columns `k` or `k-1` of `A[1:k,1:k]`, -depending on whether `kstep==1` or `kstep==2`, respectively. If -`upper==false` then the rows and columns `kp-k+1` of `A[k:N,k:N]` are -interchanged with either rows and columns `1` or `2` of `A[k:N,k:N]`, -depending on whether `kstep==1` or `kstep==2`, respectively. `herm=true` -then it is assumed that `A` is Hermitian, and conjugation is applied to -the appropriate entries of the interchanged rows and columns. If -`herm=false` no conjugation is performed. - -This is an internal helper function for the main Bunch-Kaufman -factorization function, `generic_bunchkaufman!`. As such, validity of the -input values is not verified. -""" -function bk_rowcol_swap!( - A::AbstractMatrix{TS}, - k::Integer, - kp::Integer, - kstep::Integer, - upper::Bool, - herm::Bool - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - kk = upper ? k - kstep + 1 : k + kstep - 1 - if kp != kk - if kp > 1 - thisview = upper ? view(A, 1:(kp-1), :) : view(A, (kp+1):size(A,1), :) - Base.swapcols!(thisview, kp, kk) - end - thisrange = upper ? ((kp+1):(kk-1)) : ((kk+1):(kp-1)) - if !herm - # Real/complex symmetric case - for j in thisrange - A[j,kk], A[kp,j] = A[kp,j], A[j,kk] - end - A[kk,kk], A[kp,kp] = A[kp,kp], A[kk,kk] - else - # Hermitian case - for j in thisrange - A[j,kk], A[kp,j] = conj(A[kp,j]), conj(A[j,kk]) - end - A[kp,kk] = conj(A[kp,kk]) - A[kk,kk], A[kp,kp] = real(A[kp,kp]), real(A[kk,kk]) - end - if kstep == 2 - if herm - # Force diagonal entry to be purely real - A[k,k] = real(A[k,k]) - end - if upper - A[k-1,k], A[kp,k] = A[kp,k], A[k-1,k] - else - A[k+1,k], A[kp,k] = A[kp,k], A[k+1,k] - end - end - return true - else - return false - end -end - - -""" -generic_bunchkaufman!(uplo, A, syhe, rook::Bool=false) -> -LD<:AbstractMatrix, ipiv<:AbstractVector{Integer}, info::BlasInt - -Computes the Bunch-Kaufman factorization of a symmetric or Hermitian -matrix `A` of size `NxN` as `P'*U*D*U'*P` or `P'*L*D*L'*P`, depending on -which triangle is stored in `A`. Note that if `A` is complex symmetric -then `U'` and `L'` denote the unconjugated transposes, i.e. -`transpose(U)` and `transpose(L)`. The resulting `U` or `L` and D are -stored in-place in `A`, LAPACK style. `LD` is just a reference to `A` -(that is, `LD===A`). `ipiv` stores the permutation information of the -algorithm in LAPACK format. `info` indicates whether the factorization -was successful and non-singular when `info==0`, or else `info` takes a -different value. The outputs `LD`, `ipiv`, `info` follow the format of -the LAPACK functions of the Bunch-Kaufman factorization (`dsytrf`, -`csytrf`, `chetrf`, etc.), so this function can (ideally) be used -interchangeably with its LAPACK counterparts `LAPACK.sytrf!`, -`LAPACK.sytrf_rook!`, etc. - -`uplo` is a character, either `'U'` or `'L'`, indicating whether the -matrix is stored in the upper triangular part (`uplo=='U'`) or in the -lower triangular part (`uplo=='L'`). - -`syhe` is a character, either `'S'` or `'H'`, indicating whether the -matrix is real/complex symmetric (`syhe=='S'`, and the symmetric -Bunch-Kaufman factorization is performed) or complex hermitian -(`syhe=='H'`, and the hermitian Bunch-Kaufman factorization is -performed). - -If `rook` is `true`, rook pivoting is used (also called bounded -Bunch-Kaufman factorization). If `rook` is `false`, rook pivoting is -not used (standard Bunch-Kaufman factorization). Rook pivoting can -require up to `~N^3/6` extra comparisons in addition to the `~N^3/3` -additions and `~N^3/3` multiplications of the standard Bunch-Kaufman -factorization. However, rook pivoting guarantees that the entries of -`U` or `L` are bounded. - -This function implements the factorization algorithm entirely in -native Julia, so it supports any number type representing real or -complex numbers. -""" -function generic_bunchkaufman!( - uplo::AbstractChar, - A::AbstractMatrix{TS}, - syhe::AbstractChar, - rook::Bool=false - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Inputs must be 1-indexed; bounds may not be checked. - Base.require_one_based_indexing(A) - - # Initialize info integer as 0 - info = 0::BlasInt - # Get size of matrix - N, N2 = size(A) - # Initialize permutation vector - ipiv = Vector{BlasInt}(undef, N) - - # Check input correctness - if uplo != 'U' && uplo != 'L' - info = (-1)::BlasInt - elseif N != N2 - info = (-2)::BlasInt - elseif syhe != 'S' && syhe != 'H' - info = (-3)::BlasInt - end - if info < 0 - arg_illegal("generic_bunchkaufman!", -info, 'W') - return A, ipiv, info - end - # if rook - # error("Rook pivoting not implemented yet.") - # end - - # Initialize `alpha` for use in choosing pivot block size. - # The exact value is - # (1 + sqrt(17)) / 8 ~= 0.6404 - # For rational matrices we a the small denominator approximation: - # 16/25 = 0.64 ~= (1 + sqrt(17)) / 8 - # in order to not increase the denominator size too much in computations. - # The error of this approximation is ≤0.1%, and it still guarantees that a - # 2x2 block in the D factor has a positive-negative eigenvalue pair, as long - # as the approximation lies in (0,1). - alpha = TR <: AbstractFloat ? (1 + sqrt(TR(17))) / 8 : TR(16//25) - # Use complex 1-norm for pivot selection, as in LAPACK - abs1_fun = TS <: Real ? abs : cabs1 - - # Check if the matrix is symmetric of hermitian - if syhe == 'S' || (syhe == 'H' && TS <: Real) - # Use symmetric variant if matrix is real, regardless of 'syhe' value - syhe = 'S' - diag_abs_fun = abs1_fun - else - diag_abs_fun = cabsr - end - - # Compute machine safe minimum when working with floating point numbers. - # LAPACK doesn't use this for diagonal pivoting though... - if rook - if TR <: AbstractFloat - # eps(0) gives the smallest subnormal number, and eps(1) gives the floating - # point type epsilon. eps(0)/eps(1) gives the smallest normal number, plus - # possibly some rounding error. - sfmin = nextfloat(eps(TR(0)) / eps(TR(1)), 2) - small = 1 / prevfloat(typemax(TR), 2) - if small >= sfmin - # 1/sfmin may overflow, so use 'small' plus a bit as the safe minimum - sfmin = nextfloat(small * (1 + eps(TR(1))), 2) - end - else - # We're working with rationals in this case, so the all results are exact. - sfmin = TR(0) - end - end - - # Run factorization depending on where the data is stored - upper = (uplo == 'U') - herm = (syhe == 'H') - # TODO: Is this gonna inline properly? - @inline k_cond = upper ? k -> k >= 1 : k -> k <= N - @inline irange = upper ? j -> (j:-1:1) : j -> (j:N) - @inline conj_op = herm ? conj : identity - @inline diagreal_op = herm ? (j -> A[j,j] = TS(real(A[j,j]))) : _ -> () - k = upper ? N : 1 - # Main loop, comments refer to the upper triangular version of the factorization. - # The lower triangular version is analogous. - while k_cond(k); @inbounds begin - kstep = 1 - knext = upper ? k - 1 : k + 1 - p = k - # Determine rows and columns to be interchanged and whether - # a 1-by-1 or 2-by-2 pivot block will be used - absakk = diag_abs_fun(A[k,k]) - # IMAX is the row-index of the largest off-diagonal element in - # column K, and COLMAX is its absolute value. - # Determine both COLMAX and IMAX. - if upper && k > 1 - colmax, imax = findmax(abs1_fun, view(A, 1:(k-1), k)) - elseif (!upper) && k < N - colmax, imax = findmax(abs1_fun, view(A, (k+1):N, k)) - imax += k - else - colmax = 0 - end - if (max(absakk, colmax) == 0) || isnan(absakk) - # Column K is zero or underflow, or contains a NaN: - # set INFO and continue - if info == 0 - info = k::BlasInt - end - kp = k - if herm - # Force diagonal entry to be purely real - A[k,k] = real(A[k,k]) - end - else - if absakk >= alpha*colmax - # no interchange, use 1-by-1 pivot block - kp = k - elseif rook - # Loop until pivot found - while true - # Begin pivot search loop body - # JMAX is the column-index of the largest off-diagonal - # element in row IMAX, and ROWMAX is its absolute value. - # Determine both ROWMAX and JMAX. - if imax != k - thisview = upper ? view(A, imax, (imax+1):k) : - view(A, imax, k:(imax-1)) - rowmax, jmax = findmax(abs1_fun, thisview) - jmax += upper ? imax : k - 1 - else - # LAPACK makes rowmax=0 in this case, but I believe it's - # better to make rowmax=-1, so that we guarantee that jmax - # will be define in the next if-block. - # TODO: is this correct/safe? - rowmax = 0 - end - if (upper && imax > 1) || ((!upper) && imax < N) - # Remember that we only have the upper triangular part - # of the matrix. We inspect the part of the row in the - # lower triangular part by traversing the corresponding - # part of the transpose column. - if upper - stemp, itemp = findmax(abs1_fun, view(A, 1:(imax-1), imax)) - else - stemp, itemp = findmax(abs1_fun, view(A, (imax+1):N, imax)) - itemp += imax - end - if stemp > rowmax - rowmax = stemp - jmax = itemp - end - end - # Equivalent to testing for (used to handle NaN and Inf) - # CABS1( A( IMAX, IMAX ) ).GE.ALPHA*ROWMAX - if !(diag_abs_fun(A[imax,imax]) < alpha*rowmax) - # interchange rows and columns K and IMAX, - # use 1-by-1 pivot block - kp = imax - break - # Equivalent to testing for ROWMAX .EQ. COLMAX, - # used to handle NaN and Inf - elseif (p == jmax || rowmax <= colmax) - # interchange rows and columns K+1 and IMAX, - # use 2-by-2 pivot block - kp = imax - kstep = 2 - break - else - # Pivot NOT found, set variables and repeat - p = imax - colmax = rowmax - imax = jmax - end - # End pivot search loop body - end - else - # JMAX is the column-index of the largest off-diagonal - # element in row IMAX, and ROWMAX is its absolute value - # We don't really need JMAX, se we don't store it - thisview = upper ? view(A, imax, (imax+1):k) : view(A, imax, k:(imax-1)) - rowmax = findmax(abs1_fun, thisview)[1] - if (upper && imax > 1) || ((!upper) && imax < N) - # Remember that we only have the upper triangular part - # of the matrix. We inspect the part of the row in the - # lower triangular part by traversing the corresponding - # part of the transpose column. - thisview = upper ? view(A, 1:(imax-1), imax) : - view(A, (imax+1):N, imax) - rowmax = max(rowmax, findmax(abs1_fun, thisview)[1]) - end - if absakk >= alpha * colmax * (colmax/rowmax) - # no interchange, use 1-by-1 pivot block - kp = k - elseif diag_abs_fun(A[imax,imax]) >= alpha * rowmax - # interchange rows and columns K and IMAX, use 1-by-1 - # pivot block - kp = imax - else - # interchange rows and columns K-1 and IMAX, use 2-by-2 - # pivot block - kp = imax - p = imax - kstep = 2 - end - end - # Swap TWO rows and TWO columns - # First swap - # The first swap only needs to be done when using rook pivoting - if rook && kstep == 2 - # Interchange rows and column K and P in the leading - # submatrix A(1:k,1:k) if we have a 2-by-2 pivot - bk_rowcol_swap!(A, k, p, 1, upper, herm) - end - # Second swap - did_swap = bk_rowcol_swap!(A, k, kp, kstep, upper, herm) - if herm && (!did_swap) - # Force diagonal entries to be purely real - A[k,k] = real(A[k,k]) - if kstep == 2 - A[knext,knext] = real(A[knext,knext]) - end - end - if kstep == 1 - # 1-by-1 pivot block D(k): column k now holds - # W(k) = U(k)*D(k) - # where U(k) is the k-th column of U - # When rook=false, sfmin is not defined, but the short-circuit - # evaluation of the conditional avoids an error. - if (!rook) || absakk >= sfmin - # Perform a rank-1 update of A(1:k-1,1:k-1) as - # A := A - U(k)*D(k)*U(k)' = A - W(k)*1/D(k)*W(k)' - # Compute 1/D(k) - r1 = !herm ? 1 / A[k,k] : 1 / real(A[k,k]) - # Perform rank-1 update to store the Schur complement - # in a submatrix of A - x = upper ? view(A, 1:(k-1), k) : view(A, (k+1):N, k) - # if 'upper' this should assign by reference - thisview = upper ? A : view(A, (k+1):N, (k+1):N) - generic_adr1!(uplo, -r1, x, x, thisview, syhe) - # Store U(k) in column k - thisrange = upper ? (1:(k-1)) : ((k+1):N) - for i in thisrange - A[i,k] *= r1 - end - else - # Compute D(k) - r1 = !herm ? A[k,k] : real(A[k,k]) - # Store U(k) in column k - thisrange = upper ? (1:(k-1)) : ((k+1):N) - for i in thisrange - A[i,k] /= r1 - end - # Perform a rank-1 update of A(k+1:n,k+1:n) as - # A := A - U(k)*D(k)*U(k)**T - # = A - W(k)*(1/D(k))*W(k)**T - # = A - (W(k)/D(k))*(D(k))*(W(k)/D(K))**T - # Perform rank-1 update to store the Schur complement - # in a submatrix of A - x = upper ? view(A, 1:(k-1), k) : view(A, (k+1):N, k) - # if 'upper' this should assign by reference - thisview = upper ? A : view(A, (k+1):N, (k+1):N) - generic_adr1!(uplo, -r1, x, x, thisview, syhe) - end - elseif (upper && k > 2) || ((!upper) && k < N - 1) - # 2-by-2 pivot block D(k): columns k and k-1 now hold - # ( W(k-1) W(k) ) = ( U(k-1) U(k) )*D(k) - # where U(k) and U(k-1) are the k-th and (k-1)-th columns - # of U - # Perform a rank-2 update of A(1:k-2,1:k-2) as - # A := A - ( U(k-1) U(k) )*D(k)*( U(k-1) U(k) )' - # = A - ( W(k-1) W(k) )*inv(D(k))*( W(k-1) W(k) )' - thisrange = upper ? ((k-2):-1:1) : ((k+2):N) - if !herm - # Real/complex symmetric case - #TODO: is this way to compute the inverse backward stable? - # (it probably is as it comes from LAPACK) - dxk = A[knext,k] - dxx = A[knext,knext] / dxk - dkk = A[k,k] / dxk - t = 1 / (dkk * dxx - 1) - dxk = t / dxk - dkx = dxk - else - # Hermitian case - # TODO: is this way to compute the inverse backward stable? - # (it probably is as it is a small modification of LAPACK's - # method) - dxk = A[knext,k] - dxx = real(A[knext,knext]) / dxk - dkk = real(A[k,k]) / conj(dxk) - t = 1 / (real(dkk * dxx) - 1) - dkx = t / conj(dxk) - dxk = t / dxk - end - for j in thisrange - wknext = dxk * (dkk*A[j,knext] - A[j,k]) - wk = dkx * (dxx*A[j,k] - A[j,knext]) - for i in irange(j) - A[i,j] -= (A[i,k]*conj_op(wk) + A[i,knext]*conj_op(wknext)) - end - A[j,k] = wk - A[j,knext] = wknext - # Force diagonal entry to be purely real, but still of - # complex type TS (I don't know why in LAPACK this - # case, unlike the rest, enforces a complex type - # explicitly). - diagreal_op(j) - end - end - end - # Store details of the interchanges in IPIV - if kstep == 1 - ipiv[k] = kp - else - ipiv[k] = -p - ipiv[knext] = -kp - end - # Decrease K and return to the start of the main loop - # k -= upper ? kstep : -kstep - if upper; k -= kstep; else; k += kstep; end - end; end - return A, ipiv, info -end - - -""" -generic_syconv(F, gettri::Bool=true) -> -(TLU<:Union{AbstractMatrix,Nothing}, e<:AbstractVector, - d<:Union{AbstractVector,Nothing}) - -`generic_syconv` takes the Bunch-Kaufman object `F` and returns the -block-diagonal factor `D`, and the triangular factor `L` (or `U`) if -requested. If the `L` or `U` factor is requested then both `L` (or `U`) and -the main diagonal of `D` will be stored in `TLU`, following LAPACK format, -and `d` will be set to `nothing`. `e` contains the first subdiagonal of -`D`. If the triangular factor is not requested, then `TLU` will not be set -to `nothing`, and the main diagonal of `D` will be stored in `d`. - -`gettri` is a `Bool`, indicating whether the `L` (or `U`) triangular factor -should be computed (`gettri==true`) or not (`gettri==false`). If the -triangular factor is required, a copy of `A.LD` will be created, and the -triangular factor will be computed in-place in said copy. -""" -function generic_syconv( - F::BunchKaufman{TS}, - gettri::Bool=true - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Inputs must be 1-indexed; bounds may not be checked. - Base.require_one_based_indexing(F.LD, F.ipiv) - - # Extract necessary variables - A, ipiv, rook = gettri ? deepcopy(F.LD) : F.LD, F.ipiv, F.rook - - # Get size of matrix - N = size(A)[1] - - # Initialize off-diagonal and diagonal vector - e = Vector{TS}(undef, N) - d = gettri ? nothing : diag(A, 0) - - # Quick return if possible - if N == 0; return gettri ? A : nothing, e, d; end - - # Main loops - upper = (F.uplo == 'U') - @inline icond_d = upper ? i -> i > 1 : i -> i < N - @inline icond_T = upper ? i -> i >= 1 : i -> i <= N - @inline inext = upper ? i -> i - 1 : i -> i + 1 - # Convert VALUE - i = upper ? N : 1 - e[N+1-i] = 0 - while icond_d(i); @inbounds begin - if ipiv[i] < 0 - ix = inext(i) - e[i] = A[ix,i] - e[ix] = 0 - if gettri; A[ix,i] = 0; end - if upper; i -= 1; else; i += 1; end - else - e[i] = 0 - end - if upper; i -= 1; else; i += 1; end - end; end - # Convert PERMUTATIONS - if gettri - i = upper ? N : 1 - while icond_T(i); @inbounds begin - thisview = upper ? view(A, :, (i+1):N) : view(A, :, 1:(i-1)) - ip = ipiv[i] - if ip > 0 || rook - Base.swaprows!(thisview, abs(ip), i) - end - if ip <= 0 - ix = inext(i) - Base.swaprows!(thisview, -ipiv[ix], ix) - if upper; i -= 1; else; i += 1; end - end - if upper; i -= 1; else; i += 1; end - end; end - end - return gettri ? A : nothing, e, d -end - - -""" -generic_bksolve!(F, B) -> X<:AbstractVecOrMat - -`generic_bksolve!` solves a system of linear equations `A*X = B` where -the Bunch-Kaufman factorization of `A` is provided by `F`. -""" -function generic_bksolve!( - F::BunchKaufman{TS}, - B0::AbstractVecOrMat{TS}, - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Inputs must be 1-indexed; bounds may not be checked. - Base.require_one_based_indexing(F.LD, F.ipiv, B0) - - # Get size of matrices - N = size(F.LD)[1] - if typeof(B0) <: AbstractVector - N3 = size(B0)[1] - M = 1 - B = view(B0, :, :) - else - N3, M = size(B0) - B = B0 - end - - # Initialize info integer as 0 - info = 0::BlasInt - - # Check input correctness - if N3 != N - info = (-2)::BlasInt - end - if info < 0 - arg_illegal("generic_bksolve!", -info, 'E') - end - - # Quick return if possible - if N == 0 || M == 0; return B; end - - # Extract necessary variables - A, ipiv, symm, rook = F.LD, F.ipiv, issymmetric(F), F.rook - - # Load the requested adjoining operator - adj_op = symm ? identity : conj - - R1 = TR(1) - upper = (F.uplo == 'U') - @inline kcond1 = upper ? k -> k >= 1 : k -> k <= N - @inline kcond2 = upper ? k -> k <= N : k -> k >= 1 - @inline knext = upper ? k -> k - 1 : k -> k + 1 - @inline knext2 = upper ? k -> k + 1 : k -> k - 1 - k = upper ? N : 1 - while kcond1(k); @inbounds begin - kp = ipiv[k] - if kp > 0 - # 1 x 1 diagonal block - # Interchange rows K and IPIV(K). - Base.swaprows!(B, k, kp) - # Multiply by inv(U(K)), where U(K) is the transformation - # stored in column K of A. - Aview = upper ? view(A, 1:(k-1), k) : view(A, (k+1):N, k) - Bview = upper ? B : view(B, (k+1):N, :) - generic_adr1!('F', -R1, Aview, view(B, k, :), Bview, 'S') - # Multiply by the inverse of the diagonal block. - s = symm ? 1 / A[k,k] : 1 / real(A[k,k]) - for j in 1:M; B[k,j] *= s; end - if upper; k -= 1; else; k += 1; end - else - # 2 x 2 diagonal block - # Interchange rows K and -IPIV(K) THEN K-1 and -IPIV(K-1) - # The first interchange is only needed when rook pivoting is used - if rook; Base.swaprows!(B, k, -kp); end - kx = knext(k) - Base.swaprows!(B, kx, -ipiv[kx]) - # Multiply by inv(U(K)), where U(K) is the transformation - # stored in columns K-1 and K of A. - Aview = upper ? view(A, 1:(k-2), k) : view(A, (k+2):N, k) - Bview = upper ? B : view(B, (k+2):N, :) - generic_adr1!('F', -R1, Aview, view(B, k, :), Bview, 'S') - Aview = upper ? view(A, 1:(k-2), kx) : view(A, (k+2):N, kx) - generic_adr1!('F', -R1, Aview, view(B, kx, :), Bview, 'S') - # Multiply by the inverse of the diagonal block. - axk = A[kx,k] - axx = A[kx,kx] / axk - akk = A[k,k] / adj_op(axk) - denom = axx*akk - 1 - for j in 1:M - bx = B[kx,j] / axk - bk = B[k,j] / adj_op(axk) - B[kx,j] = (akk*bx - bk) / denom - B[k,j] = (axx*bk - bx) / denom - end - if upper; k -= 2; else; k += 2; end - end - end; end - # Next solve U'*X = B, overwriting B with X. - # K is the main loop index, increasing from 1 to N in steps of - # 1 or 2, depending on the size of the diagonal blocks. - k = upper ? 1 : N - while kcond2(k); @inbounds begin - Aview = upper ? view(A, 1:(k-1), k) : view(A, (k+1):N, k) - Bview = upper ? view(B, 1:(k-1), :) : view(B, (k+1):N, :) - B_row = view(B, k, :) - kp = ipiv[k] - if kp > 0 - # 1 x 1 diagonal block - # Multiply by inv(U**T(K)), where U(K) is the transformation - # stored in column K of A. - if symm - generic_mvpv!('T', -R1, Bview, Aview, R1, B_row) - else - conj!(B_row) - generic_mvpv!('C', -R1, Bview, Aview, R1, B_row) - conj!(B_row) - end - # Interchange rows K and IPIV(K). - Base.swaprows!(B, k, kp) - if upper; k += 1; else; k -= 1; end - else - # 2 x 2 diagonal block - # Multiply by inv(U**T(K+1)), where U(K+1) is the transformation - # stored in columns K and K+1 of A. - kx = knext2(k) - if symm - generic_mvpv!('T', -R1, Bview, Aview, R1, B_row) - Aview = upper ? view(A, 1:(k-1), kx) : view(A, (k+1):N, kx) - B_row = view(B, kx, :) - generic_mvpv!('T', -R1, Bview, Aview, R1, B_row) - elseif k > 1 - conj!(B_row) - generic_mvpv!('C', -R1, Bview, Aview, R1, B_row) - conj!(B_row) - Aview = upper ? view(A, 1:(k-1), kx) : view(A, (k+1):N, kx) - B_row = view(B, kx, :) - conj!(B_row) - generic_mvpv!('C', -R1, Bview, Aview, R1, B_row) - conj!(B_row) - end - # Interchange rows K and -IPIV(K) THEN K+1 and -IPIV(K+1). - # The second interchange is only needed when rook pivoting is used - Base.swaprows!(B, k, -kp) - if rook; Base.swaprows!(B, kx, -ipiv[kx]); end - if upper; k += 2; else; k -= 2; end - end - end; end - return B -end - - -""" -inertia(B::BunchKaufman; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ϵ) -> - np::Union{Nothing,Integer}, nn::Union{Nothing,Integer}, nz::Integer - -`inertia` computes the numerical inertia (the number of positive, -negative and zero eigenvalues, given by `np`, `nn` and `nz`, -respectively) of a real symmetric of Hermitian matrix `B` that has been -factored using the Bunch-Kaufman algorithm. For complex symmetric -matrices the inertia is not defined. in that case `np` and `nn` are set -to `nothing`, but the function still returns the number of zero -eigenvalues. The inertia is computed by counting the eigenvalues signs -of `B.D`. The number of zero eigenvalues is computed as the number of -estimated eigenvalues with complex 1-norm (defined as `|re(.)|+|im(.)|`) -less or equal than `max(atol, rtol*s₁)`, where `s₁` is an upper bound of -the largest singular value of `B.D`, `σ₁` (more specifically, -`0.5*s₁ <= σ₁ <= s₁` for real matrices and `0.35*s₁ <= σ₁ <= s₁` for -complex matrices). `atol` and `rtol` are the absolute and relative -tolerances, respectively. The default relative tolerance is `n*ϵ`, where -`n` is the size of of `A`, and `ϵ` is the [`eps`](@ref) of the number -type of `A`, if this type is a subtype of `AbstractFloat`. In any other -case (if the number type of `A` is `Rational`, for example) `ϵ` is set -to `0`. - -!!! note - Numerical inertia can be a sensitive and imprecise characterization of - ill-conditioned matrices with eigenvalues that are close in magnitude to the - threshold tolerance `max(atol, rtol*s₁)`. In such cases, slight perturbations - to the Bunch-Kaufman computation or to the matrix can change the result of - `rank` by pushing one or more eigenvalues across the threshold. These - variations can even occur due to changes in floating-point errors between - different Julia versions, architectures, compilers, or operating systems. - In particular, the size of the entries of the tringular factor directly - influende the scale of the eigenvalues of the diagonal factor, so it is - strongly recommended to use rook pivoting is the inertia is going to be - computed. - On the other hand, if the matrix has rational entries, the inertia - computation is guaranteed is to be exact, as long as there is no - under/overflow in the underlying integer type (and in such cases Julia itself - throws an error), or a positive tolerance (absolute or relative) is - specified. -""" -function inertia(B::BunchKaufman{TS}; - atol::TR = TR(0), - rtol::TR = TR(0) - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - - # Check if matrix is complex symmetric - get_inertia = !(issymmetric(B) && TS <: Complex) - - # Initialize outputs - np, nn, nz = get_inertia ? (0, 0, 0) : (nothing, nothing, 0) - - # Compute matrix size - N = size(B, 1) - - # Quick return if possible - if N == 0; return np, nn, nz; end - - # Compute default relative tolerance - if rtol <= 0 && atol <= 0 - rtol = TR <: AbstractFloat ? (N * eps(TR)) : TR(0) - end - - # We use the complex 1-norm for complex matrices - real_matrix = (TS <: Real) - abs1_fun = real_matrix ? abs : cabs1 - real_fun = real_matrix ? identity : real - - # Check if we must track the largest singular value - get_s1 = (rtol > 0) - - # Constant for lower bound estimation of the smallest eigenvalue in 2x2 blocks. - # The best (largest) value for complex matrices is 1/sqrt(2), but for rational - # matrices we use the small denominator approximation 12/17, in order to not - # increase the denominator size too much in computations. The error of this - # approximation is ≤0.2%, and we still get a valid lower bound. - c = real_matrix ? TR(1) : (TR <: AbstractFloat ? 1/sqrt(TR(2)) : TR(12//17)) - - # First pass, estimate largest singular value and group together size-1 blocks - D = B.D - s1 = TR(0) - i = 1 - while i <= N; @inbounds begin - if i < N && D[i,i+1] != 0 - # 2x2 block - # The largest singular value of a 2x2 matrix is between [1, 2] times - # its complex max-norm, which is between [c, 1] times the largest - # complex 1-norm among the entries of the 2x2 matrix. See "Roger - # Horn and Charles Johnson. Matrix Analysis, 2nd Edition, 5.6.P23". - abs_Dii = abs1_fun(D[i,i]) - abs_Dxx = abs1_fun(D[i+1,i+1]) - s1_block = 2 * max(abs_Dii, abs1_fun(D[i,i+1]), abs_Dxx) - if get_s1; s1 = max(s1, s1_block); end - # Lower bound on the smallest eigenvalue complex 2-norm is - # abs(λ₂) ≥ abs(det(block)) / s1_block - # so the bound in terms of the complex 1-norm becomes - # abs1_fun(λ₂) ≥ c * abs1_fun(det(block)) / s1_block - # For rational matrices, if λ₂=0 then det(block)=0 and then the bound - # becomes zero too. If λ₁=0 too then the block has all zero entries - # and 's1_block'=0, but 'D[i,i+1]' != 0 and so 's1_block' > 0. However, we - # may still have that 'smin_block'≈0, then the value of 'smin_block' may not - # be accurate. In that case the counting routine will detect that both - # eigenvalues are zero without using 'smin_block', so it doesn't matter. - # TODO: is this the most numerically stable way to compute the determinant? - # TODO: is this the best way to avoid under/overflow? - if abs_Dii >= abs_Dxx - smin_block = c * abs1_fun((D[i,i]/s1_block)*D[i+1,i+1] - - (D[i,i+1]/s1_block)*D[i+1,i]) - else - smin_block = c * abs1_fun(D[i,i]*(D[i+1,i+1]/s1_block) - - (D[i,i+1]/s1_block)*D[i+1,i]) - end - # Store lower bound in-place in the lower off-diagonal and upper bound - # in-place in the upper off-diagonal. The trace is stored in the first - # diagonal entry block, but only if the full inertia is needed. - D[i,i+1] = s1_block - D[i+1,i] = smin_block - if get_inertia; D[i,i] += D[i+1,i+1]; end - i += 2 - else - # 1x1 block - if get_s1; s1 = max(s1, abs1_fun(D[i,i])); end - i += 1 - end - end; end - - # Second pass, count eigenvalue signs - tol = max(atol, rtol * s1) - i = 1 - while i <= N; @inbounds begin - if i < N && D[i,i+1] != 0 - # 2x2 block. For the counting of zero eigenvalues we use the lower bound on the - # eigenvalues' magnitude. This way, if an eigenvalue is deemed non-zero, then - # it is guaranteed that its magnitude is greater than the tolerance. - s1_block = real_fun(D[i,i+1]) - if (c / 2) * s1_block <= tol - # Lower bound of largest eigenvalue is smaller than the tolerance, - # we consider the both eigenvalues of this block to be zero. - nz += 2 - i += 2 - continue - end - # Reaching this part of the lopp implies that 's1_block' != 0. - smin_block = real_fun(D[i+1,i]) - trace_block = real_fun(D[i,i]) - if smin_block > tol || trace_block == 0 - # If first condition holds then the lower bound of the smallest eigenvalue - # is larger than the tolerance. If the second condition holds then the trace - # is exactly zero, so both eigenvalues have the same magnitude, and we - # already know that the largest one is non-zero. In any case we conclude - # that both eigenvalues are non-zero. - if get_inertia - # The eigenvalues of a 2x2 block are guaranteed to be a - # positive-negative pair. - np += 1 - nn += 1 - end - else - # The lower bound of smallest eigenvalue is smaller than the tolerance and - # the trace is non-zero, so we consider the smallest eigenvalues of this - # block to be zero. - nz += 1 - if get_inertia - # The trace is non-zero, and its sign is the same of the largest - # eigenvalue. - if trace_block >= 0 - np += 1 - else - nn += 1 - end - end - end - i += 2 - else - # 1x1 block - if get_inertia - eig = real_fun(D[i,i]) - if eig > tol - np += 1 - elseif eig < -tol - nn += 1 - else - nz += 1 - end - elseif abs1_fun(D[i,i]) <= tol - nz += 1 - end - i += 1 - end - end; end - - return np, nn, nz -end - - -""" - bunchkaufman_native!(A, rook::Bool=false; check = true) -> BunchKaufman - -`bunchkaufman_native!` is the same as [`bunchkaufman!`](@ref), but it performs -the factorization in native Julia code instead of calling LAPACK. -""" -function bunchkaufman_native!(A::AbstractMatrix{TS}, - rook::Bool = false; - check::Bool = true, - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - if A isa RealHermSymComplexSym{TR} - syhe = 'S' - elseif ishermitian(A) - syhe = 'H' - elseif issymmetric(A) - syhe = 'S' - else - throw(ArgumentError("Bunch-Kaufman decomposition is only valid for " * - "symmetric or Hermitian matrices")) - end - if A isa HermOrSym - Adata = A.data - uplo = A.uplo - else - Adata = A - uplo = 'U' - end - LD, ipiv, info = generic_bunchkaufman!(uplo, Adata, syhe, rook) - check && checknonsingular(info) - return BunchKaufman(LD, ipiv, uplo, syhe == 'S', rook, info) -end - - -""" -Overload 'bunchkaufman.jl' methods through multiple dispatch -""" - -function bunchkaufman!(A::AbstractMatrix{TS}, - rook::Bool = false; - check::Bool = true - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - return bunchkaufman_native!(A, rook; check) -end - -function bunchkaufman(A::AbstractMatrix{TS}, - rook::Bool = false; - check::Bool = true - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - return bunchkaufman!(bkcopy_oftype(A, TS), rook; check) -end - -function bunchkaufman(A::AbstractMatrix{TS}, - rook::Bool = false; - check::Bool = true - ) where TS <:Union{TI, Complex{TI}} where TI <: Integer - - # Identity whether matrix is symmetric or Hermitian or none - if A isa Symmetric - TA = Symmetric - elseif A isa Hermitian - TA = Hermitian - else - TA = Nothing - end - - # Create a rational copy of input integer matrix, as the Bunch-Kaufman - # algorithm is closed over the rationals but not over the integers. - # We promote input to BigInt to avoid overflow problems - if TA == Nothing - if TS <: Integer - M = Rational{BigInt}.(bkcopy_oftype(A, TS)) - else - M = Complex{Rational{BigInt}}.(bkcopy_oftype(A, TS)) - end - else - if TS <: Integer - M = TA(Rational{BigInt}.(bkcopy_oftype(A, TS)), Symbol(A.uplo)) - else - M = TA(Complex{Rational{BigInt}}.(bkcopy_oftype(A, TS)), - Symbol(A.uplo)) - end - end - - return bunchkaufman_native!(M, rook; check) -end - -function ldiv!(B::BunchKaufman{TS}, - R::AbstractVecOrMat{TS} - ) where TS <: ClosedScalar{TR} where TR <: ClosedReal - return generic_bksolve!(B, R) -end - -function inv(B::BunchKaufman{TS}) where TS <: ClosedScalar{TR} where TR <: ClosedReal - # I don't think there's value in implementing tha LAPACK in-place inverse - # functions `dsytri`, `chetri`, etc., unless of course an efficient - # in-place inverse function `inv!` is needed. - # TODO: reduce the operation count of the inverse by not computing the - # lower/upper triangular part. - if issymmetric(B) - return copytri!(B \ I, B.uplo) - else - return copytri!(B \ I, B.uplo, true) - end -end diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl deleted file mode 100644 index 03f7c273ccbef..0000000000000 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ /dev/null @@ -1,1038 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -########################## -# Cholesky Factorization # -########################## - -# The dispatch structure in the cholesky, and cholesky! methods is a bit -# complicated and some explanation is therefore provided in the following -# -# In the methods below, LAPACK is called when possible, i.e. StridedMatrices with Float32, -# Float64, ComplexF32, and ComplexF64 element types. For other element or -# matrix types, the unblocked Julia implementation in _chol! is used. For cholesky -# and cholesky! pivoting is supported through a RowMaximum() argument. A type argument is -# necessary for type stability since the output of cholesky and cholesky! is either -# Cholesky or CholeskyPivoted. The latter is only -# supported for the four LAPACK element types. For other types, e.g. BigFloats RowMaximum() will -# give an error. It is required that the input is Hermitian (including real symmetric) either -# through the Hermitian and Symmetric views or exact symmetric or Hermitian elements which -# is checked for and an error is thrown if the check fails. - -# The internal structure is as follows -# - _chol! returns the factor and info without checking positive definiteness -# - cholesky/cholesky! returns Cholesky without checking positive definiteness - -# FixMe? The dispatch below seems overly complicated. One simplification could be to -# merge the two Cholesky types into one. It would remove the need for Val completely but -# the cost would be extra unnecessary/unused fields for the unpivoted Cholesky and runtime -# checks of those fields before calls to LAPACK to check which version of the Cholesky -# factorization the type represents. -""" - Cholesky <: Factorization - -Matrix factorization type of the Cholesky factorization of a dense symmetric/Hermitian -positive definite matrix `A`. This is the return type of [`cholesky`](@ref), -the corresponding matrix factorization function. - -The triangular Cholesky factor can be obtained from the factorization `F::Cholesky` -via `F.L` and `F.U`, where `A ≈ F.U' * F.U ≈ F.L * F.L'`. - -The following functions are available for `Cholesky` objects: [`size`](@ref), [`\\`](@ref), -[`inv`](@ref), [`det`](@ref), [`logdet`](@ref) and [`isposdef`](@ref). - -Iterating the decomposition produces the components `L` and `U`. - -# Examples -```jldoctest -julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.] -3×3 Matrix{Float64}: - 4.0 12.0 -16.0 - 12.0 37.0 -43.0 - -16.0 -43.0 98.0 - -julia> C = cholesky(A) -Cholesky{Float64, Matrix{Float64}} -U factor: -3×3 UpperTriangular{Float64, Matrix{Float64}}: - 2.0 6.0 -8.0 - ⋅ 1.0 5.0 - ⋅ ⋅ 3.0 - -julia> C.U -3×3 UpperTriangular{Float64, Matrix{Float64}}: - 2.0 6.0 -8.0 - ⋅ 1.0 5.0 - ⋅ ⋅ 3.0 - -julia> C.L -3×3 LowerTriangular{Float64, Matrix{Float64}}: - 2.0 ⋅ ⋅ - 6.0 1.0 ⋅ - -8.0 5.0 3.0 - -julia> C.L * C.U == A -true - -julia> l, u = C; # destructuring via iteration - -julia> l == C.L && u == C.U -true -``` -""" -struct Cholesky{T,S<:AbstractMatrix} <: Factorization{T} - factors::S - uplo::Char - info::BlasInt - - function Cholesky{T,S}(factors, uplo, info) where {T,S<:AbstractMatrix} - require_one_based_indexing(factors) - new(factors, uplo, info) - end -end -Cholesky(A::AbstractMatrix{T}, uplo::Symbol, info::Integer) where {T} = - Cholesky{T,typeof(A)}(A, char_uplo(uplo), info) -Cholesky(A::AbstractMatrix{T}, uplo::AbstractChar, info::Integer) where {T} = - Cholesky{T,typeof(A)}(A, uplo, info) -Cholesky(U::UpperTriangular{T}) where {T} = Cholesky{T,typeof(U.data)}(U.data, 'U', 0) -Cholesky(L::LowerTriangular{T}) where {T} = Cholesky{T,typeof(L.data)}(L.data, 'L', 0) - -# iteration for destructuring into components -Base.iterate(C::Cholesky) = (C.L, Val(:U)) -Base.iterate(C::Cholesky, ::Val{:U}) = (C.U, Val(:done)) -Base.iterate(C::Cholesky, ::Val{:done}) = nothing - - -""" - CholeskyPivoted - -Matrix factorization type of the pivoted Cholesky factorization of a dense symmetric/Hermitian -positive semi-definite matrix `A`. This is the return type of [`cholesky(_, ::RowMaximum)`](@ref), -the corresponding matrix factorization function. - -The triangular Cholesky factor can be obtained from the factorization `F::CholeskyPivoted` -via `F.L` and `F.U`, and the permutation via `F.p`, where `A[F.p, F.p] ≈ Ur' * Ur ≈ Lr * Lr'` -with `Ur = F.U[1:F.rank, :]` and `Lr = F.L[:, 1:F.rank]`, or alternatively -`A ≈ Up' * Up ≈ Lp * Lp'` with `Up = F.U[1:F.rank, invperm(F.p)]` and -`Lp = F.L[invperm(F.p), 1:F.rank]`. - -The following functions are available for `CholeskyPivoted` objects: -[`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), and [`rank`](@ref). - -Iterating the decomposition produces the components `L` and `U`. - -# Examples -```jldoctest -julia> X = [1.0, 2.0, 3.0, 4.0]; - -julia> A = X * X'; - -julia> C = cholesky(A, RowMaximum(), check = false) -CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}} -U factor with rank 1: -4×4 UpperTriangular{Float64, Matrix{Float64}}: - 4.0 2.0 3.0 1.0 - ⋅ 0.0 6.0 2.0 - ⋅ ⋅ 9.0 3.0 - ⋅ ⋅ ⋅ 1.0 -permutation: -4-element Vector{Int64}: - 4 - 2 - 3 - 1 - -julia> C.U[1:C.rank, :]' * C.U[1:C.rank, :] ≈ A[C.p, C.p] -true - -julia> l, u = C; # destructuring via iteration - -julia> l == C.L && u == C.U -true -``` -""" -struct CholeskyPivoted{T,S<:AbstractMatrix,P<:AbstractVector{<:Integer}} <: Factorization{T} - factors::S - uplo::Char - piv::P - rank::BlasInt - tol::Real - info::BlasInt - - function CholeskyPivoted{T,S,P}(factors, uplo, piv, rank, tol, info) where {T,S<:AbstractMatrix,P<:AbstractVector} - require_one_based_indexing(factors) - new{T,S,P}(factors, uplo, piv, rank, tol, info) - end -end -CholeskyPivoted(A::AbstractMatrix{T}, uplo::AbstractChar, piv::AbstractVector{<:Integer}, - rank::Integer, tol::Real, info::Integer) where T = - CholeskyPivoted{T,typeof(A),typeof(piv)}(A, uplo, piv, rank, tol, info) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(CholeskyPivoted{T,S}(factors, uplo, piv, rank, tol, info) where {T,S<:AbstractMatrix}, - CholeskyPivoted{T,S,typeof(piv)}(factors, uplo, piv, rank, tol, info), false) - - -# iteration for destructuring into components -Base.iterate(C::CholeskyPivoted) = (C.L, Val(:U)) -Base.iterate(C::CholeskyPivoted, ::Val{:U}) = (C.U, Val(:done)) -Base.iterate(C::CholeskyPivoted, ::Val{:done}) = nothing - - -# make a copy that allow inplace Cholesky factorization -choltype(A) = promote_type(typeof(sqrt(oneunit(eltype(A)))), Float32) -cholcopy(A::AbstractMatrix) = eigencopy_oftype(A, choltype(A)) - -# _chol!. Internal methods for calling unpivoted Cholesky -## BLAS/LAPACK element types -function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{UpperTriangular}) - C, info = LAPACK.potrf!('U', A) - return UpperTriangular(C), info -end -function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular}) - C, info = LAPACK.potrf!('L', A) - return LowerTriangular(C), info -end - -## Non BLAS/LAPACK element types (generic) -function _chol!(A::AbstractMatrix, ::Type{UpperTriangular}) - require_one_based_indexing(A) - n = checksquare(A) - realdiag = eltype(A) <: Complex - @inbounds begin - for k = 1:n - Akk = realdiag ? real(A[k,k]) : A[k,k] - for i = 1:k - 1 - Akk -= realdiag ? abs2(A[i,k]) : A[i,k]'A[i,k] - end - A[k,k] = Akk - Akk, info = _chol!(Akk, UpperTriangular) - if info != 0 - return UpperTriangular(A), convert(BlasInt, k) - end - A[k,k] = Akk - AkkInv = inv(copy(Akk')) - for j = k + 1:n - @simd for i = 1:k - 1 - A[k,j] -= A[i,k]'A[i,j] - end - A[k,j] = AkkInv*A[k,j] - end - end - end - return UpperTriangular(A), convert(BlasInt, 0) -end -function _chol!(A::AbstractMatrix, ::Type{LowerTriangular}) - require_one_based_indexing(A) - n = checksquare(A) - realdiag = eltype(A) <: Complex - @inbounds begin - for k = 1:n - Akk = realdiag ? real(A[k,k]) : A[k,k] - for i = 1:k - 1 - Akk -= realdiag ? abs2(A[k,i]) : A[k,i]*A[k,i]' - end - A[k,k] = Akk - Akk, info = _chol!(Akk, LowerTriangular) - if info != 0 - return LowerTriangular(A), convert(BlasInt, k) - end - A[k,k] = Akk - AkkInv = inv(copy(Akk')) - for j = 1:k - 1 - Akjc = A[k,j]' - @simd for i = k + 1:n - A[i,k] -= A[i,j]*Akjc - end - end - @simd for i = k + 1:n - A[i,k] *= AkkInv - end - end - end - return LowerTriangular(A), convert(BlasInt, 0) -end - -## Numbers -function _chol!(x::Number, _) - rx = real(x) - iszero(rx) && return (rx, convert(BlasInt, 1)) - rxr = sqrt(abs(rx)) - rval = convert(promote_type(typeof(x), typeof(rxr)), rxr) - return (rval, convert(BlasInt, rx != abs(x))) -end - -# _cholpivoted!. Internal methods for calling pivoted Cholesky -Base.@propagate_inbounds function _swap_rowcols!(A, ::Type{UpperTriangular}, n, j, q) - j == q && return - @assert j < q - # swap rows and cols without touching the possibly undef-ed triangle - A[q, q] = A[j, j] - for k in 1:j-1 # initial vertical segments - A[k,j], A[k,q] = A[k,q], A[k,j] - end - for k in j+1:q-1 # intermediate segments - A[j,k], A[k,q] = conj(A[k,q]), conj(A[j,k]) - end - A[j,q] = conj(A[j,q]) # corner case - for k in q+1:n # final horizontal segments - A[j,k], A[q,k] = A[q,k], A[j,k] - end - return -end -Base.@propagate_inbounds function _swap_rowcols!(A, ::Type{LowerTriangular}, n, j, q) - j == q && return - @assert j < q - # swap rows and cols without touching the possibly undef-ed triangle - A[q, q] = A[j, j] - for k in 1:j-1 # initial horizontal segments - A[j,k], A[q,k] = A[q,k], A[j,k] - end - for k in j+1:q-1 # intermediate segments - A[k,j], A[q,k] = conj(A[q,k]), conj(A[k,j]) - end - A[q,j] = conj(A[q,j]) # corner case - for k in q+1:n # final vertical segments - A[k,j], A[k,q] = A[k,q], A[k,j] - end - return -end -### BLAS/LAPACK element types -_cholpivoted!(A::StridedMatrix{<:BlasFloat}, ::Type{UpperTriangular}, tol::Real, check::Bool) = - LAPACK.pstrf!('U', A, tol) -_cholpivoted!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular}, tol::Real, check::Bool) = - LAPACK.pstrf!('L', A, tol) -## Non BLAS/LAPACK element types (generic) -function _cholpivoted!(A::AbstractMatrix, ::Type{UpperTriangular}, tol::Real, check::Bool) - rTA = real(eltype(A)) - # checks - Base.require_one_based_indexing(A) - n = LinearAlgebra.checksquare(A) - # initialization - piv = collect(1:n) - dots = zeros(rTA, n) - temp = similar(dots) - - @inbounds begin - # first step - Akk, q = findmax(i -> real(A[i,i]), 1:n) - stop = tol < 0 ? eps(rTA)*n*abs(Akk) : tol - Akk ≤ stop && return A, piv, convert(BlasInt, 0), convert(BlasInt, 1) - # swap - _swap_rowcols!(A, UpperTriangular, n, 1, q) - piv[1], piv[q] = piv[q], piv[1] - A[1,1] = Akk = sqrt(Akk) - AkkInv = inv(copy(Akk')) - @simd for j in 2:n - A[1, j] *= AkkInv - end - - for k in 2:n - @simd for j in k:n - dots[j] += abs2(A[k-1, j]) - temp[j] = real(A[j,j]) - dots[j] - end - Akk, q = findmax(j -> temp[j], k:n) - Akk ≤ stop && return A, piv, convert(BlasInt, k - 1), convert(BlasInt, 1) - q += k - 1 - # swap - _swap_rowcols!(A, UpperTriangular, n, k, q) - dots[k], dots[q] = dots[q], dots[k] - piv[k], piv[q] = piv[q], piv[k] - # update - A[k,k] = Akk = sqrt(Akk) - AkkInv = inv(copy(Akk')) - for j in (k+1):n - @simd for i in 1:(k-1) - A[k,j] -= A[i,k]'A[i,j] - end - A[k,j] = AkkInv * A[k,j] - end - end - return A, piv, convert(BlasInt, n), convert(BlasInt, 0) - end -end -function _cholpivoted!(A::AbstractMatrix, ::Type{LowerTriangular}, tol::Real, check::Bool) - rTA = real(eltype(A)) - # checks - Base.require_one_based_indexing(A) - n = LinearAlgebra.checksquare(A) - # initialization - piv = collect(1:n) - dots = zeros(rTA, n) - temp = similar(dots) - - @inbounds begin - # first step - Akk, q = findmax(i -> real(A[i,i]), 1:n) - stop = tol < 0 ? eps(rTA)*n*abs(Akk) : tol - Akk ≤ stop && return A, piv, convert(BlasInt, 0), convert(BlasInt, 1) - # swap - _swap_rowcols!(A, LowerTriangular, n, 1, q) - piv[1], piv[q] = piv[q], piv[1] - A[1,1] = Akk = sqrt(Akk) - AkkInv = inv(copy(Akk')) - @simd for i in 2:n - A[i,1] *= AkkInv - end - - for k in 2:n - @simd for j in k:n - dots[j] += abs2(A[j, k-1]) - temp[j] = real(A[j,j]) - dots[j] - end - Akk, q = findmax(i -> temp[i], k:n) - Akk ≤ stop && return A, piv, convert(BlasInt, k-1), convert(BlasInt, 1) - q += k - 1 - # swap - _swap_rowcols!(A, LowerTriangular, n, k, q) - dots[k], dots[q] = dots[q], dots[k] - piv[k], piv[q] = piv[q], piv[k] - # update - A[k,k] = Akk = sqrt(Akk) - for j in 1:(k-1) - Akjc = A[k,j]' - @simd for i in (k+1):n - A[i,k] -= A[i,j]*Akjc - end - end - AkkInv = inv(copy(Akk')) - @simd for i in (k+1):n - A[i, k] *= AkkInv - end - end - return A, piv, convert(BlasInt, n), convert(BlasInt, 0) - end -end -function _cholpivoted!(x::Number, tol) - rx = real(x) - iszero(rx) && return (rx, convert(BlasInt, 1)) - rxr = sqrt(abs(rx)) - rval = convert(promote_type(typeof(x), typeof(rxr)), rxr) - return (rval, convert(BlasInt, !(rx == abs(x) > tol))) -end - -# cholesky!. Destructive methods for computing Cholesky factorization of real symmetric -# or Hermitian matrix -## No pivoting (default) -function cholesky!(A::SelfAdjoint, ::NoPivot = NoPivot(); check::Bool = true) - C, info = _chol!(A.data, A.uplo == 'U' ? UpperTriangular : LowerTriangular) - check && checkpositivedefinite(info) - return Cholesky(C.data, A.uplo, info) -end - -### for AbstractMatrix, check that matrix is symmetric/Hermitian -""" - cholesky!(A::AbstractMatrix, NoPivot(); check = true) -> Cholesky - -The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, -instead of creating a copy. An [`InexactError`](@ref) exception is thrown if -the factorization produces a number not representable by the element type of -`A`, e.g. for integer types. - -# Examples -```jldoctest -julia> A = [1 2; 2 50] -2×2 Matrix{Int64}: - 1 2 - 2 50 - -julia> cholesky!(A) -ERROR: InexactError: Int64(6.782329983125268) -Stacktrace: -[...] -``` -""" -function cholesky!(A::AbstractMatrix, ::NoPivot = NoPivot(); check::Bool = true) - checksquare(A) - if !ishermitian(A) # return with info = -1 if not Hermitian - check && checkpositivedefinite(convert(BlasInt, -1)) - return Cholesky(A, 'U', convert(BlasInt, -1)) - else - return cholesky!(Hermitian(A), NoPivot(); check = check) - end -end -@deprecate cholesky!(A::StridedMatrix, ::Val{false}; check::Bool = true) cholesky!(A, NoPivot(); check) false -@deprecate cholesky!(A::RealHermSymComplexHerm, ::Val{false}; check::Bool = true) cholesky!(A, NoPivot(); check) false - -## With pivoting -### Non BLAS/LAPACK element types (generic). -function cholesky!(A::SelfAdjoint, ::RowMaximum; tol = 0.0, check::Bool = true) - AA, piv, rank, info = _cholpivoted!(A.data, A.uplo == 'U' ? UpperTriangular : LowerTriangular, tol, check) - C = CholeskyPivoted(AA, A.uplo, piv, rank, tol, info) - check && chkfullrank(C) - return C -end -@deprecate cholesky!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; kwargs...) cholesky!(A, RowMaximum(); kwargs...) false - -""" - cholesky!(A::AbstractMatrix, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted - -The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, -instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the -factorization produces a number not representable by the element type of `A`, -e.g. for integer types. -""" -function cholesky!(A::AbstractMatrix, ::RowMaximum; tol = 0.0, check::Bool = true) - checksquare(A) - if !ishermitian(A) - C = CholeskyPivoted(A, 'U', Vector{BlasInt}(), convert(BlasInt, 1), - tol, convert(BlasInt, -1)) - check && checkpositivedefinite(convert(BlasInt, -1)) - return C - else - return cholesky!(Hermitian(A), RowMaximum(); tol, check) - end -end -@deprecate cholesky!(A::StridedMatrix, ::Val{true}; kwargs...) cholesky!(A, RowMaximum(); kwargs...) false - -# cholesky. Non-destructive methods for computing Cholesky factorization of real symmetric -# or Hermitian matrix -## No pivoting (default) -""" - cholesky(A, NoPivot(); check = true) -> Cholesky - -Compute the Cholesky factorization of a dense symmetric positive definite matrix `A` -and return a [`Cholesky`](@ref) factorization. The matrix `A` can either be a [`Symmetric`](@ref) or [`Hermitian`](@ref) -[`AbstractMatrix`](@ref) or a *perfectly* symmetric or Hermitian `AbstractMatrix`. - -The triangular Cholesky factor can be obtained from the factorization `F` via `F.L` and `F.U`, -where `A ≈ F.U' * F.U ≈ F.L * F.L'`. - -The following functions are available for `Cholesky` objects: [`size`](@ref), [`\\`](@ref), -[`inv`](@ref), [`det`](@ref), [`logdet`](@ref) and [`isposdef`](@ref). - -If you have a matrix `A` that is slightly non-Hermitian due to roundoff errors in its construction, -wrap it in `Hermitian(A)` before passing it to `cholesky` in order to treat it as perfectly Hermitian. - -When `check = true`, an error is thrown if the decomposition fails. -When `check = false`, responsibility for checking the decomposition's -validity (via [`issuccess`](@ref)) lies with the user. - -# Examples -```jldoctest -julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.] -3×3 Matrix{Float64}: - 4.0 12.0 -16.0 - 12.0 37.0 -43.0 - -16.0 -43.0 98.0 - -julia> C = cholesky(A) -Cholesky{Float64, Matrix{Float64}} -U factor: -3×3 UpperTriangular{Float64, Matrix{Float64}}: - 2.0 6.0 -8.0 - ⋅ 1.0 5.0 - ⋅ ⋅ 3.0 - -julia> C.U -3×3 UpperTriangular{Float64, Matrix{Float64}}: - 2.0 6.0 -8.0 - ⋅ 1.0 5.0 - ⋅ ⋅ 3.0 - -julia> C.L -3×3 LowerTriangular{Float64, Matrix{Float64}}: - 2.0 ⋅ ⋅ - 6.0 1.0 ⋅ - -8.0 5.0 3.0 - -julia> C.L * C.U == A -true -``` -""" -cholesky(A::AbstractMatrix, ::NoPivot=NoPivot(); check::Bool = true) = - _cholesky(cholcopy(A); check) -@deprecate cholesky(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, ::Val{false}; check::Bool = true) cholesky(A, NoPivot(); check) false - -function cholesky(A::AbstractMatrix{Float16}, ::NoPivot=NoPivot(); check::Bool = true) - X = _cholesky(cholcopy(A); check = check) - return Cholesky{Float16}(X) -end -@deprecate cholesky(A::Union{StridedMatrix{Float16},RealHermSymComplexHerm{Float16,<:StridedMatrix}}, ::Val{false}; check::Bool = true) cholesky(A, NoPivot(); check) false -# allow packages like SparseArrays.jl to hook into here and redirect to out-of-place `cholesky` -_cholesky(A::AbstractMatrix, args...; kwargs...) = cholesky!(A, args...; kwargs...) - -# allow cholesky of cholesky -cholesky(A::Cholesky) = A - -## With pivoting -""" - cholesky(A, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted - -Compute the pivoted Cholesky factorization of a dense symmetric positive semi-definite matrix `A` -and return a [`CholeskyPivoted`](@ref) factorization. The matrix `A` can either be a [`Symmetric`](@ref) -or [`Hermitian`](@ref) [`AbstractMatrix`](@ref) or a *perfectly* symmetric or Hermitian `AbstractMatrix`. - -The triangular Cholesky factor can be obtained from the factorization `F` via `F.L` and `F.U`, -and the permutation via `F.p`, where `A[F.p, F.p] ≈ Ur' * Ur ≈ Lr * Lr'` with `Ur = F.U[1:F.rank, :]` -and `Lr = F.L[:, 1:F.rank]`, or alternatively `A ≈ Up' * Up ≈ Lp * Lp'` with -`Up = F.U[1:F.rank, invperm(F.p)]` and `Lp = F.L[invperm(F.p), 1:F.rank]`. - -The following functions are available for `CholeskyPivoted` objects: -[`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), and [`rank`](@ref). - -The argument `tol` determines the tolerance for determining the rank. -For negative values, the tolerance is equal to `eps()*size(A,1)*maximum(diag(A))`. - -If you have a matrix `A` that is slightly non-Hermitian due to roundoff errors in its construction, -wrap it in `Hermitian(A)` before passing it to `cholesky` in order to treat it as perfectly Hermitian. - -When `check = true`, an error is thrown if the decomposition fails. -When `check = false`, responsibility for checking the decomposition's -validity (via [`issuccess`](@ref)) lies with the user. - -# Examples -```jldoctest -julia> X = [1.0, 2.0, 3.0, 4.0]; - -julia> A = X * X'; - -julia> C = cholesky(A, RowMaximum(), check = false) -CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}} -U factor with rank 1: -4×4 UpperTriangular{Float64, Matrix{Float64}}: - 4.0 2.0 3.0 1.0 - ⋅ 0.0 6.0 2.0 - ⋅ ⋅ 9.0 3.0 - ⋅ ⋅ ⋅ 1.0 -permutation: -4-element Vector{Int64}: - 4 - 2 - 3 - 1 - -julia> C.U[1:C.rank, :]' * C.U[1:C.rank, :] ≈ A[C.p, C.p] -true - -julia> l, u = C; # destructuring via iteration - -julia> l == C.L && u == C.U -true -``` -""" -cholesky(A::AbstractMatrix, ::RowMaximum; tol = 0.0, check::Bool = true) = - _cholesky(cholcopy(A), RowMaximum(); tol, check) -@deprecate cholesky(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, ::Val{true}; tol = 0.0, check::Bool = true) cholesky(A, RowMaximum(); tol, check) false - -function cholesky(A::AbstractMatrix{Float16}, ::RowMaximum; tol = 0.0, check::Bool = true) - X = _cholesky(cholcopy(A), RowMaximum(); tol, check) - return CholeskyPivoted{Float16}(X) -end - -## Number -function cholesky(x::Number, uplo::Symbol=:U) - C, info = _chol!(x, uplo) - xf = fill(C, 1, 1) - Cholesky(xf, uplo, info) -end - - -function Cholesky{T}(C::Cholesky) where T - Cnew = convert(AbstractMatrix{T}, C.factors) - Cholesky{T, typeof(Cnew)}(Cnew, C.uplo, C.info) -end -Cholesky{T,S}(C::Cholesky) where {T,S<:AbstractMatrix} = Cholesky{T,S}(C.factors, C.uplo, C.info) -Factorization{T}(C::Cholesky{T}) where {T} = C -Factorization{T}(C::Cholesky) where {T} = Cholesky{T}(C) -CholeskyPivoted{T}(C::CholeskyPivoted{T}) where {T} = C -CholeskyPivoted{T}(C::CholeskyPivoted) where {T} = - CholeskyPivoted(AbstractMatrix{T}(C.factors), C.uplo, C.piv, C.rank, C.tol, C.info) -CholeskyPivoted{T,S}(C::CholeskyPivoted) where {T,S<:AbstractMatrix} = - CholeskyPivoted{T,S,typeof(C.piv)}(C.factors, C.uplo, C.piv, C.rank, C.tol, C.info) -CholeskyPivoted{T,S,P}(C::CholeskyPivoted) where {T,S<:AbstractMatrix,P<:AbstractVector{<:Integer}} = - CholeskyPivoted{T,S,P}(C.factors, C.uplo, C.piv, C.rank, C.tol, C.info) -Factorization{T}(C::CholeskyPivoted{T}) where {T} = C -Factorization{T}(C::CholeskyPivoted) where {T} = CholeskyPivoted{T}(C) - -AbstractMatrix(C::Cholesky) = C.uplo == 'U' ? C.U'C.U : C.L*C.L' -AbstractArray(C::Cholesky) = AbstractMatrix(C) -Matrix(C::Cholesky) = Array(AbstractArray(C)) -Array(C::Cholesky) = Matrix(C) - -function AbstractMatrix(F::CholeskyPivoted) - ip = invperm(F.p) - U = F.U[1:F.rank,ip] - U'U -end -AbstractArray(F::CholeskyPivoted) = AbstractMatrix(F) -Matrix(F::CholeskyPivoted) = Array(AbstractArray(F)) -Array(F::CholeskyPivoted) = Matrix(F) - -copy(C::Cholesky) = Cholesky(copy(C.factors), C.uplo, C.info) -copy(C::CholeskyPivoted) = CholeskyPivoted(copy(C.factors), C.uplo, C.piv, C.rank, C.tol, C.info) - -size(C::Union{Cholesky, CholeskyPivoted}) = size(C.factors) -size(C::Union{Cholesky, CholeskyPivoted}, d::Integer) = size(C.factors, d) - -function _choleskyUfactor(Cfactors, Cuplo) - if Cuplo === 'U' - return UpperTriangular(Cfactors) - else - return copy(LowerTriangular(Cfactors)') - end -end -function _choleskyLfactor(Cfactors, Cuplo) - if Cuplo === 'L' - return LowerTriangular(Cfactors) - else - return copy(UpperTriangular(Cfactors)') - end -end - -function getproperty(C::Cholesky, d::Symbol) - Cfactors = getfield(C, :factors) - Cuplo = getfield(C, :uplo) - if d === :U - _choleskyUfactor(Cfactors, Cuplo) - elseif d === :L - _choleskyLfactor(Cfactors, Cuplo) - elseif d === :UL - return (Cuplo === 'U' ? UpperTriangular(Cfactors) : LowerTriangular(Cfactors)) - else - return getfield(C, d) - end -end -Base.propertynames(F::Cholesky, private::Bool=false) = - (:U, :L, :UL, (private ? fieldnames(typeof(F)) : ())...) - -function Base.:(==)(C1::Cholesky, C2::Cholesky) - C1.uplo == C2.uplo || return false - C1.uplo == 'L' ? (C1.L == C2.L) : (C1.U == C2.U) -end - -function getproperty(C::CholeskyPivoted{T}, d::Symbol) where {T} - Cfactors = getfield(C, :factors) - Cuplo = getfield(C, :uplo) - if d === :U - _choleskyUfactor(Cfactors, Cuplo) - elseif d === :L - _choleskyLfactor(Cfactors, Cuplo) - elseif d === :p - return getfield(C, :piv) - elseif d === :P - n = size(C, 1) - P = zeros(T, n, n) - for i = 1:n - P[getfield(C, :piv)[i], i] = one(T) - end - return P - else - return getfield(C, d) - end -end -Base.propertynames(F::CholeskyPivoted, private::Bool=false) = - (:U, :L, :p, :P, (private ? fieldnames(typeof(F)) : ())...) - -function Base.:(==)(C1::CholeskyPivoted, C2::CholeskyPivoted) - (C1.uplo == C2.uplo && C1.p == C2.p) || return false - C1.uplo == 'L' ? (C1.L == C2.L) : (C1.U == C2.U) -end - -issuccess(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 - -adjoint(C::Union{Cholesky,CholeskyPivoted}) = C - -function show(io::IO, mime::MIME{Symbol("text/plain")}, C::Cholesky) - if issuccess(C) - summary(io, C); println(io) - println(io, "$(C.uplo) factor:") - show(io, mime, C.UL) - else - print(io, "Failed factorization of type $(typeof(C))") - end -end - -function show(io::IO, mime::MIME{Symbol("text/plain")}, C::CholeskyPivoted) - summary(io, C); println(io) - println(io, "$(C.uplo) factor with rank $(rank(C)):") - show(io, mime, C.uplo == 'U' ? C.U : C.L) - println(io, "\npermutation:") - show(io, mime, C.p) -end - -ldiv!(C::Cholesky{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.potrs!(C.uplo, C.factors, B) - -function ldiv!(C::Cholesky, B::AbstractVecOrMat) - if C.uplo == 'L' - return ldiv!(adjoint(LowerTriangular(C.factors)), ldiv!(LowerTriangular(C.factors), B)) - else - return ldiv!(UpperTriangular(C.factors), ldiv!(adjoint(UpperTriangular(C.factors)), B)) - end -end - -function ldiv!(C::CholeskyPivoted{T,<:StridedMatrix}, B::StridedVector{T}) where T<:BlasFloat - invpermute!(LAPACK.potrs!(C.uplo, C.factors, permute!(B, C.piv)), C.piv) -end -function ldiv!(C::CholeskyPivoted{T,<:StridedMatrix}, B::StridedMatrix{T}) where T<:BlasFloat - n = size(C, 1) - for i=1:size(B, 2) - permute!(view(B, 1:n, i), C.piv) - end - LAPACK.potrs!(C.uplo, C.factors, B) - for i=1:size(B, 2) - invpermute!(view(B, 1:n, i), C.piv) - end - B -end - -function ldiv!(C::CholeskyPivoted, B::AbstractVector) - if C.uplo == 'L' - ldiv!(adjoint(LowerTriangular(C.factors)), - ldiv!(LowerTriangular(C.factors), permute!(B, C.piv))) - else - ldiv!(UpperTriangular(C.factors), - ldiv!(adjoint(UpperTriangular(C.factors)), permute!(B, C.piv))) - end - invpermute!(B, C.piv) -end - -function ldiv!(C::CholeskyPivoted, B::AbstractMatrix) - n = size(C, 1) - for i in 1:size(B, 2) - permute!(view(B, 1:n, i), C.piv) - end - if C.uplo == 'L' - ldiv!(adjoint(LowerTriangular(C.factors)), - ldiv!(LowerTriangular(C.factors), B)) - else - ldiv!(UpperTriangular(C.factors), - ldiv!(adjoint(UpperTriangular(C.factors)), B)) - end - for i in 1:size(B, 2) - invpermute!(view(B, 1:n, i), C.piv) - end - B -end - -function rdiv!(B::AbstractMatrix, C::Cholesky) - if C.uplo == 'L' - return rdiv!(rdiv!(B, adjoint(LowerTriangular(C.factors))), LowerTriangular(C.factors)) - else - return rdiv!(rdiv!(B, UpperTriangular(C.factors)), adjoint(UpperTriangular(C.factors))) - end -end - -function LinearAlgebra.rdiv!(B::AbstractMatrix, C::CholeskyPivoted) - n = size(C, 2) - for i in 1:size(B, 1) - permute!(view(B, i, 1:n), C.piv) - end - if C.uplo == 'L' - rdiv!(rdiv!(B, adjoint(LowerTriangular(C.factors))), - LowerTriangular(C.factors)) - else - rdiv!(rdiv!(B, UpperTriangular(C.factors)), - adjoint(UpperTriangular(C.factors))) - end - for i in 1:size(B, 1) - invpermute!(view(B, i, 1:n), C.piv) - end - B -end - -isposdef(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 - -function det(C::Cholesky) - dd = one(real(eltype(C))) - @inbounds for i in 1:size(C.factors,1) - dd *= real(C.factors[i,i])^2 - end - return dd -end - -function logdet(C::Cholesky) - dd = zero(real(eltype(C))) - @inbounds for i in 1:size(C.factors,1) - dd += log(real(C.factors[i,i])) - end - dd + dd # instead of 2.0dd which can change the type -end - -function det(C::CholeskyPivoted) - if C.rank < size(C.factors, 1) - return zero(real(eltype(C))) - else - dd = one(real(eltype(C))) - for i in 1:size(C.factors,1) - dd *= real(C.factors[i,i])^2 - end - return dd - end -end - -function logdet(C::CholeskyPivoted) - if C.rank < size(C.factors, 1) - return real(eltype(C))(-Inf) - else - dd = zero(real(eltype(C))) - for i in 1:size(C.factors,1) - dd += log(real(C.factors[i,i])) - end - return dd + dd # instead of 2.0dd which can change the type - end -end - -logabsdet(C::Union{Cholesky, CholeskyPivoted}) = logdet(C), one(eltype(C)) # since C is p.s.d. - -inv!(C::Cholesky{<:BlasFloat,<:StridedMatrix}) = - copytri!(LAPACK.potri!(C.uplo, C.factors), C.uplo, true) - -inv(C::Cholesky{<:BlasFloat,<:StridedMatrix}) = inv!(copy(C)) - -function inv(C::CholeskyPivoted{<:BlasFloat,<:StridedMatrix}) - ipiv = invperm(C.piv) - copytri!(LAPACK.potri!(C.uplo, copy(C.factors)), C.uplo, true)[ipiv, ipiv] -end - -function chkfullrank(C::CholeskyPivoted) - if C.rank < size(C.factors, 1) - throw(RankDeficientException(C.rank)) - end -end - -rank(C::CholeskyPivoted) = C.rank - -""" - lowrankupdate!(C::Cholesky, v::AbstractVector) -> CC::Cholesky - -Update a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then -`CC = cholesky(C.U'C.U + v*v')` but the computation of `CC` only uses `O(n^2)` -operations. The input factorization `C` is updated in place such that on exit `C == CC`. -The vector `v` is destroyed during the computation. -""" -function lowrankupdate!(C::Cholesky, v::AbstractVector) - A = C.factors - n = length(v) - if size(C, 1) != n - throw(DimensionMismatch("updating vector must fit size of factorization")) - end - if C.uplo == 'U' - conj!(v) - end - - for i = 1:n - - # Compute Givens rotation - c, s, r = givensAlgorithm(A[i,i], v[i]) - - # Store new diagonal element - A[i,i] = r - - # Update remaining elements in row/column - if C.uplo == 'U' - for j = i + 1:n - Aij = A[i,j] - vj = v[j] - A[i,j] = c*Aij + s*vj - v[j] = -s'*Aij + c*vj - end - else - for j = i + 1:n - Aji = A[j,i] - vj = v[j] - A[j,i] = c*Aji + s*vj - v[j] = -s'*Aji + c*vj - end - end - end - return C -end - -""" - lowrankdowndate!(C::Cholesky, v::AbstractVector) -> CC::Cholesky - -Downdate a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then -`CC = cholesky(C.U'C.U - v*v')` but the computation of `CC` only uses `O(n^2)` -operations. The input factorization `C` is updated in place such that on exit `C == CC`. -The vector `v` is destroyed during the computation. -""" -function lowrankdowndate!(C::Cholesky, v::AbstractVector) - A = C.factors - n = length(v) - if size(C, 1) != n - throw(DimensionMismatch("updating vector must fit size of factorization")) - end - if C.uplo == 'U' - conj!(v) - end - - for i = 1:n - - Aii = A[i,i] - - # Compute Givens rotation - s = conj(v[i]/Aii) - s2 = abs2(s) - if s2 > 1 - throw(LinearAlgebra.PosDefException(i)) - end - c = sqrt(1 - abs2(s)) - - # Store new diagonal element - A[i,i] = c*Aii - - # Update remaining elements in row/column - if C.uplo == 'U' - for j = i + 1:n - vj = v[j] - Aij = (A[i,j] - s*vj)/c - A[i,j] = Aij - v[j] = -s'*Aij + c*vj - end - else - for j = i + 1:n - vj = v[j] - Aji = (A[j,i] - s*vj)/c - A[j,i] = Aji - v[j] = -s'*Aji + c*vj - end - end - end - return C -end - -""" - lowrankupdate(C::Cholesky, v::AbstractVector) -> CC::Cholesky - -Update a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` -then `CC = cholesky(C.U'C.U + v*v')` but the computation of `CC` only uses -`O(n^2)` operations. -""" -lowrankupdate(C::Cholesky, v::AbstractVector) = lowrankupdate!(copy(C), copy(v)) - -""" - lowrankdowndate(C::Cholesky, v::AbstractVector) -> CC::Cholesky - -Downdate a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` -then `CC = cholesky(C.U'C.U - v*v')` but the computation of `CC` only uses -`O(n^2)` operations. -""" -lowrankdowndate(C::Cholesky, v::AbstractVector) = lowrankdowndate!(copy(C), copy(v)) - -function diag(C::Cholesky{T}, k::Int = 0) where {T} - N = size(C, 1) - absk = abs(k) - iabsk = N - absk - z = Vector{T}(undef, iabsk) - UL = C.factors - if C.uplo == 'U' - for i in 1:iabsk - z[i] = zero(T) - for j in 1:min(i, i+absk) - z[i] += UL[j, i]'UL[j, i+absk] - end - end - else - for i in 1:iabsk - z[i] = zero(T) - for j in 1:min(i, i+absk) - z[i] += UL[i, j]*UL[i+absk, j]' - end - end - end - if !(T <: Real) && k < 0 - z .= adjoint.(z) - end - return z -end diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl deleted file mode 100644 index 5e47984120196..0000000000000 --- a/stdlib/LinearAlgebra/src/dense.jl +++ /dev/null @@ -1,1885 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Linear algebra functions for dense matrices in column major format - -## BLAS cutoff threshold constants - -#TODO const DOT_CUTOFF = 128 -const ASUM_CUTOFF = 32 -const NRM2_CUTOFF = 32 - -# Generic cross-over constant based on benchmarking on a single thread with an i7 CPU @ 2.5GHz -# L1 cache: 32K, L2 cache: 256K, L3 cache: 6144K -# This constant should ideally be determined by the actual CPU cache size -const ISONE_CUTOFF = 2^21 # 2M - -function isone(A::AbstractMatrix) - require_one_based_indexing(A) # multiplication not defined yet among offset matrices - m, n = size(A) - m != n && return false # only square matrices can satisfy x == one(x) - if sizeof(A) < ISONE_CUTOFF - _isone_triacheck(A) - else - _isone_cachefriendly(A) - end -end - -@inline function _isone_triacheck(A::AbstractMatrix) - @inbounds for i in axes(A,2), j in axes(A,1) - if i == j - isone(A[i,i]) || return false - else - iszero(A[i,j]) && iszero(A[j,i]) || return false - end - end - return true -end - -# Inner loop over rows to be friendly to the CPU cache -@inline function _isone_cachefriendly(A::AbstractMatrix) - @inbounds for i in axes(A,2), j in axes(A,1) - if i == j - isone(A[i,i]) || return false - else - iszero(A[j,i]) || return false - end - end - return true -end - - -""" - isposdef!(A) -> Bool - -Test whether a matrix is positive definite (and Hermitian) by trying to perform a -Cholesky factorization of `A`, overwriting `A` in the process. -See also [`isposdef`](@ref). - -# Examples -```jldoctest -julia> A = [1. 2.; 2. 50.]; - -julia> isposdef!(A) -true - -julia> A -2×2 Matrix{Float64}: - 1.0 2.0 - 2.0 6.78233 -``` -""" -isposdef!(A::AbstractMatrix) = - ishermitian(A) && isposdef(cholesky!(Hermitian(A); check = false)) - -""" - isposdef(A) -> Bool - -Test whether a matrix is positive definite (and Hermitian) by trying to perform a -Cholesky factorization of `A`. - -See also [`isposdef!`](@ref), [`cholesky`](@ref). - -# Examples -```jldoctest -julia> A = [1 2; 2 50] -2×2 Matrix{Int64}: - 1 2 - 2 50 - -julia> isposdef(A) -true -``` -""" -isposdef(A::AbstractMatrix) = - ishermitian(A) && isposdef(cholesky(Hermitian(A); check = false)) -isposdef(x::Number) = imag(x)==0 && real(x) > 0 - -function norm(x::StridedVector{T}, rx::Union{UnitRange{TI},AbstractRange{TI}}) where {T<:BlasFloat,TI<:Integer} - if minimum(rx) < 1 || maximum(rx) > length(x) - throw(BoundsError(x, rx)) - end - GC.@preserve x BLAS.nrm2(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx)) -end - -norm1(x::Union{Array{T},StridedVector{T}}) where {T<:BlasReal} = - length(x) < ASUM_CUTOFF ? generic_norm1(x) : BLAS.asum(x) - -norm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} = - length(x) < NRM2_CUTOFF ? generic_norm2(x) : BLAS.nrm2(x) - -# Conservative assessment of types that have zero(T) defined for themselves -""" - haszero(T::Type) - -Return whether a type `T` has a unique zero element defined using `zero(T)`. -If a type `M` specializes `zero(M)`, it may also choose to set `haszero(M)` to `true`. -By default, `haszero` is assumed to be `false`, in which case the zero elements -are deduced from values rather than the type. - -!!! note - `haszero` is a conservative check that is used to dispatch to - optimized paths. Extending it is optional, but encouraged. -""" -haszero(::Type) = false -haszero(::Type{T}) where {T<:Number} = isconcretetype(T) -haszero(::Type{Union{Missing,T}}) where {T<:Number} = haszero(T) -@propagate_inbounds _zero(M::AbstractArray{T}, inds...) where {T} = haszero(T) ? zero(T) : zero(M[inds...]) - -""" - triu!(M, k::Integer) - -Return the upper triangle of `M` starting from the `k`th superdiagonal, -overwriting `M` in the process. - -# Examples -```jldoctest -julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5] -5×5 Matrix{Int64}: - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - -julia> triu!(M, 1) -5×5 Matrix{Int64}: - 0 2 3 4 5 - 0 0 3 4 5 - 0 0 0 4 5 - 0 0 0 0 5 - 0 0 0 0 0 -``` -""" -function triu!(M::AbstractMatrix, k::Integer) - require_one_based_indexing(M) - m, n = size(M) - for j in 1:min(n, m + k) - for i in max(1, j - k + 1):m - @inbounds M[i,j] = _zero(M, i,j) - end - end - M -end - -triu(M::Matrix, k::Integer) = triu!(copy(M), k) - -""" - tril!(M, k::Integer) - -Return the lower triangle of `M` starting from the `k`th superdiagonal, overwriting `M` in -the process. - -# Examples -```jldoctest -julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5] -5×5 Matrix{Int64}: - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - -julia> tril!(M, 2) -5×5 Matrix{Int64}: - 1 2 3 0 0 - 1 2 3 4 0 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 -``` -""" -function tril!(M::AbstractMatrix, k::Integer) - require_one_based_indexing(M) - m, n = size(M) - for j in max(1, k + 1):n - for i in 1:min(j - k - 1, m) - @inbounds M[i,j] = _zero(M, i,j) - end - end - M -end - -tril(M::Matrix, k::Integer) = tril!(copy(M), k) - -""" - fillband!(A::AbstractMatrix, x, l, u) - -Fill the band between diagonals `l` and `u` with the value `x`. -""" -function fillband!(A::AbstractMatrix{T}, x, l, u) where T - require_one_based_indexing(A) - m, n = size(A) - xT = convert(T, x) - for j in axes(A,2) - for i in max(1,j-u):min(m,j-l) - @inbounds A[i, j] = xT - end - end - return A -end - -diagind(m::Integer, n::Integer, k::Integer=0) = diagind(IndexLinear(), m, n, k) -diagind(::IndexLinear, m::Integer, n::Integer, k::Integer=0) = - k <= 0 ? range(1-k, step=m+1, length=min(m+k, n)) : range(k*m+1, step=m+1, length=min(m, n-k)) - -function diagind(::IndexCartesian, m::Integer, n::Integer, k::Integer=0) - Cstart = CartesianIndex(1 + max(0,-k), 1 + max(0,k)) - Cstep = CartesianIndex(1, 1) - length = max(0, k <= 0 ? min(m+k, n) : min(m, n-k)) - StepRangeLen(Cstart, Cstep, length) -end - -""" - diagind(M::AbstractMatrix, k::Integer = 0, indstyle::IndexStyle = IndexLinear()) - diagind(M::AbstractMatrix, indstyle::IndexStyle = IndexLinear()) - -An `AbstractRange` giving the indices of the `k`th diagonal of the matrix `M`. -Optionally, an index style may be specified which determines the type of the range returned. -If `indstyle isa IndexLinear` (default), this returns an `AbstractRange{Integer}`. -On the other hand, if `indstyle isa IndexCartesian`, this returns an `AbstractRange{CartesianIndex{2}}`. - -If `k` is not provided, it is assumed to be `0` (corresponding to the main diagonal). - -See also: [`diag`](@ref), [`diagm`](@ref), [`Diagonal`](@ref). - -# Examples -```jldoctest -julia> A = [1 2 3; 4 5 6; 7 8 9] -3×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - 7 8 9 - -julia> diagind(A, -1) -2:4:6 - -julia> diagind(A, IndexCartesian()) -StepRangeLen(CartesianIndex(1, 1), CartesianIndex(1, 1), 3) -``` - -!!! compat "Julia 1.11" - Specifying an `IndexStyle` requires at least Julia 1.11. -""" -function diagind(A::AbstractMatrix, k::Integer=0, indexstyle::IndexStyle = IndexLinear()) - require_one_based_indexing(A) - diagind(indexstyle, size(A,1), size(A,2), k) -end - -diagind(A::AbstractMatrix, indexstyle::IndexStyle) = diagind(A, 0, indexstyle) - -""" - diag(M, k::Integer=0) - -The `k`th diagonal of a matrix, as a vector. - -See also [`diagm`](@ref), [`diagind`](@ref), [`Diagonal`](@ref), [`isdiag`](@ref). - -# Examples -```jldoctest -julia> A = [1 2 3; 4 5 6; 7 8 9] -3×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - 7 8 9 - -julia> diag(A,1) -2-element Vector{Int64}: - 2 - 6 -``` -""" -diag(A::AbstractMatrix, k::Integer=0) = A[diagind(A, k, IndexStyle(A))] - -""" - diagview(M, k::Integer=0) - -Return a view into the `k`th diagonal of the matrix `M`. - -See also [`diag`](@ref), [`diagind`](@ref). - -# Examples -```jldoctest -julia> A = [1 2 3; 4 5 6; 7 8 9] -3×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - 7 8 9 - -julia> diagview(A) -3-element view(::Vector{Int64}, 1:4:9) with eltype Int64: - 1 - 5 - 9 - -julia> diagview(A, 1) -2-element view(::Vector{Int64}, 4:4:8) with eltype Int64: - 2 - 6 -``` -""" -diagview(A::AbstractMatrix, k::Integer=0) = @view A[diagind(A, k, IndexStyle(A))] - -""" - diagm(kv::Pair{<:Integer,<:AbstractVector}...) - diagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) - -Construct a matrix from `Pair`s of diagonals and vectors. -Vector `kv.second` will be placed on the `kv.first` diagonal. -By default the matrix is square and its size is inferred -from `kv`, but a non-square size `m`×`n` (padded with zeros as needed) -can be specified by passing `m,n` as the first arguments. -For repeated diagonal indices `kv.first` the values in the corresponding -vectors `kv.second` will be added. - -`diagm` constructs a full matrix; if you want storage-efficient -versions with fast arithmetic, see [`Diagonal`](@ref), [`Bidiagonal`](@ref) -[`Tridiagonal`](@ref) and [`SymTridiagonal`](@ref). - -# Examples -```jldoctest -julia> diagm(1 => [1,2,3]) -4×4 Matrix{Int64}: - 0 1 0 0 - 0 0 2 0 - 0 0 0 3 - 0 0 0 0 - -julia> diagm(1 => [1,2,3], -1 => [4,5]) -4×4 Matrix{Int64}: - 0 1 0 0 - 4 0 2 0 - 0 5 0 3 - 0 0 0 0 - -julia> diagm(1 => [1,2,3], 1 => [1,2,3]) -4×4 Matrix{Int64}: - 0 2 0 0 - 0 0 4 0 - 0 0 0 6 - 0 0 0 0 -``` -""" -diagm(kv::Pair{<:Integer,<:AbstractVector}...) = _diagm(nothing, kv...) -diagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) = _diagm((Int(m),Int(n)), kv...) -function _diagm(size, kv::Pair{<:Integer,<:AbstractVector}...) - A = diagm_container(size, kv...) - for p in kv - inds = diagind(A, p.first) - for (i, val) in enumerate(p.second) - A[inds[i]] += val - end - end - return A -end -function diagm_size(size::Nothing, kv::Pair{<:Integer,<:AbstractVector}...) - mnmax = mapreduce(x -> length(x.second) + abs(Int(x.first)), max, kv; init=0) - return mnmax, mnmax -end -function diagm_size(size::Tuple{Int,Int}, kv::Pair{<:Integer,<:AbstractVector}...) - mmax = mapreduce(x -> length(x.second) - min(0,Int(x.first)), max, kv; init=0) - nmax = mapreduce(x -> length(x.second) + max(0,Int(x.first)), max, kv; init=0) - m, n = size - (m ≥ mmax && n ≥ nmax) || throw(DimensionMismatch(lazy"invalid size=$size")) - return m, n -end -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...)...) -end -diagm_container(size, kv::Pair{<:Integer,<:BitVector}...) = - falses(diagm_size(size, kv...)...) - -""" - diagm(v::AbstractVector) - diagm(m::Integer, n::Integer, v::AbstractVector) - -Construct a matrix with elements of the vector as diagonal elements. -By default, the matrix is square and its size is given by -`length(v)`, but a non-square size `m`×`n` can be specified -by passing `m,n` as the first arguments. - -# Examples -```jldoctest -julia> diagm([1,2,3]) -3×3 Matrix{Int64}: - 1 0 0 - 0 2 0 - 0 0 3 -``` -""" -diagm(v::AbstractVector) = diagm(0 => v) -diagm(m::Integer, n::Integer, v::AbstractVector) = diagm(m, n, 0 => v) - -function tr(A::StridedMatrix{T}) where T - checksquare(A) - isempty(A) && return zero(T) - reduce(+, (A[i] for i in diagind(A, IndexStyle(A)))) -end - -_kronsize(A::AbstractMatrix, B::AbstractMatrix) = map(*, size(A), size(B)) -_kronsize(A::AbstractMatrix, B::AbstractVector) = (size(A, 1)*length(B), size(A, 2)) -_kronsize(A::AbstractVector, B::AbstractMatrix) = (length(A)*size(B, 1), size(B, 2)) - -""" - kron!(C, A, B) - -Computes the Kronecker product of `A` and `B` and stores the result in `C`, -overwriting the existing content of `C`. This is the in-place version of [`kron`](@ref). - -!!! compat "Julia 1.6" - This function requires Julia 1.6 or later. -""" -function kron!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMat) - size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) - _kron!(C, A, B) -end -function kron!(c::AbstractVector, a::AbstractVector, b::AbstractVector) - length(c) == length(a) * length(b) || throw(DimensionMismatch("kron!")) - m = firstindex(c) - @inbounds for i in eachindex(a) - ai = a[i] - for k in eachindex(b) - c[m] = ai*b[k] - m += 1 - end - end - return c -end -kron!(c::AbstractVecOrMat, a::AbstractVecOrMat, b::Number) = mul!(c, a, b) -kron!(c::AbstractVecOrMat, a::Number, b::AbstractVecOrMat) = mul!(c, a, b) - -function _kron!(C, A::AbstractMatrix, B::AbstractMatrix) - m = firstindex(C) - @inbounds for j in axes(A,2), l in axes(B,2), i in axes(A,1) - Aij = A[i,j] - for k in axes(B,1) - C[m] = Aij*B[k,l] - m += 1 - end - end - return C -end -function _kron!(C, A::AbstractMatrix, b::AbstractVector) - m = firstindex(C) - @inbounds for j in axes(A,2), i in axes(A,1) - Aij = A[i,j] - for k in eachindex(b) - C[m] = Aij*b[k] - m += 1 - end - end - return C -end -function _kron!(C, a::AbstractVector, B::AbstractMatrix) - m = firstindex(C) - @inbounds for l in axes(B,2), i in eachindex(a) - ai = a[i] - for k in axes(B,1) - C[m] = ai*B[k,l] - m += 1 - end - end - return C -end - -""" - kron(A, B) - -Computes the Kronecker product of two vectors, matrices or numbers. - -For real vectors `v` and `w`, the Kronecker product is related to the outer product by -`kron(v,w) == vec(w * transpose(v))` or -`w * transpose(v) == reshape(kron(v,w), (length(w), length(v)))`. -Note how the ordering of `v` and `w` differs on the left and right -of these expressions (due to column-major storage). -For complex vectors, the outer product `w * v'` also differs by conjugation of `v`. - -# Examples -```jldoctest -julia> A = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> B = [im 1; 1 -im] -2×2 Matrix{Complex{Int64}}: - 0+1im 1+0im - 1+0im 0-1im - -julia> kron(A, B) -4×4 Matrix{Complex{Int64}}: - 0+1im 1+0im 0+2im 2+0im - 1+0im 0-1im 2+0im 0-2im - 0+3im 3+0im 0+4im 4+0im - 3+0im 0-3im 4+0im 0-4im - -julia> v = [1, 2]; w = [3, 4, 5]; - -julia> w*transpose(v) -3×2 Matrix{Int64}: - 3 6 - 4 8 - 5 10 - -julia> reshape(kron(v,w), (length(w), length(v))) -3×2 Matrix{Int64}: - 3 6 - 4 8 - 5 10 -``` -""" -function kron(A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}) where {T,S} - C = Matrix{promote_op(*,T,S)}(undef, _kronsize(A, B)) - return kron!(C, A, B) -end -function kron(a::AbstractVector{T}, b::AbstractVector{S}) where {T,S} - c = Vector{promote_op(*,T,S)}(undef, length(a)*length(b)) - return kron!(c, a, b) -end -kron(a::Number, b::Union{Number, AbstractVecOrMat}) = a * b -kron(a::AbstractVecOrMat, b::Number) = a * b -kron(a::AdjointAbsVec, b::AdjointAbsVec) = adjoint(kron(adjoint(a), adjoint(b))) -kron(a::AdjOrTransAbsVec, b::AdjOrTransAbsVec) = transpose(kron(transpose(a), transpose(b))) - -# Matrix power -(^)(A::AbstractMatrix, p::Integer) = p < 0 ? power_by_squaring(inv(A), -p) : power_by_squaring(A, p) -function (^)(A::AbstractMatrix{T}, p::Integer) where T<:Integer - # make sure that e.g. [1 1;1 0]^big(3) - # gets promotes in a similar way as 2^big(3) - TT = promote_op(^, T, typeof(p)) - return power_by_squaring(convert(AbstractMatrix{TT}, A), p) -end -function integerpow(A::AbstractMatrix{T}, p) where T - TT = promote_op(^, T, typeof(p)) - return (TT == T ? A : convert(AbstractMatrix{TT}, A))^Integer(p) -end -function schurpow(A::AbstractMatrix, p) - if istriu(A) - # Integer part - retmat = A ^ floor(Integer, p) - # Real part - if p - floor(p) == 0.5 - # special case: A^0.5 === sqrt(A) - retmat = retmat * sqrt(A) - else - retmat = retmat * powm!(UpperTriangular(float.(A)), real(p - floor(p))) - end - else - S,Q,d = Schur{Complex}(schur(A)) - # Integer part - R = S ^ floor(Integer, p) - # Real part - if p - floor(p) == 0.5 - # special case: A^0.5 === sqrt(A) - R = R * sqrt(S) - else - R = R * powm!(UpperTriangular(float.(S)), real(p - floor(p))) - end - retmat = Q * R * Q' - end - - # if A has nonpositive real eigenvalues, retmat is a nonprincipal matrix power. - if isreal(retmat) - return real(retmat) - else - return retmat - end -end -function (^)(A::AbstractMatrix{T}, p::Real) where T - checksquare(A) - # Quicker return if A is diagonal - if isdiag(A) - TT = promote_op(^, T, typeof(p)) - retmat = copymutable_oftype(A, TT) - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = retmat[i] ^ p - end - return retmat - end - - # For integer powers, use power_by_squaring - isinteger(p) && return integerpow(A, p) - - # If possible, use diagonalization - if ishermitian(A) - return (Hermitian(A)^p) - end - - # Otherwise, use Schur decomposition - return schurpow(A, p) -end - -""" - ^(A::AbstractMatrix, p::Number) - -Matrix power, equivalent to ``\\exp(p\\log(A))`` - -# Examples -```jldoctest -julia> [1 2; 0 3]^3 -2×2 Matrix{Int64}: - 1 26 - 0 27 -``` -""" -(^)(A::AbstractMatrix, p::Number) = exp(p*log(A)) - -# Matrix exponential - -""" - exp(A::AbstractMatrix) - -Compute the matrix exponential of `A`, defined by - -```math -e^A = \\sum_{n=0}^{\\infty} \\frac{A^n}{n!}. -``` - -For symmetric or Hermitian `A`, an eigendecomposition ([`eigen`](@ref)) is -used, otherwise the scaling and squaring algorithm (see [^H05]) is chosen. - -[^H05]: Nicholas J. Higham, "The squaring and scaling method for the matrix exponential revisited", SIAM Journal on Matrix Analysis and Applications, 26(4), 2005, 1179-1193. [doi:10.1137/090768539](https://doi.org/10.1137/090768539) - -# Examples -```jldoctest -julia> A = Matrix(1.0I, 2, 2) -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 1.0 - -julia> exp(A) -2×2 Matrix{Float64}: - 2.71828 0.0 - 0.0 2.71828 -``` -""" -exp(A::AbstractMatrix) = exp!(copy_similar(A, eigtype(eltype(A)))) -exp(A::AdjointAbsMat) = adjoint(exp(parent(A))) -exp(A::TransposeAbsMat) = transpose(exp(parent(A))) - -""" - cis(A::AbstractMatrix) - -More efficient method for `exp(im*A)` of square matrix `A` -(especially if `A` is `Hermitian` or real-`Symmetric`). - -See also [`cispi`](@ref), [`sincos`](@ref), [`exp`](@ref). - -!!! compat "Julia 1.7" - Support for using `cis` with matrices was added in Julia 1.7. - -# Examples -```jldoctest -julia> cis([π 0; 0 π]) ≈ -I -true -``` -""" -cis(A::AbstractMatrix) = exp(im * A) # fallback -cis(A::AbstractMatrix{<:Base.HWNumber}) = exp_maybe_inplace(float.(im .* A)) - -exp_maybe_inplace(A::StridedMatrix{<:Union{ComplexF32, ComplexF64}}) = exp!(A) -exp_maybe_inplace(A) = exp(A) - -""" - ^(b::Number, A::AbstractMatrix) - -Matrix exponential, equivalent to ``\\exp(\\log(b)A)``. - -!!! compat "Julia 1.1" - Support for raising `Irrational` numbers (like `ℯ`) - to a matrix was added in Julia 1.1. - -# Examples -```jldoctest -julia> 2^[1 2; 0 3] -2×2 Matrix{Float64}: - 2.0 6.0 - 0.0 8.0 - -julia> ℯ^[1 2; 0 3] -2×2 Matrix{Float64}: - 2.71828 17.3673 - 0.0 20.0855 -``` -""" -Base.:^(b::Number, A::AbstractMatrix) = exp!(log(b)*A) -# method for ℯ to explicitly elide the log(b) multiplication -Base.:^(::Irrational{:ℯ}, A::AbstractMatrix) = exp(A) - -## Destructive matrix exponential using algorithm from Higham, 2008, -## "Functions of Matrices: Theory and Computation", SIAM -function exp!(A::StridedMatrix{T}) where T<:BlasFloat - n = checksquare(A) - if isdiag(A) - for i in diagind(A, IndexStyle(A)) - A[i] = exp(A[i]) - end - return A - elseif ishermitian(A) - return copytri!(parent(exp(Hermitian(A))), 'U', true) - end - ilo, ihi, scale = LAPACK.gebal!('B', A) # modifies A - nA = opnorm(A, 1) - ## For sufficiently small nA, use lower order Padé-Approximations - if (nA <= 2.1) - if nA > 0.95 - C = T[17643225600.,8821612800.,2075673600.,302702400., - 30270240., 2162160., 110880., 3960., - 90., 1.] - elseif nA > 0.25 - C = T[17297280.,8648640.,1995840.,277200., - 25200., 1512., 56., 1.] - elseif nA > 0.015 - C = T[30240.,15120.,3360., - 420., 30., 1.] - else - C = T[120.,60.,12.,1.] - end - A2 = A * A - # Compute U and V: Even/odd terms in Padé numerator & denom - # Expansion of k=1 in for loop - P = A2 - U = similar(P) - V = similar(P) - for ind in CartesianIndices(P) - U[ind] = C[4]*P[ind] + C[2]*I[ind] - V[ind] = C[3]*P[ind] + C[1]*I[ind] - end - for k in 2:(div(length(C), 2) - 1) - P *= A2 - for ind in eachindex(P, U, V) - U[ind] += C[2k + 2] * P[ind] - V[ind] += C[2k + 1] * P[ind] - end - end - - # U = A * U, but we overwrite P to avoid an allocation - mul!(P, A, U) - # P may be seen as an alias for U in the following code - - # Padé approximant: (V-U)\(V+U) - VminU, VplusU = V, U # Reuse already allocated arrays - for ind in eachindex(V, U) - vi, ui = V[ind], P[ind] - VminU[ind] = vi - ui - VplusU[ind] = vi + ui - end - X = LAPACK.gesv!(VminU, VplusU)[1] - else - s = log2(nA/5.4) # power of 2 later reversed by squaring - if s > 0 - si = ceil(Int,s) - twopowsi = convert(T,2^si) - for ind in eachindex(A) - A[ind] /= twopowsi - end - end - CC = T[64764752532480000.,32382376266240000.,7771770303897600., - 1187353796428800., 129060195264000., 10559470521600., - 670442572800., 33522128640., 1323241920., - 40840800., 960960., 16380., - 182., 1.] - A2 = A * A - A4 = A2 * A2 - A6 = A2 * A4 - tmp1, tmp2 = similar(A6), similar(A6) - - # Allocation economical version of: - # U = A * (A6 * (CC[14].*A6 .+ CC[12].*A4 .+ CC[10].*A2) .+ - # CC[8].*A6 .+ CC[6].*A4 .+ CC[4]*A2+CC[2]*I) - for ind in eachindex(tmp1) - tmp1[ind] = CC[14]*A6[ind] + CC[12]*A4[ind] + CC[10]*A2[ind] - tmp2[ind] = CC[8]*A6[ind] + CC[6]*A4[ind] + CC[4]*A2[ind] - end - mul!(tmp2, true,CC[2]*I, true, true) # tmp2 .+= CC[2]*I - U = mul!(tmp2, A6, tmp1, true, true) - U, tmp1 = mul!(tmp1, A, U), A # U = A * U0 - - # Allocation economical version of: - # V = A6 * (CC[13].*A6 .+ CC[11].*A4 .+ CC[9].*A2) .+ - # CC[7].*A6 .+ CC[5].*A4 .+ CC[3]*A2 .+ CC[1]*I - for ind in eachindex(tmp1) - tmp1[ind] = CC[13]*A6[ind] + CC[11]*A4[ind] + CC[9]*A2[ind] - tmp2[ind] = CC[7]*A6[ind] + CC[5]*A4[ind] + CC[3]*A2[ind] - end - mul!(tmp2, true, CC[1]*I, true, true) # tmp2 .+= CC[1]*I - V = mul!(tmp2, A6, tmp1, true, true) - - for ind in eachindex(tmp1) - tmp1[ind] = V[ind] + U[ind] - tmp2[ind] = V[ind] - U[ind] # tmp2 already contained V but this seems more readable - end - X = LAPACK.gesv!(tmp2, tmp1)[1] # X now contains r_13 in Higham 2008 - - if s > 0 - # Repeated squaring to compute X = r_13^(2^si) - for t=1:si - mul!(tmp2, X, X) - X, tmp2 = tmp2, X - end - end - end - - # Undo the balancing - for j = ilo:ihi - scj = scale[j] - for i = 1:n - X[j,i] *= scj - end - for i = 1:n - X[i,j] /= scj - end - end - - if ilo > 1 # apply lower permutations in reverse order - for j in (ilo-1):-1:1 - rcswap!(j, Int(scale[j]), X) - end - end - if ihi < n # apply upper permutations in forward order - for j in (ihi+1):n - rcswap!(j, Int(scale[j]), X) - end - end - X -end - -## Swap rows i and j and columns i and j in X -function rcswap!(i::Integer, j::Integer, X::AbstractMatrix{<:Number}) - for k = axes(X,1) - X[k,i], X[k,j] = X[k,j], X[k,i] - end - for k = axes(X,2) - X[i,k], X[j,k] = X[j,k], X[i,k] - end -end - -""" - log(A::AbstractMatrix) - -If `A` has no negative real eigenvalue, compute the principal matrix logarithm of `A`, i.e. -the unique matrix ``X`` such that ``e^X = A`` and ``-\\pi < Im(\\lambda) < \\pi`` for all -the eigenvalues ``\\lambda`` of ``X``. If `A` has nonpositive eigenvalues, a nonprincipal -matrix function is returned whenever possible. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is -used, if `A` is triangular an improved version of the inverse scaling and squaring method is -employed (see [^AH12] and [^AHR13]). If `A` is real with no negative eigenvalues, then -the real Schur form is computed. Otherwise, the complex Schur form is computed. Then -the upper (quasi-)triangular algorithm in [^AHR13] is used on the upper (quasi-)triangular -factor. - -[^AH12]: Awad H. Al-Mohy and Nicholas J. Higham, "Improved inverse scaling and squaring algorithms for the matrix logarithm", SIAM Journal on Scientific Computing, 34(4), 2012, C153-C169. [doi:10.1137/110852553](https://doi.org/10.1137/110852553) - -[^AHR13]: Awad H. Al-Mohy, Nicholas J. Higham and Samuel D. Relton, "Computing the Fréchet derivative of the matrix logarithm and estimating the condition number", SIAM Journal on Scientific Computing, 35(4), 2013, C394-C410. [doi:10.1137/120885991](https://doi.org/10.1137/120885991) - -# Examples -```jldoctest -julia> A = Matrix(2.7182818*I, 2, 2) -2×2 Matrix{Float64}: - 2.71828 0.0 - 0.0 2.71828 - -julia> log(A) -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 1.0 -``` -""" -function log(A::AbstractMatrix) - # If possible, use diagonalization - if ishermitian(A) - logHermA = log(Hermitian(A)) - return ishermitian(logHermA) ? copytri!(parent(logHermA), 'U', true) : parent(logHermA) - elseif istriu(A) - return triu!(parent(log(UpperTriangular(A)))) - elseif isreal(A) - SchurF = schur(real(A)) - if istriu(SchurF.T) - logA = SchurF.Z * log(UpperTriangular(SchurF.T)) * SchurF.Z' - else - # real log exists whenever all eigenvalues are positive - is_log_real = !any(x -> isreal(x) && real(x) ≤ 0, SchurF.values) - if is_log_real - logA = SchurF.Z * log_quasitriu(SchurF.T) * SchurF.Z' - else - SchurS = Schur{Complex}(SchurF) - logA = SchurS.Z * log(UpperTriangular(SchurS.T)) * SchurS.Z' - end - end - return eltype(A) <: Complex ? complex(logA) : logA - else - SchurF = schur(A) - return SchurF.vectors * log(UpperTriangular(SchurF.T)) * SchurF.vectors' - end -end - -log(A::AdjointAbsMat) = adjoint(log(parent(A))) -log(A::TransposeAbsMat) = transpose(log(parent(A))) - -""" - sqrt(A::AbstractMatrix) - -If `A` has no negative real eigenvalues, compute the principal matrix square root of `A`, -that is the unique matrix ``X`` with eigenvalues having positive real part such that -``X^2 = A``. Otherwise, a nonprincipal square root is returned. - -If `A` is real-symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is -used to compute the square root. For such matrices, eigenvalues λ that -appear to be slightly negative due to roundoff errors are treated as if they were zero. -More precisely, matrices with all eigenvalues `≥ -rtol*(max |λ|)` are treated as semidefinite -(yielding a Hermitian square root), with negative eigenvalues taken to be zero. -`rtol` is a keyword argument to `sqrt` (in the Hermitian/real-symmetric case only) that -defaults to machine precision scaled by `size(A,1)`. - -Otherwise, the square root is determined by means of the -Björck-Hammarling method [^BH83], which computes the complex Schur form ([`schur`](@ref)) -and then the complex square root of the triangular factor. -If a real square root exists, then an extension of this method [^H87] that computes the real -Schur form and then the real square root of the quasi-triangular factor is instead used. - -[^BH83]: - - Åke Björck and Sven Hammarling, "A Schur method for the square root of a matrix", - Linear Algebra and its Applications, 52-53, 1983, 127-140. - [doi:10.1016/0024-3795(83)80010-X](https://doi.org/10.1016/0024-3795(83)80010-X) - -[^H87]: - - Nicholas J. Higham, "Computing real square roots of a real matrix", - Linear Algebra and its Applications, 88-89, 1987, 405-430. - [doi:10.1016/0024-3795(87)90118-2](https://doi.org/10.1016/0024-3795(87)90118-2) - -# Examples -```jldoctest -julia> A = [4 0; 0 4] -2×2 Matrix{Int64}: - 4 0 - 0 4 - -julia> sqrt(A) -2×2 Matrix{Float64}: - 2.0 0.0 - 0.0 2.0 -``` -""" -sqrt(::AbstractMatrix) - -function sqrt(A::AbstractMatrix{T}) where {T<:Union{Real,Complex}} - if checksquare(A) == 0 - return copy(A) - elseif ishermitian(A) - sqrtHermA = sqrt(Hermitian(A)) - return ishermitian(sqrtHermA) ? copytri!(parent(sqrtHermA), 'U', true) : parent(sqrtHermA) - elseif istriu(A) - return triu!(parent(sqrt(UpperTriangular(A)))) - elseif isreal(A) - SchurF = schur(real(A)) - if istriu(SchurF.T) - sqrtA = SchurF.Z * sqrt(UpperTriangular(SchurF.T)) * SchurF.Z' - else - # real sqrt exists whenever no eigenvalues are negative - is_sqrt_real = !any(x -> isreal(x) && real(x) < 0, SchurF.values) - # sqrt_quasitriu uses LAPACK functions for non-triu inputs - if typeof(sqrt(zero(T))) <: BlasFloat && is_sqrt_real - sqrtA = SchurF.Z * sqrt_quasitriu(SchurF.T) * SchurF.Z' - else - SchurS = Schur{Complex}(SchurF) - sqrtA = SchurS.Z * sqrt(UpperTriangular(SchurS.T)) * SchurS.Z' - end - end - return eltype(A) <: Complex ? complex(sqrtA) : sqrtA - else - SchurF = schur(A) - return SchurF.vectors * sqrt(UpperTriangular(SchurF.T)) * SchurF.vectors' - end -end - -sqrt(A::AdjointAbsMat) = adjoint(sqrt(parent(A))) -sqrt(A::TransposeAbsMat) = transpose(sqrt(parent(A))) - -""" - cbrt(A::AbstractMatrix{<:Real}) - -Computes the real-valued cube root of a real-valued matrix `A`. If `T = cbrt(A)`, then -we have `T*T*T ≈ A`, see example given below. - -If `A` is symmetric, i.e., of type `HermOrSym{<:Real}`, then ([`eigen`](@ref)) is used to -find the cube root. Otherwise, a specialized version of the p-th root algorithm [^S03] is -utilized, which exploits the real-valued Schur decomposition ([`schur`](@ref)) -to compute the cube root. - -[^S03]: - - Matthew I. Smith, "A Schur Algorithm for Computing Matrix pth Roots", - SIAM Journal on Matrix Analysis and Applications, vol. 24, 2003, pp. 971–989. - [doi:10.1137/S0895479801392697](https://doi.org/10.1137/s0895479801392697) - -# Examples -```jldoctest -julia> A = [0.927524 -0.15857; -1.3677 -1.01172] -2×2 Matrix{Float64}: - 0.927524 -0.15857 - -1.3677 -1.01172 - -julia> T = cbrt(A) -2×2 Matrix{Float64}: - 0.910077 -0.151019 - -1.30257 -0.936818 - -julia> T*T*T ≈ A -true -``` -""" -function cbrt(A::AbstractMatrix{<:Real}) - if checksquare(A) == 0 - return copy(A) - elseif issymmetric(A) - return cbrt(Symmetric(A, :U)) - else - S = schur(A) - return S.Z * _cbrt_quasi_triu!(S.T) * S.Z' - end -end - -# Cube roots of adjoint and transpose matrices -cbrt(A::AdjointAbsMat) = adjoint(cbrt(parent(A))) -cbrt(A::TransposeAbsMat) = transpose(cbrt(parent(A))) - -function applydiagonal(f, A) - dinv = f(Diagonal(A)) - copyto!(similar(A, eltype(dinv)), dinv) -end - -function inv(A::StridedMatrix{T}) where T - checksquare(A) - if isdiag(A) - Ai = applydiagonal(inv, A) - elseif istriu(A) - Ai = triu!(parent(inv(UpperTriangular(A)))) - elseif istril(A) - Ai = tril!(parent(inv(LowerTriangular(A)))) - else - Ai = inv!(lu(A)) - Ai = convert(typeof(parent(Ai)), Ai) - end - return Ai -end - -# helper function to perform a broadcast in-place if the destination is strided -# otherwise, this performs an out-of-place broadcast -@inline _broadcast!!(f, dest::StridedArray, args...) = broadcast!(f, dest, args...) -@inline _broadcast!!(f, dest, args...) = broadcast(f, args...) - -""" - cos(A::AbstractMatrix) - -Compute the matrix cosine of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the cosine. Otherwise, the cosine is determined by calling [`exp`](@ref). - -# Examples -```jldoctest -julia> cos(fill(1.0, (2,2))) -2×2 Matrix{Float64}: - 0.291927 -0.708073 - -0.708073 0.291927 -``` -""" -function cos(A::AbstractMatrix{<:Real}) - if isdiag(A) - return applydiagonal(cos, A) - elseif issymmetric(A) - return copytri!(parent(cos(Symmetric(A))), 'U') - end - M = im .* float.(A) - return real(exp_maybe_inplace(M)) -end -function cos(A::AbstractMatrix{<:Complex}) - if isdiag(A) - return applydiagonal(cos, A) - elseif ishermitian(A) - return copytri!(parent(cos(Hermitian(A))), 'U', true) - end - M = im .* float.(A) - N = -M - X = exp_maybe_inplace(M) - Y = exp_maybe_inplace(N) - # Compute (X + Y)/2 and return the result. - # Compute the result in-place if X is strided - _broadcast!!((x,y) -> (x + y)/2, X, X, Y) -end - -""" - sin(A::AbstractMatrix) - -Compute the matrix sine of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the sine. Otherwise, the sine is determined by calling [`exp`](@ref). - -# Examples -```jldoctest -julia> sin(fill(1.0, (2,2))) -2×2 Matrix{Float64}: - 0.454649 0.454649 - 0.454649 0.454649 -``` -""" -function sin(A::AbstractMatrix{<:Real}) - if isdiag(A) - return applydiagonal(sin, A) - elseif issymmetric(A) - return copytri!(parent(sin(Symmetric(A))), 'U') - end - M = im .* float.(A) - return imag(exp_maybe_inplace(M)) -end -function sin(A::AbstractMatrix{<:Complex}) - if isdiag(A) - return applydiagonal(sin, A) - elseif ishermitian(A) - return copytri!(parent(sin(Hermitian(A))), 'U', true) - end - M = im .* float.(A) - Mneg = -M - X = exp_maybe_inplace(M) - Y = exp_maybe_inplace(Mneg) - # Compute (X - Y)/2im and return the result. - # Compute the result in-place if X is strided - _broadcast!!((x,y) -> (x - y)/2im, X, X, Y) -end - -""" - sincos(A::AbstractMatrix) - -Compute the matrix sine and cosine of a square matrix `A`. - -# Examples -```jldoctest -julia> S, C = sincos(fill(1.0, (2,2))); - -julia> S -2×2 Matrix{Float64}: - 0.454649 0.454649 - 0.454649 0.454649 - -julia> C -2×2 Matrix{Float64}: - 0.291927 -0.708073 - -0.708073 0.291927 -``` -""" -function sincos(A::AbstractMatrix{<:Real}) - if issymmetric(A) - symsinA, symcosA = sincos(Symmetric(A)) - sinA = copytri!(parent(symsinA), 'U') - cosA = copytri!(parent(symcosA), 'U') - return sinA, cosA - end - M = im .* float.(A) - c, s = reim(exp_maybe_inplace(M)) - return s, c -end -function sincos(A::AbstractMatrix{<:Complex}) - if ishermitian(A) - hermsinA, hermcosA = sincos(Hermitian(A)) - sinA = copytri!(parent(hermsinA), 'U', true) - cosA = copytri!(parent(hermcosA), 'U', true) - return sinA, cosA - end - M = im .* float.(A) - Mneg = -M - X = exp_maybe_inplace(M) - Y = exp_maybe_inplace(Mneg) - _sincos(X, Y) -end -function _sincos(X::StridedMatrix, Y::StridedMatrix) - @inbounds for i in eachindex(X, Y) - x, y = X[i]/2, Y[i]/2 - X[i] = Complex(imag(x)-imag(y), real(y)-real(x)) - Y[i] = x+y - end - return X, Y -end -function _sincos(X, Y) - T = eltype(X) - S = T(0.5)*im .* (Y .- X) - C = T(0.5) .* (X .+ Y) - S, C -end - -""" - tan(A::AbstractMatrix) - -Compute the matrix tangent of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the tangent. Otherwise, the tangent is determined by calling [`exp`](@ref). - -# Examples -```jldoctest -julia> tan(fill(1.0, (2,2))) -2×2 Matrix{Float64}: - -1.09252 -1.09252 - -1.09252 -1.09252 -``` -""" -function tan(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(tan, A) - elseif ishermitian(A) - return copytri!(parent(tan(Hermitian(A))), 'U', true) - end - S, C = sincos(A) - S /= C - return S -end - -""" - cosh(A::AbstractMatrix) - -Compute the matrix hyperbolic cosine of a square matrix `A`. -""" -function cosh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(cosh, A) - elseif ishermitian(A) - return copytri!(parent(cosh(Hermitian(A))), 'U', true) - end - X = exp(A) - negA = @. float(-A) - Y = exp_maybe_inplace(negA) - _broadcast!!((x,y) -> (x + y)/2, X, X, Y) -end - -""" - sinh(A::AbstractMatrix) - -Compute the matrix hyperbolic sine of a square matrix `A`. -""" -function sinh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(sinh, A) - elseif ishermitian(A) - return copytri!(parent(sinh(Hermitian(A))), 'U', true) - end - X = exp(A) - negA = @. float(-A) - Y = exp_maybe_inplace(negA) - _broadcast!!((x,y) -> (x - y)/2, X, X, Y) -end - -""" - tanh(A::AbstractMatrix) - -Compute the matrix hyperbolic tangent of a square matrix `A`. -""" -function tanh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(tanh, A) - elseif ishermitian(A) - return copytri!(parent(tanh(Hermitian(A))), 'U', true) - end - X = exp(A) - negA = @. float(-A) - Y = exp_maybe_inplace(negA) - X′, Y′ = _subadd!!(X, Y) - return X′ / Y′ -end -function _subadd!!(X::StridedMatrix, Y::StridedMatrix) - @inbounds for i in eachindex(X, Y) - x, y = X[i], Y[i] - X[i] = x - y - Y[i] = x + y - end - return X, Y -end -_subadd!!(X, Y) = X - Y, X + Y - -""" - acos(A::AbstractMatrix) - -Compute the inverse matrix cosine of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the inverse cosine. Otherwise, the inverse cosine is determined by using -[`log`](@ref) and [`sqrt`](@ref). For the theory and logarithmic formulas used to compute -this function, see [^AH16_1]. - -[^AH16_1]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) - -# Examples -```julia-repl -julia> acos(cos([0.5 0.1; -0.2 0.3])) -2×2 Matrix{ComplexF64}: - 0.5-8.32667e-17im 0.1+0.0im - -0.2+2.63678e-16im 0.3-3.46945e-16im -``` -""" -function acos(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(acos, A) - elseif ishermitian(A) - acosHermA = acos(Hermitian(A)) - return isa(acosHermA, Hermitian) ? copytri!(parent(acosHermA), 'U', true) : parent(acosHermA) - end - SchurF = Schur{Complex}(schur(A)) - U = UpperTriangular(SchurF.T) - R = triu!(parent(-im * log(U + im * sqrt(I - U^2)))) - return SchurF.Z * R * SchurF.Z' -end - -""" - asin(A::AbstractMatrix) - -Compute the inverse matrix sine of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the inverse sine. Otherwise, the inverse sine is determined by using [`log`](@ref) -and [`sqrt`](@ref). For the theory and logarithmic formulas used to compute this function, -see [^AH16_2]. - -[^AH16_2]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) - -# Examples -```julia-repl -julia> asin(sin([0.5 0.1; -0.2 0.3])) -2×2 Matrix{ComplexF64}: - 0.5-4.16334e-17im 0.1-5.55112e-17im - -0.2+9.71445e-17im 0.3-1.249e-16im -``` -""" -function asin(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(asin, A) - elseif ishermitian(A) - asinHermA = asin(Hermitian(A)) - return isa(asinHermA, Hermitian) ? copytri!(parent(asinHermA), 'U', true) : parent(asinHermA) - end - SchurF = Schur{Complex}(schur(A)) - U = UpperTriangular(SchurF.T) - R = triu!(parent(-im * log(im * U + sqrt(I - U^2)))) - return SchurF.Z * R * SchurF.Z' -end - -""" - atan(A::AbstractMatrix) - -Compute the inverse matrix tangent of a square matrix `A`. - -If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to -compute the inverse tangent. Otherwise, the inverse tangent is determined by using -[`log`](@ref). For the theory and logarithmic formulas used to compute this function, see -[^AH16_3]. - -[^AH16_3]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) - -# Examples -```julia-repl -julia> atan(tan([0.5 0.1; -0.2 0.3])) -2×2 Matrix{ComplexF64}: - 0.5+1.38778e-17im 0.1-2.77556e-17im - -0.2+6.93889e-17im 0.3-4.16334e-17im -``` -""" -function atan(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(atan, A) - elseif ishermitian(A) - return copytri!(parent(atan(Hermitian(A))), 'U', true) - end - SchurF = Schur{Complex}(schur(A)) - U = im * UpperTriangular(SchurF.T) - R = triu!(parent(log((I + U) / (I - U)) / 2im)) - return SchurF.Z * R * SchurF.Z' -end - -""" - acosh(A::AbstractMatrix) - -Compute the inverse hyperbolic matrix cosine of a square matrix `A`. For the theory and -logarithmic formulas used to compute this function, see [^AH16_4]. - -[^AH16_4]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) -""" -function acosh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(acosh, A) - elseif ishermitian(A) - acoshHermA = acosh(Hermitian(A)) - return isa(acoshHermA, Hermitian) ? copytri!(parent(acoshHermA), 'U', true) : parent(acoshHermA) - end - SchurF = Schur{Complex}(schur(A)) - U = UpperTriangular(SchurF.T) - R = triu!(parent(log(U + sqrt(U - I) * sqrt(U + I)))) - return SchurF.Z * R * SchurF.Z' -end - -""" - asinh(A::AbstractMatrix) - -Compute the inverse hyperbolic matrix sine of a square matrix `A`. For the theory and -logarithmic formulas used to compute this function, see [^AH16_5]. - -[^AH16_5]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) -""" -function asinh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(asinh, A) - elseif ishermitian(A) - return copytri!(parent(asinh(Hermitian(A))), 'U', true) - end - SchurF = Schur{Complex}(schur(A)) - U = UpperTriangular(SchurF.T) - R = triu!(parent(log(U + sqrt(I + U^2)))) - return SchurF.Z * R * SchurF.Z' -end - -""" - atanh(A::AbstractMatrix) - -Compute the inverse hyperbolic matrix tangent of a square matrix `A`. For the theory and -logarithmic formulas used to compute this function, see [^AH16_6]. - -[^AH16_6]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) -""" -function atanh(A::AbstractMatrix) - if isdiag(A) - return applydiagonal(atanh, A) - elseif ishermitian(A) - return copytri!(parent(atanh(Hermitian(A))), 'U', true) - end - SchurF = Schur{Complex}(schur(A)) - U = UpperTriangular(SchurF.T) - R = triu!(parent(log((I + U) / (I - U)) / 2)) - return SchurF.Z * R * SchurF.Z' -end - -for (finv, f, finvh, fh, fn) in ((:sec, :cos, :sech, :cosh, "secant"), - (:csc, :sin, :csch, :sinh, "cosecant"), - (:cot, :tan, :coth, :tanh, "cotangent")) - name = string(finv) - hname = string(finvh) - @eval begin - @doc """ - $($name)(A::AbstractMatrix) - - Compute the matrix $($fn) of a square matrix `A`. - """ ($finv)(A::AbstractMatrix{T}) where {T} = inv(($f)(A)) - @doc """ - $($hname)(A::AbstractMatrix) - - Compute the matrix hyperbolic $($fn) of square matrix `A`. - """ ($finvh)(A::AbstractMatrix{T}) where {T} = inv(($fh)(A)) - end -end - -for (tfa, tfainv, hfa, hfainv, fn) in ((:asec, :acos, :asech, :acosh, "secant"), - (:acsc, :asin, :acsch, :asinh, "cosecant"), - (:acot, :atan, :acoth, :atanh, "cotangent")) - tname = string(tfa) - hname = string(hfa) - @eval begin - @doc """ - $($tname)(A::AbstractMatrix) - Compute the inverse matrix $($fn) of `A`. """ ($tfa)(A::AbstractMatrix{T}) where {T} = ($tfainv)(inv(A)) - @doc """ - $($hname)(A::AbstractMatrix) - Compute the inverse matrix hyperbolic $($fn) of `A`. """ ($hfa)(A::AbstractMatrix{T}) where {T} = ($hfainv)(inv(A)) - end -end - -""" - factorize(A) - -Compute a convenient factorization of `A`, based upon the type of the input matrix. -If `A` is passed as a generic matrix, `factorize` checks to see if it is -symmetric/triangular/etc. To this end, `factorize` may check every element of `A` to -verify/rule out each property. It will short-circuit as soon as it can rule out -symmetry/triangular structure. The return value can be reused for efficient solving -of multiple systems. For example: `A=factorize(A); x=A\\b; y=A\\C`. - -| Properties of `A` | type of factorization | -|:---------------------------|:-----------------------------------------------| -| Dense Symmetric/Hermitian | Bunch-Kaufman (see [`bunchkaufman`](@ref)) | -| Sparse Symmetric/Hermitian | LDLt (see [`ldlt`](@ref)) | -| Triangular | Triangular | -| Diagonal | Diagonal | -| Bidiagonal | Bidiagonal | -| Tridiagonal | LU (see [`lu`](@ref)) | -| Symmetric real tridiagonal | LDLt (see [`ldlt`](@ref)) | -| General square | LU (see [`lu`](@ref)) | -| General non-square | QR (see [`qr`](@ref)) | - -# Examples -```jldoctest -julia> A = Array(Bidiagonal(fill(1.0, (5, 5)), :U)) -5×5 Matrix{Float64}: - 1.0 1.0 0.0 0.0 0.0 - 0.0 1.0 1.0 0.0 0.0 - 0.0 0.0 1.0 1.0 0.0 - 0.0 0.0 0.0 1.0 1.0 - 0.0 0.0 0.0 0.0 1.0 - -julia> factorize(A) # factorize will check to see that A is already factorized -5×5 Bidiagonal{Float64, Vector{Float64}}: - 1.0 1.0 ⋅ ⋅ ⋅ - ⋅ 1.0 1.0 ⋅ ⋅ - ⋅ ⋅ 1.0 1.0 ⋅ - ⋅ ⋅ ⋅ 1.0 1.0 - ⋅ ⋅ ⋅ ⋅ 1.0 -``` - -This returns a `5×5 Bidiagonal{Float64}`, which can now be passed to other linear algebra -functions (e.g. eigensolvers) which will use specialized methods for `Bidiagonal` types. -""" -function factorize(A::AbstractMatrix{T}) where T - m, n = size(A) - if m == n - if m == 1 return A[1] end - utri = true - utri1 = true - herm = true - sym = true - for j = 1:n-1, i = j+1:m - if utri1 - if A[i,j] != 0 - utri1 = i == j + 1 - utri = false - end - end - if sym - sym &= A[i,j] == A[j,i] - end - if herm - herm &= A[i,j] == conj(A[j,i]) - end - if !(utri1|herm|sym) break end - end - ltri = true - ltri1 = true - for j = 3:n, i = 1:j-2 - ltri1 &= A[i,j] == 0 - if !ltri1 break end - end - if ltri1 - for i = 1:n-1 - if A[i,i+1] != 0 - ltri &= false - break - end - end - if ltri - if utri - return Diagonal(A) - end - if utri1 - return Bidiagonal(diag(A), diag(A, -1), :L) - end - return LowerTriangular(A) - end - if utri - return Bidiagonal(diag(A), diag(A, 1), :U) - end - if utri1 - # TODO: enable once a specialized, non-dense bunchkaufman method exists - # if (herm & (T <: Complex)) | sym - # return bunchkaufman(SymTridiagonal(diag(A), diag(A, -1))) - # end - return lu(Tridiagonal(diag(A, -1), diag(A), diag(A, 1))) - end - end - if utri - return UpperTriangular(A) - end - if herm - return factorize(Hermitian(A)) - end - if sym - return factorize(Symmetric(A)) - end - return lu(A) - end - qr(A, ColumnNorm()) -end -factorize(A::Adjoint) = adjoint(factorize(parent(A))) -factorize(A::Transpose) = transpose(factorize(parent(A))) -factorize(a::Number) = a # same as how factorize behaves on Diagonal types - -## Moore-Penrose pseudoinverse - -""" - pinv(M; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ϵ) - pinv(M, rtol::Real) = pinv(M; rtol=rtol) # to be deprecated in Julia 2.0 - -Computes the Moore-Penrose pseudoinverse. - -For matrices `M` with floating point elements, it is convenient to compute -the pseudoinverse by inverting only singular values greater than -`max(atol, rtol*σ₁)` where `σ₁` is the largest singular value of `M`. - -The optimal choice of absolute (`atol`) and relative tolerance (`rtol`) varies -both with the value of `M` and the intended application of the pseudoinverse. -The default relative tolerance is `n*ϵ`, where `n` is the size of the smallest -dimension of `M`, and `ϵ` is the [`eps`](@ref) of the element type of `M`. - -For inverting dense ill-conditioned matrices in a least-squares sense, -`rtol = sqrt(eps(real(float(oneunit(eltype(M))))))` is recommended. - -For more information, see [^issue8859], [^B96], [^S84], [^KY88]. - -# Examples -```jldoctest -julia> M = [1.5 1.3; 1.2 1.9] -2×2 Matrix{Float64}: - 1.5 1.3 - 1.2 1.9 - -julia> N = pinv(M) -2×2 Matrix{Float64}: - 1.47287 -1.00775 - -0.930233 1.16279 - -julia> M * N -2×2 Matrix{Float64}: - 1.0 -2.22045e-16 - 4.44089e-16 1.0 -``` - -[^issue8859]: Issue 8859, "Fix least squares", [https://github.com/JuliaLang/julia/pull/8859](https://github.com/JuliaLang/julia/pull/8859) - -[^B96]: Åke Björck, "Numerical Methods for Least Squares Problems", SIAM Press, Philadelphia, 1996, "Other Titles in Applied Mathematics", Vol. 51. [doi:10.1137/1.9781611971484](http://epubs.siam.org/doi/book/10.1137/1.9781611971484) - -[^S84]: G. W. Stewart, "Rank Degeneracy", SIAM Journal on Scientific and Statistical Computing, 5(2), 1984, 403-413. [doi:10.1137/0905030](http://epubs.siam.org/doi/abs/10.1137/0905030) - -[^KY88]: Konstantinos Konstantinides and Kung Yao, "Statistical analysis of effective singular values in matrix rank determination", IEEE Transactions on Acoustics, Speech and Signal Processing, 36(5), 1988, 757-763. [doi:10.1109/29.1585](https://doi.org/10.1109/29.1585) -""" -function pinv(A::AbstractMatrix{T}; atol::Real = 0.0, rtol::Real = (eps(real(float(oneunit(T))))*min(size(A)...))*iszero(atol)) where T - m, n = size(A) - Tout = typeof(zero(T)/sqrt(oneunit(T) + oneunit(T))) - if m == 0 || n == 0 - return similar(A, Tout, (n, m)) - end - if isdiag(A) - dA = diagview(A) - maxabsA = maximum(abs, dA) - tol = max(rtol * maxabsA, atol) - B = fill!(similar(A, Tout, (n, m)), 0) - diagview(B) .= (x -> abs(x) > tol ? pinv(x) : zero(x)).(dA) - return B - end - SVD = svd(A) - tol2 = max(rtol*maximum(SVD.S), atol) - Stype = eltype(SVD.S) - Sinv = fill!(similar(A, Stype, length(SVD.S)), 0) - index = SVD.S .> tol2 - Sinv[index] .= pinv.(view(SVD.S, index)) - return SVD.Vt' * (Diagonal(Sinv) * SVD.U') -end -function pinv(x::Number) - xi = inv(x) - return ifelse(isfinite(xi), xi, zero(xi)) -end - -## Basis for null space - -""" - nullspace(M; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ϵ) - nullspace(M, rtol::Real) = nullspace(M; rtol=rtol) # to be deprecated in Julia 2.0 - -Computes a basis for the nullspace of `M` by including the singular -vectors of `M` whose singular values have magnitudes smaller than `max(atol, rtol*σ₁)`, -where `σ₁` is `M`'s largest singular value. - -By default, the relative tolerance `rtol` is `n*ϵ`, where `n` -is the size of the smallest dimension of `M`, and `ϵ` is the [`eps`](@ref) of -the element type of `M`. - -# Examples -```jldoctest -julia> M = [1 0 0; 0 1 0; 0 0 0] -3×3 Matrix{Int64}: - 1 0 0 - 0 1 0 - 0 0 0 - -julia> nullspace(M) -3×1 Matrix{Float64}: - 0.0 - 0.0 - 1.0 - -julia> nullspace(M, rtol=3) -3×3 Matrix{Float64}: - 0.0 1.0 0.0 - 1.0 0.0 0.0 - 0.0 0.0 1.0 - -julia> nullspace(M, atol=0.95) -3×1 Matrix{Float64}: - 0.0 - 0.0 - 1.0 -``` -""" -function nullspace(A::AbstractVecOrMat; atol::Real = 0.0, rtol::Real = (min(size(A, 1), size(A, 2))*eps(real(float(oneunit(eltype(A))))))*iszero(atol)) - m, n = size(A, 1), size(A, 2) - (m == 0 || n == 0) && return Matrix{eigtype(eltype(A))}(I, n, n) - SVD = svd(A; full=true) - tol = max(atol, SVD.S[1]*rtol) - indstart = sum(s -> s .> tol, SVD.S) + 1 - return copy((@view SVD.Vt[indstart:end,:])') -end - -""" - cond(M, p::Real=2) - -Condition number of the matrix `M`, computed using the operator `p`-norm. Valid values for -`p` are `1`, `2` (default), or `Inf`. -""" -function cond(A::AbstractMatrix, p::Real=2) - if p == 2 - v = svdvals(A) - maxv = maximum(v) - return iszero(maxv) ? oftype(real(maxv), Inf) : maxv / minimum(v) - elseif p == 1 || p == Inf - checksquare(A) - try - Ainv = inv(A) - return opnorm(A, p)*opnorm(Ainv, p) - catch e - if isa(e, LAPACKException) || isa(e, SingularException) - return convert(float(real(eltype(A))), Inf) - else - rethrow() - end - end - end - throw(ArgumentError(lazy"p-norm must be 1, 2 or Inf, got $p")) -end - -## Lyapunov and Sylvester equation - -# AX + XB + C = 0 - -""" - sylvester(A, B, C) - -Computes the solution `X` to the Sylvester equation `AX + XB + C = 0`, where `A`, `B` and -`C` have compatible dimensions and `A` and `-B` have no eigenvalues with equal real part. - -# Examples -```jldoctest -julia> A = [3. 4.; 5. 6] -2×2 Matrix{Float64}: - 3.0 4.0 - 5.0 6.0 - -julia> B = [1. 1.; 1. 2.] -2×2 Matrix{Float64}: - 1.0 1.0 - 1.0 2.0 - -julia> C = [1. 2.; -2. 1] -2×2 Matrix{Float64}: - 1.0 2.0 - -2.0 1.0 - -julia> X = sylvester(A, B, C) -2×2 Matrix{Float64}: - -4.46667 1.93333 - 3.73333 -1.8 - -julia> A*X + X*B ≈ -C -true -``` -""" -function sylvester(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix) - T = promote_type(float(eltype(A)), float(eltype(B)), float(eltype(C))) - return sylvester(copy_similar(A, T), copy_similar(B, T), copy_similar(C, T)) -end -function sylvester(A::AbstractMatrix{T}, B::AbstractMatrix{T}, C::AbstractMatrix{T}) where {T<:BlasFloat} - RA, QA = schur(A) - RB, QB = schur(B) - D = QA' * C * QB - D .= .-D - Y, scale = LAPACK.trsyl!('N', 'N', RA, RB, D) - rmul!(QA * Y * QB', inv(scale)) -end - -Base.@propagate_inbounds function _sylvester_2x1!(A, B, C) - b = B[1] - a21, a12 = A[2, 1], A[1, 2] - m11 = b + A[1, 1] - m22 = b + A[2, 2] - d = m11 * m22 - a12 * a21 - c1, c2 = C - C[1] = (a12 * c2 - m22 * c1) / d - C[2] = (a21 * c1 - m11 * c2) / d - return C -end -Base.@propagate_inbounds function _sylvester_1x2!(A, B, C) - a = A[1] - b21, b12 = B[2, 1], B[1, 2] - m11 = a + B[1, 1] - m22 = a + B[2, 2] - d = m11 * m22 - b21 * b12 - c1, c2 = C - C[1] = (b21 * c2 - m22 * c1) / d - C[2] = (b12 * c1 - m11 * c2) / d - return C -end -function _sylvester_2x2!(A, B, C) - _, scale = LAPACK.trsyl!('N', 'N', A, B, C) - rmul!(C, -inv(scale)) - return C -end - -sylvester(a::Union{Real,Complex}, b::Union{Real,Complex}, c::Union{Real,Complex}) = -c / (a + b) - -# AX + XA' + C = 0 - -""" - lyap(A, C) - -Computes the solution `X` to the continuous Lyapunov equation `AX + XA' + C = 0`, where no -eigenvalue of `A` has a zero real part and no two eigenvalues are negative complex -conjugates of each other. - -# Examples -```jldoctest -julia> A = [3. 4.; 5. 6] -2×2 Matrix{Float64}: - 3.0 4.0 - 5.0 6.0 - -julia> B = [1. 1.; 1. 2.] -2×2 Matrix{Float64}: - 1.0 1.0 - 1.0 2.0 - -julia> X = lyap(A, B) -2×2 Matrix{Float64}: - 0.5 -0.5 - -0.5 0.25 - -julia> A*X + X*A' ≈ -B -true -``` -""" -function lyap(A::AbstractMatrix, C::AbstractMatrix) - T = promote_type(float(eltype(A)), float(eltype(C))) - return lyap(copy_similar(A, T), copy_similar(C, T)) -end -function lyap(A::AbstractMatrix{T}, C::AbstractMatrix{T}) where {T<:BlasFloat} - R, Q = schur(A) - D = Q' * C * Q - D .= .-D - Y, scale = LAPACK.trsyl!('N', T <: Complex ? 'C' : 'T', R, R, D) - rmul!(Q * Y * Q', inv(scale)) -end -lyap(a::Union{Real,Complex}, c::Union{Real,Complex}) = -c/(2real(a)) diff --git a/stdlib/LinearAlgebra/src/deprecated.jl b/stdlib/LinearAlgebra/src/deprecated.jl deleted file mode 100644 index 28c090634a2d8..0000000000000 --- a/stdlib/LinearAlgebra/src/deprecated.jl +++ /dev/null @@ -1,7 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# To be deprecated in 2.0 -rank(A::AbstractMatrix, tol::Real) = rank(A,rtol=tol) -nullspace(A::AbstractVector, tol::Real) = nullspace(reshape(A, length(A), 1), rtol= tol) -nullspace(A::AbstractMatrix, tol::Real) = nullspace(A, rtol=tol) -pinv(A::AbstractMatrix{T}, tol::Real) where T = pinv(A, rtol=tol) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl deleted file mode 100644 index 243df4d82eec2..0000000000000 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ /dev/null @@ -1,1148 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## Diagonal matrices - -struct Diagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} - diag::V - - function Diagonal{T,V}(diag) where {T,V<:AbstractVector{T}} - require_one_based_indexing(diag) - new{T,V}(diag) - end -end -Diagonal(v::AbstractVector{T}) where {T} = Diagonal{T,typeof(v)}(v) -Diagonal{T}(v::AbstractVector) where {T} = Diagonal(convert(AbstractVector{T}, v)::AbstractVector{T}) - -function Base.promote_rule(A::Type{<:Diagonal{<:Any,V}}, B::Type{<:Diagonal{<:Any,W}}) where {V,W} - X = promote_type(V, W) - T = eltype(X) - isconcretetype(T) && return Diagonal{T,X} - return typejoin(A, B) -end - -""" - Diagonal(V::AbstractVector) - -Construct a lazy matrix with `V` as its diagonal. - -See also [`UniformScaling`](@ref) for the lazy identity matrix `I`, -[`diagm`](@ref) to make a dense matrix, and [`diag`](@ref) to extract diagonal elements. - -# Examples -```jldoctest -julia> d = Diagonal([1, 10, 100]) -3×3 Diagonal{$Int, Vector{$Int}}: - 1 ⋅ ⋅ - ⋅ 10 ⋅ - ⋅ ⋅ 100 - -julia> diagm([7, 13]) -2×2 Matrix{$Int}: - 7 0 - 0 13 - -julia> ans + I -2×2 Matrix{Int64}: - 8 0 - 0 14 - -julia> I(2) -2×2 Diagonal{Bool, Vector{Bool}}: - 1 ⋅ - ⋅ 1 -``` - -!!! note - A one-column matrix is not treated like a vector, but instead calls the - method `Diagonal(A::AbstractMatrix)` which extracts 1-element `diag(A)`: - -```jldoctest -julia> A = transpose([7.0 13.0]) -2×1 transpose(::Matrix{Float64}) with eltype Float64: - 7.0 - 13.0 - -julia> Diagonal(A) -1×1 Diagonal{Float64, Vector{Float64}}: - 7.0 -``` -""" -Diagonal(V::AbstractVector) - -""" - Diagonal(A::AbstractMatrix) - -Construct a matrix from the principal diagonal of `A`. -The input matrix `A` may be rectangular, but the output will -be square. - -# Examples -```jldoctest -julia> A = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> D = Diagonal(A) -2×2 Diagonal{Int64, Vector{Int64}}: - 1 ⋅ - ⋅ 4 - -julia> A = [1 2 3; 4 5 6] -2×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - -julia> Diagonal(A) -2×2 Diagonal{Int64, Vector{Int64}}: - 1 ⋅ - ⋅ 5 -``` -""" -Diagonal(A::AbstractMatrix) = Diagonal(diag(A)) -Diagonal{T}(A::AbstractMatrix) where T = Diagonal{T}(diag(A)) -Diagonal{T,V}(A::AbstractMatrix) where {T,V<:AbstractVector{T}} = Diagonal{T,V}(diag(A)) -function convert(::Type{T}, A::AbstractMatrix) where T<:Diagonal - checksquare(A) - isdiag(A) ? T(A) : throw(InexactError(:convert, T, A)) -end - -Diagonal(D::Diagonal) = D -Diagonal{T}(D::Diagonal{T}) where {T} = D -Diagonal{T}(D::Diagonal) where {T} = Diagonal{T}(D.diag) - -AbstractMatrix{T}(D::Diagonal) where {T} = Diagonal{T}(D) -AbstractMatrix{T}(D::Diagonal{T}) where {T} = copy(D) -Matrix(D::Diagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(D) -Matrix(D::Diagonal{Any}) = Matrix{Any}(D) -Array(D::Diagonal{T}) where {T} = Matrix(D) -function Matrix{T}(D::Diagonal) where {T} - B = Matrix{T}(undef, size(D)) - if haszero(T) # optimized path for types with zero(T) defined - size(B,1) > 1 && fill!(B, zero(T)) - copyto!(diagview(B), D.diag) - else - copyto!(B, D) - end - return B -end - -""" - Diagonal{T}(undef, n) - -Construct an uninitialized `Diagonal{T}` of length `n`. See `undef`. -""" -Diagonal{T}(::UndefInitializer, n::Integer) where T = Diagonal(Vector{T}(undef, n)) - -similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T)) -similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(D.diag, T, dims) - -# copyto! for matching axes -_copyto_banded!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1) - -size(D::Diagonal) = (n = length(D.diag); (n,n)) - -axes(D::Diagonal) = (ax = axes(D.diag, 1); (ax, ax)) - -@inline function Base.isassigned(D::Diagonal, i::Int, j::Int) - @boundscheck checkbounds(Bool, D, i, j) || return false - if i == j - @inbounds r = isassigned(D.diag, i) - else - r = true - end - r -end - -@inline function Base.isstored(D::Diagonal, i::Int, j::Int) - @boundscheck checkbounds(D, i, j) - if i == j - @inbounds r = Base.isstored(D.diag, i) - else - r = false - end - r -end - -function Base.minimum(D::Diagonal{T}) where T <: Number - mindiag = minimum(D.diag) - size(D, 1) > 1 && return (min(zero(T), mindiag)) - return mindiag -end - -function Base.maximum(D::Diagonal{T}) where T <: Number - maxdiag = Base.maximum(D.diag) - size(D, 1) > 1 && return (max(zero(T), maxdiag)) - return maxdiag -end - -@inline function getindex(D::Diagonal, i::Int, j::Int) - @boundscheck checkbounds(D, i, j) - if i == j - @inbounds r = D.diag[i] - else - r = diagzero(D, i, j) - end - r -end -""" - diagzero(A::AbstractMatrix, i, j) - -Return the appropriate zero element `A[i, j]` corresponding to a banded matrix `A`. -""" -diagzero(A::AbstractMatrix, i, j) = zero(eltype(A)) -diagzero(A::AbstractMatrix{M}, i, j) where {M<:AbstractMatrix} = - zeroslike(M, axes(A[i,i], 1), axes(A[j,j], 2)) -diagzero(A::AbstractMatrix, inds...) = diagzero(A, to_indices(A, inds)...) -# dispatching on the axes permits specializing on the axis types to return something other than an Array -zeroslike(M::Type, ax::Vararg{Union{AbstractUnitRange, Integer}}) = zeroslike(M, ax) -""" - zeroslike(::Type{M}, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) where {M<:AbstractMatrix} - zeroslike(::Type{M}, sz::Tuple{Integer, Vararg{Integer}}) where {M<:AbstractMatrix} - -Return an appropriate zero-ed array similar to `M`, with either the axes `ax` or the size `sz`. -This will be used as a structural zero element of a matrix-valued banded matrix. -By default, `zeroslike` falls back to using the size along each axis to construct the array. -""" -zeroslike(M::Type, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) = zeroslike(M, map(length, ax)) -zeroslike(M::Type, sz::Tuple{Integer, Vararg{Integer}}) = zeros(M, sz) -zeroslike(::Type{M}, sz::Tuple{Integer, Vararg{Integer}}) where {M<:AbstractMatrix} = zeros(eltype(M), sz) - -@inline function getindex(D::Diagonal, b::BandIndex) - @boundscheck checkbounds(D, b) - if b.band == 0 - @inbounds r = D.diag[b.index] - else - r = diagzero(D, Tuple(_cartinds(b))...) - end - r -end - -function setindex!(D::Diagonal, v, i::Int, j::Int) - @boundscheck checkbounds(D, i, j) - if i == j - @inbounds D.diag[i] = v - elseif !iszero(v) - throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)")) - end - return D -end - - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString) - i==j ? s : Base.replace_with_centered_mark(s) -end -function Base.show(io::IO, A::Diagonal) - print(io, "Diagonal(") - show(io, A.diag) - print(io, ")") -end - -parent(D::Diagonal) = D.diag - -copy(D::Diagonal) = Diagonal(copy(D.diag)) - -Base._reverse(A::Diagonal, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::Diagonal, ::Colon) = Diagonal(reverse(A.diag)) -Base._reverse!(A::Diagonal, ::Colon) = (reverse!(A.diag); A) - -ishermitian(D::Diagonal{<:Number}) = isreal(D.diag) -ishermitian(D::Diagonal) = all(ishermitian, D.diag) -issymmetric(D::Diagonal{<:Number}) = true -issymmetric(D::Diagonal) = all(issymmetric, D.diag) -isposdef(D::Diagonal) = all(isposdef, D.diag) - -factorize(D::Diagonal) = D - -real(D::Diagonal) = Diagonal(real(D.diag)) -imag(D::Diagonal) = Diagonal(imag(D.diag)) - -iszero(D::Diagonal) = all(iszero, D.diag) -isone(D::Diagonal) = all(isone, D.diag) -isdiag(D::Diagonal) = all(isdiag, D.diag) -isdiag(D::Diagonal{<:Number}) = true -Base.@constprop :aggressive istriu(D::Diagonal, k::Integer=0) = k <= 0 || iszero(D.diag) ? true : false -Base.@constprop :aggressive istril(D::Diagonal, k::Integer=0) = k >= 0 || iszero(D.diag) ? true : false -function triu!(D::Diagonal{T}, k::Integer=0) where T - n = size(D,1) - if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(string("the requested diagonal, $k, must be at least ", - "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) - elseif k > 0 - fill!(D.diag, zero(T)) - end - return D -end - -function tril!(D::Diagonal{T}, k::Integer=0) where T - n = size(D,1) - if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) - elseif k < 0 - fill!(D.diag, zero(T)) - end - return D -end - -(==)(Da::Diagonal, Db::Diagonal) = Da.diag == Db.diag -(-)(A::Diagonal) = Diagonal(-A.diag) -(+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag) -(-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag) - -(*)(x::Number, D::Diagonal) = Diagonal(x * D.diag) -(*)(D::Diagonal, x::Number) = Diagonal(D.diag * x) -function lmul!(x::Number, D::Diagonal) - if size(D,1) > 1 - # ensure that zeros are preserved on scaling - y = D[2,1] * x - iszero(y) || throw(ArgumentError(LazyString("cannot set index (2, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. D.diag = x * D.diag - return D -end -function rmul!(D::Diagonal, x::Number) - if size(D,1) > 1 - # ensure that zeros are preserved on scaling - y = x * D[2,1] - iszero(y) || throw(ArgumentError(LazyString("cannot set index (2, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. D.diag *= x - return D -end -(/)(D::Diagonal, x::Number) = Diagonal(D.diag / x) -(\)(x::Number, D::Diagonal) = Diagonal(x \ D.diag) -(^)(D::Diagonal, a::Number) = Diagonal(D.diag .^ a) -(^)(D::Diagonal, a::Real) = Diagonal(D.diag .^ a) # for disambiguation -(^)(D::Diagonal, a::Integer) = Diagonal(D.diag .^ a) # for disambiguation -Base.literal_pow(::typeof(^), D::Diagonal, valp::Val) = - Diagonal(Base.literal_pow.(^, D.diag, valp)) # for speed -Base.literal_pow(::typeof(^), D::Diagonal, ::Val{-1}) = inv(D) # for disambiguation - -function _muldiag_size_check(szA::NTuple{2,Integer}, szB::Tuple{Integer,Vararg{Integer}}) - nA = szA[2] - mB = szB[1] - @noinline throw_dimerr(szB::NTuple{2}, nA, mB) = throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match first dimension of B, $mB")) - @noinline throw_dimerr(szB::NTuple{1}, nA, mB) = throw(DimensionMismatch(lazy"second dimension of D, $nA, does not match length of V, $mB")) - nA == mB || throw_dimerr(szB, nA, mB) - return nothing -end -# the output matrix should have the same size as the non-diagonal input matrix or vector -@noinline throw_dimerr(szC, szA) = throw(DimensionMismatch(lazy"output matrix has size: $szC, but should have size $szA")) -function _size_check_out(szC::NTuple{2}, szA::NTuple{2}, szB::NTuple{2}) - (szC[1] == szA[1] && szC[2] == szB[2]) || throw_dimerr(szC, (szA[1], szB[2])) -end -function _size_check_out(szC::NTuple{1}, szA::NTuple{2}, szB::NTuple{1}) - szC[1] == szA[1] || throw_dimerr(szC, (szA[1],)) -end -function _muldiag_size_check(szC::Tuple{Vararg{Integer}}, szA::Tuple{Vararg{Integer}}, szB::Tuple{Vararg{Integer}}) - _muldiag_size_check(szA, szB) - _size_check_out(szC, szA, szB) -end - -function (*)(Da::Diagonal, Db::Diagonal) - _muldiag_size_check(size(Da), size(Db)) - return Diagonal(Da.diag .* Db.diag) -end - -function (*)(D::Diagonal, V::AbstractVector) - _muldiag_size_check(size(D), size(V)) - return D.diag .* V -end - -function rmul!(A::AbstractMatrix, D::Diagonal) - _muldiag_size_check(size(A), size(D)) - for I in CartesianIndices(A) - row, col = Tuple(I) - @inbounds A[row, col] *= D.diag[col] - end - return A -end -# T .= T * D -function rmul!(T::Tridiagonal, D::Diagonal) - _muldiag_size_check(size(T), size(D)) - (; dl, d, du) = T - d[1] *= D.diag[1] - for i in axes(dl,1) - dl[i] *= D.diag[i] - du[i] *= D.diag[i+1] - d[i+1] *= D.diag[i+1] - end - return T -end - -function lmul!(D::Diagonal, B::AbstractVecOrMat) - _muldiag_size_check(size(D), size(B)) - for I in CartesianIndices(B) - row = I[1] - @inbounds B[I] = D.diag[row] * B[I] - end - return B -end - -# in-place multiplication with a diagonal -# T .= D * T -function lmul!(D::Diagonal, T::Tridiagonal) - _muldiag_size_check(size(D), size(T)) - (; dl, d, du) = T - d[1] = D.diag[1] * d[1] - for i in axes(dl,1) - dl[i] = D.diag[i+1] * dl[i] - du[i] = D.diag[i] * du[i] - d[i+1] = D.diag[i+1] * d[i+1] - end - return T -end - -@inline function __muldiag_nonzeroalpha!(out, D::Diagonal, B, alpha::Number, beta::Number) - @inbounds for j in axes(B, 2) - @simd for i in axes(B, 1) - @stable_muladdmul _modify!(MulAddMul(alpha,beta), D.diag[i] * B[i,j], out, (i,j)) - end - end - return out -end -_has_matching_zeros(out::UpperOrUnitUpperTriangular, A::UpperOrUnitUpperTriangular) = true -_has_matching_zeros(out::LowerOrUnitLowerTriangular, A::LowerOrUnitLowerTriangular) = true -_has_matching_zeros(out, A) = false -function _rowrange_tri_stored(B::UpperOrUnitUpperTriangular, col) - isunit = B isa UnitUpperTriangular - 1:min(col-isunit, size(B,1)) -end -function _rowrange_tri_stored(B::LowerOrUnitLowerTriangular, col) - isunit = B isa UnitLowerTriangular - col+isunit:size(B,1) -end -_rowrange_tri_zeros(B::UpperOrUnitUpperTriangular, col) = col+1:size(B,1) -_rowrange_tri_zeros(B::LowerOrUnitLowerTriangular, col) = 1:col-1 -function __muldiag_nonzeroalpha!(out, D::Diagonal, B::UpperOrLowerTriangular, alpha::Number, beta::Number) - isunit = B isa UnitUpperOrUnitLowerTriangular - out_maybeparent, B_maybeparent = _has_matching_zeros(out, B) ? (parent(out), parent(B)) : (out, B) - for j in axes(B, 2) - # store the diagonal separately for unit triangular matrices - if isunit - @inbounds @stable_muladdmul _modify!(MulAddMul(alpha,beta), D.diag[j] * B[j,j], out, (j,j)) - end - # The indices of out corresponding to the stored indices of B - rowrange = _rowrange_tri_stored(B, j) - @inbounds @simd for i in rowrange - @stable_muladdmul _modify!(MulAddMul(alpha,beta), D.diag[i] * B_maybeparent[i,j], out_maybeparent, (i,j)) - end - # Fill the indices of out corresponding to the zeros of B - # we only fill these if out and B don't have matching zeros - if !_has_matching_zeros(out, B) - rowrange = _rowrange_tri_zeros(B, j) - @inbounds @simd for i in rowrange - @stable_muladdmul _modify!(MulAddMul(alpha,beta), D.diag[i] * B[i,j], out, (i,j)) - end - end - end - return out -end - -@inline function __muldiag_nonzeroalpha_right!(out, A, D::Diagonal, alpha::Number, beta::Number) - @inbounds for j in axes(A, 2) - dja = @stable_muladdmul MulAddMul(alpha,false)(D.diag[j]) - @simd for i in axes(A, 1) - @stable_muladdmul _modify!(MulAddMul(true,beta), A[i,j] * dja, out, (i,j)) - end - end - return out -end - -function __muldiag_nonzeroalpha!(out, A, D::Diagonal, alpha::Number, beta::Number) - __muldiag_nonzeroalpha_right!(out, A, D, alpha, beta) -end -function __muldiag_nonzeroalpha!(out, A::UpperOrLowerTriangular, D::Diagonal, alpha::Number, beta::Number) - isunit = A isa UnitUpperOrUnitLowerTriangular - # if both A and out have the same upper/lower triangular structure, - # we may directly read and write from the parents - out_maybeparent, A_maybeparent = _has_matching_zeros(out, A) ? (parent(out), parent(A)) : (out, A) - for j in axes(A, 2) - dja = @stable_muladdmul MulAddMul(alpha,false)(@inbounds D.diag[j]) - # store the diagonal separately for unit triangular matrices - if isunit - # since alpha is multiplied to the diagonal element of D, - # we may skip alpha in the second multiplication by setting ais1 to true - @inbounds @stable_muladdmul _modify!(MulAddMul(true,beta), A[j,j] * dja, out, (j,j)) - end - # indices of out corresponding to the stored indices of A - rowrange = _rowrange_tri_stored(A, j) - @inbounds @simd for i in rowrange - # since alpha is multiplied to the diagonal element of D, - # we may skip alpha in the second multiplication by setting ais1 to true - @stable_muladdmul _modify!(MulAddMul(true,beta), A_maybeparent[i,j] * dja, out_maybeparent, (i,j)) - end - # Fill the indices of out corresponding to the zeros of A - # we only fill these if out and A don't have matching zeros - if !_has_matching_zeros(out, A) - rowrange = _rowrange_tri_zeros(A, j) - @inbounds @simd for i in rowrange - @stable_muladdmul _modify!(MulAddMul(true,beta), A[i,j] * dja, out, (i,j)) - end - end - end - return out -end - -# ambiguity resolution -function __muldiag_nonzeroalpha!(out, D1::Diagonal, D2::Diagonal, alpha::Number, beta::Number) - __muldiag_nonzeroalpha_right!(out, D1, D2, alpha, beta) -end - -@inline function __muldiag_nonzeroalpha!(out::Diagonal, D1::Diagonal, D2::Diagonal, alpha::Number, beta::Number) - d1 = D1.diag - d2 = D2.diag - outd = out.diag - @inbounds @simd for i in eachindex(d1, d2, outd) - @stable_muladdmul _modify!(MulAddMul(alpha,beta), d1[i] * d2[i], outd, i) - end - return out -end - -# muldiag handles the zero-alpha case, so that we need only -# specialize the non-trivial case -function _mul_diag!(out, A, B, alpha, beta) - require_one_based_indexing(out, A, B) - _muldiag_size_check(size(out), size(A), size(B)) - if iszero(alpha) - _rmul_or_fill!(out, beta) - else - __muldiag_nonzeroalpha!(out, A, B, alpha, beta) - end - return out -end - -_mul!(out::AbstractVector, D::Diagonal, V::AbstractVector, alpha::Number, beta::Number) = - _mul_diag!(out, D, V, alpha, beta) -_mul!(out::AbstractMatrix, D::Diagonal, V::AbstractVector, alpha::Number, beta::Number) = - _mul_diag!(out, D, V, alpha, beta) -for MT in (:AbstractMatrix, :AbstractTriangular) - @eval begin - _mul!(out::AbstractMatrix, D::Diagonal, B::$MT, alpha::Number, beta::Number) = - _mul_diag!(out, D, B, alpha, beta) - _mul!(out::AbstractMatrix, A::$MT, D::Diagonal, alpha::Number, beta::Number) = - _mul_diag!(out, A, D, alpha, beta) - end -end -_mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, alpha::Number, beta::Number) = - _mul_diag!(C, Da, Db, alpha, beta) - -function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal) - _muldiag_size_check(size(Da), size(A)) - _muldiag_size_check(size(A), size(Db)) - return broadcast(*, Da.diag, A, permutedims(Db.diag)) -end - -function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) - _muldiag_size_check(size(Da), size(Db)) - _muldiag_size_check(size(Db), size(Dc)) - return Diagonal(Da.diag .* Db.diag .* Dc.diag) -end - -/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(matprod_dest(A, D, promote_op(/, eltype(A), eltype(D))), A, D) - -rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) -# avoid copy when possible via internal 3-arg backend -function _rdiv!(B::AbstractVecOrMat, A::AbstractVecOrMat, D::Diagonal) - require_one_based_indexing(A) - dd = D.diag - m, n = size(A, 1), size(A, 2) - if (k = length(dd)) != n - throw(DimensionMismatch(lazy"left hand side has $n columns but D is $k by $k")) - end - @inbounds for j in 1:n - ddj = dd[j] - iszero(ddj) && throw(SingularException(j)) - for i in 1:m - B[i, j] = A[i, j] / ddj - end - end - B -end - -function \(D::Diagonal, B::AbstractVector) - j = findfirst(iszero, D.diag) - isnothing(j) || throw(SingularException(j)) - return D.diag .\ B -end -\(D::Diagonal, B::AbstractMatrix) = ldiv!(matprod_dest(D, B, promote_op(\, eltype(D), eltype(B))), D, B) - -ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B) -function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) - require_one_based_indexing(A, B) - dd = D.diag - d = length(dd) - m, n = size(A, 1), size(A, 2) - m′, n′ = size(B, 1), size(B, 2) - m == d || throw(DimensionMismatch(lazy"right hand side has $m rows but D is $d by $d")) - (m, n) == (m′, n′) || throw(DimensionMismatch(lazy"expect output to be $m by $n, but got $m′ by $n′")) - j = findfirst(iszero, D.diag) - isnothing(j) || throw(SingularException(j)) - @inbounds for j = 1:n, i = 1:m - B[i, j] = dd[i] \ A[i, j] - end - B -end - -function _rdiv!(Dc::Diagonal, Db::Diagonal, Da::Diagonal) - n, k = length(Db.diag), length(Da.diag) - n == k || throw(DimensionMismatch(lazy"left hand side has $n columns but D is $k by $k")) - j = findfirst(iszero, Da.diag) - isnothing(j) || throw(SingularException(j)) - Dc.diag .= Db.diag ./ Da.diag - Dc -end -ldiv!(Dc::Diagonal, Da::Diagonal, Db::Diagonal) = Diagonal(ldiv!(Dc.diag, Da, Db.diag)) - -# optimizations for (Sym)Tridiagonal and Diagonal -@propagate_inbounds _getudiag(T::Tridiagonal, i) = T.du[i] -@propagate_inbounds _getudiag(S::SymTridiagonal, i) = S.ev[i] -@propagate_inbounds _getdiag(T::Tridiagonal, i) = T.d[i] -@propagate_inbounds _getdiag(S::SymTridiagonal, i) = symmetric(S.dv[i], :U)::symmetric_type(eltype(S.dv)) -@propagate_inbounds _getldiag(T::Tridiagonal, i) = T.dl[i] -@propagate_inbounds _getldiag(S::SymTridiagonal, i) = transpose(S.ev[i]) - -function (\)(D::Diagonal, S::SymTridiagonal) - T = promote_op(\, eltype(D), eltype(S)) - du = similar(S.ev, T, max(length(S.dv)-1, 0)) - d = similar(S.dv, T, length(S.dv)) - dl = similar(S.ev, T, max(length(S.dv)-1, 0)) - ldiv!(Tridiagonal(dl, d, du), D, S) -end -(\)(D::Diagonal, T::Tridiagonal) = ldiv!(similar(T, promote_op(\, eltype(D), eltype(T))), D, T) -function ldiv!(T::Tridiagonal, D::Diagonal, S::Union{SymTridiagonal,Tridiagonal}) - m = size(S, 1) - dd = D.diag - if (k = length(dd)) != m - throw(DimensionMismatch(lazy"diagonal matrix is $k by $k but right hand side has $m rows")) - end - if length(T.d) != m - throw(DimensionMismatch(lazy"target matrix size $(size(T)) does not match input matrix size $(size(S))")) - end - m == 0 && return T - j = findfirst(iszero, dd) - isnothing(j) || throw(SingularException(j)) - ddj = dd[1] - T.d[1] = ddj \ _getdiag(S, 1) - @inbounds if m > 1 - T.du[1] = ddj \ _getudiag(S, 1) - for j in 2:m-1 - ddj = dd[j] - T.dl[j-1] = ddj \ _getldiag(S, j-1) - T.d[j] = ddj \ _getdiag(S, j) - T.du[j] = ddj \ _getudiag(S, j) - end - ddj = dd[m] - T.dl[m-1] = ddj \ _getldiag(S, m-1) - T.d[m] = ddj \ _getdiag(S, m) - end - return T -end - -function (/)(S::SymTridiagonal, D::Diagonal) - T = promote_op(\, eltype(D), eltype(S)) - du = similar(S.ev, T, max(length(S.dv)-1, 0)) - d = similar(S.dv, T, length(S.dv)) - dl = similar(S.ev, T, max(length(S.dv)-1, 0)) - _rdiv!(Tridiagonal(dl, d, du), S, D) -end -(/)(T::Tridiagonal, D::Diagonal) = _rdiv!(matprod_dest(T, D, promote_op(/, eltype(T), eltype(D))), T, D) -function _rdiv!(T::Tridiagonal, S::Union{SymTridiagonal,Tridiagonal}, D::Diagonal) - n = size(S, 2) - dd = D.diag - if (k = length(dd)) != n - throw(DimensionMismatch(lazy"left hand side has $n columns but D is $k by $k")) - end - if length(T.d) != n - throw(DimensionMismatch(lazy"target matrix size $(size(T)) does not match input matrix size $(size(S))")) - end - n == 0 && return T - j = findfirst(iszero, dd) - isnothing(j) || throw(SingularException(j)) - ddj = dd[1] - T.d[1] = _getdiag(S, 1) / ddj - @inbounds if n > 1 - T.dl[1] = _getldiag(S, 1) / ddj - for j in 2:n-1 - ddj = dd[j] - T.dl[j] = _getldiag(S, j) / ddj - T.d[j] = _getdiag(S, j) / ddj - T.du[j-1] = _getudiag(S, j-1) / ddj - end - ddj = dd[n] - T.d[n] = _getdiag(S, n) / ddj - T.du[n-1] = _getudiag(S, n-1) / ddj - end - return T -end - -# Optimizations for [l/r]mul!, l/rdiv!, *, / and \ between Triangular and Diagonal. -# These functions are generally more efficient if we calculate the whole data field. -# The following code implements them in a unified pattern to avoid missing. -@inline function _setdiag!(data, f, diag, diag′ = nothing) - @inbounds for i in 1:length(diag) - data[i,i] = isnothing(diag′) ? f(diag[i]) : f(diag[i],diag′[i]) - end - data -end -for Tri in (:UpperTriangular, :LowerTriangular) - UTri = Symbol(:Unit, Tri) - # 2 args - for (fun, f) in zip((:*, :rmul!, :rdiv!, :/), (:identity, :identity, :inv, :inv)) - @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D)) - @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag)) - end - @eval *(A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = - @invoke *(A::AbstractMatrix, D::Diagonal) - @eval *(A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}, D::Diagonal) = - @invoke *(A::AbstractMatrix, D::Diagonal) - for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv)) - @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data)) - @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag)) - end - @eval *(D::Diagonal, A::$Tri{<:Any, <:StridedMaybeAdjOrTransMat}) = - @invoke *(D::Diagonal, A::AbstractMatrix) - @eval *(D::Diagonal, A::$UTri{<:Any, <:StridedMaybeAdjOrTransMat}) = - @invoke *(D::Diagonal, A::AbstractMatrix) - # 3-arg ldiv! - @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) - @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) -end - -@inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) - valA = A.diag; mA, nA = size(A) - valB = B.diag; mB, nB = size(B) - nC = checksquare(C) - @boundscheck nC == nA*nB || - throw(DimensionMismatch(lazy"expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)")) - zerofilled = false - if !(isempty(A) || isempty(B)) - z = A[1,1] * B[1,1] - if haszero(typeof(z)) - # in this case, the zero is unique - fill!(C, zero(z)) - zerofilled = true - end - end - for i in eachindex(valA), j in eachindex(valB) - idx = (i-1)*nB+j - @inbounds C[idx, idx] = valA[i] * valB[j] - end - if !zerofilled - for j in axes(A,2), i in axes(A,1) - Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in axes(B,2), l in axes(B,1) - i == j && k == l && continue - @inbounds C[Δrow + l, Δcol + k] = A[i,j] * B[l,k] - end - end - end - return C -end - -kron(A::Diagonal, B::Diagonal) = Diagonal(kron(A.diag, B.diag)) - -function kron(A::Diagonal, B::SymTridiagonal) - kdv = kron(diag(A), B.dv) - # We don't need to drop the last element - kev = kron(diag(A), _pushzero(_evview(B))) - SymTridiagonal(kdv, kev) -end -function kron(A::Diagonal, B::Tridiagonal) - # `_droplast!` is only guaranteed to work with `Vector` - kd = convert(Vector, kron(diag(A), B.d)) - kdl = _droplast!(convert(Vector, kron(diag(A), _pushzero(B.dl)))) - kdu = _droplast!(convert(Vector, kron(diag(A), _pushzero(B.du)))) - Tridiagonal(kdl, kd, kdu) -end - -@inline function kron!(C::AbstractMatrix, A::Diagonal, B::AbstractMatrix) - require_one_based_indexing(B) - (mA, nA) = size(A) - (mB, nB) = size(B) - (mC, nC) = size(C) - @boundscheck (mC, nC) == (mA * mB, nA * nB) || - throw(DimensionMismatch(lazy"expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) - zerofilled = false - if !(isempty(A) || isempty(B)) - z = A[1,1] * B[1,1] - if haszero(typeof(z)) - # in this case, the zero is unique - fill!(C, zero(z)) - zerofilled = true - end - end - m = 1 - for j in axes(A,2) - A_jj = @inbounds A[j,j] - for k in axes(B,2) - for l in axes(B,1) - @inbounds C[m] = A_jj * B[l,k] - m += 1 - end - m += (nA - 1) * mB - end - if !zerofilled - # populate the zero elements - for i in axes(A,1) - i == j && continue - A_ij = @inbounds A[i, j] - Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in axes(B,2), l in axes(B,1) - B_lk = @inbounds B[l, k] - @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk - end - end - end - m += mB - end - return C -end - -@inline function kron!(C::AbstractMatrix, A::AbstractMatrix, B::Diagonal) - require_one_based_indexing(A) - (mA, nA) = size(A) - (mB, nB) = size(B) - (mC, nC) = size(C) - @boundscheck (mC, nC) == (mA * mB, nA * nB) || - throw(DimensionMismatch(lazy"expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) - zerofilled = false - if !(isempty(A) || isempty(B)) - z = A[1,1] * B[1,1] - if haszero(typeof(z)) - # in this case, the zero is unique - fill!(C, zero(z)) - zerofilled = true - end - end - m = 1 - for j in axes(A,2) - for l in axes(B,1) - Bll = @inbounds B[l,l] - for i in axes(A,1) - @inbounds C[m] = A[i,j] * Bll - m += nB - end - m += 1 - end - if !zerofilled - for i in axes(A,1) - A_ij = @inbounds A[i, j] - Δrow, Δcol = (i-1)*mB, (j-1)*nB - for k in axes(B,2), l in axes(B,1) - l == k && continue - B_lk = @inbounds B[l, k] - @inbounds C[Δrow + l, Δcol + k] = A_ij * B_lk - end - end - end - m -= nB - end - return C -end - -conj(D::Diagonal) = Diagonal(conj(D.diag)) -transpose(D::Diagonal{<:Number}) = D -transpose(D::Diagonal) = Diagonal(transpose.(D.diag)) -adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag))) -adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag))) -adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) -permutedims(D::Diagonal) = D -permutedims(D::Diagonal, perm) = (Base.checkdims_perm(axes(D), axes(D), perm); D) - -function diag(D::Diagonal, k::Integer=0) - # every branch call similar(..., ::Int) to make sure the - # same vector type is returned independent of k - v = similar(D.diag, max(0, length(D.diag)-abs(k))) - if k == 0 - copyto!(v, D.diag) - else - for i in eachindex(v) - v[i] = D[BandIndex(k, i)] - end - end - return v -end -tr(D::Diagonal) = sum(tr, D.diag) -det(D::Diagonal) = prod(det, D.diag) -function logdet(D::Diagonal{<:Complex}) # make sure branch cut is correct - z = sum(log, D.diag) - complex(real(z), rem2pi(imag(z), RoundNearest)) -end - -# Matrix functions -for f in (:exp, :cis, :log, :sqrt, - :cos, :sin, :tan, :csc, :sec, :cot, - :cosh, :sinh, :tanh, :csch, :sech, :coth, - :acos, :asin, :atan, :acsc, :asec, :acot, - :acosh, :asinh, :atanh, :acsch, :asech, :acoth) - @eval $f(D::Diagonal) = Diagonal($f.(D.diag)) -end - -# Cube root of a real-valued diagonal matrix -cbrt(A::Diagonal{<:Real}) = Diagonal(cbrt.(A.diag)) - -function inv(D::Diagonal{T}) where T - Di = similar(D.diag, typeof(inv(oneunit(T)))) - for i = 1:length(D.diag) - if iszero(D.diag[i]) - throw(SingularException(i)) - end - Di[i] = inv(D.diag[i]) - end - Diagonal(Di) -end - -function pinv(D::Diagonal{T}) where T - Di = similar(D.diag, typeof(inv(oneunit(T)))) - for i = 1:length(D.diag) - if !iszero(D.diag[i]) - invD = inv(D.diag[i]) - if isfinite(invD) - Di[i] = invD - continue - end - end - # fallback - Di[i] = zero(T) - end - Diagonal(Di) -end -function pinv(D::Diagonal{T}, tol::Real) where T - Di = similar(D.diag, typeof(inv(oneunit(T)))) - if !isempty(D.diag) - maxabsD = maximum(abs, D.diag) - for i = 1:length(D.diag) - if abs(D.diag[i]) > tol*maxabsD - invD = inv(D.diag[i]) - if isfinite(invD) - Di[i] = invD - continue - end - end - # fallback - Di[i] = zero(T) - end - end - Diagonal(Di) -end - -# TODO Docstrings for eigvals, eigvecs, eigen all mention permute, scale, sortby as keyword args -# but not all of them below provide them. Do we need to fix that? -#Eigensystem -eigvals(D::Diagonal{<:Number}; permute::Bool=true, scale::Bool=true) = copy(D.diag) -eigvals(D::Diagonal; permute::Bool=true, scale::Bool=true) = - reduce(vcat, eigvals(x) for x in D.diag) #For block matrices, etc. -function eigvecs(D::Diagonal{T}) where T<:AbstractMatrix - diag_vecs = [ eigvecs(x) for x in D.diag ] - matT = reduce((a,b) -> promote_type(typeof(a),typeof(b)), diag_vecs) - ncols_diag = [ size(x, 2) for x in D.diag ] - nrows = size(D, 1) - vecs = Matrix{Vector{eltype(matT)}}(undef, nrows, sum(ncols_diag)) - for j in axes(D, 2), i in axes(D, 1) - jj = sum(view(ncols_diag,1:j-1)) - if i == j - for k in 1:ncols_diag[j] - vecs[i,jj+k] = diag_vecs[i][:,k] - end - else - for k in 1:ncols_diag[j] - vecs[i,jj+k] = zeros(eltype(T), ncols_diag[i]) - end - end - end - return vecs -end -function eigen(D::Diagonal; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing) - if any(!isfinite, D.diag) - throw(ArgumentError("matrix contains Infs or NaNs")) - end - Td = Base.promote_op(/, eltype(D), eltype(D)) - λ = eigvals(D) - if !isnothing(sortby) - p = sortperm(λ; alg=QuickSort, by=sortby) - λ = λ[p] - evecs = zeros(Td, size(D)) - @inbounds for i in eachindex(p) - evecs[p[i],i] = one(Td) - end - else - evecs = Diagonal(ones(Td, length(λ))) - end - Eigen(λ, evecs) -end -function eigen(D::Diagonal{<:AbstractMatrix}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing) - if any(any(!isfinite, x) for x in D.diag) - throw(ArgumentError("matrix contains Infs or NaNs")) - end - λ = eigvals(D) - evecs = eigvecs(D) - if !isnothing(sortby) - p = sortperm(λ; alg=QuickSort, by=sortby) - λ = λ[p] - evecs = evecs[:,p] - end - Eigen(λ, evecs) -end -function eigen(Da::Diagonal, Db::Diagonal; sortby::Union{Function,Nothing}=nothing) - if any(!isfinite, Da.diag) || any(!isfinite, Db.diag) - throw(ArgumentError("matrices contain Infs or NaNs")) - end - if any(iszero, Db.diag) - throw(ArgumentError("right-hand side diagonal matrix is singular")) - end - return GeneralizedEigen(eigen(Db \ Da; sortby)...) -end -function eigen(A::AbstractMatrix, D::Diagonal; sortby::Union{Function,Nothing}=nothing) - if any(iszero, D.diag) - throw(ArgumentError("right-hand side diagonal matrix is singular")) - end - if size(A, 1) == size(A, 2) && isdiag(A) - return eigen(Diagonal(A), D; sortby) - elseif all(isposdef, D.diag) - S = promote_type(eigtype(eltype(A)), eltype(D)) - return eigen(A, cholesky(Diagonal{S}(D)); sortby) - else - return eigen!(D \ A; sortby) - end -end - -#Singular system -svdvals(D::Diagonal{<:Number}) = sort!(abs.(D.diag), rev = true) -svdvals(D::Diagonal) = [svdvals(v) for v in D.diag] -function svd(D::Diagonal{T}) where {T<:Number} - d = D.diag - s = abs.(d) - piv = sortperm(s, rev = true) - S = s[piv] - Td = typeof(oneunit(T)/oneunit(T)) - U = zeros(Td, size(D)) - Vt = copy(U) - for i in 1:length(d) - j = piv[i] - U[j,i] = d[j] / S[i] - Vt[i,j] = one(Td) - end - return SVD(U, S, Vt) -end - -*(x::AdjointAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) -*(x::TransposeAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) -/(u::AdjointAbsVec, D::Diagonal) = (D' \ u')' -/(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ transpose(u)) -# disambiguation methods: Call unoptimized version for user defined AbstractTriangular. -*(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal) -*(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix) - -dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y) - -dot(A::Diagonal, B::Diagonal) = dot(A.diag, B.diag) -function dot(D::Diagonal, B::AbstractMatrix) - size(D) == size(B) || throw(DimensionMismatch(lazy"Matrix sizes $(size(D)) and $(size(B)) differ")) - return dot(D.diag, diagview(B)) -end - -dot(A::AbstractMatrix, B::Diagonal) = conj(dot(B, A)) - -function _mapreduce_prod(f, x, D::Diagonal, y) - if !(length(x) == length(D.diag) == length(y)) - throw(DimensionMismatch(lazy"x has length $(length(x)), D has size $(size(D)), and y has $(length(y))")) - end - if isempty(x) && isempty(D) && isempty(y) - return zero(promote_op(f, eltype(x), eltype(D), eltype(y))) - else - return mapreduce(t -> f(t[1], t[2], t[3]), +, zip(x, D.diag, y)) - end -end - -function cholesky!(A::Diagonal, ::NoPivot = NoPivot(); check::Bool = true) - info = 0 - for (i, di) in enumerate(A.diag) - if isreal(di) && real(di) > 0 - A.diag[i] = √di - elseif check - throw(PosDefException(i)) - else - info = i - break - end - end - Cholesky(A, 'U', convert(BlasInt, info)) -end -@deprecate cholesky!(A::Diagonal, ::Val{false}; check::Bool = true) cholesky!(A::Diagonal, NoPivot(); check) false -@deprecate cholesky(A::Diagonal, ::Val{false}; check::Bool = true) cholesky(A::Diagonal, NoPivot(); check) false - -function cholesky!(A::Diagonal, ::RowMaximum; tol=0.0, check=true) - if !ishermitian(A) - C = CholeskyPivoted(A, 'U', Vector{BlasInt}(), convert(BlasInt, 1), - tol, convert(BlasInt, -1)) - check && checkpositivedefinite(convert(BlasInt, -1)) - else - d = A.diag - n = length(d) - info = 0 - rank = n - p = sortperm(d, rev = true, by = real) - tol = tol < 0 ? n*eps(eltype(A))*real(d[p[1]]) : tol # LAPACK behavior - permute!(d, p) - @inbounds for i in eachindex(d) - di = d[i] - rootdi, j = _cholpivoted!(di, tol) - if j == 0 - d[i] = rootdi - else - rank = i - 1 - info = 1 - break - end - end - C = CholeskyPivoted(A, 'U', p, convert(BlasInt, rank), tol, convert(BlasInt, info)) - check && chkfullrank(C) - end - return C -end - -inv(C::Cholesky{<:Any,<:Diagonal}) = Diagonal(map(inv∘abs2, C.factors.diag)) - -cholcopy(A::Diagonal) = copymutable_oftype(A, choltype(A)) -cholcopy(A::RealHermSymComplexHerm{<:Any,<:Diagonal}) = Diagonal(copy_similar(diag(A), choltype(A))) - -function getproperty(C::Cholesky{<:Any,<:Diagonal}, d::Symbol) - Cfactors = getfield(C, :factors) - if d in (:U, :L, :UL) - return Cfactors - else - return getfield(C, d) - end -end - -Base._sum(A::Diagonal, ::Colon) = sum(A.diag) -function Base._sum(A::Diagonal, dims::Integer) - res = Base.reducedim_initarray(A, dims, zero(eltype(A))) - if dims <= 2 - for i = 1:length(A.diag) - @inbounds res[i] = A.diag[i] - end - else - for i = 1:length(A.diag) - @inbounds res[i,i] = A.diag[i] - end - end - res -end - -function logabsdet(A::Diagonal) - mapreduce(x -> (log(abs(x)), sign(x)), ((d1, s1), (d2, s2)) -> (d1 + d2, s1 * s2), - A.diag) -end - -function Base.muladd(A::Diagonal, B::Diagonal, z::Diagonal) - Diagonal(A.diag .* B.diag .+ z.diag) -end - -uppertriangular(D::Diagonal) = D -lowertriangular(D::Diagonal) = D diff --git a/stdlib/LinearAlgebra/src/eigen.jl b/stdlib/LinearAlgebra/src/eigen.jl deleted file mode 100644 index e0124f2e9d870..0000000000000 --- a/stdlib/LinearAlgebra/src/eigen.jl +++ /dev/null @@ -1,682 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Eigendecomposition -""" - Eigen <: Factorization - -Matrix factorization type of the eigenvalue/spectral decomposition of a square -matrix `A`. This is the return type of [`eigen`](@ref), the corresponding matrix -factorization function. - -If `F::Eigen` is the factorization object, the eigenvalues can be obtained via -`F.values` and the eigenvectors as the columns of the matrix `F.vectors`. -(The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -# Examples -```jldoctest -julia> F = eigen([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) -Eigen{Float64, Float64, Matrix{Float64}, Vector{Float64}} -values: -3-element Vector{Float64}: - 1.0 - 3.0 - 18.0 -vectors: -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - 0.0 1.0 0.0 - 0.0 0.0 1.0 - -julia> F.values -3-element Vector{Float64}: - 1.0 - 3.0 - 18.0 - -julia> F.vectors -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - 0.0 1.0 0.0 - 0.0 0.0 1.0 - -julia> vals, vecs = F; # destructuring via iteration - -julia> vals == F.values && vecs == F.vectors -true -``` -""" -struct Eigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T} - values::U - vectors::S - Eigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} = - new(values, vectors) -end -Eigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} = - Eigen{T,V,typeof(vectors),typeof(values)}(values, vectors) - -# Generalized eigenvalue problem. -""" - GeneralizedEigen <: Factorization - -Matrix factorization type of the generalized eigenvalue/spectral decomposition of -`A` and `B`. This is the return type of [`eigen`](@ref), the corresponding -matrix factorization function, when called with two matrix arguments. - -If `F::GeneralizedEigen` is the factorization object, the eigenvalues can be obtained via -`F.values` and the eigenvectors as the columns of the matrix `F.vectors`. -(The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -# Examples -```jldoctest -julia> A = [1 0; 0 -1] -2×2 Matrix{Int64}: - 1 0 - 0 -1 - -julia> B = [0 1; 1 0] -2×2 Matrix{Int64}: - 0 1 - 1 0 - -julia> F = eigen(A, B) -GeneralizedEigen{ComplexF64, ComplexF64, Matrix{ComplexF64}, Vector{ComplexF64}} -values: -2-element Vector{ComplexF64}: - 0.0 - 1.0im - 0.0 + 1.0im -vectors: -2×2 Matrix{ComplexF64}: - 0.0+1.0im 0.0-1.0im - -1.0+0.0im -1.0-0.0im - -julia> F.values -2-element Vector{ComplexF64}: - 0.0 - 1.0im - 0.0 + 1.0im - -julia> F.vectors -2×2 Matrix{ComplexF64}: - 0.0+1.0im 0.0-1.0im - -1.0+0.0im -1.0-0.0im - -julia> vals, vecs = F; # destructuring via iteration - -julia> vals == F.values && vecs == F.vectors -true -``` -""" -struct GeneralizedEigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T} - values::U - vectors::S - GeneralizedEigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} = - new(values, vectors) -end -GeneralizedEigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} = - GeneralizedEigen{T,V,typeof(vectors),typeof(values)}(values, vectors) - -# iteration for destructuring into components -Base.iterate(S::Union{Eigen,GeneralizedEigen}) = (S.values, Val(:vectors)) -Base.iterate(S::Union{Eigen,GeneralizedEigen}, ::Val{:vectors}) = (S.vectors, Val(:done)) -Base.iterate(S::Union{Eigen,GeneralizedEigen}, ::Val{:done}) = nothing - -isposdef(A::Union{Eigen,GeneralizedEigen}) = isreal(A.values) && all(x -> x > 0, A.values) - -# pick a canonical ordering to avoid returning eigenvalues in "random" order -# as is the LAPACK default (for complex λ — LAPACK sorts by λ for the Hermitian/Symmetric case) -eigsortby(λ::Real) = λ -eigsortby(λ::Complex) = (real(λ),imag(λ)) -function sorteig!(λ::AbstractVector, X::AbstractMatrix, sortby::Union{Function,Nothing}=eigsortby) - if sortby !== nothing && !issorted(λ, by=sortby) - p = sortperm(λ; alg=QuickSort, by=sortby) - permute!(λ, p) - Base.permutecols!!(X, p) - end - return λ, X -end -sorteig!(λ::AbstractVector, sortby::Union{Function,Nothing}=eigsortby) = sortby === nothing ? λ : sort!(λ, by=sortby) - -""" - eigen!(A; permute, scale, sortby) - eigen!(A, B; sortby) - -Same as [`eigen`](@ref), but saves space by overwriting the input `A` (and -`B`), instead of creating a copy. -""" -function eigen!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal - n = size(A, 2) - n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0)) - issymmetric(A) && return eigen!(Symmetric(A), sortby=sortby) - A, WR, WI, VL, VR, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A) - iszero(WI) && return Eigen(sorteig!(WR, VR, sortby)...) - evec = zeros(Complex{T}, n, n) - j = 1 - while j <= n - if WI[j] == 0 - evec[:,j] = view(VR, :, j) - else - for i = 1:n - evec[i,j] = VR[i,j] + im*VR[i,j+1] - evec[i,j+1] = VR[i,j] - im*VR[i,j+1] - end - j += 1 - end - j += 1 - end - return Eigen(sorteig!(complex.(WR, WI), evec, sortby)...) -end - -function eigen!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex - n = size(A, 2) - n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0)) - ishermitian(A) && return eigen!(Hermitian(A), sortby=sortby) - E = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A) - eval, evec = E[2], E[4] - return Eigen(sorteig!(eval, evec, sortby)...) -end - -""" - eigen(A; permute::Bool=true, scale::Bool=true, sortby) -> Eigen - -Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` -which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the -matrix `F.vectors`. This corresponds to solving an eigenvalue problem of the form -`Ax = λx`, where `A` is a matrix, `x` is an eigenvector, and `λ` is an eigenvalue. -(The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). - -For general nonsymmetric matrices it is possible to specify how the matrix is balanced -before the eigenvector calculation. The option `permute=true` permutes the matrix to become -closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to -make rows and columns more equal in norm. The default is `true` for both options. - -By default, the eigenvalues and vectors are sorted lexicographically by `(real(λ),imag(λ))`. -A different comparison function `by(λ)` can be passed to `sortby`, or you can pass -`sortby=nothing` to leave the eigenvalues in an arbitrary order. Some special matrix types -(e.g. [`Diagonal`](@ref) or [`SymTridiagonal`](@ref)) may implement their own sorting convention and not -accept a `sortby` keyword. - -# Examples -```jldoctest -julia> F = eigen([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) -Eigen{Float64, Float64, Matrix{Float64}, Vector{Float64}} -values: -3-element Vector{Float64}: - 1.0 - 3.0 - 18.0 -vectors: -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - 0.0 1.0 0.0 - 0.0 0.0 1.0 - -julia> F.values -3-element Vector{Float64}: - 1.0 - 3.0 - 18.0 - -julia> F.vectors -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - 0.0 1.0 0.0 - 0.0 0.0 1.0 - -julia> vals, vecs = F; # destructuring via iteration - -julia> vals == F.values && vecs == F.vectors -true -``` -""" -function eigen(A::AbstractMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T - _eigen(A; permute, scale, sortby) -end -function eigen(A::AbstractMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where {T <: Union{Float16,Complex{Float16}}} - E = _eigen(A; permute, scale, sortby) - values = convert(AbstractVector{isreal(E.values) ? Float16 : Complex{Float16}}, E.values) - vectors = convert(AbstractMatrix{isreal(E.vectors) ? Float16 : Complex{Float16}}, E.vectors) - return Eigen(values, vectors) -end -function _eigen(A::AbstractMatrix{T}; permute=true, scale=true, sortby=eigsortby) where {T} - isdiag(A) && return eigen(Diagonal{eigtype(T)}(diag(A)); sortby) - if ishermitian(A) - eigen!(eigencopy_oftype(Hermitian(A), eigtype(T)); sortby) - else - eigen!(eigencopy_oftype(A, eigtype(T)); permute, scale, sortby) - end -end - -eigen(x::Number) = Eigen([x], fill(one(x), 1, 1)) - -""" - eigvecs(A; permute::Bool=true, scale::Bool=true, `sortby`) -> Matrix - -Return a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can -be obtained from the slice `M[:, k]`.) The `permute`, `scale`, and `sortby` keywords are the same as -for [`eigen`](@ref). - -# Examples -```jldoctest -julia> eigvecs([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) -3×3 Matrix{Float64}: - 1.0 0.0 0.0 - 0.0 1.0 0.0 - 0.0 0.0 1.0 -``` -""" -eigvecs(A::Union{Number, AbstractMatrix}; kws...) = - eigvecs(eigen(A; kws...)) -eigvecs(F::Union{Eigen, GeneralizedEigen}) = F.vectors - -eigvals(F::Union{Eigen, GeneralizedEigen}) = F.values - -""" - eigvals!(A; permute::Bool=true, scale::Bool=true, sortby) -> values - -Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. -The `permute`, `scale`, and `sortby` keywords are the same as for [`eigen`](@ref). - -!!! note - The input matrix `A` will not contain its eigenvalues after `eigvals!` is - called on it - `A` is used as a workspace. - -# Examples -```jldoctest -julia> A = [1. 2.; 3. 4.] -2×2 Matrix{Float64}: - 1.0 2.0 - 3.0 4.0 - -julia> eigvals!(A) -2-element Vector{Float64}: - -0.3722813232690143 - 5.372281323269014 - -julia> A -2×2 Matrix{Float64}: - -0.372281 -1.0 - 0.0 5.37228 -``` -""" -function eigvals!(A::StridedMatrix{<:BlasReal}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) - issymmetric(A) && return sorteig!(eigvals!(Symmetric(A)), sortby) - _, valsre, valsim, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A) - return sorteig!(iszero(valsim) ? valsre : complex.(valsre, valsim), sortby) -end -function eigvals!(A::StridedMatrix{<:BlasComplex}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) - ishermitian(A) && return sorteig!(eigvals(Hermitian(A)), sortby) - return sorteig!(LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A)[2], sortby) -end - -# promotion type to use for eigenvalues of a Matrix{T} -eigtype(T) = promote_type(Float32, typeof(zero(T)/sqrt(abs2(one(T))))) - -""" - eigvals(A; permute::Bool=true, scale::Bool=true, sortby) -> values - -Return the eigenvalues of `A`. - -For general non-symmetric matrices it is possible to specify how the matrix is balanced -before the eigenvalue calculation. The `permute`, `scale`, and `sortby` keywords are -the same as for [`eigen`](@ref). - -# Examples -```jldoctest -julia> diag_matrix = [1 0; 0 4] -2×2 Matrix{Int64}: - 1 0 - 0 4 - -julia> eigvals(diag_matrix) -2-element Vector{Float64}: - 1.0 - 4.0 -``` -""" -eigvals(A::AbstractMatrix{T}; kws...) where T = - eigvals!(eigencopy_oftype(A, eigtype(T)); kws...) - -""" -For a scalar input, `eigvals` will return a scalar. - -# Examples -```jldoctest -julia> eigvals(-2) --2 -``` -""" -eigvals(x::Number; kwargs...) = imag(x) == 0 ? real(x) : x - -""" - eigmax(A; permute::Bool=true, scale::Bool=true) - -Return the largest eigenvalue of `A`. -The option `permute=true` permutes the matrix to become -closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to -make rows and columns more equal in norm. -Note that if the eigenvalues of `A` are complex, -this method will fail, since complex numbers cannot -be sorted. - -# Examples -```jldoctest -julia> A = [0 im; -im 0] -2×2 Matrix{Complex{Int64}}: - 0+0im 0+1im - 0-1im 0+0im - -julia> eigmax(A) -1.0 - -julia> A = [0 im; -1 0] -2×2 Matrix{Complex{Int64}}: - 0+0im 0+1im - -1+0im 0+0im - -julia> eigmax(A) -ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: -`A` cannot have complex eigenvalues. -Stacktrace: -[...] -``` -""" -function eigmax(A::Union{Number, AbstractMatrix}; permute::Bool=true, scale::Bool=true) - v = eigvals(A, permute = permute, scale = scale) - if eltype(v)<:Complex - throw(DomainError(A, "`A` cannot have complex eigenvalues.")) - end - maximum(v) -end - -""" - eigmin(A; permute::Bool=true, scale::Bool=true) - -Return the smallest eigenvalue of `A`. -The option `permute=true` permutes the matrix to become -closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to -make rows and columns more equal in norm. -Note that if the eigenvalues of `A` are complex, -this method will fail, since complex numbers cannot -be sorted. - -# Examples -```jldoctest -julia> A = [0 im; -im 0] -2×2 Matrix{Complex{Int64}}: - 0+0im 0+1im - 0-1im 0+0im - -julia> eigmin(A) --1.0 - -julia> A = [0 im; -1 0] -2×2 Matrix{Complex{Int64}}: - 0+0im 0+1im - -1+0im 0+0im - -julia> eigmin(A) -ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: -`A` cannot have complex eigenvalues. -Stacktrace: -[...] -``` -""" -function eigmin(A::Union{Number, AbstractMatrix}; - permute::Bool=true, scale::Bool=true) - v = eigvals(A, permute = permute, scale = scale) - if eltype(v)<:Complex - throw(DomainError(A, "`A` cannot have complex eigenvalues.")) - end - minimum(v) -end - -inv(A::Eigen) = A.vectors * inv(Diagonal(A.values)) / A.vectors -det(A::Eigen) = prod(A.values) - -# Generalized eigenproblem -function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal - issymmetric(A) && isposdef(B) && return eigen!(Symmetric(A), Symmetric(B), sortby=sortby) - n = size(A, 1) - if LAPACK.version() < v"3.6.0" - alphar, alphai, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) - else - alphar, alphai, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) - end - iszero(alphai) && return GeneralizedEigen(sorteig!(alphar ./ beta, vr, sortby)...) - - vecs = zeros(Complex{T}, n, n) - j = 1 - while j <= n - if alphai[j] == 0 - vecs[:,j] = view(vr, :, j) - else - for i = 1:n - vecs[i,j ] = vr[i,j] + im*vr[i,j+1] - vecs[i,j+1] = vr[i,j] - im*vr[i,j+1] - end - j += 1 - end - j += 1 - end - return GeneralizedEigen(sorteig!(complex.(alphar, alphai)./beta, vecs, sortby)...) -end - -function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex - ishermitian(A) && isposdef(B) && return eigen!(Hermitian(A), Hermitian(B), sortby=sortby) - if LAPACK.version() < v"3.6.0" - alpha, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) - else - alpha, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) - end - return GeneralizedEigen(sorteig!(alpha./beta, vr, sortby)...) -end - -""" - eigen(A, B; sortby) -> GeneralizedEigen - -Compute the generalized eigenvalue decomposition of `A` and `B`, returning a -[`GeneralizedEigen`](@ref) factorization object `F` which contains the generalized eigenvalues in -`F.values` and the generalized eigenvectors in the columns of the matrix `F.vectors`. -This corresponds to solving a generalized eigenvalue problem of the form -`Ax = λBx`, where `A, B` are matrices, `x` is an eigenvector, and `λ` is an eigenvalue. -(The `k`th generalized eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -By default, the eigenvalues and vectors are sorted lexicographically by `(real(λ),imag(λ))`. -A different comparison function `by(λ)` can be passed to `sortby`, or you can pass -`sortby=nothing` to leave the eigenvalues in an arbitrary order. - -# Examples -```jldoctest -julia> A = [1 0; 0 -1] -2×2 Matrix{Int64}: - 1 0 - 0 -1 - -julia> B = [0 1; 1 0] -2×2 Matrix{Int64}: - 0 1 - 1 0 - -julia> F = eigen(A, B); - -julia> F.values -2-element Vector{ComplexF64}: - 0.0 - 1.0im - 0.0 + 1.0im - -julia> F.vectors -2×2 Matrix{ComplexF64}: - 0.0+1.0im 0.0-1.0im - -1.0+0.0im -1.0-0.0im - -julia> vals, vecs = F; # destructuring via iteration - -julia> vals == F.values && vecs == F.vectors -true -``` -""" -function eigen(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}; kws...) where {TA,TB} - S = promote_type(eigtype(TA), TB) - eigen!(copy_similar(A, S), copy_similar(B, S); kws...) -end -eigen(A::Number, B::Number) = eigen(fill(A,1,1), fill(B,1,1)) - -""" - LinearAlgebra.eigencopy_oftype(A::AbstractMatrix, ::Type{S}) - -Creates a dense copy of `A` with eltype `S` by calling `copy_similar(A, S)`. -In the case of `Hermitian` or `Symmetric` matrices additionally retains the wrapper, -together with the `uplo` field. -""" -eigencopy_oftype(A, S) = copy_similar(A, S) - -""" - eigvals!(A, B; sortby) -> values - -Same as [`eigvals`](@ref), but saves space by overwriting the input `A` (and `B`), -instead of creating copies. - -!!! note - The input matrices `A` and `B` will not contain their eigenvalues after - `eigvals!` is called. They are used as workspaces. - -# Examples -```jldoctest -julia> A = [1. 0.; 0. -1.] -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 -1.0 - -julia> B = [0. 1.; 1. 0.] -2×2 Matrix{Float64}: - 0.0 1.0 - 1.0 0.0 - -julia> eigvals!(A, B) -2-element Vector{ComplexF64}: - 0.0 - 1.0im - 0.0 + 1.0im - -julia> A -2×2 Matrix{Float64}: - -0.0 -1.0 - 1.0 -0.0 - -julia> B -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 1.0 -``` -""" -function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal - issymmetric(A) && isposdef(B) && return sorteig!(eigvals!(Symmetric(A), Symmetric(B)), sortby) - if LAPACK.version() < v"3.6.0" - alphar, alphai, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) - else - alphar, alphai, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) - end - return sorteig!((iszero(alphai) ? alphar : complex.(alphar, alphai))./beta, sortby) -end -function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex - ishermitian(A) && isposdef(B) && return sorteig!(eigvals!(Hermitian(A), Hermitian(B)), sortby) - if LAPACK.version() < v"3.6.0" - alpha, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) - else - alpha, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) - end - return sorteig!(alpha./beta, sortby) -end - -""" - eigvals(A, B) -> values - -Compute the generalized eigenvalues of `A` and `B`. - -# Examples -```jldoctest -julia> A = [1 0; 0 -1] -2×2 Matrix{Int64}: - 1 0 - 0 -1 - -julia> B = [0 1; 1 0] -2×2 Matrix{Int64}: - 0 1 - 1 0 - -julia> eigvals(A,B) -2-element Vector{ComplexF64}: - 0.0 - 1.0im - 0.0 + 1.0im -``` -""" -function eigvals(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}; kws...) where {TA,TB} - S = promote_type(eigtype(TA), TB) - return eigvals!(copy_similar(A, S), copy_similar(B, S); kws...) -end - -""" - eigvecs(A, B) -> Matrix - -Return a matrix `M` whose columns are the generalized eigenvectors of `A` and `B`. (The `k`th eigenvector can -be obtained from the slice `M[:, k]`.) - -# Examples -```jldoctest -julia> A = [1 0; 0 -1] -2×2 Matrix{Int64}: - 1 0 - 0 -1 - -julia> B = [0 1; 1 0] -2×2 Matrix{Int64}: - 0 1 - 1 0 - -julia> eigvecs(A, B) -2×2 Matrix{ComplexF64}: - 0.0+1.0im 0.0-1.0im - -1.0+0.0im -1.0-0.0im -``` -""" -eigvecs(A::AbstractMatrix, B::AbstractMatrix; kws...) = eigvecs(eigen(A, B; kws...)) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Union{Eigen,GeneralizedEigen}) - summary(io, F); println(io) - println(io, "values:") - show(io, mime, F.values) - println(io, "\nvectors:") - show(io, mime, F.vectors) -end - -_equalcheck(f, Avalues, Avectors, Bvalues, Bvectors) = f(Avalues, Bvalues) && f(Avectors, Bvectors) -for T in (Eigen, GeneralizedEigen) - @eval begin - function Base.hash(F::$T, h::UInt) - return hash(F.values, hash(F.vectors, hash($T, h))) - end - function Base.:(==)(A::$T, B::$T) - return _equalcheck(==, A..., B...) - end - function Base.isequal(A::$T, B::$T) - return _equalcheck(isequal, A..., B...) - end - end -end - -# Conversion methods - -## Can we determine the source/result is Real? This is not stored in the type Eigen -AbstractMatrix(F::Eigen) = F.vectors * Diagonal(F.values) / F.vectors -AbstractArray(F::Eigen) = AbstractMatrix(F) -Matrix(F::Eigen) = Array(AbstractArray(F)) -Array(F::Eigen) = Matrix(F) diff --git a/stdlib/LinearAlgebra/src/exceptions.jl b/stdlib/LinearAlgebra/src/exceptions.jl deleted file mode 100644 index 7791b1ddef416..0000000000000 --- a/stdlib/LinearAlgebra/src/exceptions.jl +++ /dev/null @@ -1,76 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -export LAPACKException, - SingularException, - PosDefException, - RankDeficientException, - ZeroPivotException - -""" - LAPACKException - -Generic LAPACK exception thrown either during direct calls to the [LAPACK functions](@ref man-linalg-lapack-functions) -or during calls to other functions that use the LAPACK functions internally but lack specialized error handling. The `info` field -contains additional information on the underlying error and depends on the LAPACK function that was invoked. -""" -struct LAPACKException <: Exception - info::BlasInt -end - -""" - SingularException - -Exception thrown when the input matrix has one or more zero-valued eigenvalues, and is not invertible. -A linear solve involving such a matrix cannot be computed. -The `info` field indicates the location of (one of) the singular value(s). -""" -struct SingularException <: Exception - info::BlasInt -end - -""" - PosDefException - -Exception thrown when the input matrix was not [positive definite](https://en.wikipedia.org/wiki/Definiteness_of_a_matrix). -Some linear algebra functions and factorizations are only applicable to positive definite matrices. -The `info` field indicates the location of (one of) the eigenvalue(s) which is (are) less than/equal to 0. -""" -struct PosDefException <: Exception - info::BlasInt -end -function Base.showerror(io::IO, ex::PosDefException) - print(io, "PosDefException: matrix is not ") - if ex.info == -1 - print(io, "Hermitian") - else - print(io, "positive definite") - end - print(io, "; Factorization failed.") -end - -""" - RankDeficientException - -Exception thrown when the input matrix is [rank deficient](https://en.wikipedia.org/wiki/Rank_(linear_algebra)). Some -linear algebra functions, such as the Cholesky decomposition, are only applicable to matrices that are not rank -deficient. The `info` field indicates the computed rank of the matrix. -""" -struct RankDeficientException <: Exception - info::BlasInt -end - -""" - ZeroPivotException <: Exception - -Exception thrown when a matrix factorization/solve encounters a zero in a pivot (diagonal) -position and cannot proceed. This may *not* mean that the matrix is singular: -it may be fruitful to switch to a different factorization such as pivoted LU -that can re-order variables to eliminate spurious zero pivots. -The `info` field indicates the location of (one of) the zero pivot(s). -""" -struct ZeroPivotException <: Exception - info::BlasInt -end -function Base.showerror(io::IO, ex::ZeroPivotException) - print(io, "ZeroPivotException: factorization encountered one or more zero pivots. Consider switching to a pivoted LU factorization.") -end diff --git a/stdlib/LinearAlgebra/src/factorization.jl b/stdlib/LinearAlgebra/src/factorization.jl deleted file mode 100644 index 4cefc661741be..0000000000000 --- a/stdlib/LinearAlgebra/src/factorization.jl +++ /dev/null @@ -1,202 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## Matrix factorizations and decompositions -""" - LinearAlgebra.Factorization - -Abstract type for [matrix factorizations](https://en.wikipedia.org/wiki/Matrix_decomposition) -a.k.a. matrix decompositions. -See [online documentation](@ref man-linalg-factorizations) for a list of available -matrix factorizations. -""" -abstract type Factorization{T} end - -""" - AdjointFactorization - -Lazy wrapper type for the adjoint of the underlying `Factorization` object. Usually, the -`AdjointFactorization` constructor should not be called directly, use -[`adjoint(:: Factorization)`](@ref) instead. -""" -struct AdjointFactorization{T,S<:Factorization} <: Factorization{T} - parent::S -end -AdjointFactorization(F::Factorization) = - AdjointFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) - -""" - TransposeFactorization - -Lazy wrapper type for the transpose of the underlying `Factorization` object. Usually, the -`TransposeFactorization` constructor should not be called directly, use -[`transpose(:: Factorization)`](@ref) instead. -""" -struct TransposeFactorization{T,S<:Factorization} <: Factorization{T} - parent::S -end -TransposeFactorization(F::Factorization) = - TransposeFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) - -eltype(::Type{<:Factorization{T}}) where {T} = T -size(F::AdjointFactorization) = reverse(size(parent(F))) -size(F::TransposeFactorization) = reverse(size(parent(F))) -size(F::Union{AdjointFactorization,TransposeFactorization}, d::Integer) = d in (1, 2) ? size(F)[d] : 1 -parent(F::Union{AdjointFactorization,TransposeFactorization}) = F.parent - -""" - adjoint(F::Factorization) - -Lazy adjoint of the factorization `F`. By default, returns an -[`AdjointFactorization`](@ref) wrapper. -""" -adjoint(F::Factorization) = AdjointFactorization(F) -""" - transpose(F::Factorization) - -Lazy transpose of the factorization `F`. By default, returns a [`TransposeFactorization`](@ref), -except for `Factorization`s with real `eltype`, in which case returns an [`AdjointFactorization`](@ref). -""" -transpose(F::Factorization) = TransposeFactorization(F) -transpose(F::Factorization{<:Real}) = AdjointFactorization(F) -adjoint(F::AdjointFactorization) = F.parent -transpose(F::TransposeFactorization) = F.parent -transpose(F::AdjointFactorization{<:Real}) = F.parent -conj(A::TransposeFactorization) = adjoint(A.parent) -conj(A::AdjointFactorization) = transpose(A.parent) - -# These functions expect a non-zero info to be positive, indicating the position where a problem was detected -checkpositivedefinite(info) = info == 0 || throw(PosDefException(info)) -checknonsingular(info) = info == 0 || throw(SingularException(info)) -checknozeropivot(info) = info == 0 || throw(ZeroPivotException(info)) - -""" - issuccess(F::Factorization) - -Test that a factorization of a matrix succeeded. - -!!! compat "Julia 1.6" - `issuccess(::CholeskyPivoted)` requires Julia 1.6 or later. - -# Examples - -```jldoctest -julia> F = cholesky([1 0; 0 1]); - -julia> issuccess(F) -true -``` -""" -issuccess(F::Factorization) - -function logdet(F::Factorization) - d, s = logabsdet(F) - return d + log(s) -end - -function det(F::Factorization) - d, s = logabsdet(F) - return exp(d)*s -end - -convert(::Type{T}, f::T) where {T<:Factorization} = f -convert(::Type{T}, f::Factorization) where {T<:Factorization} = T(f)::T - -convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f)::T - -### General promotion rules -Factorization{T}(F::Factorization{T}) where {T} = F -# This no longer looks odd since the return _is_ a Factorization! -Factorization{T}(A::AdjointFactorization) where {T} = - adjoint(Factorization{T}(parent(A))) -Factorization{T}(A::TransposeFactorization) where {T} = - transpose(Factorization{T}(parent(A))) -inv(F::Factorization{T}) where {T} = (n = size(F, 1); ldiv!(F, Matrix{T}(I, n, n))) - -Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, 1:nfields(F); init=h) -Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F)) -Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))::Bool - -function Base.show(io::IO, x::AdjointFactorization) - print(io, "adjoint of ") - show(io, parent(x)) -end -function Base.show(io::IO, x::TransposeFactorization) - print(io, "transpose of ") - show(io, parent(x)) -end -function Base.show(io::IO, ::MIME"text/plain", x::AdjointFactorization) - print(io, "adjoint of ") - show(io, MIME"text/plain"(), parent(x)) -end -function Base.show(io::IO, ::MIME"text/plain", x::TransposeFactorization) - print(io, "transpose of ") - show(io, MIME"text/plain"(), parent(x)) -end - -function (\)(F::Factorization, B::AbstractVecOrMat) - require_one_based_indexing(B) - TFB = typeof(oneunit(eltype(F)) \ oneunit(eltype(B))) - ldiv!(F, copy_similar(B, TFB)) -end -(\)(F::TransposeFactorization, B::AbstractVecOrMat) = conj!(adjoint(F.parent) \ conj.(B)) -# With a real lhs and complex rhs with the same precision, we can reinterpret -# the complex rhs as a real rhs with twice the number of columns or rows -function (\)(F::Factorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} - require_one_based_indexing(B) - c2r = reshape(copy(transpose(reinterpret(T, reshape(B, (1, length(B)))))), size(B, 1), 2*size(B, 2)) - x = ldiv!(F, c2r) - return reshape(copy(reinterpret(Complex{T}, copy(transpose(reshape(x, div(length(x), 2), 2))))), _ret_size(F, B)) -end -# don't do the reinterpretation for [Adjoint/Transpose]Factorization -(\)(F::TransposeFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - conj!(adjoint(parent(F)) \ conj.(B)) -(\)(F::AdjointFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = - @invoke \(F::typeof(F), B::VecOrMat) - -function ldiv!(Y::AbstractVector, A::Factorization, B::AbstractVector) - require_one_based_indexing(Y, B) - m, n = size(A) - if m > n - Bc = copy(B) - ldiv!(A, Bc) - return copyto!(Y, 1, Bc, 1, n) - else - return ldiv!(A, copyto!(Y, B)) - end -end -function ldiv!(Y::AbstractMatrix, A::Factorization, B::AbstractMatrix) - require_one_based_indexing(Y, B) - m, n = size(A) - if m > n - Bc = copy(B) - ldiv!(A, Bc) - return copyto!(Y, view(Bc, 1:n, :)) - else - copyto!(view(Y, 1:m, :), view(B, 1:m, :)) - return ldiv!(A, Y) - end -end - -function (/)(B::AbstractMatrix, F::Factorization) - require_one_based_indexing(B) - TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) - rdiv!(copy_similar(B, TFB), F) -end -# reinterpretation trick for complex lhs and real factorization -function (/)(B::Union{Matrix{Complex{T}},AdjOrTrans{Complex{T},Vector{Complex{T}}}}, F::Factorization{T}) where {T<:BlasReal} - require_one_based_indexing(B) - x = rdiv!(copy(reinterpret(T, B)), F) - return copy(reinterpret(Complex{T}, x)) -end -# don't do the reinterpretation for [Adjoint/Transpose]Factorization -(/)(B::Union{Matrix{Complex{T}},AdjOrTrans{Complex{T},Vector{Complex{T}}}}, F::TransposeFactorization{T}) where {T<:BlasReal} = - @invoke /(B::AbstractMatrix, F::Factorization) -(/)(B::Matrix{Complex{T}}, F::AdjointFactorization{T}) where {T<:BlasReal} = - @invoke /(B::AbstractMatrix, F::Factorization) -(/)(B::Adjoint{Complex{T},Vector{Complex{T}}}, F::AdjointFactorization{T}) where {T<:BlasReal} = - (F' \ B')' -(/)(B::Transpose{Complex{T},Vector{Complex{T}}}, F::TransposeFactorization{T}) where {T<:BlasReal} = - transpose(transpose(F) \ transpose(B)) - -rdiv!(B::AbstractMatrix, A::TransposeFactorization) = transpose(ldiv!(A.parent, transpose(B))) -rdiv!(B::AbstractMatrix, A::AdjointFactorization) = adjoint(ldiv!(A.parent, adjoint(B))) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl deleted file mode 100644 index 2b03b24932c80..0000000000000 --- a/stdlib/LinearAlgebra/src/generic.jl +++ /dev/null @@ -1,2093 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## linalg.jl: Some generic Linear Algebra definitions - -# Elements of `out` may not be defined (e.g., for `BigFloat`). To make -# `mul!(out, A, B)` work for such cases, `out .*ₛ beta` short-circuits -# `out * beta`. Using `broadcasted` to avoid the multiplication -# inside this function. -function *ₛ end -Broadcast.broadcasted(::typeof(*ₛ), out, beta) = - iszero(beta::Number) ? false : broadcasted(*, out, beta) - -""" - MulAddMul(alpha, beta) - -A callable for operating short-circuiting version of `x * alpha + y * beta`. - -# Examples -```jldoctest -julia> using LinearAlgebra: MulAddMul - -julia> _add = MulAddMul(1, 0); - -julia> _add(123, nothing) -123 - -julia> MulAddMul(12, 34)(56, 78) == 56 * 12 + 78 * 34 -true -``` -""" -struct MulAddMul{ais1, bis0, TA, TB} - alpha::TA - beta::TB -end - -@inline function MulAddMul(alpha::TA, beta::TB) where {TA,TB} - if isone(alpha) - if iszero(beta) - return MulAddMul{true,true,TA,TB}(alpha, beta) - else - return MulAddMul{true,false,TA,TB}(alpha, beta) - end - else - if iszero(beta) - return MulAddMul{false,true,TA,TB}(alpha, beta) - else - return MulAddMul{false,false,TA,TB}(alpha, beta) - end - end -end - -""" - @stable_muladdmul - -Replaces a function call, that has a `MulAddMul(alpha, beta)` constructor as an -argument, with a branch over possible values of `isone(alpha)` and `iszero(beta)` -and constructs `MulAddMul{isone(alpha), iszero(beta)}` explicitly in each branch. -For example, 'f(x, y, MulAddMul(alpha, beta))` is transformed into -``` -if isone(alpha) - if iszero(beta) - f(x, y, MulAddMul{true, true, typeof(alpha), typeof(beta)}(alpha, beta)) - else - f(x, y, MulAddMul{true, false, typeof(alpha), typeof(beta)}(alpha, beta)) - end -else - if iszero(beta) - f(x, y, MulAddMul{false, true, typeof(alpha), typeof(beta)}(alpha, beta)) - else - f(x, y, MulAddMul{false, false, typeof(alpha), typeof(beta)}(alpha, beta)) - end -end -``` -This avoids the type instability of the `MulAddMul(alpha, beta)` constructor, -which causes runtime dispatch in case alpha and zero are not constants. -""" -macro stable_muladdmul(expr) - expr.head == :call || throw(ArgumentError("Can only handle function calls.")) - for (i, e) in enumerate(expr.args) - e isa Expr || continue - if e.head == :call && e.args[1] == :MulAddMul && length(e.args) == 3 - local asym = e.args[2] - local bsym = e.args[3] - - local e_sub11 = copy(expr) - e_sub11.args[i] = :(MulAddMul{true, true, typeof($asym), typeof($bsym)}($asym, $bsym)) - - local e_sub10 = copy(expr) - e_sub10.args[i] = :(MulAddMul{true, false, typeof($asym), typeof($bsym)}($asym, $bsym)) - - local e_sub01 = copy(expr) - e_sub01.args[i] = :(MulAddMul{false, true, typeof($asym), typeof($bsym)}($asym, $bsym)) - - local e_sub00 = copy(expr) - e_sub00.args[i] = :(MulAddMul{false, false, typeof($asym), typeof($bsym)}($asym, $bsym)) - - local e_out = quote - if isone($asym) - if iszero($bsym) - $e_sub11 - else - $e_sub10 - end - else - if iszero($bsym) - $e_sub01 - else - $e_sub00 - end - end - end - return esc(e_out) - end - end - throw(ArgumentError("No valid MulAddMul expression found.")) -end - -MulAddMul() = MulAddMul{true,true,Bool,Bool}(true, false) - -@inline (::MulAddMul{true})(x) = x -@inline (p::MulAddMul{false})(x) = x * p.alpha -@inline (::MulAddMul{true, true})(x, _) = x -@inline (p::MulAddMul{false, true})(x, _) = x * p.alpha -@inline (p::MulAddMul{true, false})(x, y) = x + y * p.beta -@inline (p::MulAddMul{false, false})(x, y) = x * p.alpha + y * p.beta - -""" - _modify!(_add::MulAddMul, x, C, idx) - -Short-circuiting version of `C[idx] = _add(x, C[idx])`. - -Short-circuiting the indexing `C[idx]` is necessary for avoiding `UndefRefError` -when mutating an array of non-primitive numbers such as `BigFloat`. - -# Examples -```jldoctest -julia> using LinearAlgebra: MulAddMul, _modify! - -julia> _add = MulAddMul(1, 0); - C = Vector{BigFloat}(undef, 1); - -julia> _modify!(_add, 123, C, 1) - -julia> C -1-element Vector{BigFloat}: - 123.0 -``` -""" -@inline @propagate_inbounds function _modify!(p::MulAddMul{ais1, bis0}, - x, C, idx′) where {ais1, bis0} - # `idx′` may be an integer, a tuple of integer, or a `CartesianIndex`. - # Let `CartesianIndex` constructor normalize them so that it can be - # used uniformly. It also acts as a workaround for performance penalty - # of splatting a number (#29114): - idx = CartesianIndex(idx′) - if bis0 - C[idx] = p(x) - else - C[idx] = p(x, C[idx]) - end - return -end - -@inline function _rmul_or_fill!(C::AbstractArray, beta::Number) - if isempty(C) - return C - end - if iszero(beta) - fill!(C, zero(eltype(C))) - else - rmul!(C, beta) - end - return C -end - - -function generic_mul!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) - if length(C) != length(X) - throw(DimensionMismatch(lazy"first array has length $(length(C)) which does not match the length of the second, $(length(X)).")) - end - for (IC, IX) in zip(eachindex(C), eachindex(X)) - @inbounds @stable_muladdmul _modify!(MulAddMul(alpha,beta), X[IX] * s, C, IC) - end - C -end - -function generic_mul!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) - if length(C) != length(X) - throw(DimensionMismatch(LazyString(lazy"first array has length $(length(C)) which does not", - lazy"match the length of the second, $(length(X))."))) - end - for (IC, IX) in zip(eachindex(C), eachindex(X)) - @inbounds @stable_muladdmul _modify!(MulAddMul(alpha,beta), s * X[IX], C, IC) - end - C -end - -@inline mul!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) = - _lscale_add!(C, s, X, alpha, beta) - -_lscale_add!(C::StridedArray, s::Number, X::StridedArray, alpha::Number, beta::Number) = - generic_mul!(C, s, X, alpha, beta) -@inline function _lscale_add!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) - if axes(C) == axes(X) - if isone(alpha) - if iszero(beta) - @. C = s * X - else - @. C = s * X + C * beta - end - else - if iszero(beta) - @. C = s * X * alpha - else - @. C = s * X * alpha + C * beta - end - end - else - generic_mul!(C, s, X, alpha, beta) - end - return C -end -@inline mul!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) = - _rscale_add!(C, X, s, alpha, beta) - -_rscale_add!(C::StridedArray, X::StridedArray, s::Number, alpha::Number, beta::Number) = - generic_mul!(C, X, s, alpha, beta) -@inline function _rscale_add!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) - if axes(C) == axes(X) - if isone(alpha) - if iszero(beta) - @. C = X * s - else - @. C = X * s + C * beta - end - else - s_alpha = s * alpha - if iszero(beta) - @. C = X * s_alpha - else - @. C = X * s_alpha + C * beta - end - end - else - generic_mul!(C, X, s, alpha, beta) - end - return C -end - -# For better performance when input and output are the same array -# See https://github.com/JuliaLang/julia/issues/8415#issuecomment-56608729 -""" - rmul!(A::AbstractArray, b::Number) - -Scale an array `A` by a scalar `b` overwriting `A` in-place. Use -[`lmul!`](@ref) to multiply scalar from left. The scaling operation -respects the semantics of the multiplication [`*`](@ref) between an -element of `A` and `b`. In particular, this also applies to -multiplication involving non-finite numbers such as `NaN` and `±Inf`. - -!!! compat "Julia 1.1" - Prior to Julia 1.1, `NaN` and `±Inf` entries in `A` were treated - inconsistently. - -# Examples -```jldoctest -julia> A = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> rmul!(A, 2) -2×2 Matrix{Int64}: - 2 4 - 6 8 - -julia> rmul!([NaN], 0.0) -1-element Vector{Float64}: - NaN -``` -""" -function rmul!(X::AbstractArray, s::Number) - @simd for I in eachindex(X) - @inbounds X[I] *= s - end - X -end - - -""" - lmul!(a::Number, B::AbstractArray) - -Scale an array `B` by a scalar `a` overwriting `B` in-place. Use -[`rmul!`](@ref) to multiply scalar from right. The scaling operation -respects the semantics of the multiplication [`*`](@ref) between `a` -and an element of `B`. In particular, this also applies to -multiplication involving non-finite numbers such as `NaN` and `±Inf`. - -!!! compat "Julia 1.1" - Prior to Julia 1.1, `NaN` and `±Inf` entries in `B` were treated - inconsistently. - -# Examples -```jldoctest -julia> B = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> lmul!(2, B) -2×2 Matrix{Int64}: - 2 4 - 6 8 - -julia> lmul!(0.0, [Inf]) -1-element Vector{Float64}: - NaN -``` -""" -function lmul!(s::Number, X::AbstractArray) - @simd for I in eachindex(X) - @inbounds X[I] = s*X[I] - end - X -end - -""" - rdiv!(A::AbstractArray, b::Number) - -Divide each entry in an array `A` by a scalar `b` overwriting `A` -in-place. Use [`ldiv!`](@ref) to divide scalar from left. - -# Examples -```jldoctest -julia> A = [1.0 2.0; 3.0 4.0] -2×2 Matrix{Float64}: - 1.0 2.0 - 3.0 4.0 - -julia> rdiv!(A, 2.0) -2×2 Matrix{Float64}: - 0.5 1.0 - 1.5 2.0 -``` -""" -function rdiv!(X::AbstractArray, s::Number) - @simd for I in eachindex(X) - @inbounds X[I] /= s - end - X -end - -""" - ldiv!(a::Number, B::AbstractArray) - -Divide each entry in an array `B` by a scalar `a` overwriting `B` -in-place. Use [`rdiv!`](@ref) to divide scalar from right. - -# Examples -```jldoctest -julia> B = [1.0 2.0; 3.0 4.0] -2×2 Matrix{Float64}: - 1.0 2.0 - 3.0 4.0 - -julia> ldiv!(2.0, B) -2×2 Matrix{Float64}: - 0.5 1.0 - 1.5 2.0 -``` -""" -function ldiv!(s::Number, X::AbstractArray) - @simd for I in eachindex(X) - @inbounds X[I] = s\X[I] - end - X -end -ldiv!(Y::AbstractArray, s::Number, X::AbstractArray) = Y .= s .\ X - -# Generic fallback. This assumes that B and Y have the same sizes. -ldiv!(Y::AbstractArray, A::AbstractMatrix, B::AbstractArray) = ldiv!(A, copyto!(Y, B)) - - -""" - cross(x, y) - ×(x,y) - -Compute the cross product of two 3-vectors. - -# Examples -```jldoctest -julia> a = [0;1;0] -3-element Vector{Int64}: - 0 - 1 - 0 - -julia> b = [0;0;1] -3-element Vector{Int64}: - 0 - 0 - 1 - -julia> cross(a,b) -3-element Vector{Int64}: - 1 - 0 - 0 -``` -""" -function cross(a::AbstractVector, b::AbstractVector) - if !(length(a) == length(b) == 3) - throw(DimensionMismatch("cross product is only defined for vectors of length 3")) - end - a1, a2, a3 = a - b1, b2, b3 = b - [a2*b3-a3*b2, a3*b1-a1*b3, a1*b2-a2*b1] -end - -""" - triu(M, k::Integer = 0) - -Return the upper triangle of `M` starting from the `k`th superdiagonal. - -# Examples -```jldoctest -julia> a = fill(1.0, (4,4)) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - -julia> triu(a,3) -4×4 Matrix{Float64}: - 0.0 0.0 0.0 1.0 - 0.0 0.0 0.0 0.0 - 0.0 0.0 0.0 0.0 - 0.0 0.0 0.0 0.0 - -julia> triu(a,-3) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 -``` -""" -function triu(M::AbstractMatrix, k::Integer = 0) - d = similar(M) - A = triu!(d,k) - if iszero(k) - copytrito!(A, M, 'U') - else - for col in axes(A,2) - rows = firstindex(A,1):min(col-k, lastindex(A,1)) - A[rows, col] = @view M[rows, col] - end - end - return A -end - -""" - tril(M, k::Integer = 0) - -Return the lower triangle of `M` starting from the `k`th superdiagonal. - -# Examples -```jldoctest -julia> a = fill(1.0, (4,4)) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - -julia> tril(a,3) -4×4 Matrix{Float64}: - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - 1.0 1.0 1.0 1.0 - -julia> tril(a,-3) -4×4 Matrix{Float64}: - 0.0 0.0 0.0 0.0 - 0.0 0.0 0.0 0.0 - 0.0 0.0 0.0 0.0 - 1.0 0.0 0.0 0.0 -``` -""" -function tril(M::AbstractMatrix,k::Integer=0) - d = similar(M) - A = tril!(d,k) - if iszero(k) - copytrito!(A, M, 'L') - else - for col in axes(A,2) - rows = max(firstindex(A,1),col-k):lastindex(A,1) - A[rows, col] = @view M[rows, col] - end - end - return A -end - -""" - triu!(M) - -Upper triangle of a matrix, overwriting `M` in the process. -See also [`triu`](@ref). -""" -triu!(M::AbstractMatrix) = triu!(M,0) - -""" - tril!(M) - -Lower triangle of a matrix, overwriting `M` in the process. -See also [`tril`](@ref). -""" -tril!(M::AbstractMatrix) = tril!(M,0) - -diag(A::AbstractVector) = throw(ArgumentError("use diagm instead of diag to construct a diagonal matrix")) - -########################################################################################### -# Dot products and norms - -# special cases of norm; note that they don't need to handle isempty(x) -generic_normMinusInf(x) = float(mapreduce(norm, min, x)) - -generic_normInf(x) = float(mapreduce(norm, max, x)) - -generic_norm1(x) = mapreduce(float ∘ norm, +, x) - -# faster computation of norm(x)^2, avoiding overflow for integers -norm_sqr(x) = norm(x)^2 -norm_sqr(x::Number) = abs2(x) -norm_sqr(x::Union{T,Complex{T},Rational{T}}) where {T<:Integer} = abs2(float(x)) - -function generic_norm2(x) - maxabs = normInf(x) - (ismissing(maxabs) || iszero(maxabs) || isinf(maxabs)) && return maxabs - (v, s) = iterate(x)::Tuple - T = typeof(maxabs) - if isfinite(length(x)*maxabs*maxabs) && !iszero(maxabs*maxabs) # Scaling not necessary - sum::promote_type(Float64, T) = norm_sqr(v) - while true - y = iterate(x, s) - y === nothing && break - (v, s) = y - sum += norm_sqr(v) - end - ismissing(sum) && return missing - return convert(T, sqrt(sum)) - else - sum = abs2(norm(v)/maxabs) - while true - y = iterate(x, s) - y === nothing && break - (v, s) = y - sum += (norm(v)/maxabs)^2 - end - ismissing(sum) && return missing - return convert(T, maxabs*sqrt(sum)) - end -end - -# Compute L_p norm ‖x‖ₚ = sum(abs(x).^p)^(1/p) -# (Not technically a "norm" for p < 1.) -function generic_normp(x, p) - (v, s) = iterate(x)::Tuple - if p > 1 || p < -1 # might need to rescale to avoid overflow - maxabs = p > 1 ? normInf(x) : normMinusInf(x) - (ismissing(maxabs) || iszero(maxabs) || isinf(maxabs)) && return maxabs - T = typeof(maxabs) - else - T = typeof(float(norm(v))) - end - spp::promote_type(Float64, T) = p - if -1 <= p <= 1 || (isfinite(length(x)*maxabs^spp) && !iszero(maxabs^spp)) # scaling not necessary - sum::promote_type(Float64, T) = norm(v)^spp - while true - y = iterate(x, s) - y === nothing && break - (v, s) = y - ismissing(v) && return missing - sum += norm(v)^spp - end - return convert(T, sum^inv(spp)) - else # rescaling - sum = (norm(v)/maxabs)^spp - ismissing(sum) && return missing - while true - y = iterate(x, s) - y === nothing && break - (v, s) = y - ismissing(v) && return missing - sum += (norm(v)/maxabs)^spp - end - return convert(T, maxabs*sum^inv(spp)) - end -end - -normMinusInf(x) = generic_normMinusInf(x) -normInf(x) = generic_normInf(x) -norm1(x) = generic_norm1(x) -norm2(x) = generic_norm2(x) -normp(x, p) = generic_normp(x, p) - - -""" - norm(A, p::Real=2) - -For any iterable container `A` (including arrays of any dimension) of numbers (or any -element type for which `norm` is defined), compute the `p`-norm (defaulting to `p=2`) as if -`A` were a vector of the corresponding length. - -The `p`-norm is defined as -```math -\\|A\\|_p = \\left( \\sum_{i=1}^n | a_i | ^p \\right)^{1/p} -``` -with ``a_i`` the entries of ``A``, ``| a_i |`` the [`norm`](@ref) of ``a_i``, and -``n`` the length of ``A``. Since the `p`-norm is computed using the [`norm`](@ref)s -of the entries of `A`, the `p`-norm of a vector of vectors is not compatible with -the interpretation of it as a block vector in general if `p != 2`. - -`p` can assume any numeric value (even though not all values produce a -mathematically valid vector norm). In particular, `norm(A, Inf)` returns the largest value -in `abs.(A)`, whereas `norm(A, -Inf)` returns the smallest. If `A` is a matrix and `p=2`, -then this is equivalent to the Frobenius norm. - -The second argument `p` is not necessarily a part of the interface for `norm`, i.e. a custom -type may only implement `norm(A)` without second argument. - -Use [`opnorm`](@ref) to compute the operator norm of a matrix. - -# Examples -```jldoctest -julia> v = [3, -2, 6] -3-element Vector{Int64}: - 3 - -2 - 6 - -julia> norm(v) -7.0 - -julia> norm(v, 1) -11.0 - -julia> norm(v, Inf) -6.0 - -julia> norm([1 2 3; 4 5 6; 7 8 9]) -16.881943016134134 - -julia> norm([1 2 3 4 5 6 7 8 9]) -16.881943016134134 - -julia> norm(1:9) -16.881943016134134 - -julia> norm(hcat(v,v), 1) == norm(vcat(v,v), 1) != norm([v,v], 1) -true - -julia> norm(hcat(v,v), 2) == norm(vcat(v,v), 2) == norm([v,v], 2) -true - -julia> norm(hcat(v,v), Inf) == norm(vcat(v,v), Inf) != norm([v,v], Inf) -true -``` -""" -Base.@constprop :aggressive function norm(itr, p::Real) - isempty(itr) && return float(norm(zero(eltype(itr)))) - norm_recursive_check(itr) - if p == 2 - return norm2(itr) - elseif p == 1 - return norm1(itr) - elseif p == Inf - return normInf(itr) - elseif p == 0 - return typeof(float(norm(first(itr))))(count(!iszero, itr)) - elseif p == -Inf - return normMinusInf(itr) - else - normp(itr, p) - end -end -# Split into a separate method to reduce latency in norm(x) calls (#56330) -function norm(itr) - isempty(itr) && return float(norm(zero(eltype(itr)))) - norm_recursive_check(itr) - norm2(itr) -end -function norm_recursive_check(itr) - v, s = iterate(itr) - !isnothing(s) && !ismissing(v) && v == itr && throw(ArgumentError( - "cannot evaluate norm recursively if the type of the initial element is identical to that of the container")) - return nothing -end - -""" - norm(x::Number, p::Real=2) - -For numbers, return ``\\left( |x|^p \\right)^{1/p}``. - -# Examples -```jldoctest -julia> norm(2, 1) -2.0 - -julia> norm(-2, 1) -2.0 - -julia> norm(2, 2) -2.0 - -julia> norm(-2, 2) -2.0 - -julia> norm(2, Inf) -2.0 - -julia> norm(-2, Inf) -2.0 -``` -""" -@inline function norm(x::Number, p::Real=2) - afx = abs(float(x)) - if p == 0 - if iszero(x) - return zero(afx) - elseif !isnan(x) - return oneunit(afx) - else - return afx - end - else - return afx - end -end -norm(::Missing, p::Real=2) = missing - -# special cases of opnorm -function opnorm1(A::AbstractMatrix{T}) where T - require_one_based_indexing(A) - Tnorm = typeof(float(real(zero(T)))) - Tsum = promote_type(Float64, Tnorm) - nrm::Tsum = 0 - for j in axes(A,2) - nrmj::Tsum = 0 - for i in axes(A,1) - nrmj += norm(@inbounds A[i,j]) - end - nrm = max(nrm,nrmj) - end - return convert(Tnorm, nrm) -end - -function opnorm2(A::AbstractMatrix{T}) where T - require_one_based_indexing(A) - m,n = size(A) - Tnorm = typeof(float(real(zero(T)))) - if m == 0 || n == 0 return zero(Tnorm) end - if m == 1 || n == 1 return norm2(A) end - return svdvals(A)[1] -end - -function opnormInf(A::AbstractMatrix{T}) where T - require_one_based_indexing(A) - Tnorm = typeof(float(real(zero(T)))) - Tsum = promote_type(Float64, Tnorm) - nrm::Tsum = 0 - for i in axes(A,1) - nrmi::Tsum = 0 - for j in axes(A,2) - nrmi += norm(@inbounds A[i,j]) - end - nrm = max(nrm,nrmi) - end - return convert(Tnorm, nrm) -end - - -""" - opnorm(A::AbstractMatrix, p::Real=2) - -Compute the operator norm (or matrix norm) induced by the vector `p`-norm, -where valid values of `p` are `1`, `2`, or `Inf`. (Note that for sparse matrices, -`p=2` is currently not implemented.) Use [`norm`](@ref) to compute the Frobenius -norm. - -When `p=1`, the operator norm is the maximum absolute column sum of `A`: -```math -\\|A\\|_1 = \\max_{1 ≤ j ≤ n} \\sum_{i=1}^m | a_{ij} | -``` -with ``a_{ij}`` the entries of ``A``, and ``m`` and ``n`` its dimensions. - -When `p=2`, the operator norm is the spectral norm, equal to the largest -singular value of `A`. - -When `p=Inf`, the operator norm is the maximum absolute row sum of `A`: -```math -\\|A\\|_\\infty = \\max_{1 ≤ i ≤ m} \\sum _{j=1}^n | a_{ij} | -``` - -# Examples -```jldoctest -julia> A = [1 -2 -3; 2 3 -1] -2×3 Matrix{Int64}: - 1 -2 -3 - 2 3 -1 - -julia> opnorm(A, Inf) -6.0 - -julia> opnorm(A, 1) -5.0 -``` -""" -Base.@constprop :aggressive function opnorm(A::AbstractMatrix, p::Real) - if p == 2 - return opnorm2(A) - elseif p == 1 - return opnorm1(A) - elseif p == Inf - return opnormInf(A) - else - throw(ArgumentError(lazy"invalid p-norm p=$p. Valid: 1, 2, Inf")) - end -end -opnorm(A::AbstractMatrix) = opnorm2(A) - -""" - opnorm(x::Number, p::Real=2) - -For numbers, return ``\\left( |x|^p \\right)^{1/p}``. -This is equivalent to [`norm`](@ref). -""" -@inline opnorm(x::Number, p::Real=2) = norm(x, p) - -""" - opnorm(A::Adjoint{<:Any,<:AbstractVector}, q::Real=2) - opnorm(A::Transpose{<:Any,<:AbstractVector}, q::Real=2) - -For Adjoint/Transpose-wrapped vectors, return the operator ``q``-norm of `A`, which is -equivalent to the `p`-norm with value `p = q/(q-1)`. They coincide at `p = q = 2`. -Use [`norm`](@ref) to compute the `p` norm of `A` as a vector. - -The difference in norm between a vector space and its dual arises to preserve -the relationship between duality and the dot product, and the result is -consistent with the operator `p`-norm of a `1 × n` matrix. - -# Examples -```jldoctest -julia> v = [1; im]; - -julia> vc = v'; - -julia> opnorm(vc, 1) -1.0 - -julia> norm(vc, 1) -2.0 - -julia> norm(v, 1) -2.0 - -julia> opnorm(vc, 2) -1.4142135623730951 - -julia> norm(vc, 2) -1.4142135623730951 - -julia> norm(v, 2) -1.4142135623730951 - -julia> opnorm(vc, Inf) -2.0 - -julia> norm(vc, Inf) -1.0 - -julia> norm(v, Inf) -1.0 -``` -""" -opnorm(v::TransposeAbsVec, q::Real) = q == Inf ? norm(v.parent, 1) : norm(v.parent, q/(q-1)) -opnorm(v::AdjointAbsVec, q::Real) = q == Inf ? norm(conj(v.parent), 1) : norm(conj(v.parent), q/(q-1)) -opnorm(v::AdjointAbsVec) = norm(conj(v.parent)) -opnorm(v::TransposeAbsVec) = norm(v.parent) - -norm(v::AdjOrTrans, p::Real) = norm(v.parent, p) - -""" - dot(x, y) - x ⋅ y - -Compute the dot product between two vectors. For complex vectors, the first -vector is conjugated. - -`dot` also works on arbitrary iterable objects, including arrays of any dimension, -as long as `dot` is defined on the elements. - -`dot` is semantically equivalent to `sum(dot(vx,vy) for (vx,vy) in zip(x, y))`, -with the added restriction that the arguments must have equal lengths. - -`x ⋅ y` (where `⋅` can be typed by tab-completing `\\cdot` in the REPL) is a synonym for -`dot(x, y)`. - -# Examples -```jldoctest -julia> dot([1; 1], [2; 3]) -5 - -julia> dot([im; im], [1; 1]) -0 - 2im - -julia> dot(1:5, 2:6) -70 - -julia> x = fill(2., (5,5)); - -julia> y = fill(3., (5,5)); - -julia> dot(x, y) -150.0 -``` -""" -function dot end - -function dot(x, y) # arbitrary iterables - ix = iterate(x) - iy = iterate(y) - if ix === nothing - if iy !== nothing - throw(DimensionMismatch("x and y are of different lengths!")) - end - return dot(zero(eltype(x)), zero(eltype(y))) - end - if iy === nothing - throw(DimensionMismatch("x and y are of different lengths!")) - end - (vx, xs) = ix - (vy, ys) = iy - typeof(vx) == typeof(x) && typeof(vy) == typeof(y) && throw(ArgumentError( - "cannot evaluate dot recursively if the type of an element is identical to that of the container")) - s = dot(vx, vy) - while true - ix = iterate(x, xs) - iy = iterate(y, ys) - ix === nothing && break - iy === nothing && break - (vx, xs), (vy, ys) = ix, iy - s += dot(vx, vy) - end - if !(iy === nothing && ix === nothing) - throw(DimensionMismatch("x and y are of different lengths!")) - end - return s -end - -dot(x::Number, y::Number) = conj(x) * y - -function dot(x::AbstractArray, y::AbstractArray) - lx = length(x) - if lx != length(y) - throw(DimensionMismatch(lazy"first array has length $(lx) which does not match the length of the second, $(length(y)).")) - end - if lx == 0 - return dot(zero(eltype(x)), zero(eltype(y))) - end - s = zero(dot(first(x), first(y))) - for (Ix, Iy) in zip(eachindex(x), eachindex(y)) - s += dot(@inbounds(x[Ix]), @inbounds(y[Iy])) - end - s -end - -function dot(x::Adjoint{<:Union{Real,Complex}}, y::Adjoint{<:Union{Real,Complex}}) - return conj(dot(parent(x), parent(y))) -end -dot(x::Transpose, y::Transpose) = dot(parent(x), parent(y)) - -""" - dot(x, A, y) - -Compute the generalized dot product `dot(x, A*y)` between two vectors `x` and `y`, -without storing the intermediate result of `A*y`. As for the two-argument -[`dot(_,_)`](@ref), this acts recursively. Moreover, for complex vectors, the -first vector is conjugated. - -!!! compat "Julia 1.4" - Three-argument `dot` requires at least Julia 1.4. - -# Examples -```jldoctest -julia> dot([1; 1], [1 2; 3 4], [2; 3]) -26 - -julia> dot(1:5, reshape(1:25, 5, 5), 2:6) -4850 - -julia> ⋅(1:5, reshape(1:25, 5, 5), 2:6) == dot(1:5, reshape(1:25, 5, 5), 2:6) -true -``` -""" -dot(x, A, y) = dot(x, A*y) # generic fallback for cases that are not covered by specialized methods - -function dot(x::AbstractVector, A::AbstractMatrix, y::AbstractVector) - (axes(x)..., axes(y)...) == axes(A) || throw(DimensionMismatch()) - T = typeof(dot(first(x), first(A), first(y))) - s = zero(T) - i₁ = first(eachindex(x)) - x₁ = first(x) - for j in eachindex(y) - yj = @inbounds y[j] - if !iszero(yj) - temp = zero(adjoint(@inbounds A[i₁,j]) * x₁) - @inbounds @simd for i in eachindex(x) - temp += adjoint(A[i,j]) * x[i] - end - s += dot(temp, yj) - end - end - return s -end -dot(x::AbstractVector, adjA::Adjoint, y::AbstractVector) = adjoint(dot(y, adjA.parent, x)) -dot(x::AbstractVector, transA::Transpose{<:Real}, y::AbstractVector) = adjoint(dot(y, transA.parent, x)) - -########################################################################################### - -""" - rank(A::AbstractMatrix; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ϵ) - rank(A::AbstractMatrix, rtol::Real) - -Compute the numerical rank of a matrix by counting how many outputs of -`svdvals(A)` are greater than `max(atol, rtol*σ₁)` where `σ₁` is `A`'s largest -calculated singular value. `atol` and `rtol` are the absolute and relative -tolerances, respectively. The default relative tolerance is `n*ϵ`, where `n` -is the size of the smallest dimension of `A`, and `ϵ` is the [`eps`](@ref) of -the element type of `A`. - -!!! note - Numerical rank can be a sensitive and imprecise characterization of - ill-conditioned matrices with singular values that are close to the threshold - tolerance `max(atol, rtol*σ₁)`. In such cases, slight perturbations to the - singular-value computation or to the matrix can change the result of `rank` - by pushing one or more singular values across the threshold. These variations - can even occur due to changes in floating-point errors between different Julia - versions, architectures, compilers, or operating systems. - -!!! compat "Julia 1.1" - The `atol` and `rtol` keyword arguments requires at least Julia 1.1. - In Julia 1.0 `rtol` is available as a positional argument, but this - will be deprecated in Julia 2.0. - -# Examples -```jldoctest -julia> rank(Matrix(I, 3, 3)) -3 - -julia> rank(diagm(0 => [1, 0, 2])) -2 - -julia> rank(diagm(0 => [1, 0.001, 2]), rtol=0.1) -2 - -julia> rank(diagm(0 => [1, 0.001, 2]), rtol=0.00001) -3 - -julia> rank(diagm(0 => [1, 0.001, 2]), atol=1.5) -1 -``` -""" -function rank(A::AbstractMatrix; atol::Real = 0.0, rtol::Real = (min(size(A)...)*eps(real(float(one(eltype(A))))))*iszero(atol)) - isempty(A) && return 0 # 0-dimensional case - s = svdvals(A) - tol = max(atol, rtol*s[1]) - count(>(tol), s) -end -rank(x::Union{Number,AbstractVector}) = iszero(x) ? 0 : 1 - -""" - tr(M) - -Matrix trace. Sums the diagonal elements of `M`. - -# Examples -```jldoctest -julia> A = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> tr(A) -5 -``` -""" -function tr(A) - checksquare(A) - sum(diag(A)) -end -tr(x::Number) = x - -#kron(a::AbstractVector, b::AbstractVector) -#kron(a::AbstractMatrix{T}, b::AbstractMatrix{S}) where {T,S} - -#det(a::AbstractMatrix) - -""" - inv(M) - -Matrix inverse. Computes matrix `N` such that -`M * N = I`, where `I` is the identity matrix. -Computed by solving the left-division -`N = M \\ I`. - -# Examples -```jldoctest -julia> M = [2 5; 1 3] -2×2 Matrix{Int64}: - 2 5 - 1 3 - -julia> N = inv(M) -2×2 Matrix{Float64}: - 3.0 -5.0 - -1.0 2.0 - -julia> M*N == N*M == Matrix(I, 2, 2) -true -``` -""" -function inv(A::AbstractMatrix{T}) where T - n = checksquare(A) - S = typeof(zero(T)/one(T)) # dimensionful - S0 = typeof(zero(T)/oneunit(T)) # dimensionless - dest = Matrix{S0}(I, n, n) - ldiv!(factorize(convert(AbstractMatrix{S}, A)), dest) -end -inv(A::Adjoint) = adjoint(inv(parent(A))) -inv(A::Transpose) = transpose(inv(parent(A))) - -pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T<:Real} = _vectorpinv(transpose, v, tol) -pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T<:Complex} = _vectorpinv(adjoint, v, tol) -pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T} = _vectorpinv(adjoint, v, tol) -function _vectorpinv(dualfn::Tf, v::AbstractVector{Tv}, tol) where {Tv,Tf} - res = dualfn(similar(v, typeof(zero(Tv) / (abs2(one(Tv)) + abs2(one(Tv)))))) - den = sum(abs2, v) - # as tol is the threshold relative to the maximum singular value, for a vector with - # single singular value σ=√den, σ ≦ tol*σ is equivalent to den=0 ∨ tol≥1 - if iszero(den) || tol >= one(tol) - fill!(res, zero(eltype(res))) - else - res .= dualfn(v) ./ den - end - return res -end - -# this method is just an optimization: literal negative powers of A are -# already turned by literal_pow into powers of inv(A), but for A^-1 this -# would turn into inv(A)^1 = copy(inv(A)), which makes an extra copy. -@inline Base.literal_pow(::typeof(^), A::AbstractMatrix, ::Val{-1}) = inv(A) - -""" - \\(A, B) - -Matrix division using a polyalgorithm. For input matrices `A` and `B`, the result `X` is -such that `A*X == B` when `A` is square. The solver that is used depends upon the structure -of `A`. If `A` is upper or lower triangular (or diagonal), no factorization of `A` is -required and the system is solved with either forward or backward substitution. -For non-triangular square matrices, an LU factorization is used. - -For rectangular `A` the result is the minimum-norm least squares solution computed by a -pivoted QR factorization of `A` and a rank estimate of `A` based on the R factor. - -When `A` is sparse, a similar polyalgorithm is used. For indefinite matrices, the `LDLt` -factorization does not use pivoting during the numerical factorization and therefore the -procedure can fail even for invertible matrices. - -See also: [`factorize`](@ref), [`pinv`](@ref). - -# Examples -```jldoctest -julia> A = [1 0; 1 -2]; B = [32; -4]; - -julia> X = A \\ B -2-element Vector{Float64}: - 32.0 - 18.0 - -julia> A * X == B -true -``` -""" -function (\)(A::AbstractMatrix, B::AbstractVecOrMat) - require_one_based_indexing(A, B) - m, n = size(A) - if m == n - if istril(A) - if istriu(A) - return Diagonal(A) \ B - else - return LowerTriangular(A) \ B - end - end - if istriu(A) - return UpperTriangular(A) \ B - end - return lu(A) \ B - end - return qr(A, ColumnNorm()) \ B -end - -(\)(a::AbstractVector, b::AbstractArray) = pinv(a) * b -""" - A / B - -Matrix right-division: `A / B` is equivalent to `(B' \\ A')'` where [`\\`](@ref) is the left-division operator. -For square matrices, the result `X` is such that `A == X*B`. - -See also: [`rdiv!`](@ref). - -# Examples -```jldoctest -julia> A = Float64[1 4 5; 3 9 2]; B = Float64[1 4 2; 3 4 2; 8 7 1]; - -julia> X = A / B -2×3 Matrix{Float64}: - -0.65 3.75 -1.2 - 3.25 -2.75 1.0 - -julia> isapprox(A, X*B) -true - -julia> isapprox(X, A*pinv(B)) -true -``` -""" -function (/)(A::AbstractVecOrMat, B::AbstractVecOrMat) - size(A,2) != size(B,2) && throw(DimensionMismatch("Both inputs should have the same number of columns")) - return copy(adjoint(adjoint(B) \ adjoint(A))) -end -# \(A::StridedMatrix,x::Number) = inv(A)*x Should be added at some point when the old elementwise version has been deprecated long enough -# /(x::Number,A::StridedMatrix) = x*inv(A) -/(x::Number, v::AbstractVector) = x*pinv(v) - -cond(x::Number) = iszero(x) ? Inf : 1.0 -cond(x::Number, p) = cond(x) - -#Skeel condition numbers -condskeel(A::AbstractMatrix, p::Real=Inf) = opnorm(abs.(inv(A))*abs.(A), p) - -""" - condskeel(M, [x, p::Real=Inf]) - -```math -\\kappa_S(M, p) = \\left\\Vert \\left\\vert M \\right\\vert \\left\\vert M^{-1} \\right\\vert \\right\\Vert_p \\\\ -\\kappa_S(M, x, p) = \\frac{\\left\\Vert \\left\\vert M \\right\\vert \\left\\vert M^{-1} \\right\\vert \\left\\vert x \\right\\vert \\right\\Vert_p}{\\left \\Vert x \\right \\Vert_p} -``` - -Skeel condition number ``\\kappa_S`` of the matrix `M`, optionally with respect to the -vector `x`, as computed using the operator `p`-norm. ``\\left\\vert M \\right\\vert`` -denotes the matrix of (entry wise) absolute values of ``M``; -``\\left\\vert M \\right\\vert_{ij} = \\left\\vert M_{ij} \\right\\vert``. -Valid values for `p` are `1`, `2` and `Inf` (default). - -This quantity is also known in the literature as the Bauer condition number, relative -condition number, or componentwise relative condition number. -""" -function condskeel(A::AbstractMatrix, x::AbstractVector, p::Real=Inf) - norm(abs.(inv(A))*(abs.(A)*abs.(x)), p) / norm(x, p) -end - -issymmetric(A::AbstractMatrix{<:Real}) = ishermitian(A) - -""" - issymmetric(A) -> Bool - -Test whether a matrix is symmetric. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> issymmetric(a) -true - -julia> b = [1 im; -im 1] -2×2 Matrix{Complex{Int64}}: - 1+0im 0+1im - 0-1im 1+0im - -julia> issymmetric(b) -false -``` -""" -function issymmetric(A::AbstractMatrix) - indsm, indsn = axes(A) - if indsm != indsn - return false - end - for i = first(indsn):last(indsn), j = (i):last(indsn) - if A[i,j] != transpose(A[j,i]) - return false - end - end - return true -end - -issymmetric(x::Number) = x == x - -""" - ishermitian(A) -> Bool - -Test whether a matrix is Hermitian. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> ishermitian(a) -true - -julia> b = [1 im; -im 1] -2×2 Matrix{Complex{Int64}}: - 1+0im 0+1im - 0-1im 1+0im - -julia> ishermitian(b) -true -``` -""" -function ishermitian(A::AbstractMatrix) - indsm, indsn = axes(A) - if indsm != indsn - return false - end - for i = indsn, j = i:last(indsn) - if A[i,j] != adjoint(A[j,i]) - return false - end - end - return true -end - -ishermitian(x::Number) = (x == conj(x)) - -# helper function equivalent to `iszero(v)`, but potentially without the fast exit feature -# of `all` if this improves performance -_iszero(V) = iszero(V) -# A Base.FastContiguousSubArray view of a StridedArray -FastContiguousSubArrayStrided{T,N,P<:StridedArray,I<:Tuple{AbstractUnitRange, Vararg{Any}}} = Base.SubArray{T,N,P,I,true} -# using mapreduce instead of all permits vectorization -_iszero(V::FastContiguousSubArrayStrided) = mapreduce(iszero, &, V, init=true) - -""" - istriu(A::AbstractMatrix, k::Integer = 0) -> Bool - -Test whether `A` is upper triangular starting from the `k`th superdiagonal. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> istriu(a) -false - -julia> istriu(a, -1) -true - -julia> c = [1 1 1; 1 1 1; 0 1 1] -3×3 Matrix{Int64}: - 1 1 1 - 1 1 1 - 0 1 1 - -julia> istriu(c) -false - -julia> istriu(c, -1) -true -``` -""" -istriu(A::AbstractMatrix, k::Integer = 0) = _isbanded_impl(A, k, size(A,2)-1) -istriu(x::Number) = true - -""" - istril(A::AbstractMatrix, k::Integer = 0) -> Bool - -Test whether `A` is lower triangular starting from the `k`th superdiagonal. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> istril(a) -false - -julia> istril(a, 1) -true - -julia> c = [1 1 0; 1 1 1; 1 1 1] -3×3 Matrix{Int64}: - 1 1 0 - 1 1 1 - 1 1 1 - -julia> istril(c) -false - -julia> istril(c, 1) -true -``` -""" -istril(A::AbstractMatrix, k::Integer = 0) = _isbanded_impl(A, -size(A,1)+1, k) -istril(x::Number) = true - -""" - isbanded(A::AbstractMatrix, kl::Integer, ku::Integer) -> Bool - -Test whether `A` is banded with lower bandwidth starting from the `kl`th superdiagonal -and upper bandwidth extending through the `ku`th superdiagonal. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> LinearAlgebra.isbanded(a, 0, 0) -false - -julia> LinearAlgebra.isbanded(a, -1, 1) -true - -julia> b = [1 0; -im -1] # lower bidiagonal -2×2 Matrix{Complex{Int64}}: - 1+0im 0+0im - 0-1im -1+0im - -julia> LinearAlgebra.isbanded(b, 0, 0) -false - -julia> LinearAlgebra.isbanded(b, -1, 0) -true -``` -""" -isbanded(A::AbstractMatrix, kl::Integer, ku::Integer) = _isbanded(A, kl, ku) -_isbanded(A::AbstractMatrix, kl::Integer, ku::Integer) = istriu(A, kl) && istril(A, ku) -# Performance optimization for StridedMatrix by better utilizing cache locality -# The istriu and istril loops are merged -# the additional indirection allows us to reuse the isbanded loop within istriu/istril -# without encountering cycles -_isbanded(A::StridedMatrix, kl::Integer, ku::Integer) = _isbanded_impl(A, kl, ku) -function _isbanded_impl(A, kl, ku) - Base.require_one_based_indexing(A) - - #= - We split the column range into four possible groups, depending on the values of kl and ku. - - The first is the bottom left triangle, where bands below kl must be zero, - but there are no bands above ku in that column. - - The second is where there are both bands below kl and above ku in the column. - These are the middle columns typically. - - The third is the top right, where there are bands above ku but no bands below kl - in the column. - - The fourth is mainly relevant for wide matrices, where there is a block to the right - beyond ku, where the elements should all be zero. The reason we separate this from the - third group is that we may loop over all the rows using A[:, col] instead of A[rowrange, col], - which is usually faster. - =# - - last_col_nonzeroblocks = size(A,1) + ku # fully zero rectangular block beyond this column - last_col_emptytoprows = ku + 1 # empty top rows before this column - last_col_nonemptybottomrows = size(A,1) + kl - 1 # empty bottom rows after this column - - colrange_onlybottomrows = firstindex(A,2):min(last_col_nonemptybottomrows, last_col_emptytoprows) - colrange_topbottomrows = max(last_col_emptytoprows, last(colrange_onlybottomrows))+1:last_col_nonzeroblocks - colrange_onlytoprows_nonzero = last(colrange_topbottomrows)+1:last_col_nonzeroblocks - colrange_zero_block = last_col_nonzeroblocks+1:lastindex(A,2) - - for col in intersect(axes(A,2), colrange_onlybottomrows) # only loop over the bottom rows - botrowinds = max(firstindex(A,1), col-kl+1):lastindex(A,1) - bottomrows = @view A[botrowinds, col] - _iszero(bottomrows) || return false - end - for col in intersect(axes(A,2), colrange_topbottomrows) - toprowinds = firstindex(A,1):min(col-ku-1, lastindex(A,1)) - toprows = @view A[toprowinds, col] - _iszero(toprows) || return false - botrowinds = max(firstindex(A,1), col-kl+1):lastindex(A,1) - bottomrows = @view A[botrowinds, col] - _iszero(bottomrows) || return false - end - for col in intersect(axes(A,2), colrange_onlytoprows_nonzero) - toprowinds = firstindex(A,1):min(col-ku-1, lastindex(A,1)) - toprows = @view A[toprowinds, col] - _iszero(toprows) || return false - end - for col in intersect(axes(A,2), colrange_zero_block) - _iszero(@view A[:, col]) || return false - end - return true -end - -""" - isdiag(A) -> Bool - -Test whether a matrix is diagonal in the sense that `iszero(A[i,j])` is true unless `i == j`. -Note that it is not necessary for `A` to be square; -if you would also like to check that, you need to check that `size(A, 1) == size(A, 2)`. - -# Examples -```jldoctest -julia> a = [1 2; 2 -1] -2×2 Matrix{Int64}: - 1 2 - 2 -1 - -julia> isdiag(a) -false - -julia> b = [im 0; 0 -im] -2×2 Matrix{Complex{Int64}}: - 0+1im 0+0im - 0+0im 0-1im - -julia> isdiag(b) -true - -julia> c = [1 0 0; 0 2 0] -2×3 Matrix{Int64}: - 1 0 0 - 0 2 0 - -julia> isdiag(c) -true - -julia> d = [1 0 0; 0 2 3] -2×3 Matrix{Int64}: - 1 0 0 - 0 2 3 - -julia> isdiag(d) -false -``` -""" -isdiag(A::AbstractMatrix) = isbanded(A, 0, 0) -isdiag(x::Number) = true - -""" - axpy!(α, x::AbstractArray, y::AbstractArray) - -Overwrite `y` with `x * α + y` and return `y`. -If `x` and `y` have the same axes, it's equivalent with `y .+= x .* a`. - -# Examples -```jldoctest -julia> x = [1; 2; 3]; - -julia> y = [4; 5; 6]; - -julia> axpy!(2, x, y) -3-element Vector{Int64}: - 6 - 9 - 12 -``` -""" -function axpy!(α, x::AbstractArray, y::AbstractArray) - n = length(x) - if n != length(y) - throw(DimensionMismatch(lazy"x has length $n, but y has length $(length(y))")) - end - iszero(α) && return y - for (IY, IX) in zip(eachindex(y), eachindex(x)) - @inbounds y[IY] += x[IX]*α - end - return y -end - -function axpy!(α, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractArray, ry::AbstractArray{<:Integer}) - if length(rx) != length(ry) - throw(DimensionMismatch(lazy"rx has length $(length(rx)), but ry has length $(length(ry))")) - elseif !checkindex(Bool, eachindex(IndexLinear(), x), rx) - throw(BoundsError(x, rx)) - elseif !checkindex(Bool, eachindex(IndexLinear(), y), ry) - throw(BoundsError(y, ry)) - end - iszero(α) && return y - for (IY, IX) in zip(eachindex(ry), eachindex(rx)) - @inbounds y[ry[IY]] += x[rx[IX]]*α - end - return y -end - -""" - axpby!(α, x::AbstractArray, β, y::AbstractArray) - -Overwrite `y` with `x * α + y * β` and return `y`. -If `x` and `y` have the same axes, it's equivalent with `y .= x .* a .+ y .* β`. - -# Examples -```jldoctest -julia> x = [1; 2; 3]; - -julia> y = [4; 5; 6]; - -julia> axpby!(2, x, 2, y) -3-element Vector{Int64}: - 10 - 14 - 18 -``` -""" -function axpby!(α, x::AbstractArray, β, y::AbstractArray) - if length(x) != length(y) - throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) - end - iszero(α) && isone(β) && return y - for (IX, IY) in zip(eachindex(x), eachindex(y)) - @inbounds y[IY] = x[IX]*α + y[IY]*β - end - y -end - -DenseLike{T} = Union{DenseArray{T}, Base.StridedReshapedArray{T}, Base.StridedReinterpretArray{T}} -StridedVecLike{T} = Union{DenseLike{T}, Base.FastSubArray{T,<:Any,<:DenseLike{T}}} -axpy!(α::Number, x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasFloat} = BLAS.axpy!(α, x, y) -axpby!(α::Number, x::StridedVecLike{T}, β::Number, y::StridedVecLike{T}) where {T<:BlasFloat} = BLAS.axpby!(α, x, β, y) -function axpy!(α::Number, - x::StridedVecLike{T}, rx::AbstractRange{<:Integer}, - y::StridedVecLike{T}, ry::AbstractRange{<:Integer}, -) where {T<:BlasFloat} - if Base.has_offset_axes(rx, ry) - return @invoke axpy!(α, - x::AbstractArray, rx::AbstractArray{<:Integer}, - y::AbstractArray, ry::AbstractArray{<:Integer}, - ) - end - @views BLAS.axpy!(α, x[rx], y[ry]) - return y -end - -""" - rotate!(x, y, c, s) - -Overwrite `x` with `c*x + s*y` and `y` with `-conj(s)*x + c*y`. -Returns `x` and `y`. - -!!! compat "Julia 1.5" - `rotate!` requires at least Julia 1.5. -""" -function rotate!(x::AbstractVector, y::AbstractVector, c, s) - require_one_based_indexing(x, y) - n = length(x) - if n != length(y) - throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) - end - for i in eachindex(x,y) - @inbounds begin - xi, yi = x[i], y[i] - x[i] = c *xi + s*yi - y[i] = -conj(s)*xi + c*yi - end - end - return x, y -end - -""" - reflect!(x, y, c, s) - -Overwrite `x` with `c*x + s*y` and `y` with `conj(s)*x - c*y`. -Returns `x` and `y`. - -!!! compat "Julia 1.5" - `reflect!` requires at least Julia 1.5. -""" -function reflect!(x::AbstractVector, y::AbstractVector, c, s) - require_one_based_indexing(x, y) - n = length(x) - if n != length(y) - throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) - end - for i in eachindex(x,y) - @inbounds begin - xi, yi = x[i], y[i] - x[i] = c *xi + s*yi - y[i] = conj(s)*xi - c*yi - end - end - return x, y -end - -# Elementary reflection similar to LAPACK. The reflector is not Hermitian but -# ensures that tridiagonalization of Hermitian matrices become real. See lawn72 -@inline function reflector!(x::AbstractVector{T}) where {T} - require_one_based_indexing(x) - n = length(x) - n == 0 && return zero(eltype(x)) - ξ1 = @inbounds x[1] - normu = norm(x) - if iszero(normu) - return zero(ξ1/normu) - end - ν = T(copysign(normu, real(ξ1))) - ξ1 += ν - @inbounds x[1] = -ν - for i in 2:n - @inbounds x[i] /= ξ1 - end - ξ1/ν -end - -""" - reflectorApply!(x, τ, A) - -Multiplies `A` in-place by a Householder reflection on the left. It is equivalent to `A .= (I - conj(τ)*[1; x[2:end]]*[1; x[2:end]]')*A`. -""" -@inline function reflectorApply!(x::AbstractVector, τ::Number, A::AbstractVecOrMat) - require_one_based_indexing(x, A) - m, n = size(A, 1), size(A, 2) - if length(x) != m - throw(DimensionMismatch(lazy"reflector has length $(length(x)), which must match the first dimension of matrix A, $m")) - end - m == 0 && return A - for j in axes(A,2) - Aj, xj = @inbounds view(A, 2:m, j), view(x, 2:m) - vAj = conj(τ)*(@inbounds(A[1, j]) + dot(xj, Aj)) - @inbounds A[1, j] -= vAj - axpy!(-vAj, xj, Aj) - end - return A -end - -""" - det(M) - -Matrix determinant. - -See also: [`logdet`](@ref) and [`logabsdet`](@ref). - -# Examples -```jldoctest -julia> M = [1 0; 2 2] -2×2 Matrix{Int64}: - 1 0 - 2 2 - -julia> det(M) -2.0 -``` -Note that, in general, `det` computes a floating-point approximation of the -determinant, even for integer matrices, typically via Gaussian elimination. -Julia includes an exact algorithm for integer determinants (the Bareiss algorithm), -but only uses it by default for `BigInt` matrices (since determinants quickly -overflow any fixed integer precision): -```jldoctest -julia> det(BigInt[1 0; 2 2]) # exact integer determinant -2 -``` -""" -function det(A::AbstractMatrix{T}) where {T} - if istriu(A) || istril(A) - S = promote_type(T, typeof((one(T)*zero(T) + zero(T))/one(T))) - return convert(S, det(UpperTriangular(A))) - end - return det(lu(A; check = false)) -end -det(x::Number) = x - -# Resolve Issue #40128 -det(A::AbstractMatrix{BigInt}) = det_bareiss(A) - -""" - logabsdet(M) - -Log of absolute value of matrix determinant. Equivalent to -`(log(abs(det(M))), sign(det(M)))`, but may provide increased accuracy and/or speed. - -# Examples -```jldoctest -julia> A = [-1. 0.; 0. 1.] -2×2 Matrix{Float64}: - -1.0 0.0 - 0.0 1.0 - -julia> det(A) --1.0 - -julia> logabsdet(A) -(0.0, -1.0) - -julia> B = [2. 0.; 0. 1.] -2×2 Matrix{Float64}: - 2.0 0.0 - 0.0 1.0 - -julia> det(B) -2.0 - -julia> logabsdet(B) -(0.6931471805599453, 1.0) -``` -""" -function logabsdet(A::AbstractMatrix) - if istriu(A) || istril(A) - return logabsdet(UpperTriangular(A)) - end - return logabsdet(lu(A, check=false)) -end -logabsdet(a::Number) = log(abs(a)), sign(a) - -""" - logdet(M) - -Logarithm of matrix determinant. Equivalent to `log(det(M))`, but may provide -increased accuracy and avoids overflow/underflow. - -# Examples -```jldoctest -julia> M = [1 0; 2 2] -2×2 Matrix{Int64}: - 1 0 - 2 2 - -julia> logdet(M) -0.6931471805599453 - -julia> logdet(Matrix(I, 3, 3)) -0.0 -``` -""" -function logdet(A::AbstractMatrix) - d,s = logabsdet(A) - return d + log(s) -end - -logdet(A) = log(det(A)) - -const NumberArray{T<:Number} = AbstractArray{T} - -exactdiv(a, b) = a/b -exactdiv(a::Integer, b::Integer) = div(a, b) - -""" - det_bareiss!(M) - -Calculates the determinant of a matrix using the -[Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm) using -inplace operations. - -# Examples -```jldoctest -julia> M = [1 0; 2 2] -2×2 Matrix{Int64}: - 1 0 - 2 2 - -julia> LinearAlgebra.det_bareiss!(M) -2 -``` -""" -function det_bareiss!(M) - Base.require_one_based_indexing(M) - n = checksquare(M) - sign, prev = Int8(1), one(eltype(M)) - for i in axes(M,2)[begin:end-1] - if iszero(M[i,i]) # swap with another col to make nonzero - swapto = findfirst(!iszero, @view M[i,i+1:end]) - isnothing(swapto) && return zero(prev) - sign = -sign - Base.swapcols!(M, i, i + swapto) - end - for k in i+1:n, j in i+1:n - M[j,k] = exactdiv(M[j,k]*M[i,i] - M[j,i]*M[i,k], prev) - end - prev = M[i,i] - end - return sign * M[end,end] -end -""" - LinearAlgebra.det_bareiss(M) - -Calculates the determinant of a matrix using the -[Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm). -Also refer to [`det_bareiss!`](@ref). -""" -det_bareiss(M) = det_bareiss!(copymutable(M)) - - - -""" - promote_leaf_eltypes(itr) - -For an (possibly nested) iterable object `itr`, promote the types of leaf -elements. Equivalent to `promote_type(typeof(leaf1), typeof(leaf2), ...)`. -Currently supports only numeric leaf elements. - -# Examples -```jldoctest -julia> a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] -3-element Vector{Any}: - Any[1, 2, [3, 4]] - 5.0 - Any[0 + 6im, [7.0, 8.0]] - -julia> LinearAlgebra.promote_leaf_eltypes(a) -ComplexF64 (alias for Complex{Float64}) -``` -""" -promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:Number} = T -promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:NumberArray} = eltype(T) -promote_leaf_eltypes(x::T) where {T} = T -promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_eltypes, promote_type, x; init=Bool) - -# isapprox: approximate equality of arrays [like isapprox(Number,Number)] -# Supports nested arrays; e.g., for `a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]]` -# `a ≈ a` is `true`. -function isapprox(x::AbstractArray, y::AbstractArray; - atol::Real=0, - rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), - nans::Bool=false, norm::Function=norm) - d = norm(x - y) - if isfinite(d) - return iszero(rtol) ? d <= atol : d <= max(atol, rtol*max(norm(x), norm(y))) - else - # Fall back to a component-wise approximate comparison - # (mapreduce instead of all for greater generality [#44893]) - return mapreduce((a, b) -> isapprox(a, b; rtol=rtol, atol=atol, nans=nans), &, x, y) - end -end - -""" - normalize!(a::AbstractArray, p::Real=2) - -Normalize the array `a` in-place so that its `p`-norm equals unity, -i.e. `norm(a, p) == 1`. -See also [`normalize`](@ref) and [`norm`](@ref). -""" -function normalize!(a::AbstractArray, p::Real=2) - nrm = norm(a, p) - __normalize!(a, nrm) -end - -@inline function __normalize!(a::AbstractArray, nrm) - # The largest positive floating point number whose inverse is less than infinity - δ = inv(prevfloat(typemax(nrm))) - if nrm ≥ δ # Safe to multiply with inverse - invnrm = inv(nrm) - rmul!(a, invnrm) - else # scale elements to avoid overflow - εδ = eps(one(nrm))/δ - rmul!(a, εδ) - rmul!(a, inv(nrm*εδ)) - end - return a -end - -""" - normalize(a, p::Real=2) - -Normalize `a` so that its `p`-norm equals unity, -i.e. `norm(a, p) == 1`. For scalars, this is similar to sign(a), -except normalize(0) = NaN. -See also [`normalize!`](@ref), [`norm`](@ref), and [`sign`](@ref). - -# Examples -```jldoctest -julia> a = [1,2,4]; - -julia> b = normalize(a) -3-element Vector{Float64}: - 0.2182178902359924 - 0.4364357804719848 - 0.8728715609439696 - -julia> norm(b) -1.0 - -julia> c = normalize(a, 1) -3-element Vector{Float64}: - 0.14285714285714285 - 0.2857142857142857 - 0.5714285714285714 - -julia> norm(c, 1) -1.0 - -julia> a = [1 2 4 ; 1 2 4] -2×3 Matrix{Int64}: - 1 2 4 - 1 2 4 - -julia> norm(a) -6.48074069840786 - -julia> normalize(a) -2×3 Matrix{Float64}: - 0.154303 0.308607 0.617213 - 0.154303 0.308607 0.617213 - -julia> normalize(3, 1) -1.0 - -julia> normalize(-8, 1) --1.0 - -julia> normalize(0, 1) -NaN -``` -""" -function normalize(a::AbstractArray, p::Real = 2) - nrm = norm(a, p) - if !isempty(a) - aa = copymutable_oftype(a, typeof(first(a)/nrm)) - return __normalize!(aa, nrm) - else - T = typeof(zero(eltype(a))/nrm) - return T[] - end -end - -normalize(x) = x / norm(x) -normalize(x, p::Real) = x / norm(x, p) - -""" - copytrito!(B, A, uplo) -> B - -Copies a triangular part of a matrix `A` to another matrix `B`. -`uplo` specifies the part of the matrix `A` to be copied to `B`. -Set `uplo = 'L'` for the lower triangular part or `uplo = 'U'` -for the upper triangular part. - -!!! compat "Julia 1.11" - `copytrito!` requires at least Julia 1.11. - -# Examples -```jldoctest -julia> A = [1 2 ; 3 4]; - -julia> B = [0 0 ; 0 0]; - -julia> copytrito!(B, A, 'L') -2×2 Matrix{Int64}: - 1 0 - 3 4 -``` -""" -function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) - require_one_based_indexing(A, B) - BLAS.chkuplo(uplo) - m,n = size(A) - A = Base.unalias(B, A) - if uplo == 'U' - LAPACK.lacpy_size_check(size(B), (n < m ? n : m, n)) - for j in axes(A,2), i in axes(A,1)[begin : min(j,end)] - # extract the parents for UpperTriangular matrices - Bv, Av = uppertridata(B), uppertridata(A) - @inbounds Bv[i,j] = Av[i,j] - end - else # uplo == 'L' - LAPACK.lacpy_size_check(size(B), (m, m < n ? m : n)) - for j in axes(A,2), i in axes(A,1)[j:end] - # extract the parents for LowerTriangular matrices - Bv, Av = lowertridata(B), lowertridata(A) - @inbounds Bv[i,j] = Av[i,j] - end - end - return B -end -# Forward LAPACK-compatible strided matrices to lacpy -function copytrito!(B::StridedMatrixStride1{T}, A::StridedMatrixStride1{T}, uplo::AbstractChar) where {T<:BlasFloat} - LAPACK.lacpy!(B, A, uplo) -end diff --git a/stdlib/LinearAlgebra/src/givens.jl b/stdlib/LinearAlgebra/src/givens.jl deleted file mode 100644 index 4239c8dc4ed48..0000000000000 --- a/stdlib/LinearAlgebra/src/givens.jl +++ /dev/null @@ -1,429 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# givensAlgorithm functions are derived from LAPACK, see below - -abstract type AbstractRotation{T} end -struct AdjointRotation{T,S<:AbstractRotation{T}} <: AbstractRotation{T} - R::S -end - -transpose(R::AbstractRotation) = error("transpose not implemented for $(typeof(R)). Consider using adjoint instead of transpose.") - -(*)(R::AbstractRotation, A::AbstractVector) = _rot_mul_vecormat(R, A) -(*)(R::AbstractRotation, A::AbstractMatrix) = _rot_mul_vecormat(R, A) -function _rot_mul_vecormat(R::AbstractRotation{T}, A::AbstractVecOrMat{S}) where {T,S} - TS = typeof(zero(T)*zero(S) + zero(T)*zero(S)) - lmul!(convert(AbstractRotation{TS}, R), copy_similar(A, TS)) -end - -(*)(A::AbstractVector, R::AbstractRotation) = _vecormat_mul_rot(A, R) -(*)(A::AbstractMatrix, R::AbstractRotation) = _vecormat_mul_rot(A, R) -function _vecormat_mul_rot(A::AbstractVecOrMat{T}, R::AbstractRotation{S}) where {T,S} - TS = typeof(zero(T)*zero(S) + zero(T)*zero(S)) - rmul!(copy_similar(A, TS), convert(AbstractRotation{TS}, R)) -end - -""" - LinearAlgebra.Givens(i1,i2,c,s) -> G - -A Givens rotation linear operator. The fields `c` and `s` represent the cosine and sine of -the rotation angle, respectively. The `Givens` type supports left multiplication `G*A` and -conjugated transpose right multiplication `A*G'`. The type doesn't have a `size` and can -therefore be multiplied with matrices of arbitrary size as long as `i2<=size(A,2)` for -`G*A` or `i2<=size(A,1)` for `A*G'`. - -See also [`givens`](@ref). -""" -struct Givens{T} <: AbstractRotation{T} - i1::Int - i2::Int - c::T - s::T -end -struct Rotation{T} <: AbstractRotation{T} - rotations::Vector{Givens{T}} -end - -convert(::Type{T}, r::T) where {T<:AbstractRotation} = r -convert(::Type{T}, r::AbstractRotation) where {T<:AbstractRotation} = T(r)::T -convert(::Type{AbstractRotation{T}}, r::AdjointRotation) where {T} = convert(AbstractRotation{T}, r.R)' -convert(::Type{AbstractRotation{T}}, r::AdjointRotation{T}) where {T} = r - -Givens(i1, i2, c, s) = Givens(i1, i2, promote(c, s)...) -Givens{T}(G::Givens{T}) where {T} = G -Givens{T}(G::Givens) where {T} = Givens(G.i1, G.i2, convert(T, G.c), convert(T, G.s)) -Rotation{T}(R::Rotation{T}) where {T} = R -Rotation{T}(R::Rotation) where {T} = Rotation{T}([Givens{T}(g) for g in R.rotations]) -AbstractRotation{T}(G::Givens) where {T} = Givens{T}(G) -AbstractRotation{T}(R::Rotation) where {T} = Rotation{T}(R) - -adjoint(G::Givens) = Givens(G.i1, G.i2, G.c', -G.s) -adjoint(R::AbstractRotation) = AdjointRotation(R) -adjoint(adjR::AdjointRotation) = adjR.R - -Base.copy(aR::AdjointRotation{T,Rotation{T}}) where {T} = - Rotation{T}([r' for r in Iterators.reverse(aR.R.rotations)]) - -floatmin2(::Type{Float32}) = reinterpret(Float32, 0x26000000) -floatmin2(::Type{Float64}) = reinterpret(Float64, 0x21a0000000000000) -floatmin2(::Type{T}) where {T} = (twopar = 2one(T); twopar^trunc(Integer,log(floatmin(T)/eps(T))/log(twopar)/twopar)) - -# derived from LAPACK's dlartg -# Copyright: -# Univ. of Tennessee -# Univ. of California Berkeley -# Univ. of Colorado Denver -# NAG Ltd. -function givensAlgorithm(f::T, g::T) where T<:AbstractFloat - onepar = one(T) - T0 = typeof(onepar) # dimensionless - zeropar = T0(zero(T)) # must be dimensionless - - # need both dimensionful and dimensionless versions of these: - safmn2 = floatmin2(T0) - safmn2u = floatmin2(T) - safmx2 = one(T)/safmn2 - safmx2u = oneunit(T)/safmn2 - - if g == 0 - cs = onepar - sn = zeropar - r = f - elseif f == 0 - cs = zeropar - sn = onepar - r = g - else - f1 = f - g1 = g - scalepar = max(abs(f1), abs(g1)) - if scalepar >= safmx2u - count = 0 - while true - count += 1 - f1 *= safmn2 - g1 *= safmn2 - scalepar = max(abs(f1), abs(g1)) - if scalepar < safmx2u || count >= 20 break end - end - r = sqrt(f1*f1 + g1*g1) - cs = f1/r - sn = g1/r - for i = 1:count - r *= safmx2 - end - elseif scalepar <= safmn2u - count = 0 - while true - count += 1 - f1 *= safmx2 - g1 *= safmx2 - scalepar = max(abs(f1), abs(g1)) - if scalepar > safmn2u break end - end - r = sqrt(f1*f1 + g1*g1) - cs = f1/r - sn = g1/r - for i = 1:count - r *= safmn2 - end - else - r = sqrt(f1*f1 + g1*g1) - cs = f1/r - sn = g1/r - end - if abs(f) > abs(g) && cs < 0 - cs = -cs - sn = -sn - r = -r - end - end - return cs, sn, r -end - -# derived from LAPACK's zlartg -# Copyright: -# Univ. of Tennessee -# Univ. of California Berkeley -# Univ. of Colorado Denver -# NAG Ltd. -function givensAlgorithm(f::Complex{T}, g::Complex{T}) where T<:AbstractFloat - onepar = one(T) - T0 = typeof(onepar) # dimensionless - zeropar = T0(zero(T)) # must be dimensionless - czero = complex(zeropar) - - abs1(ff) = max(abs(real(ff)), abs(imag(ff))) - safmin = floatmin(T0) - safmn2 = floatmin2(T0) - safmn2u = floatmin2(T) - safmx2 = one(T)/safmn2 - safmx2u = oneunit(T)/safmn2 - scalepar = max(abs1(f), abs1(g)) - fs = f - gs = g - count = 0 - if scalepar >= safmx2u - while true - count += 1 - fs *= safmn2 - gs *= safmn2 - scalepar *= safmn2 - if scalepar < safmx2u || count >= 20 break end - end - elseif scalepar <= safmn2u - if g == 0 - cs = onepar - sn = czero - r = f - return cs, sn, r - end - while true - count -= 1 - fs *= safmx2 - gs *= safmx2 - scalepar *= safmx2 - if scalepar > safmn2u break end - end - end - f2 = abs2(fs) - g2 = abs2(gs) - if f2 <= max(g2, oneunit(T))*safmin - # This is a rare case: F is very small. - if f == 0 - cs = zero(T) - r = complex(abs(g)) - # do complex/real division explicitly with two real divisions - d = abs(gs) - sn = complex(real(gs)/d, -imag(gs)/d) - return cs, sn, r - end - f2s = abs(fs) - # g2 and g2s are accurate - # g2 is at least safmin, and g2s is at least safmn2 - g2s = sqrt(g2) - # error in cs from underflow in f2s is at most - # unfl / safmn2 .lt. sqrt(unfl*eps) .lt. eps - # if max(g2,one)=g2, then f2 .lt. g2*safmin, - # and so cs .lt. sqrt(safmin) - # if max(g2,one)=one, then f2 .lt. safmin - # and so cs .lt. sqrt(safmin)/safmn2 = sqrt(eps) - # therefore, cs = f2s/g2s / sqrt( 1 + (f2s/g2s)**2 ) = f2s/g2s - cs = f2s/g2s - # make sure abs(ff) = 1 - # do complex/real division explicitly with 2 real divisions - if abs1(f) > 1 - d = abs(f) - ff = complex(real(f)/d, imag(f)/d) - else - dr = safmx2*real(f) - di = safmx2*imag(f) - d = hypot(dr, di) - ff = complex(dr/d, di/d) - end - sn = ff*complex(real(gs)/g2s, -imag(gs)/g2s) - r = cs*f + sn*g - else - # This is the most common case. - # Neither F2 nor F2/G2 are less than SAFMIN - # F2S cannot overflow, and it is accurate - f2s = sqrt(onepar + g2/f2) - # do the f2s(real)*fs(complex) multiply with two real multiplies - r = complex(f2s*real(fs), f2s*imag(fs)) - cs = onepar/f2s - d = f2 + g2 - # do complex/real division explicitly with two real divisions - sn = complex(real(r)/d, imag(r)/d) - sn *= conj(gs) - if count != 0 - if count > 0 - for i = 1:count - r *= safmx2 - end - else - for i = 1:-count - r *= safmn2 - end - end - end - end - return cs, sn, r -end - -# enable for unitful quantities -function givensAlgorithm(f::T, g::T) where T - fs = f / oneunit(T) - gs = g / oneunit(T) - typeof(fs) === T && typeof(gs) === T && - !isa(fs, Union{AbstractFloat,Complex{<:AbstractFloat}}) && - throw(MethodError(givensAlgorithm, (fs, gs))) - - c, s, r = givensAlgorithm(fs, gs) - return c, s, r * oneunit(T) -end - -givensAlgorithm(f, g) = givensAlgorithm(promote(float(f), float(g))...) - -""" - - givens(f::T, g::T, i1::Integer, i2::Integer) where {T} -> (G::Givens, r::T) - -Computes the Givens rotation `G` and scalar `r` such that for any vector `x` where -``` -x[i1] = f -x[i2] = g -``` -the result of the multiplication -``` -y = G*x -``` -has the property that -``` -y[i1] = r -y[i2] = 0 -``` - -See also [`LinearAlgebra.Givens`](@ref). -""" -function givens(f::T, g::T, i1::Integer, i2::Integer) where T - if i1 == i2 - throw(ArgumentError("Indices must be distinct.")) - end - c, s, r = givensAlgorithm(f, g) - if i1 > i2 - s = -conj(s) - i1, i2 = i2, i1 - end - Givens(i1, i2, c, s), r -end -""" - givens(A::AbstractArray, i1::Integer, i2::Integer, j::Integer) -> (G::Givens, r) - -Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication -``` -B = G*A -``` -has the property that -``` -B[i1,j] = r -B[i2,j] = 0 -``` - -See also [`LinearAlgebra.Givens`](@ref). -""" -givens(A::AbstractMatrix, i1::Integer, i2::Integer, j::Integer) = - givens(A[i1,j], A[i2,j], i1, i2) - - -""" - givens(x::AbstractVector, i1::Integer, i2::Integer) -> (G::Givens, r) - -Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication -``` -B = G*x -``` -has the property that -``` -B[i1] = r -B[i2] = 0 -``` - -See also [`LinearAlgebra.Givens`](@ref). -""" -givens(x::AbstractVector, i1::Integer, i2::Integer) = givens(x[i1], x[i2], i1, i2) - -function getindex(G::Givens, i::Integer, j::Integer) - if i == j - if i == G.i1 || i == G.i2 - G.c - else - oneunit(G.c) - end - elseif i == G.i1 && j == G.i2 - G.s - elseif i == G.i2 && j == G.i1 - -conj(G.s) - else - zero(G.s) - end -end - -@inline function lmul!(G::Givens, A::AbstractVecOrMat) - require_one_based_indexing(A) - m, n = size(A, 1), size(A, 2) - if G.i2 > m - throw(DimensionMismatch("column indices for rotation are outside the matrix")) - end - @inbounds for i = 1:n - a1, a2 = A[G.i1,i], A[G.i2,i] - A[G.i1,i] = G.c *a1 + G.s*a2 - A[G.i2,i] = -conj(G.s)*a1 + G.c*a2 - end - return A -end -@inline function rmul!(A::AbstractMatrix, G::Givens) - require_one_based_indexing(A) - m, n = size(A, 1), size(A, 2) - if G.i2 > n - throw(DimensionMismatch("column indices for rotation are outside the matrix")) - end - @inbounds for i = 1:m - a1, a2 = A[i,G.i1], A[i,G.i2] - A[i,G.i1] = a1*G.c - a2*G.s' - A[i,G.i2] = a1*G.s + a2*G.c - end - return A -end - -function lmul!(G::Givens, R::Rotation) - push!(R.rotations, G) - return R -end -function rmul!(R::Rotation, G::Givens) - pushfirst!(R.rotations, G) - return R -end - -function lmul!(R::Rotation, A::AbstractVecOrMat) - @inbounds for i in eachindex(R.rotations) - lmul!(R.rotations[i], A) - end - return A -end -function rmul!(A::AbstractMatrix, R::Rotation) - @inbounds for i in eachindex(R.rotations) - rmul!(A, R.rotations[i]) - end - return A -end - -function lmul!(adjR::AdjointRotation{<:Any,<:Rotation}, A::AbstractVecOrMat) - R = adjR.R - @inbounds for i in eachindex(R.rotations) - lmul!(adjoint(R.rotations[i]), A) - end - return A -end -function rmul!(A::AbstractMatrix, adjR::AdjointRotation{<:Any,<:Rotation}) - R = adjR.R - @inbounds for i in eachindex(R.rotations) - rmul!(A, adjoint(R.rotations[i])) - end - return A -end - -function *(G1::Givens{S}, G2::Givens{T}) where {S,T} - TS = promote_type(T, S) - Rotation{TS}([convert(AbstractRotation{TS}, G2), convert(AbstractRotation{TS}, G1)]) -end -function *(G::Givens{T}, Gs::Givens{T}...) where {T} - return Rotation([reverse(Gs)..., G]) -end -function *(G::Givens{S}, R::Rotation{T}) where {S,T} - TS = promote_type(T, S) - Rotation(vcat(convert(AbstractRotation{TS}, R).rotations, convert(AbstractRotation{TS}, G))) -end -function *(R::Rotation{S}, G::Givens{T}) where {S,T} - TS = promote_type(T, S) - Rotation(vcat(convert(AbstractRotation{TS}, G), convert(AbstractRotation{TS}, R).rotations)) -end diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl deleted file mode 100644 index ed654c33aba55..0000000000000 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ /dev/null @@ -1,624 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -###################################################################################### -# Upper-Hessenberg matrices H+μI, analogous to the UpperTriangular type - -""" - UpperHessenberg(A::AbstractMatrix) - -Construct an `UpperHessenberg` view of the matrix `A`. -Entries of `A` below the first subdiagonal are ignored. - -!!! compat "Julia 1.3" - This type was added in Julia 1.3. - -Efficient algorithms are implemented for `H \\ b`, `det(H)`, and similar. - -See also the [`hessenberg`](@ref) function to factor any matrix into a similar -upper-Hessenberg matrix. - -If `F::Hessenberg` is the factorization object, the unitary matrix can be accessed -with `F.Q` and the Hessenberg matrix with `F.H`. When `Q` is extracted, the resulting -type is the `HessenbergQ` object, and may be converted to a regular matrix with -[`convert(Array, _)`](@ref) (or `Array(_)` for short). - -Iterating the decomposition produces the factors `F.Q` and `F.H`. - -# Examples -```jldoctest -julia> A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16] -4×4 Matrix{Int64}: - 1 2 3 4 - 5 6 7 8 - 9 10 11 12 - 13 14 15 16 - -julia> UpperHessenberg(A) -4×4 UpperHessenberg{Int64, Matrix{Int64}}: - 1 2 3 4 - 5 6 7 8 - ⋅ 10 11 12 - ⋅ ⋅ 15 16 -``` -""" -struct UpperHessenberg{T,S<:AbstractMatrix{T}} <: AbstractMatrix{T} - data::S - - function UpperHessenberg{T,S}(data) where {T,S<:AbstractMatrix{T}} - require_one_based_indexing(data) - new{T,S}(data) - end -end -UpperHessenberg(H::UpperHessenberg) = H -UpperHessenberg{T}(A::AbstractMatrix) where {T} = UpperHessenberg(convert(AbstractMatrix{T}, A)) -UpperHessenberg{T}(H::UpperHessenberg) where {T} = UpperHessenberg{T}(H.data) -UpperHessenberg(A::AbstractMatrix) = UpperHessenberg{eltype(A),typeof(A)}(A) -Matrix(H::UpperHessenberg{T}) where {T} = Matrix{T}(H) -Array(H::UpperHessenberg) = Matrix(H) -size(H::UpperHessenberg) = size(H.data) -axes(H::UpperHessenberg) = axes(H.data) -parent(H::UpperHessenberg) = H.data - -# similar behaves like UpperTriangular -similar(H::UpperHessenberg, ::Type{T}) where {T} = UpperHessenberg(similar(H.data, T)) -similar(H::UpperHessenberg, ::Type{T}, dims::Dims{N}) where {T,N} = similar(H.data, T, dims) - -AbstractMatrix{T}(H::UpperHessenberg) where {T} = UpperHessenberg{T}(H) -AbstractMatrix{T}(H::UpperHessenberg{T}) where {T} = copy(H) - -Base.dataids(A::UpperHessenberg) = Base.dataids(parent(A)) -Base.unaliascopy(A::UpperHessenberg) = UpperHessenberg(Base.unaliascopy(parent(A))) - -copy(H::UpperHessenberg) = UpperHessenberg(copy(H.data)) -real(H::UpperHessenberg{<:Complex}) = UpperHessenberg(triu!(real(H.data),-1)) -imag(H::UpperHessenberg) = UpperHessenberg(triu!(imag(H.data),-1)) - -Base.@constprop :aggressive function istriu(A::UpperHessenberg, k::Integer=0) - k <= -1 && return true - return _istriu(A, k) -end -# additional indirection to dispatch to optimized method for banded parents (defined in special.jl) -@inline function _istriu(A::UpperHessenberg, k) - P = parent(A) - m = size(A, 1) - for j in firstindex(P,2):min(m + k - 1, lastindex(P,2)) - Prows = @view P[max(begin, j - k + 1):min(j+1,end), j] - _iszero(Prows) || return false - end - return true -end - -function Matrix{T}(H::UpperHessenberg) where T - m,n = size(H) - return triu!(copyto!(Matrix{T}(undef, m, n), H.data), -1) -end - -Base.isassigned(H::UpperHessenberg, i::Int, j::Int) = - i <= j+1 ? isassigned(H.data, i, j) : true - -Base.@propagate_inbounds getindex(H::UpperHessenberg{T}, i::Int, j::Int) where {T} = - i <= j+1 ? convert(T, H.data[i,j]) : zero(T) - -Base._reverse(A::UpperHessenberg, dims) = reverse!(Matrix(A); dims) - -Base.@propagate_inbounds function setindex!(A::UpperHessenberg, x, i::Integer, j::Integer) - if i > j+1 - x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " * - lazy"($i, $j) of an UpperHessenberg matrix to a nonzero value ($x)")) - else - A.data[i,j] = x - end - return A -end - -function Base.replace_in_print_matrix(A::UpperHessenberg, i::Integer, j::Integer, s::AbstractString) - return i <= j+1 ? s : Base.replace_with_centered_mark(s) -end - -Base.copy(A::Adjoint{<:Any,<:UpperHessenberg}) = tril!(adjoint!(similar(A.parent.data), A.parent.data), 1) -Base.copy(A::Transpose{<:Any,<:UpperHessenberg}) = tril!(transpose!(similar(A.parent.data), A.parent.data), 1) - --(A::UpperHessenberg) = UpperHessenberg(-A.data) -rmul!(H::UpperHessenberg, x::Number) = (rmul!(H.data, x); H) -lmul!(x::Number, H::UpperHessenberg) = (lmul!(x, H.data); H) - -fillstored!(H::UpperHessenberg, x) = (fillband!(H.data, x, -1, size(H,2)-1); H) - -+(A::UpperHessenberg, B::UpperHessenberg) = UpperHessenberg(A.data+B.data) --(A::UpperHessenberg, B::UpperHessenberg) = UpperHessenberg(A.data-B.data) - -for T = (:UniformScaling, :Diagonal, :Bidiagonal, :Tridiagonal, :SymTridiagonal, - :UpperTriangular, :UnitUpperTriangular) - for op = (:+, :-) - @eval begin - $op(H::UpperHessenberg, x::$T) = UpperHessenberg($op(H.data, x)) - $op(x::$T, H::UpperHessenberg) = UpperHessenberg($op(x, H.data)) - end - end -end - -for T = (:Number, :UniformScaling, :Diagonal) - @eval begin - *(H::UpperHessenberg, x::$T) = UpperHessenberg(H.data * x) - *(x::$T, H::UpperHessenberg) = UpperHessenberg(x * H.data) - /(H::UpperHessenberg, x::$T) = UpperHessenberg(H.data / x) - \(x::$T, H::UpperHessenberg) = UpperHessenberg(x \ H.data) - end -end - -function *(H::UpperHessenberg, U::UpperOrUnitUpperTriangular) - HH = mul!(matprod_dest(H, U, promote_op(matprod, eltype(H), eltype(U))), H, U) - UpperHessenberg(HH) -end -function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) - HH = mul!(matprod_dest(U, H, promote_op(matprod, eltype(U), eltype(H))), U, H) - UpperHessenberg(HH) -end - -function /(H::UpperHessenberg, U::UpperTriangular) - HH = _rdiv!(matprod_dest(H, U, promote_op(/, eltype(H), eltype(U))), H, U) - UpperHessenberg(HH) -end -function /(H::UpperHessenberg, U::UnitUpperTriangular) - HH = _rdiv!(matprod_dest(H, U, promote_op(/, eltype(H), eltype(U))), H, U) - UpperHessenberg(HH) -end - -function \(U::UpperTriangular, H::UpperHessenberg) - HH = ldiv!(matprod_dest(U, H, promote_op(\, eltype(U), eltype(H))), U, H) - UpperHessenberg(HH) -end -function \(U::UnitUpperTriangular, H::UpperHessenberg) - HH = ldiv!(matprod_dest(U, H, promote_op(\, eltype(U), eltype(H))), U, H) - UpperHessenberg(HH) -end - -# Solving (H+µI)x = b: we can do this in O(m²) time and O(m) memory -# (in-place in x) by the RQ algorithm from: -# -# G. Henry, "The shifted Hessenberg system solve computation," Tech. Rep. 94–163, -# Center for Appl. Math., Cornell University (1994). -# -# as reviewed in -# -# C. Beattie et al., "A note on shifted Hessenberg systems and frequency -# response computation," ACM Trans. Math. Soft. 38, pp. 12:6–12:16 (2011) -# -# (Note, however, that there is apparently a typo in Algorithm 1 of the -# Beattie paper: the Givens rotation uses u(k), not H(k,k) - σ.) -# -# Essentially, it works by doing a Givens RQ factorization of H+µI from -# right to left, and doing backsubstitution *simultaneously*. - -# solve (H+μI)X = B, storing result in B -function ldiv!(F::UpperHessenberg, B::AbstractVecOrMat; shift::Number=false) - checksquare(F) - m = size(F,1) - m != size(B,1) && throw(DimensionMismatch(lazy"wrong right-hand-side # rows != $m")) - require_one_based_indexing(B) - n = size(B,2) - H = F.data - μ = shift - u = Vector{typeof(zero(eltype(H))+μ)}(undef, m) # for last rotated col of H-μI - copyto!(u, 1, H, m*(m-1)+1, m) # u .= H[:,m] - u[m] += μ - X = B # not a copy, just rename to match paper - cs = Vector{Tuple{real(eltype(u)),eltype(u)}}(undef, length(u)) # store Givens rotations - @inbounds for k = m:-1:2 - c, s, ρ = givensAlgorithm(u[k], H[k,k-1]) - cs[k] = (c, s) - for i = 1:n - X[k,i] /= ρ - t₁ = s * X[k,i]; t₂ = c * X[k,i] - @simd for j = 1:k-2 - X[j,i] -= u[j]*t₂ + H[j,k-1]*t₁ - end - X[k-1,i] -= u[k-1]*t₂ + (H[k-1,k-1] + μ) * t₁ - end - @simd for j = 1:k-2 - u[j] = H[j,k-1]*c - u[j]*s' - end - u[k-1] = (H[k-1,k-1] + μ) * c - u[k-1]*s' - end - for i = 1:n - τ₁ = X[1,i] / u[1] - @inbounds for j = 2:m - τ₂ = X[j,i] - c, s = cs[j] - X[j-1,i] = c*τ₁ + s*τ₂ - τ₁ = c*τ₂ - s'τ₁ - end - X[m,i] = τ₁ - end - return X -end - -# solve X(H+μI) = B, storing result in B -# -# Note: this can be derived from the Henry (1994) algorithm -# by transformation to F(Hᵀ+µI)F FXᵀ = FBᵀ, where -# F is the permutation matrix that reverses the order -# of rows/cols. Essentially, we take the ldiv! algorithm, -# swap indices of H and X to transpose, and reverse the -# order of the H indices (or the order of the loops). -function rdiv!(B::AbstractMatrix, F::UpperHessenberg; shift::Number=false) - checksquare(F) - m = size(F,1) - m != size(B,2) && throw(DimensionMismatch(lazy"wrong right-hand-side # cols != $m")) - require_one_based_indexing(B) - n = size(B,1) - H = F.data - μ = shift - u = Vector{typeof(zero(eltype(H))+μ)}(undef, m) # for last rotated row of H-μI - u .= @view H[1,:] - u[1] += μ - X = B # not a copy, just rename to match paper - cs = Vector{Tuple{real(eltype(u)),eltype(u)}}(undef, length(u)) # store Givens rotations - @inbounds for k = 1:m-1 - c, s, ρ = givensAlgorithm(u[k], H[k+1,k]) - cs[k] = (c, s) - for i = 1:n - X[i,k] /= ρ - t₁ = s * X[i,k]; t₂ = c * X[i,k] - @simd for j = k+2:m - X[i,j] -= u[j]*t₂ + H[k+1,j]*t₁ - end - X[i,k+1] -= u[k+1]*t₂ + (H[k+1,k+1] + μ) * t₁ - end - @simd for j = k+2:m - u[j] = H[k+1,j]*c - u[j]*s' - end - u[k+1] = (H[k+1,k+1] + μ) * c - u[k+1]*s' - end - for i = 1:n - τ₁ = X[i,m] / u[m] - @inbounds for j = m-1:-1:1 - τ₂ = X[i,j] - c, s = cs[j] - X[i,j+1] = c*τ₁ + s*τ₂ - τ₁ = c*τ₂ - s'τ₁ - end - X[i,1] = τ₁ - end - return X -end - -# Hessenberg-matrix determinant formula for H+μI based on: -# -# N. D. Cahill, J. R. D’Errico, D. A. Narayan, and J. Y. Narayan, "Fibonacci determinants," -# College Math. J. 33, pp. 221-225 (2003). -# -# as reviewed in Theorem 2.1 of: -# -# K. Kaygisiz and A. Sahin, "Determinant and permanent of Hessenberg matrix and generalized Lucas polynomials," -# arXiv:1111.4067 (2011). -# -# Cost is O(m²) with O(m) storage. -function det(F::UpperHessenberg; shift::Number=false) - checksquare(F) - H = F.data - m = size(H,1) - μ = shift - m == 0 && return one(zero(eltype(H)) + μ) - determinant = H[1,1] + μ - prevdeterminant = one(determinant) - m == 1 && return determinant - prods = Vector{typeof(determinant)}(undef, m-1) # temporary storage for partial products - @inbounds for n = 2:m - prods[n-1] = prevdeterminant - prevdeterminant = determinant - determinant *= H[n,n] + μ - h = H[n,n-1] - @simd for r = n-1:-2:2 - determinant -= H[r,n] * (prods[r] *= h) - H[r-1,n] * (prods[r-1] *= h) - end - if iseven(n) - determinant -= H[1,n] * (prods[1] *= h) - end - end - return determinant -end - -# O(m²) log-determinant based on first doing Givens RQ to put H+μI into upper-triangular form and then -# taking the product of the diagonal entries. The trick is that we only need O(m) temporary storage, -# because we don't need to store the whole Givens-rotated matrix, only the most recent column. -# We do RQ (column rotations) rather than QR (row rotations) for more consecutive memory access. -# (We could also use it for det instead of the Cahill algorithm above. Cahill is slightly faster -# for very small matrices where you are likely to use det, and also uses only ± and * so it can -# be applied to Hessenberg matrices over other number fields.) -function logabsdet(F::UpperHessenberg; shift::Number=false) - checksquare(F) - H = F.data - m = size(H,1) - μ = shift - P = one(zero(eltype(H)) + μ) - logdeterminant = zero(real(P)) - m == 0 && return (logdeterminant, P) - g = Vector{typeof(P)}(undef, m) # below, g is the k-th col of Givens-rotated H+μI matrix - copyto!(g, 1, H, m*(m-1)+1, m) # g .= H[:,m] - g[m] += μ - @inbounds for k = m:-1:2 - c, s, ρ = givensAlgorithm(g[k], H[k,k-1]) - logdeterminant += log(abs(ρ)) - P *= sign(ρ) - g[k-1] = c*(H[k-1,k-1] + μ) - s'*g[k-1] - @simd for j = 1:k-2 - g[j] = c*H[j,k-1] - s'*g[j] - end - end - logdeterminant += log(abs(g[1])) - P *= sign(g[1]) - return (logdeterminant, P) -end - -function dot(x::AbstractVector, H::UpperHessenberg, y::AbstractVector) - require_one_based_indexing(x, y) - m = size(H, 1) - (length(x) == m == length(y)) || throw(DimensionMismatch()) - if iszero(m) - return dot(zero(eltype(x)), zero(eltype(H)), zero(eltype(y))) - end - x₁ = x[1] - r = dot(x₁, H[1,1], y[1]) - r += dot(x[2], H[2,1], y[1]) - @inbounds for j in 2:m-1 - yj = y[j] - if !iszero(yj) - temp = adjoint(H[1,j]) * x₁ - @simd for i in 2:j+1 - temp += adjoint(H[i,j]) * x[i] - end - r += dot(temp, yj) - end - end - ym = y[m] - if !iszero(ym) - temp = adjoint(H[1,m]) * x₁ - @simd for i in 2:m - temp += adjoint(H[i,m]) * x[i] - end - r += dot(temp, ym) - end - return r -end - -###################################################################################### -# Hessenberg factorizations Q(H+μI)Q' of A+μI: - -""" - Hessenberg <: Factorization - -A `Hessenberg` object represents the Hessenberg factorization `QHQ'` of a square -matrix, or a shift `Q(H+μI)Q'` thereof, which is produced by the [`hessenberg`](@ref) function. -""" -struct Hessenberg{T,SH<:AbstractMatrix,S<:AbstractMatrix,W<:AbstractVector,V<:Number} <: Factorization{T} - H::SH # UpperHessenberg or SymTridiagonal - uplo::Char - factors::S # reflector data in uplo triangle, may share data with H - τ::W # more Q (reflector) data - μ::V # diagonal shift for copy-free (F+μI) \ b solves and similar -end -Hessenberg(factors::AbstractMatrix, τ::AbstractVector, H::AbstractMatrix=UpperHessenberg(factors), uplo::AbstractChar='L'; μ::Number=false) = - Hessenberg{typeof(zero(eltype(factors))+μ),typeof(H),typeof(factors),typeof(τ),typeof(μ)}(H, uplo, factors, τ, μ) -Hessenberg(F::Hessenberg) = F -Hessenberg(F::Hessenberg, μ::Number) = Hessenberg(F.factors, F.τ, F.H, F.uplo; μ=μ) - -copy(F::Hessenberg{<:Any,<:UpperHessenberg}) = Hessenberg(copy(F.factors), copy(F.τ); μ=F.μ) -copy(F::Hessenberg{<:Any,<:SymTridiagonal}) = Hessenberg(copy(F.factors), copy(F.τ), copy(F.H), F.uplo; μ=F.μ) -size(F::Hessenberg, d::Integer) = size(F.H, d) -size(F::Hessenberg) = size(F.H) - -transpose(F::Hessenberg{<:Real}) = F' -transpose(::Hessenberg) = - throw(ArgumentError("transpose of Hessenberg decomposition is not supported, consider using adjoint")) - -# iteration for destructuring into components -Base.iterate(S::Hessenberg) = (S.Q, Val(:H)) -Base.iterate(S::Hessenberg, ::Val{:H}) = (S.H, Val(:μ)) -Base.iterate(S::Hessenberg, ::Val{:μ}) = (S.μ, Val(:done)) -Base.iterate(S::Hessenberg, ::Val{:done}) = nothing - -hessenberg!(A::StridedMatrix{<:BlasFloat}) = Hessenberg(LAPACK.gehrd!(A)...) - -function hessenberg!(A::Union{Symmetric{<:BlasReal,<:StridedMatrix},Hermitian{<:BlasFloat,<:StridedMatrix}}) - factors, τ, d, e = LAPACK.hetrd!(A.uplo, A.data) - return Hessenberg(factors, τ, SymTridiagonal(d, e), A.uplo) -end - -""" - hessenberg!(A) -> Hessenberg - -`hessenberg!` is the same as [`hessenberg`](@ref), but saves space by overwriting -the input `A`, instead of creating a copy. -""" -hessenberg!(A::AbstractMatrix) - -""" - hessenberg(A) -> Hessenberg - -Compute the Hessenberg decomposition of `A` and return a `Hessenberg` object. If `F` is the -factorization object, the unitary matrix can be accessed with `F.Q` (of type `LinearAlgebra.HessenbergQ`) -and the Hessenberg matrix with `F.H` (of type [`UpperHessenberg`](@ref)), either of -which may be converted to a regular matrix with `Matrix(F.H)` or `Matrix(F.Q)`. - -If `A` is [`Hermitian`](@ref) or real-[`Symmetric`](@ref), then the Hessenberg -decomposition produces a real-symmetric tridiagonal matrix and `F.H` is of type -[`SymTridiagonal`](@ref). - -Note that the shifted factorization `A+μI = Q (H+μI) Q'` can be -constructed efficiently by `F + μ*I` using the [`UniformScaling`](@ref) -object [`I`](@ref), which creates a new `Hessenberg` object with shared storage -and a modified shift. The shift of a given `F` is obtained by `F.μ`. -This is useful because multiple shifted solves `(F + μ*I) \\ b` -(for different `μ` and/or `b`) can be performed efficiently once `F` is created. - -Iterating the decomposition produces the factors `F.Q, F.H, F.μ`. - -# Examples -```julia-repl -julia> A = [4. 9. 7.; 4. 4. 1.; 4. 3. 2.] -3×3 Matrix{Float64}: - 4.0 9.0 7.0 - 4.0 4.0 1.0 - 4.0 3.0 2.0 - -julia> F = hessenberg(A) -Hessenberg{Float64, UpperHessenberg{Float64, Matrix{Float64}}, Matrix{Float64}, Vector{Float64}, Bool} -Q factor: 3×3 LinearAlgebra.HessenbergQ{Float64, Matrix{Float64}, Vector{Float64}, false} -H factor: -3×3 UpperHessenberg{Float64, Matrix{Float64}}: - 4.0 -11.3137 -1.41421 - -5.65685 5.0 2.0 - ⋅ -8.88178e-16 1.0 - -julia> F.Q * F.H * F.Q' -3×3 Matrix{Float64}: - 4.0 9.0 7.0 - 4.0 4.0 1.0 - 4.0 3.0 2.0 - -julia> q, h = F; # destructuring via iteration - -julia> q == F.Q && h == F.H -true -``` -""" -hessenberg(A::AbstractMatrix{T}) where T = - hessenberg!(eigencopy_oftype(A, eigtype(T))) - -function show(io::IO, mime::MIME"text/plain", F::Hessenberg) - summary(io, F) - if !iszero(F.μ) - print("\nwith shift μI for μ = ", F.μ) - end - print(io, "\nQ factor: ") - show(io, mime, F.Q) - println(io, "\nH factor:") - show(io, mime, F.H) -end - -function getproperty(F::Hessenberg, d::Symbol) - d === :Q && return HessenbergQ(F) - return getfield(F, d) -end - -Base.propertynames(F::Hessenberg, private::Bool=false) = - (:Q, :H, :μ, (private ? (:τ, :factors, :uplo) : ())...) - -AbstractArray(F::Hessenberg) = AbstractMatrix(F) -Matrix(F::Hessenberg) = Array(AbstractArray(F)) -Array(F::Hessenberg) = Matrix(F) -function AbstractMatrix(F::Hessenberg) - Q = F.Q - A = rmul!(lmul!(Q, Matrix{eltype(Q)}(F.H)), Q') - μ = F.μ - if iszero(μ) - return A - elseif typeof(zero(eltype(A))+μ) <: eltype(A) # can shift A in-place - for i = 1:size(A,1) - @inbounds A[i,i] += μ - end - return A - else - return A + μ*I # allocate another matrix, e.g. if A is real and μ is complex - end -end - -# multiply x by the entries of M in the upper-k triangle, which contains -# the entries of the upper-Hessenberg matrix H for k=-1 -function rmul_triu!(M::AbstractMatrix, x, k::Integer=0) - require_one_based_indexing(M) - m, n = size(M) - for j = 1:n, i = 1:min(j-k,m) - @inbounds M[i,j] *= x - end - return M -end -function lmul_triu!(x, M::AbstractMatrix, k::Integer=0) - require_one_based_indexing(M) - m, n = size(M) - for j = 1:n, i = 1:min(j-k,m) - @inbounds M[i,j] = x * M[i,j] - end - return M -end - -# when H is UpperHessenberg, it shares data with F.factors -# multiply Hessenberg by scalar (but don't modify lower triangle of F.H.data) -rmul!(F::Hessenberg{<:Any,<:UpperHessenberg{T}}, x::T) where {T<:Number} = Hessenberg(rmul_triu!(F.factors, x, -1), F.τ; μ=F.μ*x) -lmul!(x::T, F::Hessenberg{<:Any,<:UpperHessenberg{T}}) where {T<:Number} = Hessenberg(lmul_triu!(x, F.factors, -1), F.τ; μ=x*F.μ) - -rmul!(F::Hessenberg{<:Any,<:SymTridiagonal{T}}, x::T) where {T<:Number} = Hessenberg(F.factors, F.τ, SymTridiagonal(F.H.dv*x, F.H.ev*x), F.uplo; μ=F.μ*x) -lmul!(x::T, F::Hessenberg{<:Any,<:SymTridiagonal{T}}) where {T<:Number} = Hessenberg(F.factors, F.τ, SymTridiagonal(x*F.H.dv, x*F.H.ev), F.uplo; μ=x*F.μ) - -# Promote F * x or x * F. In general, we don't know how to do promotions -# that would change the element type of F.H, however. -function (*)(F::Hessenberg{<:Any,<:AbstractMatrix{T}}, x::S) where {T,S<:Number} - TS = typeof(zero(T) * x) - if TS === T - return rmul!(copy(F), convert(T, x)) - else - throw(MethodError(*, (F, x))) - end -end -function (*)(x::S, F::Hessenberg{<:Any,<:AbstractMatrix{T}}) where {T,S<:Number} - TS = typeof(zero(T) * x) - if TS === T - return lmul!(convert(T, x), copy(F)) - else - throw(MethodError(*, (x, F))) - end -end --(F::Hessenberg) = F * -one(eltype(F.H)) - -# shift Hessenberg by λI -+(F::Hessenberg, J::UniformScaling) = Hessenberg(F, F.μ + J.λ) -+(J::UniformScaling, F::Hessenberg) = Hessenberg(F, J.λ + F.μ) --(F::Hessenberg, J::UniformScaling) = Hessenberg(F, F.μ - J.λ) --(J::UniformScaling, F::Hessenberg) = Hessenberg(-F, J.λ - F.μ) - -function ldiv!(F::Hessenberg, B::AbstractVecOrMat) - Q = F.Q - if iszero(F.μ) - return lmul!(Q, ldiv!(F.H, lmul!(Q', B))) - else - return lmul!(Q, ldiv!(F.H, lmul!(Q', B); shift=F.μ)) - end -end - -function rdiv!(B::AbstractMatrix, F::Hessenberg) - Q = F.Q - return rmul!(rdiv!(rmul!(B, Q), F.H; shift=F.μ), Q') -end - -# handle case of real H and complex μ — we need to work around the -# fact that we can't multiple a real F.Q by a complex matrix directly in LAPACK -function ldiv!(F::Hessenberg{<:Complex,<:Any,<:AbstractMatrix{<:Real}}, B::AbstractVecOrMat{<:Complex}) - Q = F.Q - Br = lmul!(Q', real(B)) - Bi = lmul!(Q', imag(B)) - ldiv!(F.H, B .= Complex.(Br,Bi); shift=F.μ) - Br .= real.(B); Bi .= imag.(B) - Br = lmul!(Q, Br) - Bi = lmul!(Q, Bi) - return B .= Complex.(Br,Bi) -end -function rdiv!(B::AbstractVecOrMat{<:Complex}, F::Hessenberg{<:Complex,<:Any,<:AbstractMatrix{<:Real}}) - Q = F.Q - Br = rmul!(real(B), Q) - Bi = rmul!(imag(B), Q) - rdiv!(B .= Complex.(Br,Bi), F.H; shift=F.μ) - Br .= real.(B); Bi .= imag.(B) - Br = rmul!(Br, Q') - Bi = rmul!(Bi, Q') - return B .= Complex.(Br,Bi) -end - -ldiv!(F::AdjointFactorization{<:Any,<:Hessenberg}, B::AbstractVecOrMat) = rdiv!(B', F')' - -det(F::Hessenberg) = det(F.H; shift=F.μ) -logabsdet(F::Hessenberg) = logabsdet(F.H; shift=F.μ) -function logdet(F::Hessenberg) - d,s = logabsdet(F) - return d + log(s) -end diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl deleted file mode 100644 index f53e8bd98454d..0000000000000 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ /dev/null @@ -1,7218 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module LAPACK -@doc """ -Interfaces to LAPACK subroutines. -""" LAPACK - -using ..LinearAlgebra.BLAS: @blasfunc, chkuplo - -using ..LinearAlgebra: libblastrampoline, BlasFloat, BlasInt, LAPACKException, DimensionMismatch, - SingularException, PosDefException, chkstride1, checksquare, triu, tril, dot - -using Base: iszero, require_one_based_indexing - - -# Legacy binding maintained for backwards-compatibility but new packages -# should not look at this, instead preferring to parse the output -# of BLAS.get_config() -const liblapack = libblastrampoline - -#Generic LAPACK error handlers -""" -Handle only negative LAPACK error codes - -*NOTE* use only if the positive error code is useful. -""" -function chkargsok(ret::BlasInt) - if ret < 0 - throw(ArgumentError(lazy"invalid argument #$(-ret) to LAPACK call")) - end -end - -"Handle all nonzero info codes" -function chklapackerror(ret::BlasInt, f...) - if ret == 0 - return - elseif ret < 0 - throw(ArgumentError(lazy"invalid argument #$(-ret) to LAPACK call")) - else # ret > 0 - chklapackerror_positive(ret, f...) - end -end - -chklapackerror_positive(ret, f...) = throw(LAPACKException(ret)) - -function chknonsingular(ret::BlasInt) - if ret > 0 - throw(SingularException(ret)) - end -end - -function chkposdef(ret::BlasInt) - if ret > 0 - throw(PosDefException(ret)) - end -end - -# Generic fallback function to assert that parameters are valid -# In specific cases, the following functions may be more useful -macro chkvalidparam(position::Int, param, validvalues) - :(chkvalidparam($position, $(string(param)), $(esc(param)), $validvalues)) -end -function chkvalidparam(position::Int, var::String, val, validvals) - # mimic `repr` for chars without explicitly calling it - # This is because `repr` introduces dynamic dispatch - _repr(c::AbstractChar) = "'$c'" - _repr(c) = c - if val ∉ validvals - throw(ArgumentError( - lazy"argument #$position: $var must be one of $validvals, but $(_repr(val)) was passed")) - end - return val -end - -"Check that {c}transpose is correctly specified" -function chktrans(trans::AbstractChar) - if !(trans == 'N' || trans == 'C' || trans == 'T') - throw(ArgumentError(lazy"trans argument must be 'N' (no transpose), 'T' (transpose), or 'C' (conjugate transpose), got '$trans'")) - end - trans -end - -"Check that left/right hand side multiply is correctly specified" -function chkside(side::AbstractChar) - if !(side == 'L' || side == 'R') - throw(ArgumentError(lazy"side argument must be 'L' (left hand multiply) or 'R' (right hand multiply), got '$side'")) - end - side -end - -"Check that unit diagonal flag is correctly specified" -function chkdiag(diag::AbstractChar) - if !(diag == 'U' || diag =='N') - throw(ArgumentError(lazy"diag argument must be 'U' (unit diagonal) or 'N' (non-unit diagonal), got '$diag'")) - end - diag -end - -subsetrows(X::AbstractVector, Y::AbstractArray, k) = Y[1:k] -subsetrows(X::AbstractMatrix, Y::AbstractArray, k) = Y[1:k, :] - -function chkfinite(A::AbstractMatrix) - for a in A - if !isfinite(a) - throw(ArgumentError("matrix contains Infs or NaNs")) - end - end - return true -end - -function chkuplofinite(A::AbstractMatrix, uplo::AbstractChar) - require_one_based_indexing(A) - chkuplo(uplo) - m, n = size(A) - if uplo == 'U' - @inbounds for j in 1:n, i in 1:j - if !isfinite(A[i,j]) - throw(ArgumentError("matrix contains Infs or NaNs")) - end - end - else - @inbounds for j in 1:n, i in j:m - if !isfinite(A[i,j]) - throw(ArgumentError("matrix contains Infs or NaNs")) - end - end - end -end - -# LAPACK version number -function version() - major = Ref{BlasInt}(0) - minor = Ref{BlasInt}(0) - patch = Ref{BlasInt}(0) - ccall((@blasfunc(ilaver_), libblastrampoline), Cvoid, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - major, minor, patch) - return VersionNumber(major[], minor[], patch[]) -end - -# (GB) general banded matrices, LU decomposition and solver -for (gbtrf, gbtrs, elty) in - ((:dgbtrf_,:dgbtrs_,:Float64), - (:sgbtrf_,:sgbtrs_,:Float32), - (:zgbtrf_,:zgbtrs_,:ComplexF64), - (:cgbtrf_,:cgbtrs_,:ComplexF32)) - @eval begin - # SUBROUTINE DGBTRF( M, N, KL, KU, AB, LDAB, IPIV, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, KL, KU, LDAB, M, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION AB( LDAB, * ) - function gbtrf!(kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix{$elty}) - require_one_based_indexing(AB) - chkstride1(AB) - n = size(AB, 2) - mnmn = min(m, n) - ipiv = similar(AB, BlasInt, mnmn) - info = Ref{BlasInt}() - ccall((@blasfunc($gbtrf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), - m, n, kl, ku, AB, max(1,stride(AB,2)), ipiv, info) - chklapackerror(info[]) - AB, ipiv - end - - # SUBROUTINE DGBTRS( TRANS, N, KL, KU, NRHS, AB, LDAB, IPIV, B, LDB, INFO) - # * .. Scalar Arguments .. - # CHARACTER TRANS - # INTEGER INFO, KL, KU, LDAB, LDB, N, NRHS - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION AB( LDAB, * ), B( LDB, * ) - function gbtrs!(trans::AbstractChar, kl::Integer, ku::Integer, m::Integer, - AB::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, - B::AbstractVecOrMat{$elty}) - require_one_based_indexing(AB, B) - chkstride1(AB, B, ipiv) - chktrans(trans) - info = Ref{BlasInt}() - n = size(AB,2) - if m != n || m != size(B,1) - throw(DimensionMismatch(lazy"matrix AB has dimensions $(size(AB)), but right hand side matrix B has dimensions $(size(B))")) - end - ccall((@blasfunc($gbtrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong), - trans, n, kl, ku, size(B,2), AB, max(1,stride(AB,2)), ipiv, - B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -""" - gbtrf!(kl, ku, m, AB) -> (AB, ipiv) - -Compute the LU factorization of a banded matrix `AB`. `kl` is the first -subdiagonal containing a nonzero band, `ku` is the last superdiagonal -containing one, and `m` is the first dimension of the matrix `AB`. Returns -the LU factorization in-place and `ipiv`, the vector of pivots used. -""" -gbtrf!(kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix) - -""" - gbtrs!(trans, kl, ku, m, AB, ipiv, B) - -Solve the equation `AB * X = B`. `trans` determines the orientation of `AB`. It may -be `N` (no transpose), `T` (transpose), or `C` (conjugate transpose). `kl` is the -first subdiagonal containing a nonzero band, `ku` is the last superdiagonal -containing one, and `m` is the first dimension of the matrix `AB`. `ipiv` is the vector -of pivots returned from `gbtrf!`. Returns the vector or matrix `X`, overwriting `B` in-place. -""" -gbtrs!(trans::AbstractChar, kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) - -## (GE) general matrices: balancing and back-transforming -for (gebal, gebak, elty, relty) in - ((:dgebal_, :dgebak_, :Float64, :Float64), - (:sgebal_, :sgebak_, :Float32, :Float32), - (:zgebal_, :zgebak_, :ComplexF64, :Float64), - (:cgebal_, :cgebak_, :ComplexF32, :Float32)) - @eval begin - # SUBROUTINE DGEBAL( JOB, N, A, LDA, ILO, IHI, SCALE, INFO ) - #* .. Scalar Arguments .. - # CHARACTER JOB - # INTEGER IHI, ILP, INFO, LDA, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), SCALE( * ) - function gebal!(job::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - @chkvalidparam 1 job ('N', 'P', 'S', 'B') - n = checksquare(A) - chkfinite(A) # balancing routines don't support NaNs and Infs - ihi = Ref{BlasInt}() - ilo = Ref{BlasInt}() - scale = similar(A, $relty, n) - info = Ref{BlasInt}() - ccall((@blasfunc($gebal), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong), - job, n, A, max(1,stride(A,2)), ilo, ihi, scale, info, 1) - chklapackerror(info[]) - ilo[], ihi[], scale - end - - # SUBROUTINE DGEBAK( JOB, SIDE, N, ILO, IHI, SCALE, M, V, LDV, INFO ) - #* .. Scalar Arguments .. - # CHARACTER JOB, SIDE - # INTEGER IHI, ILP, INFO, LDV, M, N - # .. Array Arguments .. - # DOUBLE PRECISION SCALE( * ), V( LDV, * ) - function gebak!(job::AbstractChar, side::AbstractChar, - ilo::BlasInt, ihi::BlasInt, scale::AbstractVector{$relty}, - V::AbstractMatrix{$elty}) - require_one_based_indexing(scale, V) - @chkvalidparam 1 job ('N', 'P', 'S', 'B') - chkstride1(scale, V) - chkside(side) - chkfinite(V) # balancing routines don't support NaNs and Infs - n = checksquare(V) - info = Ref{BlasInt}() - ccall((@blasfunc($gebak), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - job, side, size(V,1), ilo, ihi, scale, n, V, max(1,stride(V,2)), info, - 1, 1) - chklapackerror(info[]) - V - end - end -end - -""" - gebal!(job, A) -> (ilo, ihi, scale) - -Balance the matrix `A` before computing its eigensystem or Schur factorization. -`job` can be one of `N` (`A` will not be permuted or scaled), `P` (`A` will only -be permuted), `S` (`A` will only be scaled), or `B` (`A` will be both permuted -and scaled). Modifies `A` in-place and returns `ilo`, `ihi`, and `scale`. If -permuting was turned on, `A[i,j] = 0` if `j > i` and `1 < j < ilo` or `j > ihi`. -`scale` contains information about the scaling/permutations performed. -""" -gebal!(job::AbstractChar, A::AbstractMatrix) - -""" - gebak!(job, side, ilo, ihi, scale, V) - -Transform the eigenvectors `V` of a matrix balanced using `gebal!` to -the unscaled/unpermuted eigenvectors of the original matrix. Modifies `V` -in-place. `side` can be `L` (left eigenvectors are transformed) or `R` -(right eigenvectors are transformed). -""" -gebak!(job::AbstractChar, side::AbstractChar, ilo::BlasInt, ihi::BlasInt, scale::AbstractVector, V::AbstractMatrix) - -# (GE) general matrices, direct decompositions -# -# These mutating functions take as arguments all the values they -# return, even if the value of the function does not depend on them -# (e.g. the tau argument). This is so that a factorization can be -# updated in place. The condensed mutating functions, usually a -# function of A only, are defined after this block. -for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty) in - ((:dgebrd_,:dgelqf_,:dgeqlf_,:dgeqrf_,:dgeqp3_,:dgeqrt_,:dgeqrt3_,:dgerqf_,:dgetrf_,:Float64,:Float64), - (:sgebrd_,:sgelqf_,:sgeqlf_,:sgeqrf_,:sgeqp3_,:sgeqrt_,:sgeqrt3_,:sgerqf_,:sgetrf_,:Float32,:Float32), - (:zgebrd_,:zgelqf_,:zgeqlf_,:zgeqrf_,:zgeqp3_,:zgeqrt_,:zgeqrt3_,:zgerqf_,:zgetrf_,:ComplexF64,:Float64), - (:cgebrd_,:cgelqf_,:cgeqlf_,:cgeqrf_,:cgeqp3_,:cgeqrt_,:cgeqrt3_,:cgerqf_,:cgetrf_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DGEBRD( M, N, A, LDA, D, E, TAUQ, TAUP, WORK, LWORK, - # INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAUP( * ), - # TAUQ( * ), WORK( * ) - function gebrd!(A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - m, n = size(A) - k = min(m, n) - d = similar(A, $relty, k) - e = similar(A, $relty, k) - tauq = similar(A, $elty, k) - taup = similar(A, $elty, k) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gebrd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, max(1,stride(A,2)), - d, e, tauq, taup, - work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, d, e, tauq, taup - end - - # SUBROUTINE DGELQF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function gelqf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m = BlasInt(size(A, 1)) - n = BlasInt(size(A, 2)) - lda = BlasInt(max(1,stride(A, 2))) - if length(tau) != min(m,n) - throw(DimensionMismatch(lazy"tau has length $(length(tau)), but needs length $(min(m,n))")) - end - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gelqf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, lda, tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, tau - end - - # SUBROUTINE DGEQLF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function geqlf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m = BlasInt(size(A, 1)) - n = BlasInt(size(A, 2)) - lda = BlasInt(max(1,stride(A, 2))) - if length(tau) != min(m,n) - throw(DimensionMismatch(lazy"tau has length $(length(tau)), but needs length $(min(m,n))")) - end - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($geqlf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, lda, tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, tau - end - - # SUBROUTINE DGEQP3( M, N, A, LDA, JPVT, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # * .. Array Arguments .. - # INTEGER JPVT( * ) - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function geqp3!(A::AbstractMatrix{$elty}, jpvt::AbstractVector{BlasInt}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, jpvt, tau) - chkstride1(A,jpvt,tau) - m,n = size(A) - if length(tau) != min(m,n) - throw(DimensionMismatch(lazy"tau has length $(length(tau)), but needs length $(min(m,n))")) - end - if length(jpvt) != n - throw(DimensionMismatch(lazy"jpvt has length $(length(jpvt)), but needs length $n")) - end - lda = stride(A,2) - if lda == 0 - return A, tau, jpvt - end # Early exit - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - cmplx = eltype(A)<:Complex - if cmplx - rwork = Vector{$relty}(undef, 2n) - end - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - if cmplx - ccall((@blasfunc($geqp3), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}), - m, n, A, lda, - jpvt, tau, work, lwork, - rwork, info) - else - ccall((@blasfunc($geqp3), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - m, n, A, lda, - jpvt, tau, work, - lwork, info) - end - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - return A, tau, jpvt - end - - function geqrt!(A::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}) - require_one_based_indexing(A, T) - chkstride1(A) - m, n = size(A) - minmn = min(m, n) - nb = size(T, 1) - if nb > minmn - throw(ArgumentError(lazy"block size $nb > $minmn too large")) - end - lda = max(1, stride(A,2)) - work = Vector{$elty}(undef, nb*n) - if minmn > 0 - info = Ref{BlasInt}() - ccall((@blasfunc($geqrt), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}), - m, n, nb, A, - lda, T, max(1,stride(T,2)), work, - info) - chklapackerror(info[]) - end - A, T - end - - function geqrt3!(A::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}) - require_one_based_indexing(A, T) - chkstride1(A) - chkstride1(T) - m, n = size(A) - p, q = size(T) - if m < n - throw(DimensionMismatch(lazy"input matrix A has dimensions ($m,$n), but should have more rows than columns")) - end - if p != n || q != n - throw(DimensionMismatch(lazy"block reflector T has dimensions ($p,$q), but should have dimensions ($n,$n)")) - end - if n > 0 # this implies `m > 0` because of `m >= n` - info = Ref{BlasInt}() - ccall((@blasfunc($geqrt3), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, max(1, stride(A, 2)), - T, max(1,stride(T,2)), info) - chklapackerror(info[]) - end - A, T - end - - ## geqrfp! - positive elements on diagonal of R - not defined yet - # SUBROUTINE DGEQRFP( M, N, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function geqrf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m, n = size(A) - if length(tau) != min(m,n) - throw(DimensionMismatch(lazy"tau has length $(length(tau)), but needs length $(min(m,n))")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($geqrf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, max(1,stride(A,2)), tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = max(BlasInt(1),BlasInt(real(work[1]))) - resize!(work, lwork) - end - end - A, tau - end - - # SUBROUTINE DGERQF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function gerqf!(A::AbstractMatrix{$elty},tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m, n = size(A) - if length(tau) != min(m,n) - throw(DimensionMismatch(lazy"tau has length $(length(tau)), but needs length $(min(m,n))")) - end - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gerqf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, max(1,stride(A,2)), tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = max(BlasInt(m), BlasInt(real(work[1]))) - resize!(work, lwork) - end - end - A, tau - end - - # SUBROUTINE DGETRF( M, N, A, LDA, IPIV, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, M, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ) - function getrf!(A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}; check::Bool=true) - require_one_based_indexing(A) - check && chkfinite(A) - chkstride1(A) - m, n = size(A) - lda = max(1,stride(A, 2)) - info = Ref{BlasInt}() - ccall((@blasfunc($getrf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), - m, n, A, lda, ipiv, info) - chkargsok(info[]) - A, ipiv, info[] #Error code is stored in LU factorization type - end - end -end - -""" - gebrd!(A) -> (A, d, e, tauq, taup) - -Reduce `A` in-place to bidiagonal form `A = QBP'`. Returns `A`, containing the -bidiagonal matrix `B`; `d`, containing the diagonal elements of `B`; `e`, -containing the off-diagonal elements of `B`; `tauq`, containing the -elementary reflectors representing `Q`; and `taup`, containing the -elementary reflectors representing `P`. -""" -gebrd!(A::AbstractMatrix) - -""" - gelqf!(A, tau) - -Compute the `LQ` factorization of `A`, `A = LQ`. `tau` contains scalars -which parameterize the elementary reflectors of the factorization. `tau` -must have length greater than or equal to the smallest dimension of `A`. - -Returns -`A` and `tau` modified in-place. -""" -gelqf!(A::AbstractMatrix, tau::AbstractVector) - -""" - geqlf!(A, tau) - -Compute the `QL` factorization of `A`, `A = QL`. `tau` contains scalars -which parameterize the elementary reflectors of the factorization. `tau` -must have length greater than or equal to the smallest dimension of `A`. - -Returns `A` and `tau` modified in-place. -""" -geqlf!(A::AbstractMatrix, tau::AbstractVector) - -""" - geqp3!(A, [jpvt, tau]) -> (A, tau, jpvt) - -Compute the pivoted `QR` factorization of `A`, `AP = QR` using BLAS level 3. -`P` is a pivoting matrix, represented by `jpvt`. `tau` stores the elementary -reflectors. The arguments `jpvt` and `tau` are optional and allow -for passing preallocated arrays. When passed, `jpvt` must have length greater -than or equal to `n` if `A` is an `(m x n)` matrix and `tau` must have length -greater than or equal to the smallest dimension of `A`. On entry, if `jpvt[j]` -does not equal zero then the `j`th column of `A` is permuted to the front of -`AP`. - -`A`, `jpvt`, and `tau` are modified in-place. -""" -geqp3!(A::AbstractMatrix, jpvt::AbstractVector{BlasInt}, tau::AbstractVector) - -function geqp3!(A::AbstractMatrix{<:BlasFloat}, jpvt::AbstractVector{BlasInt}) - require_one_based_indexing(A, jpvt) - m, n = size(A) - geqp3!(A, jpvt, similar(A, min(m, n))) -end - -function geqp3!(A::AbstractMatrix{<:BlasFloat}) - require_one_based_indexing(A) - m, n = size(A) - geqp3!(A, zeros(BlasInt, n), similar(A, min(m, n))) -end - -""" - geqrt!(A, T) - -Compute the blocked `QR` factorization of `A`, `A = QR`. `T` contains upper -triangular block reflectors which parameterize the elementary reflectors of -the factorization. The first dimension of `T` sets the block size and it must -be between 1 and `n`. The second dimension of `T` must equal the smallest -dimension of `A`. - -Returns `A` and `T` modified in-place. -""" -geqrt!(A::AbstractMatrix, T::AbstractMatrix) - -""" - geqrt3!(A, T) - -Recursively computes the blocked `QR` factorization of `A`, `A = QR`. `T` -contains upper triangular block reflectors which parameterize the -elementary reflectors of the factorization. The first dimension of `T` sets the -block size and it must be between 1 and `n`. The second dimension of `T` must -equal the smallest dimension of `A`. - -Returns `A` and `T` modified in-place. -""" -geqrt3!(A::AbstractMatrix, T::AbstractMatrix) - -""" - geqrf!(A, tau) - -Compute the `QR` factorization of `A`, `A = QR`. `tau` contains scalars -which parameterize the elementary reflectors of the factorization. `tau` -must have length greater than or equal to the smallest dimension of `A`. - -Returns `A` and `tau` modified in-place. -""" -geqrf!(A::AbstractMatrix, tau::AbstractVector) - -""" - gerqf!(A, tau) - -Compute the `RQ` factorization of `A`, `A = RQ`. `tau` contains scalars -which parameterize the elementary reflectors of the factorization. `tau` -must have length greater than or equal to the smallest dimension of `A`. - -Returns `A` and `tau` modified in-place. -""" -gerqf!(A::AbstractMatrix, tau::AbstractVector) - -""" - getrf!(A, ipiv) -> (A, ipiv, info) - -Compute the pivoted `LU` factorization of `A`, `A = LU`. `ipiv` contains the pivoting -information and `info` a code which indicates success (`info = 0`), a singular value -in `U` (`info = i`, in which case `U[i,i]` is singular), or an error code (`info < 0`). -""" -getrf!(A::AbstractMatrix, ipiv::AbstractVector; check::Bool=true) - -""" - gelqf!(A) -> (A, tau) - -Compute the `LQ` factorization of `A`, `A = LQ`. - -Returns `A`, modified in-place, and `tau`, which contains scalars -which parameterize the elementary reflectors of the factorization. -""" -gelqf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); gelqf!(A, similar(A, min(m, n)))) - -""" - geqlf!(A) -> (A, tau) - -Compute the `QL` factorization of `A`, `A = QL`. - -Returns `A`, modified in-place, and `tau`, which contains scalars -which parameterize the elementary reflectors of the factorization. -""" -geqlf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); geqlf!(A, similar(A, min(m, n)))) - -""" - geqrt!(A, nb) -> (A, T) - -Compute the blocked `QR` factorization of `A`, `A = QR`. `nb` sets the block size -and it must be between 1 and `n`, the second dimension of `A`. - -Returns `A`, modified in-place, and `T`, which contains upper -triangular block reflectors which parameterize the elementary reflectors of -the factorization. -""" -geqrt!(A::AbstractMatrix{<:BlasFloat}, nb::Integer) = geqrt!(A, similar(A, nb, minimum(size(A)))) - -""" - geqrt3!(A) -> (A, T) - -Recursively computes the blocked `QR` factorization of `A`, `A = QR`. - -Returns `A`, modified in-place, and `T`, which contains upper triangular block -reflectors which parameterize the elementary reflectors of the factorization. -""" -geqrt3!(A::AbstractMatrix{<:BlasFloat}) = (n = size(A, 2); geqrt3!(A, similar(A, n, n))) - -""" - geqrf!(A) -> (A, tau) - -Compute the `QR` factorization of `A`, `A = QR`. - -Returns `A`, modified in-place, and `tau`, which contains scalars -which parameterize the elementary reflectors of the factorization. -""" -geqrf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); geqrf!(A, similar(A, min(m, n)))) - -""" - gerqf!(A) -> (A, tau) - -Compute the `RQ` factorization of `A`, `A = RQ`. - -Returns `A`, modified in-place, and `tau`, which contains scalars -which parameterize the elementary reflectors of the factorization. -""" -gerqf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); gerqf!(A, similar(A, min(m, n)))) - -""" - getrf!(A) -> (A, ipiv, info) - -Compute the pivoted `LU` factorization of `A`, `A = LU`. - -Returns `A`, modified in-place, `ipiv`, the pivoting information, and an `info` -code which indicates success (`info = 0`), a singular value in `U` -(`info = i`, in which case `U[i,i]` is singular), or an error code (`info < 0`). -""" -getrf!(A::AbstractMatrix{T}; check::Bool=true) where {T <: BlasFloat} = ((m,n) = size(A); getrf!(A, similar(A, BlasInt, min(m, n)); check)) - -## Tools to compute and apply elementary reflectors -for (larfg, elty) in - ((:dlarfg_, Float64), - (:slarfg_, Float32), - (:zlarfg_, ComplexF64), - (:clarfg_, ComplexF32)) - @eval begin - # .. Scalar Arguments .. - # INTEGER incx, n - # DOUBLE PRECISION alpha, tau - # .. - # .. Array Arguments .. - # DOUBLE PRECISION x( * ) - function larfg!(x::AbstractVector{$elty}) - require_one_based_indexing(x) - N = BlasInt(length(x)) - α = Ref{$elty}(x[1]) - incx = BlasInt(1) - τ = Ref{$elty}(0) - ccall((@blasfunc($larfg), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}), - N, α, pointer(x, 2), incx, τ) - @inbounds x[1] = one($elty) - return τ[] - end - end -end - -for (larf, elty) in - ((:dlarf_, Float64), - (:slarf_, Float32), - (:zlarf_, ComplexF64), - (:clarf_, ComplexF32)) - @eval begin - # .. Scalar Arguments .. - # CHARACTER side - # INTEGER incv, ldc, m, n - # DOUBLE PRECISION tau - # .. - # .. Array Arguments .. - # DOUBLE PRECISION c( ldc, * ), v( * ), work( * ) - function larf!(side::AbstractChar, v::AbstractVector{$elty}, - τ::$elty, C::AbstractMatrix{$elty}, work::AbstractVector{$elty}) - require_one_based_indexing(v, C, work) - m, n = size(C) - chkside(side) - ldc = max(1, stride(C, 2)) - l = side == 'L' ? n : m - incv = BlasInt(1) - ccall((@blasfunc($larf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Clong), - side, m, n, v, incv, - τ, C, ldc, work, 1) - return C - end - - function larf!(side::AbstractChar, v::AbstractVector{$elty}, - τ::$elty, C::AbstractMatrix{$elty}) - require_one_based_indexing(v, C) - m, n = size(C) - chkside(side) - lwork = side == 'L' ? n : m - return larf!(side, v, τ, C, Vector{$elty}(undef,lwork)) - end - end -end - -## Complete orthogonaliztion tools -for (tzrzf, ormrz, elty) in - ((:dtzrzf_,:dormrz_,:Float64), - (:stzrzf_,:sormrz_,:Float32), - (:ztzrzf_,:zunmrz_,:ComplexF64), - (:ctzrzf_,:cunmrz_,:ComplexF32)) - @eval begin - # SUBROUTINE ZTZRZF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) - # - # .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, M, N - # .. - # .. Array Arguments .. - # COMPLEX*16 A( LDA, * ), TAU( * ), WORK( * ) - function tzrzf!(A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - m, n = size(A) - if n < m - throw(DimensionMismatch(lazy"input matrix A has dimensions ($m,$n), but cannot have fewer columns than rows")) - end - lda = max(1, stride(A,2)) - tau = similar(A, $elty, m) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($tzrzf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, A, lda, - tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, tau - end - - # SUBROUTINE ZUNMRZ( SIDE, TRANS, M, N, K, L, A, LDA, TAU, C, LDC, - # WORK, LWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER SIDE, TRANS - # INTEGER INFO, K, L, LDA, LDC, LWORK, M, N - # .. - # .. Array Arguments .. - # COMPLEX*16 A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) - function ormrz!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractMatrix{$elty}) - require_one_based_indexing(A, tau, C) - chktrans(trans) - chkside(side) - chkstride1(A, tau, C) - m, n = size(C) - k = length(tau) - l = size(A, 2) - size(A, 1) - lda = max(1, stride(A,2)) - ldc = max(1, stride(C,2)) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormrz), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - side, trans, m, n, - k, l, A, lda, - tau, C, ldc, work, - lwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - end -end - -""" - ormrz!(side, trans, A, tau, C) - -Multiplies the matrix `C` by `Q` from the transformation supplied by -`tzrzf!`. Depending on `side` or `trans` the multiplication can be -left-sided (`side = L, Q*C`) or right-sided (`side = R, C*Q`) and `Q` -can be unmodified (`trans = N`), transposed (`trans = T`), or conjugate -transposed (`trans = C`). Returns matrix `C` which is modified in-place -with the result of the multiplication. -""" -ormrz!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractMatrix) - -""" - tzrzf!(A) -> (A, tau) - -Transforms the upper trapezoidal matrix `A` to upper triangular form in-place. -Returns `A` and `tau`, the scalar parameters for the elementary reflectors -of the transformation. -""" -tzrzf!(A::AbstractMatrix) - -## (GE) general matrices, solvers with factorization, solver and inverse -for (gels, gesv, getrs, getri, elty) in - ((:dgels_,:dgesv_,:dgetrs_,:dgetri_,:Float64), - (:sgels_,:sgesv_,:sgetrs_,:sgetri_,:Float32), - (:zgels_,:zgesv_,:zgetrs_,:zgetri_,:ComplexF64), - (:cgels_,:cgesv_,:cgetrs_,:cgetri_,:ComplexF32)) - @eval begin - # SUBROUTINE DGELS( TRANS, M, N, NRHS, A, LDA, B, LDB, WORK, LWORK,INFO) - # * .. Scalar Arguments .. - # CHARACTER TRANS - # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS - function gels!(trans::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chktrans(trans) - chkstride1(A, B) - btrn = trans == 'T' - m, n = size(A) - if size(B,1) != (btrn ? n : m) - throw(DimensionMismatch(lazy"matrix A has dimensions ($m,$n), transposed: $btrn, but leading dimension of B is $(size(B,1))")) - end - info = Ref{BlasInt}() - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gels), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - (btrn ? 'T' : 'N'), m, n, size(B,2), A, max(1,stride(A,2)), - B, max(1,stride(B,2)), work, lwork, info, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - k = min(m, n) - F = m < n ? tril(A[1:k, 1:k]) : triu(A[1:k, 1:k]) - ssr = Vector{$elty}(undef, size(B, 2)) - for i = 1:size(B,2) - x = zero($elty) - for j = k+1:size(B,1) - x += abs2(B[j,i]) - end - ssr[i] = x - end - F, subsetrows(B, B, k), ssr - end - - # SUBROUTINE DGESV( N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function gesv!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - n = checksquare(A) - if size(B,1) != n - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - info = Ref{BlasInt}() - ccall((@blasfunc($gesv), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) - chklapackerror(info[]) - B, A, ipiv - end - - # SUBROUTINE DGETRS( TRANS, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - #* .. Scalar Arguments .. - # CHARACTER TRANS - # INTEGER INFO, LDA, LDB, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function getrs!(trans::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chktrans(trans) - chkstride1(A, B, ipiv) - n = checksquare(A) - if n != size(B, 1) - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)), but needs $n")) - end - if n != length(ipiv) - throw(DimensionMismatch(lazy"ipiv has length $(length(ipiv)), but needs to be $n")) - end - nrhs = size(B, 2) - info = Ref{BlasInt}() - ccall((@blasfunc($getrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - trans, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - - # SUBROUTINE DGETRI( N, A, LDA, IPIV, WORK, LWORK, INFO ) - #* .. Scalar Arguments .. - # INTEGER INFO, LDA, LWORK, N - #* .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function getri!(A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - if n != length(ipiv) - throw(DimensionMismatch(lazy"ipiv has length $(length(ipiv)), but needs $n")) - end - lda = max(1,stride(A, 2)) - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($getri), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - n, A, lda, ipiv, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A - end - end -end - -""" - gels!(trans, A, B) -> (F, B, ssr) - -Solves the linear equation `A * X = B`, `transpose(A) * X = B`, or `adjoint(A) * X = B` using -a QR or LQ factorization. Modifies the matrix/vector `B` in place with the -solution. `A` is overwritten with its `QR` or `LQ` factorization. `trans` -may be one of `N` (no modification), `T` (transpose), or `C` (conjugate -transpose). `gels!` searches for the minimum norm/least squares solution. -`A` may be under or over determined. The solution is returned in `B`. -""" -gels!(trans::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -""" - gesv!(A, B) -> (B, A, ipiv) - -Solves the linear equation `A * X = B` where `A` is a square matrix using -the `LU` factorization of `A`. `A` is overwritten with its `LU` -factorization and `B` is overwritten with the solution `X`. `ipiv` contains the -pivoting information for the `LU` factorization of `A`. -""" -gesv!(A::AbstractMatrix, B::AbstractVecOrMat) - -""" - getrs!(trans, A, ipiv, B) - -Solves the linear equation `A * X = B`, `transpose(A) * X = B`, or `adjoint(A) * X = B` for -square `A`. Modifies the matrix/vector `B` in place with the solution. `A` -is the `LU` factorization from `getrf!`, with `ipiv` the pivoting -information. `trans` may be one of `N` (no modification), `T` (transpose), -or `C` (conjugate transpose). -""" -getrs!(trans::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) - -""" - getri!(A, ipiv) - -Computes the inverse of `A`, using its `LU` factorization found by -`getrf!`. `ipiv` is the pivot information output and `A` -contains the `LU` factorization of `getrf!`. `A` is overwritten with -its inverse. -""" -getri!(A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -for (gesvx, elty) in - ((:dgesvx_,:Float64), - (:sgesvx_,:Float32)) - @eval begin - # SUBROUTINE DGESVX( FACT, TRANS, N, NRHS, A, LDA, AF, LDAF, IPIV, - # EQUED, R, C, B, LDB, X, LDX, RCOND, FERR, BERR, - # WORK, IWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER EQUED, FACT, TRANS - # INTEGER INFO, LDA, LDAF, LDB, LDX, N, NRHS - # DOUBLE PRECISION RCOND - # .. - # .. Array Arguments .. - # INTEGER IPIV( * ), IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), AF( LDAF, * ), B( LDB, * ), - # $ BERR( * ), C( * ), FERR( * ), R( * ), - # $ WORK( * ), X( LDX, * - # - function gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - AF::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, equed::AbstractChar, - R::AbstractVector{$elty}, C::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, AF, ipiv, R, C, B) - @chkvalidparam 1 fact ('F', 'N', 'E') - chktrans(trans) - chkstride1(ipiv, R, C, B) - n = checksquare(A) - lda = stride(A,2) - n = checksquare(AF) - ldaf = stride(AF,2) - nrhs = size(B,2) - ldb = stride(B,2) - rcond = Ref{$elty}() - ferr = similar(A, $elty, nrhs) - berr = similar(A, $elty, nrhs) - work = Vector{$elty}(undef, 4n) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - X = similar(A, $elty, n, nrhs) - ccall((@blasfunc($gesvx), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), - fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, - ldb, X, n, rcond, ferr, berr, work, iwork, info, 1, 1, 1) - chklapackerror(info[]) - if info[] == n + 1 - @warn "Matrix is singular to working precision" - else - chknonsingular(info[]) - end - #WORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[], ferr, berr, work[1] - end - - function gesvx!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - n = size(A,1) - X, equed, R, C, B, rcond, ferr, berr, rpgf = - gesvx!('N', 'N', A, - similar(A, $elty, n, n), - similar(A, BlasInt, n), - 'N', - similar(A, $elty, n), - similar(A, $elty, n), - B) - X, rcond, ferr, berr, rpgf - end - end -end -for (gesvx, elty, relty) in - ((:zgesvx_,:ComplexF64,:Float64), - (:cgesvx_,:ComplexF32 ,:Float32)) - @eval begin - # SUBROUTINE ZGESVX( FACT, TRANS, N, NRHS, A, LDA, AF, LDAF, IPIV, - # EQUED, R, C, B, LDB, X, LDX, RCOND, FERR, BERR, - # WORK, RWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER EQUED, FACT, TRANS - # INTEGER INFO, LDA, LDAF, LDB, LDX, N, NRHS - # DOUBLE PRECISION RCOND - # .. - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION BERR( * ), C( * ), FERR( * ), R( * ), - # $ RWORK( * ) - # COMPLEX*16 A( LDA, * ), AF( LDAF, * ), B( LDB, * ), - # $ WORK( * ), X( LDX, * ) - function gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - AF::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, equed::AbstractChar, - R::AbstractVector{$relty}, C::AbstractVector{$relty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, AF, ipiv, R, C, B) - @chkvalidparam 1 fact ('F', 'N', 'E') - chktrans(trans) - chkstride1(A, AF, ipiv, R, C, B) - n = checksquare(A) - lda = stride(A,2) - n = checksquare(AF) - ldaf = stride(AF,2) - nrhs = size(B,2) - ldb = stride(B,2) - rcond = Ref{$relty}() - ferr = similar(A, $relty, nrhs) - berr = similar(A, $relty, nrhs) - work = Vector{$elty}(undef, 2n) - rwork = Vector{$relty}(undef, 2n) - info = Ref{BlasInt}() - X = similar(A, $elty, n, nrhs) - ccall((@blasfunc($gesvx), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong), - fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, - ldb, X, n, rcond, ferr, berr, work, rwork, info, 1, 1, 1) - chklapackerror(info[]) - if info[] == n + 1 - @warn "Matrix is singular to working precision" - else - chknonsingular(info[]) - end - #RWORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[], ferr, berr, rwork[1] - end - - #Wrapper for the no-equilibration, no-transpose calculation - function gesvx!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - n = size(A,1) - X, equed, R, C, B, rcond, ferr, berr, rpgf = - gesvx!('N', 'N', A, - similar(A, $elty, n, n), - similar(A, BlasInt, n), - 'N', - similar(A, $relty, n), - similar(A, $relty, n), - B) - X, rcond, ferr, berr, rpgf - end - end -end - -""" - gesvx!(fact, trans, A, AF, ipiv, equed, R, C, B) -> (X, equed, R, C, B, rcond, ferr, berr, work) - -Solves the linear equation `A * X = B` (`trans = N`), `transpose(A) * X = B` -(`trans = T`), or `adjoint(A) * X = B` (`trans = C`) using the `LU` factorization -of `A`. `fact` may be `E`, in which case `A` will be equilibrated and copied -to `AF`; `F`, in which case `AF` and `ipiv` from a previous `LU` factorization -are inputs; or `N`, in which case `A` will be copied to `AF` and then -factored. If `fact = F`, `equed` may be `N`, meaning `A` has not been -equilibrated; `R`, meaning `A` was multiplied by `Diagonal(R)` from the left; -`C`, meaning `A` was multiplied by `Diagonal(C)` from the right; or `B`, meaning -`A` was multiplied by `Diagonal(R)` from the left and `Diagonal(C)` from the right. -If `fact = F` and `equed = R` or `B` the elements of `R` must all be positive. -If `fact = F` and `equed = C` or `B` the elements of `C` must all be positive. - -Returns the solution `X`; `equed`, which is an output if `fact` is not `N`, -and describes the equilibration that was performed; `R`, the row equilibration -diagonal; `C`, the column equilibration diagonal; `B`, which may be overwritten -with its equilibrated form `Diagonal(R)*B` (if `trans = N` and `equed = R,B`) or -`Diagonal(C)*B` (if `trans = T,C` and `equed = C,B`); `rcond`, the reciprocal -condition number of `A` after equilbrating; `ferr`, the forward error bound for -each solution vector in `X`; `berr`, the forward error bound for each solution -vector in `X`; and `work`, the reciprocal pivot growth factor. -""" -gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix, AF::AbstractMatrix, - ipiv::AbstractVector{BlasInt}, equed::AbstractChar, R::AbstractVector, C::AbstractVector, B::AbstractVecOrMat) - -""" - gesvx!(A, B) - -The no-equilibration, no-transpose simplification of `gesvx!`. -""" -gesvx!(A::AbstractMatrix, B::AbstractVecOrMat) - -for (gelsd, gelsy, elty) in - ((:dgelsd_,:dgelsy_,:Float64), - (:sgelsd_,:sgelsy_,:Float32)) - @eval begin - # SUBROUTINE DGELSD( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, - # $ WORK, LWORK, IWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK - # DOUBLE PRECISION RCOND - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), S( * ), WORK( * ) - function gelsd!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=-one($elty)) - require_one_based_indexing(A, B) - chkstride1(A, B) - m, n = size(A) - if size(B, 1) != m - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)) but needs $m")) - end - newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] - s = similar(A, $elty, min(m, n)) - rnk = Ref{BlasInt}() - info = Ref{BlasInt}() - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - for i = 1:2 # first call returns lwork as work[1] and iwork length as iwork[1] - ccall((@blasfunc($gelsd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - m, n, size(B,2), - A, max(1,stride(A,2)), newB, max(1,stride(B,2),n), - s, $elty(rcond), rnk, work, - lwork, iwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - resize!(iwork, iwork[1]) - end - end - subsetrows(B, newB, n), rnk[] - end - - # SUBROUTINE DGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, - # $ WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK - # DOUBLE PRECISION RCOND - # * .. - # * .. Array Arguments .. - # INTEGER JPVT( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) - function gelsy!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=eps($elty)) - require_one_based_indexing(A, B) - chkstride1(A) - m = size(A, 1) - n = size(A, 2) - nrhs = size(B, 2) - if size(B, 1) != m - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)) but needs $m")) - end - newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] - lda = max(1, stride(A,2)) - ldb = max(1, stride(newB,2)) - jpvt = zeros(BlasInt, n) - rnk = Ref{BlasInt}() - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gelsy), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - m, n, nrhs, A, - lda, newB, ldb, jpvt, - $elty(rcond), rnk, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - subsetrows(B, newB, n), rnk[] - end - end -end - -for (gelsd, gelsy, elty, relty) in - ((:zgelsd_,:zgelsy_,:ComplexF64,:Float64), - (:cgelsd_,:cgelsy_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZGELSD( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, - # $ WORK, LWORK, RWORK, IWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK - # DOUBLE PRECISION RCOND - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION RWORK( * ), S( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function gelsd!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=-one($relty)) - require_one_based_indexing(A, B) - chkstride1(A, B) - m, n = size(A) - if size(B, 1) != m - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)) but needs $m")) - end - newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] - s = similar(A, $relty, min(m, n)) - rnk = Ref{BlasInt}() - info = Ref{BlasInt}() - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 1) - iwork = Vector{BlasInt}(undef, 1) - for i = 1:2 # first call returns lwork as work[1], rwork length as rwork[1] and iwork length as iwork[1] - ccall((@blasfunc($gelsd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, - Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ref{BlasInt}, Ref{BlasInt}), - m, n, size(B,2), A, - max(1,stride(A,2)), newB, max(1,stride(B,2),n), s, - $relty(rcond), rnk, work, lwork, - rwork, iwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - resize!(rwork, BlasInt(rwork[1])) - resize!(iwork, iwork[1]) - end - end - subsetrows(B, newB, n), rnk[] - end - - # SUBROUTINE ZGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, - # $ WORK, LWORK, RWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK - # DOUBLE PRECISION RCOND - # * .. - # * .. Array Arguments .. - # INTEGER JPVT( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function gelsy!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=eps($relty)) - require_one_based_indexing(A, B) - chkstride1(A, B) - m, n = size(A) - nrhs = size(B, 2) - if size(B, 1) != m - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)) but needs $m")) - end - newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] - lda = max(1, m) - ldb = max(1, m, n) - jpvt = zeros(BlasInt, n) - rnk = Ref{BlasInt}(1) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 2n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gelsy), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}), - m, n, nrhs, A, - lda, newB, ldb, jpvt, - $relty(rcond), rnk, work, lwork, - rwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - subsetrows(B, newB, n), rnk[] - end - end -end - -""" - gelsd!(A, B, rcond) -> (B, rnk) - -Computes the least norm solution of `A * X = B` by finding the `SVD` -factorization of `A`, then dividing-and-conquering the problem. `B` -is overwritten with the solution `X`. Singular values below `rcond` -will be treated as zero. Returns the solution in `B` and the effective rank -of `A` in `rnk`. -""" -gelsd!(A::AbstractMatrix, B::AbstractVecOrMat, rcond::Real) - -""" - gelsy!(A, B, rcond) -> (B, rnk) - -Computes the least norm solution of `A * X = B` by finding the full `QR` -factorization of `A`, then dividing-and-conquering the problem. `B` -is overwritten with the solution `X`. Singular values below `rcond` -will be treated as zero. Returns the solution in `B` and the effective rank -of `A` in `rnk`. -""" -gelsy!(A::AbstractMatrix, B::AbstractVecOrMat, rcond::Real) - -for (gglse, elty) in ((:dgglse_, :Float64), - (:sgglse_, :Float32), - (:zgglse_, :ComplexF64), - (:cgglse_, :ComplexF32)) - @eval begin - # SUBROUTINE DGGLSE( M, N, P, A, LDA, B, LDB, C, D, X, WORK, LWORK, - # $ INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, LDA, LDB, LWORK, M, N, P - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), C( * ), D( * ), - # $ WORK( * ), X( * ) - function gglse!(A::AbstractMatrix{$elty}, c::AbstractVector{$elty}, - B::AbstractMatrix{$elty}, d::AbstractVector{$elty}) - require_one_based_indexing(A, c, B, d) - chkstride1(A, c, B, d) - m, n = size(A) - p = size(B, 1) - if size(B, 2) != n - throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)), needs $n")) - end - if length(c) != m - throw(DimensionMismatch(lazy"c has length $(length(c)), needs $m")) - end - if length(d) != p - throw(DimensionMismatch(lazy"d has length $(length(d)), needs $p")) - end - X = zeros($elty, n) - info = Ref{BlasInt}() - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gglse), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - m, n, p, A, max(1,stride(A,2)), B, max(1,stride(B,2)), c, d, X, - work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - X, dot(view(c, n - p + 1:m), view(c, n - p + 1:m)) - end - end -end - -""" - gglse!(A, c, B, d) -> (X,res) - -Solves the equation `A * x = c` where `x` is subject to the equality -constraint `B * x = d`. Uses the formula `||c - A*x||^2 = 0` to solve. -Returns `X` and the residual sum-of-squares. -""" -gglse!(A::AbstractMatrix, c::AbstractVector, B::AbstractMatrix, d::AbstractVector) - -# (GE) general matrices eigenvalue-eigenvector and singular value decompositions -for (geev, gesvd, gesdd, ggsvd, elty, relty) in - ((:dgeev_,:dgesvd_,:dgesdd_,:dggsvd_,:Float64,:Float64), - (:sgeev_,:sgesvd_,:sgesdd_,:sggsvd_,:Float32,:Float32), - (:zgeev_,:zgesvd_,:zgesdd_,:zggsvd_,:ComplexF64,:Float64), - (:cgeev_,:cgesvd_,:cgesdd_,:cggsvd_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DGEEV( JOBVL, JOBVR, N, A, LDA, WR, WI, VL, LDVL, VR, - # $ LDVR, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBVL, JOBVR - # INTEGER INFO, LDA, LDVL, LDVR, LWORK, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), VL( LDVL, * ), VR( LDVR, * ), - # $ WI( * ), WORK( * ), WR( * ) - function geev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - @chkvalidparam 1 jobvl ('N', 'V') - @chkvalidparam 2 jobvr ('N', 'V') - chkfinite(A) # balancing routines don't support NaNs and Infs - lvecs = jobvl == 'V' - rvecs = jobvr == 'V' - VL = similar(A, $elty, (n, lvecs ? n : 0)) - VR = similar(A, $elty, (n, rvecs ? n : 0)) - cmplx = eltype(A) <: Complex - if cmplx - W = similar(A, $elty, n) - rwork = similar(A, $relty, 2n) - else - WR = similar(A, $elty, n) - WI = similar(A, $elty, n) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - if cmplx - ccall((@blasfunc($geev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, max(1,stride(A,2)), W, VL, n, VR, n, - work, lwork, rwork, info, 1, 1) - else - ccall((@blasfunc($geev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, max(1,stride(A,2)), WR, WI, VL, n, - VR, n, work, lwork, info, 1, 1) - end - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - cmplx ? (W, VL, VR) : (WR, WI, VL, VR) - end - - # SUBROUTINE DGESDD( JOBZ, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, - # LWORK, IWORK, INFO ) - #* .. Scalar Arguments .. - # CHARACTER JOBZ - # INTEGER INFO, LDA, LDU, LDVT, LWORK, M, N - #* .. - #* .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), S( * ), U( LDU, * ), - # VT( LDVT, * ), WORK( * ) - function gesdd!(job::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - @chkvalidparam 1 job ('A', 'S', 'O', 'N') - m, n = size(A) - minmn = min(m, n) - if job == 'A' - U = similar(A, $elty, (m, m)) - VT = similar(A, $elty, (n, n)) - elseif job == 'S' - U = similar(A, $elty, (m, minmn)) - VT = similar(A, $elty, (minmn, n)) - elseif job == 'O' - U = similar(A, $elty, (m, m >= n ? 0 : m)) - VT = similar(A, $elty, (n, m >= n ? n : 0)) - else - U = similar(A, $elty, (m, 0)) - VT = similar(A, $elty, (n, 0)) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - S = similar(A, $relty, minmn) - cmplx = eltype(A)<:Complex - if cmplx - rwork = Vector{$relty}(undef, job == 'N' ? 7*minmn : minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) - end - iwork = Vector{BlasInt}(undef, 8*minmn) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - if cmplx - ccall((@blasfunc($gesdd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), - job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), - work, lwork, rwork, iwork, info, 1) - else - ccall((@blasfunc($gesdd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ref{BlasInt}, Clong), - job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), - work, lwork, iwork, info, 1) - end - chklapackerror(info[]) - if i == 1 - # Work around issue with truncated Float32 representation of lwork in - # sgesdd by using nextfloat. See - # http://icl.cs.utk.edu/lapack-forum/viewtopic.php?f=13&t=4587&p=11036&hilit=sgesdd#p11036 - # and - # https://github.com/scipy/scipy/issues/5401 - lwork = round(BlasInt, nextfloat(real(work[1]))) - resize!(work, lwork) - end - end - if job == 'O' - if m >= n - return (A, S, VT) - else - # ()__ - # ||::Z__ - # ||::|:::Z____ - # ||::|:::|====| - # ||==|===|====| - # ||""|===|====| - # || `"""|====| - # || `""""` - return (U, S, A) - end - end - return (U, S, VT) - end - - # SUBROUTINE DGESVD( JOBU, JOBVT, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBU, JOBVT - # INTEGER INFO, LDA, LDU, LDVT, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), S( * ), U( LDU, * ), - # $ VT( LDVT, * ), WORK( * ) - function gesvd!(jobu::AbstractChar, jobvt::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - @chkvalidparam 1 jobu ('A', 'S', 'O', 'N') - @chkvalidparam 2 jobvt ('A', 'S', 'O', 'N') - (jobu == jobvt == 'O') && throw(ArgumentError("jobu and jobvt cannot both be O")) - m, n = size(A) - minmn = min(m, n) - S = similar(A, $relty, minmn) - U = similar(A, $elty, jobu == 'A' ? (m, m) : (jobu == 'S' ? (m, minmn) : (m, 0))) - VT = similar(A, $elty, jobvt == 'A' ? (n, n) : (jobvt == 'S' ? (minmn, n) : (n, 0))) - work = Vector{$elty}(undef, 1) - cmplx = eltype(A) <: Complex - if cmplx - rwork = Vector{$relty}(undef, 5minmn) - end - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i in 1:2 # first call returns lwork as work[1] - if cmplx - ccall((@blasfunc($gesvd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), - jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), - work, lwork, rwork, info, 1, 1) - else - ccall((@blasfunc($gesvd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), - work, lwork, info, 1, 1) - end - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - if jobu == 'O' - return (A, S, VT) - elseif jobvt == 'O' - # =============|===========|() - # # # #:::::: - # # # #:::::: - # # # #:::::: - # # # #:::::: - # # # # # # # - # # # # # # # - # # # # # # # - return (U, S, A) # # # # # # # - else # # # # # # # - return (U, S, VT) # # # # # # # - - end - end - - # SUBROUTINE ZGGSVD( JOBU, JOBV, JOBQ, M, N, P, K, L, A, LDA, B, - # $ LDB, ALPHA, BETA, U, LDU, V, LDV, Q, LDQ, WORK, - # $ RWORK, IWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBQ, JOBU, JOBV - # INTEGER INFO, K, L, LDA, LDB, LDQ, LDU, LDV, M, N, P - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION ALPHA( * ), BETA( * ), RWORK( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), Q( LDQ, * ), - # $ U( LDU, * ), V( LDV, * ), WORK( * ) - function ggsvd!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - @chkvalidparam 1 jobu ('U', 'N') - @chkvalidparam 2 jobv ('V', 'N') - @chkvalidparam 3 jobq ('Q', 'N') - m, n = size(A) - if size(B, 2) != n - throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)) but needs $n")) - end - p = size(B, 1) - k = Vector{BlasInt}(undef, 1) - l = Vector{BlasInt}(undef, 1) - lda = max(1,stride(A, 2)) - ldb = max(1,stride(B, 2)) - alpha = similar(A, $relty, n) - beta = similar(A, $relty, n) - ldu = max(1, m) - U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) - ldv = max(1, p) - V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) - ldq = max(1, n) - Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) - work = Vector{$elty}(undef, max(3n, m, p) + n) - cmplx = eltype(A) <: Complex - if cmplx - rwork = Vector{$relty}(undef, 2n) - end - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - if cmplx - ccall((@blasfunc($ggsvd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong, Clong), - jobu, jobv, jobq, m, - n, p, k, l, - A, lda, B, ldb, - alpha, beta, U, ldu, - V, ldv, Q, ldq, - work, rwork, iwork, info, - 1, 1, 1) - else - ccall((@blasfunc($ggsvd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong, Clong), - jobu, jobv, jobq, m, - n, p, k, l, - A, lda, B, ldb, - alpha, beta, U, ldu, - V, ldv, Q, ldq, - work, iwork, info, - 1, 1, 1) - end - chklapackerror(info[]) - if m - k[1] - l[1] >= 0 - R = triu(A[1:k[1] + l[1],n - k[1] - l[1] + 1:n]) - else - R = triu([A[1:m, n - k[1] - l[1] + 1:n]; B[m - k[1] + 1:l[1], n - k[1] - l[1] + 1:n]]) - end - U, V, Q, alpha, beta, k[1], l[1], R - end - end -end - -""" - geev!(jobvl, jobvr, A) -> (W, VL, VR) - -Finds the eigensystem of `A`. If `jobvl = N`, the left eigenvectors of -`A` aren't computed. If `jobvr = N`, the right eigenvectors of `A` -aren't computed. If `jobvl = V` or `jobvr = V`, the corresponding -eigenvectors are computed. Returns the eigenvalues in `W`, the right -eigenvectors in `VR`, and the left eigenvectors in `VL`. -""" -geev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix) - -""" - gesdd!(job, A) -> (U, S, VT) - -Finds the singular value decomposition of `A`, `A = U * S * V'`, -using a divide and conquer approach. If `job = A`, all the columns of `U` and -the rows of `V'` are computed. If `job = N`, no columns of `U` or rows of `V'` -are computed. If `job = O`, `A` is overwritten with the columns of (thin) `U` -and the rows of (thin) `V'`. If `job = S`, the columns of (thin) `U` and the -rows of (thin) `V'` are computed and returned separately. -""" -gesdd!(job::AbstractChar, A::AbstractMatrix) - -""" - gesvd!(jobu, jobvt, A) -> (U, S, VT) - -Finds the singular value decomposition of `A`, `A = U * S * V'`. -If `jobu = A`, all the columns of `U` are computed. If `jobvt = A` all the rows -of `V'` are computed. If `jobu = N`, no columns of `U` are computed. If -`jobvt = N` no rows of `V'` are computed. If `jobu = O`, `A` is overwritten with -the columns of (thin) `U`. If `jobvt = O`, `A` is overwritten with the rows -of (thin) `V'`. If `jobu = S`, the columns of (thin) `U` are computed -and returned separately. If `jobvt = S` the rows of (thin) `V'` are -computed and returned separately. `jobu` and `jobvt` can't both be `O`. - -Returns `U`, `S`, and `Vt`, where `S` are the singular values of `A`. -""" -gesvd!(jobu::AbstractChar, jobvt::AbstractChar, A::AbstractMatrix) - -""" - ggsvd!(jobu, jobv, jobq, A, B) -> (U, V, Q, alpha, beta, k, l, R) - -Finds the generalized singular value decomposition of `A` and `B`, `U'*A*Q = D1*R` -and `V'*B*Q = D2*R`. `D1` has `alpha` on its diagonal and `D2` has `beta` on its -diagonal. If `jobu = U`, the orthogonal/unitary matrix `U` is computed. If -`jobv = V` the orthogonal/unitary matrix `V` is computed. If `jobq = Q`, -the orthogonal/unitary matrix `Q` is computed. If `jobu`, `jobv` or `jobq` is -`N`, that matrix is not computed. This function is only available in LAPACK -versions prior to 3.6.0. -""" -ggsvd!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - - -for (f, elty) in ((:dggsvd3_, :Float64), - (:sggsvd3_, :Float32)) - @eval begin - function ggsvd3!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - @chkvalidparam 1 jobu ('U', 'N') - @chkvalidparam 2 jobv ('V', 'N') - @chkvalidparam 3 jobq ('Q', 'N') - m, n = size(A) - if size(B, 2) != n - throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)) but needs $n")) - end - p = size(B, 1) - k = Ref{BlasInt}() - l = Ref{BlasInt}() - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - alpha = similar(A, $elty, n) - beta = similar(A, $elty, n) - ldu = max(1, m) - U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) - ldv = max(1, p) - V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) - ldq = max(1, n) - Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($f), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Clong, Clong, Clong), - jobu, jobv, jobq, m, - n, p, k, l, - A, lda, B, ldb, - alpha, beta, U, ldu, - V, ldv, Q, ldq, - work, lwork, iwork, info, - 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - if m - k[] - l[] >= 0 - R = triu(A[1:k[] + l[],n - k[] - l[] + 1:n]) - else - R = triu([A[1:m, n - k[] - l[] + 1:n]; B[m - k[] + 1:l[], n - k[] - l[] + 1:n]]) - end - return U, V, Q, alpha, beta, k[], l[], R - end - end -end - -for (f, elty, relty) in ((:zggsvd3_, :ComplexF64, :Float64), - (:cggsvd3_, :ComplexF32, :Float32)) - @eval begin - function ggsvd3!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - @chkvalidparam 1 jobu ('U', 'N') - @chkvalidparam 2 jobv ('V', 'N') - @chkvalidparam 3 jobq ('Q', 'N') - m, n = size(A) - if size(B, 2) != n - throw(DimensionMismatch(lazy"B has second dimension $(size(B,2)) but needs $n")) - end - p = size(B, 1) - k = Vector{BlasInt}(undef, 1) - l = Vector{BlasInt}(undef, 1) - lda = max(1,stride(A, 2)) - ldb = max(1,stride(B, 2)) - alpha = similar(A, $relty, n) - beta = similar(A, $relty, n) - ldu = max(1, m) - U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) - ldv = max(1, p) - V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) - ldq = max(1, n) - Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 2n) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($f), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, - Ref{BlasInt}, Clong, Clong, Clong), - jobu, jobv, jobq, m, - n, p, k, l, - A, lda, B, ldb, - alpha, beta, U, ldu, - V, ldv, Q, ldq, - work, lwork, rwork, iwork, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - if m - k[1] - l[1] >= 0 - R = triu(A[1:k[1] + l[1],n - k[1] - l[1] + 1:n]) - else - R = triu([A[1:m, n - k[1] - l[1] + 1:n]; B[m - k[1] + 1:l[1], n - k[1] - l[1] + 1:n]]) - end - return U, V, Q, alpha, beta, k[1], l[1], R - end - end -end - -""" - ggsvd3!(jobu, jobv, jobq, A, B) -> (U, V, Q, alpha, beta, k, l, R) - -Finds the generalized singular value decomposition of `A` and `B`, `U'*A*Q = D1*R` -and `V'*B*Q = D2*R`. `D1` has `alpha` on its diagonal and `D2` has `beta` on its -diagonal. If `jobu = U`, the orthogonal/unitary matrix `U` is computed. If -`jobv = V` the orthogonal/unitary matrix `V` is computed. If `jobq = Q`, -the orthogonal/unitary matrix `Q` is computed. If `jobu`, `jobv`, or `jobq` is -`N`, that matrix is not computed. This function requires LAPACK 3.6.0. -""" -ggsvd3! - -## Expert driver and generalized eigenvalue problem -for (geevx, ggev, ggev3, elty) in - ((:dgeevx_,:dggev_,:dggev3_,:Float64), - (:sgeevx_,:sggev_,:sggev3_,:Float32)) - @eval begin - # SUBROUTINE DGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, WR, WI, - # VL, LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, - # RCONDE, RCONDV, WORK, LWORK, IWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER BALANC, JOBVL, JOBVR, SENSE - # INTEGER IHI, ILO, INFO, LDA, LDVL, LDVR, LWORK, N - # DOUBLE PRECISION ABNRM - # .. - # .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), RCONDE( * ), RCONDV( * ), - # $ SCALE( * ), VL( LDVL, * ), VR( LDVR, * ), - # $ WI( * ), WORK( * ), WR( * ) - function geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 balanc ('N', 'P', 'S', 'B') - @chkvalidparam 4 sense ('N', 'E', 'V', 'B') - if sense ∈ ('E', 'B') && !(jobvl == jobvr == 'V') - throw(ArgumentError(lazy"sense = '$sense' requires jobvl = 'V' and jobvr = 'V'")) - end - n = checksquare(A) - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 0 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 0 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - chkfinite(A) # balancing routines don't support NaNs and Infs - lda = max(1,stride(A,2)) - wr = similar(A, $elty, n) - wi = similar(A, $elty, n) - VL = similar(A, $elty, ldvl, n) - VR = similar(A, $elty, ldvr, n) - ilo = Ref{BlasInt}() - ihi = Ref{BlasInt}() - scale = similar(A, $elty, n) - abnrm = Ref{$elty}() - rconde = similar(A, $elty, n) - rcondv = similar(A, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iworksize = 0 - if sense == 'N' || sense == 'E' - iworksize = 0 - elseif sense == 'V' || sense == 'B' - iworksize = 2*n - 2 - else - throw(ArgumentError(lazy"sense must be 'N', 'E', 'V' or 'B', but $sense was passed")) - end - iwork = Vector{BlasInt}(undef, iworksize) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($geevx), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Clong, Clong, Clong, Clong), - balanc, jobvl, jobvr, sense, - n, A, lda, wr, - wi, VL, max(1,ldvl), VR, - max(1,ldvr), ilo, ihi, scale, - abnrm, rconde, rcondv, work, - lwork, iwork, info, - 1, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - A, wr, wi, VL, VR, ilo[], ihi[], scale, abnrm[], rconde, rcondv - end - - # SUBROUTINE DGGEV( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, ALPHAI, - # $ BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBVL, JOBVR - # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), - # $ B( LDB, * ), BETA( * ), VL( LDVL, * ), - # $ VR( LDVR, * ), WORK( * ) - function ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n, m = checksquare(A,B) - if n != m - throw(DimensionMismatch(lazy"A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) - end - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 1 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 1 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - alphar = similar(A, $elty, n) - alphai = similar(A, $elty, n) - beta = similar(A, $elty, n) - vl = similar(A, $elty, ldvl, n) - vr = similar(A, $elty, ldvr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ggev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, - lda, B, ldb, alphar, - alphai, beta, vl, ldvl, - vr, ldvr, work, lwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - alphar, alphai, beta, vl, vr - end - - # SUBROUTINE DGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, ALPHAI, - # $ BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBVL, JOBVR - # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), - # $ B( LDB, * ), BETA( * ), VL( LDVL, * ), - # $ VR( LDVR, * ), WORK( * ) - function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n, m = checksquare(A,B) - if n != m - throw(DimensionMismatch(lazy"A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) - end - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 1 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 1 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - alphar = similar(A, $elty, n) - alphai = similar(A, $elty, n) - beta = similar(A, $elty, n) - vl = similar(A, $elty, ldvl, n) - vr = similar(A, $elty, ldvr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, - lda, B, ldb, alphar, - alphai, beta, vl, ldvl, - vr, ldvr, work, lwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - alphar, alphai, beta, vl, vr - end - end -end - -for (geevx, ggev, ggev3, elty, relty) in - ((:zgeevx_,:zggev_,:zggev3_,:ComplexF64,:Float64), - (:cgeevx_,:cggev_,:cggev3_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, W, VL, - # LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, RCONDE, - # RCONDV, WORK, LWORK, RWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER BALANC, JOBVL, JOBVR, SENSE - # INTEGER IHI, ILO, INFO, LDA, LDVL, LDVR, LWORK, N - # DOUBLE PRECISION ABNRM - # .. - # .. Array Arguments .. - # DOUBLE PRECISION RCONDE( * ), RCONDV( * ), RWORK( * ), - # $ SCALE( * ) - # COMPLEX*16 A( LDA, * ), VL( LDVL, * ), VR( LDVR, * ), - # $ W( * ), WORK( * ) - function geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - if balanc ∉ ('N', 'P', 'S', 'B') - throw(ArgumentError(lazy"balanc must be 'N', 'P', 'S', or 'B', but $balanc was passed")) - end - if sense ∉ ('N','E','V','B') - throw(ArgumentError(lazy"sense must be 'N', 'E', 'V' or 'B', but $sense was passed")) - end - if sense ∈ ('E', 'B') && !(jobvl == jobvr == 'V') - throw(ArgumentError(lazy"sense = '$sense' requires jobvl = 'V' and jobvr = 'V'")) - end - n = checksquare(A) - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 0 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 0 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - chkfinite(A) # balancing routines don't support NaNs and Infs - lda = max(1,stride(A,2)) - w = similar(A, $elty, n) - VL = similar(A, $elty, ldvl, n) - VR = similar(A, $elty, ldvr, n) - ilo = Ref{BlasInt}() - ihi = Ref{BlasInt}() - scale = similar(A, $relty, n) - abnrm = Ref{$relty}() - rconde = similar(A, $relty, n) - rcondv = similar(A, $relty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 2n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($geevx), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong, Clong), - balanc, jobvl, jobvr, sense, - n, A, lda, w, - VL, max(1,ldvl), VR, max(1,ldvr), - ilo, ihi, scale, abnrm, - rconde, rcondv, work, lwork, - rwork, info, 1, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - A, w, VL, VR, ilo[], ihi[], scale, abnrm[], rconde, rcondv - end - - # SUBROUTINE ZGGEV( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, - # $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBVL, JOBVR - # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), - # $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), - # $ WORK( * ) - function ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) - end - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 1 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 1 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - alpha = similar(A, $elty, n) - beta = similar(A, $elty, n) - vl = similar(A, $elty, ldvl, n) - vr = similar(A, $elty, ldvr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 8n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ggev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, - Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, - lda, B, ldb, alpha, - beta, vl, ldvl, vr, - ldvr, work, lwork, rwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - alpha, beta, vl, vr - end - - # SUBROUTINE ZGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, - # $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBVL, JOBVR - # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), - # $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), - # $ WORK( * ) - function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) - end - ldvl = 0 - if jobvl == 'V' - ldvl = n - elseif jobvl == 'N' - ldvl = 1 - else - throw(ArgumentError(lazy"jobvl must be 'V' or 'N', but $jobvl was passed")) - end - ldvr = 0 - if jobvr == 'V' - ldvr = n - elseif jobvr == 'N' - ldvr = 1 - else - throw(ArgumentError(lazy"jobvr must be 'V' or 'N', but $jobvr was passed")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - alpha = similar(A, $elty, n) - beta = similar(A, $elty, n) - vl = similar(A, $elty, ldvl, n) - vr = similar(A, $elty, ldvr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 8n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, - Ref{BlasInt}, Clong, Clong), - jobvl, jobvr, n, A, - lda, B, ldb, alpha, - beta, vl, ldvl, vr, - ldvr, work, lwork, rwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - end - end - alpha, beta, vl, vr - end - end -end - -""" - geevx!(balanc, jobvl, jobvr, sense, A) -> (A, w, VL, VR, ilo, ihi, scale, abnrm, rconde, rcondv) - -Finds the eigensystem of `A` with matrix balancing. If `jobvl = N`, the -left eigenvectors of `A` aren't computed. If `jobvr = N`, the right -eigenvectors of `A` aren't computed. If `jobvl = V` or `jobvr = V`, the -corresponding eigenvectors are computed. If `balanc = N`, no balancing is -performed. If `balanc = P`, `A` is permuted but not scaled. If -`balanc = S`, `A` is scaled but not permuted. If `balanc = B`, `A` is -permuted and scaled. If `sense = N`, no reciprocal condition numbers are -computed. If `sense = E`, reciprocal condition numbers are computed for -the eigenvalues only. If `sense = V`, reciprocal condition numbers are -computed for the right eigenvectors only. If `sense = B`, reciprocal -condition numbers are computed for the right eigenvectors and the -eigenvectors. If `sense = E,B`, the right and left eigenvectors must be -computed. -""" -geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix) - -""" - ggev!(jobvl, jobvr, A, B) -> (alpha, beta, vl, vr) - -Finds the generalized eigendecomposition of `A` and `B`. If `jobvl = N`, -the left eigenvectors aren't computed. If `jobvr = N`, the right -eigenvectors aren't computed. If `jobvl = V` or `jobvr = V`, the -corresponding eigenvectors are computed. -""" -ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - -""" - ggev3!(jobvl, jobvr, A, B) -> (alpha, beta, vl, vr) - -Finds the generalized eigendecomposition of `A` and `B` using a blocked -algorithm. If `jobvl = N`, the left eigenvectors aren't computed. If -`jobvr = N`, the right eigenvectors aren't computed. If `jobvl = V` or -`jobvr = V`, the corresponding eigenvectors are computed. This function -requires LAPACK 3.6.0. -""" -ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - -# One step incremental condition estimation of max/min singular values -for (laic1, elty) in - ((:dlaic1_,:Float64), - (:slaic1_,:Float32)) - @eval begin - # SUBROUTINE DLAIC1( JOB, J, X, SEST, W, GAMMA, SESTPR, S, C ) - # - # .. Scalar Arguments .. - # INTEGER J, JOB - # DOUBLE PRECISION C, GAMMA, S, SEST, SESTPR - # .. - # .. Array Arguments .. - # DOUBLE PRECISION W( J ), X( J ) - function laic1!(job::Integer, x::AbstractVector{$elty}, - sest::$elty, w::AbstractVector{$elty}, gamma::$elty) - require_one_based_indexing(x, w) - @chkvalidparam 1 job (1,2) - j = length(x) - if j != length(w) - throw(DimensionMismatch(lazy"vectors must have same length, but length of x is $j and length of w is $(length(w))")) - end - sestpr = Ref{$elty}() - s = Ref{$elty}() - c = Ref{$elty}() - ccall((@blasfunc($laic1), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, - Ptr{$elty}, Ref{$elty}, Ref{$elty}, Ref{$elty}, - Ref{$elty}), - job, j, x, sest, - w, gamma, sestpr, s, - c) - sestpr[], s[], c[] - end - end -end -for (laic1, elty, relty) in - ((:zlaic1_,:ComplexF64,:Float64), - (:claic1_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZLAIC1( JOB, J, X, SEST, W, GAMMA, SESTPR, S, C ) - # - # .. Scalar Arguments .. - # INTEGER J, JOB - # DOUBLE PRECISION SEST, SESTPR - # COMPLEX*16 C, GAMMA, S - # .. - # .. Array Arguments .. - # COMPLEX*16 W( J ), X( J ) - function laic1!(job::Integer, x::AbstractVector{$elty}, - sest::$relty, w::AbstractVector{$elty}, gamma::$elty) - require_one_based_indexing(x, w) - @chkvalidparam 1 job (1,2) - j = length(x) - if j != length(w) - throw(DimensionMismatch(lazy"vectors must have same length, but length of x is $j and length of w is $(length(w))")) - end - sestpr = Ref{$relty}() - s = Ref{$elty}() - c = Ref{$elty}() - ccall((@blasfunc($laic1), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$relty}, - Ptr{$elty}, Ref{$elty}, Ref{$relty}, Ref{$elty}, - Ref{$elty}), - job, j, x, sest, - w, gamma, sestpr, s, - c) - sestpr[], s[], c[] - end - end -end - -# (GT) General tridiagonal, decomposition, solver and direct solver -for (gtsv, gttrf, gttrs, elty) in - ((:dgtsv_,:dgttrf_,:dgttrs_,:Float64), - (:sgtsv_,:sgttrf_,:sgttrs_,:Float32), - (:zgtsv_,:zgttrf_,:zgttrs_,:ComplexF64), - (:cgtsv_,:cgttrf_,:cgttrs_,:ComplexF32)) - @eval begin - # SUBROUTINE DGTSV( N, NRHS, DL, D, DU, B, LDB, INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, LDB, N, NRHS - # .. Array Arguments .. - # DOUBLE PRECISION B( LDB, * ), D( * ), DL( * ), DU( * ) - function gtsv!(dl::AbstractVector{$elty}, d::AbstractVector{$elty}, du::AbstractVector{$elty}, - B::AbstractVecOrMat{$elty}) - require_one_based_indexing(dl, d, du, B) - chkstride1(B, dl, d, du) - n = length(d) - if !(n >= length(dl) >= n - 1) - throw(DimensionMismatch(lazy"subdiagonal has length $(length(dl)), but should be $n or $(n - 1)")) - end - if !(n >= length(du) >= n - 1) - throw(DimensionMismatch(lazy"superdiagonal has length $(length(du)), but should be $n or $(n - 1)")) - end - if n != size(B,1) - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)), but should have $n")) - end - if n == 0 - return B # Early exit if possible - end - info = Ref{BlasInt}() - ccall((@blasfunc($gtsv), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - n, size(B,2), dl, d, du, B, max(1,stride(B,2)), info) - chklapackerror(info[]) - B - end - - # SUBROUTINE DGTTRF( N, DL, D, DU, DU2, IPIV, INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, N - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION D( * ), DL( * ), DU( * ), DU2( * ) - function gttrf!(dl::AbstractVector{$elty}, d::AbstractVector{$elty}, du::AbstractVector{$elty}) - require_one_based_indexing(dl, d, du) - chkstride1(dl,d,du) - n = length(d) - if length(dl) != n - 1 - throw(DimensionMismatch(lazy"subdiagonal has length $(length(dl)), but should be $(n - 1)")) - end - if length(du) != n - 1 - throw(DimensionMismatch(lazy"superdiagonal has length $(length(du)), but should be $(n - 1)")) - end - du2 = similar(d, $elty, n-2) - ipiv = similar(d, BlasInt, n) - info = Ref{BlasInt}() - ccall((@blasfunc($gttrf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ref{BlasInt}), - n, dl, d, du, du2, ipiv, info) - chklapackerror(info[]) - dl, d, du, du2, ipiv - end - - # SUBROUTINE DGTTRS( TRANS, N, NRHS, DL, D, DU, DU2, IPIV, B, LDB, INFO ) - # .. Scalar Arguments .. - # CHARACTER TRANS - # INTEGER INFO, LDB, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION B( LDB, * ), D( * ), DL( * ), DU( * ), DU2( * ) - function gttrs!(trans::AbstractChar, dl::AbstractVector{$elty}, d::AbstractVector{$elty}, - du::AbstractVector{$elty}, du2::AbstractVector{$elty}, ipiv::AbstractVector{BlasInt}, - B::AbstractVecOrMat{$elty}) - require_one_based_indexing(dl, d, du, du2, ipiv, B) - chktrans(trans) - chkstride1(B, ipiv, dl, d, du, du2) - n = length(d) - if length(dl) != n - 1 - throw(DimensionMismatch(lazy"subdiagonal has length $(length(dl)), but should be $(n - 1)")) - end - if length(du) != n - 1 - throw(DimensionMismatch(lazy"superdiagonal has length $(length(du)), but should be $(n - 1)")) - end - if n != size(B,1) - throw(DimensionMismatch(lazy"B has leading dimension $(size(B,1)), but should have $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($gttrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - trans, n, size(B,2), dl, d, du, du2, ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -""" - gtsv!(dl, d, du, B) - -Solves the equation `A * X = B` where `A` is a tridiagonal matrix with -`dl` on the subdiagonal, `d` on the diagonal, and `du` on the -superdiagonal. - -Overwrites `B` with the solution `X` and returns it. -""" -gtsv!(dl::AbstractVector, d::AbstractVector, du::AbstractVector, B::AbstractVecOrMat) - -""" - gttrf!(dl, d, du) -> (dl, d, du, du2, ipiv) - -Finds the `LU` factorization of a tridiagonal matrix with `dl` on the -subdiagonal, `d` on the diagonal, and `du` on the superdiagonal. - -Modifies `dl`, `d`, and `du` in-place and returns them and the second -superdiagonal `du2` and the pivoting vector `ipiv`. -""" -gttrf!(dl::AbstractVector, d::AbstractVector, du::AbstractVector) - -""" - gttrs!(trans, dl, d, du, du2, ipiv, B) - -Solves the equation `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), -or `adjoint(A) * X = B` (`trans = C`) using the `LU` factorization computed by -`gttrf!`. `B` is overwritten with the solution `X`. -""" -gttrs!(trans::AbstractChar, dl::AbstractVector, d::AbstractVector, du::AbstractVector, du2::AbstractVector, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) - -## (OR) orthogonal (or UN, unitary) matrices, extractors and multiplication -for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in - ((:dorglq_,:dorgqr_,:dorgql_,:dorgrq_,:dormlq_,:dormqr_,:dormql_,:dormrq_,:dgemqrt_,:Float64), - (:sorglq_,:sorgqr_,:sorgql_,:sorgrq_,:sormlq_,:sormqr_,:sormql_,:sormrq_,:sgemqrt_,:Float32), - (:zunglq_,:zungqr_,:zungql_,:zungrq_,:zunmlq_,:zunmqr_,:zunmql_,:zunmrq_,:zgemqrt_,:ComplexF64), - (:cunglq_,:cungqr_,:cungql_,:cungrq_,:cunmlq_,:cunmqr_,:cunmql_,:cunmrq_,:cgemqrt_,:ComplexF32)) - @eval begin - # SUBROUTINE DORGLQ( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, K, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orglq!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - n = size(A, 2) - m = min(n, size(A, 1)) - if k > m - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= m = $m")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orglq), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - if m < size(A,1) - A[1:m,:] - else - A - end - end - - # SUBROUTINE DORGQR( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, K, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orgqr!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m = size(A, 1) - n = min(m, size(A, 2)) - if k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orgqr), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, k, A, - max(1,stride(A,2)), tau, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - if n < size(A,2) - A[:,1:n] - else - A - end - end - - # SUBROUTINE DORGQL( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, K, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orgql!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m = size(A, 1) - n = min(m, size(A, 2)) - if k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orgql), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, k, A, - max(1,stride(A,2)), tau, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - if n < size(A,2) - A[:,1:n] - else - A - end - end - - # SUBROUTINE DORGRQ( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # INTEGER INFO, K, LDA, LWORK, M, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orgrq!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) - require_one_based_indexing(A, tau) - chkstride1(A,tau) - m, n = size(A) - if n < m - throw(DimensionMismatch(lazy"input matrix A has dimensions ($m,$n), but cannot have fewer columns than rows")) - end - if k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orgrq), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - m, n, k, A, - max(1,stride(A,2)), tau, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A - end - - # SUBROUTINE DORMLQ( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, - # WORK, LWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER SIDE, TRANS - # INTEGER INFO, K, LDA, LDC, LWORK, M, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) - function ormlq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, tau, C) - chktrans(trans) - chkside(side) - chkstride1(A, C, tau) - m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) - nA = size(A, 2) - k = length(tau) - if side == 'L' && m != nA - throw(DimensionMismatch(lazy"for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $nA")) - end - if side == 'R' && n != nA - throw(DimensionMismatch(lazy"for a right-sided multiplication, the second dimension of C, $n, must equal the second dimension of A, $nA")) - end - if side == 'L' && k > m - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= m = $m")) - end - if side == 'R' && k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormlq), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - side, trans, m, n, k, A, max(1,stride(A,2)), tau, - C, max(1,stride(C,2)), work, lwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - - # SUBROUTINE DORMQR( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, - # WORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER SIDE, TRANS - # INTEGER INFO, K, LDA, LDC, M, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) - function ormqr!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, tau, C) - chktrans(trans) - chkside(side) - chkstride1(A, C, tau) - m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) - mA = size(A, 1) - k = length(tau) - if side == 'L' && m != mA - throw(DimensionMismatch(lazy"for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $mA")) - end - if side == 'R' && n != mA - throw(DimensionMismatch(lazy"for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $mA")) - end - if side == 'L' && k > m - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= m = $m")) - end - if side == 'R' && k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormqr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - side, trans, m, n, - k, A, max(1,stride(A,2)), tau, - C, max(1, stride(C,2)), work, lwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - - # SUBROUTINE DORMQL( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, - # WORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER SIDE, TRANS - # INTEGER INFO, K, LDA, LDC, M, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) - function ormql!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, tau, C) - chktrans(trans) - chkside(side) - chkstride1(A, C, tau) - m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) - mA = size(A, 1) - k = length(tau) - if side == 'L' && m != mA - throw(DimensionMismatch(lazy"for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $mA")) - end - if side == 'R' && n != mA - throw(DimensionMismatch(lazy"for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $mA")) - end - if side == 'L' && k > m - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= m = $m")) - end - if side == 'R' && k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormql), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - side, trans, m, n, - k, A, max(1,stride(A,2)), tau, - C, max(1, stride(C,2)), work, lwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - - # SUBROUTINE DORMRQ( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, - # WORK, LWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER SIDE, TRANS - # INTEGER INFO, K, LDA, LDC, LWORK, M, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) - function ormrq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, tau, C) - chktrans(trans) - chkside(side) - chkstride1(A, C, tau) - m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) - nA = size(A, 2) - k = length(tau) - if side == 'L' && m != nA - throw(DimensionMismatch(lazy"for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $nA")) - end - if side == 'R' && n != nA - throw(DimensionMismatch(lazy"for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $nA")) - end - if side == 'L' && k > m - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= m = $m")) - end - if side == 'R' && k > n - throw(DimensionMismatch(lazy"invalid number of reflectors: k = $k should be <= n = $n")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormrq), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - side, trans, m, n, k, A, max(1,stride(A,2)), tau, - C, max(1,stride(C,2)), work, lwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - - function gemqrt!(side::AbstractChar, trans::AbstractChar, V::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, C::AbstractVecOrMat{$elty}) - require_one_based_indexing(V, T, C) - chktrans(trans) - chkside(side) - chkstride1(V, T, C) - m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) - nb, k = size(T) - if k == 0 - return C - end - if side == 'L' - if !(0 <= k <= m) - throw(DimensionMismatch(lazy"wrong value for k = $k: must be between 0 and $m")) - end - if m != size(V,1) - throw(DimensionMismatch(lazy"first dimensions of C, $m, and V, $(size(V,1)) must match")) - end - ldv = stride(V,2) - if ldv < max(1, m) - throw(DimensionMismatch(lazy"Q and C don't fit! The stride of V, $ldv, is too small")) - end - wss = n*k - elseif side == 'R' - if !(0 <= k <= n) - throw(DimensionMismatch(lazy"wrong value for k = $k: must be between 0 and $n")) - end - if n != size(V,1) - throw(DimensionMismatch(lazy"second dimension of C, $n, and first dimension of V, $(size(V,1)) must match")) - end - ldv = stride(V,2) - if ldv < max(1, n) - throw(DimensionMismatch(lazy"Q and C don't fit! The stride of V, $ldv, is too small")) - end - wss = m*k - end - if !(1 <= nb <= k) - throw(DimensionMismatch(lazy"wrong value for nb = $nb, which must be between 1 and $k")) - end - ldc = stride(C, 2) - work = Vector{$elty}(undef, wss) - info = Ref{BlasInt}() - ccall((@blasfunc($gemqrt), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - side, trans, m, n, - k, nb, V, ldv, - T, max(1,stride(T,2)), C, max(1,ldc), - work, info, 1, 1) - chklapackerror(info[]) - return C - end - end -end - -""" - orglq!(A, tau, k = length(tau)) - -Explicitly finds the matrix `Q` of a `LQ` factorization after calling -`gelqf!` on `A`. Uses the output of `gelqf!`. `A` is overwritten by `Q`. -""" -orglq!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) - -""" - orgqr!(A, tau, k = length(tau)) - -Explicitly finds the matrix `Q` of a `QR` factorization after calling -`geqrf!` on `A`. Uses the output of `geqrf!`. `A` is overwritten by `Q`. -""" -orgqr!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) - -""" - orgql!(A, tau, k = length(tau)) - -Explicitly finds the matrix `Q` of a `QL` factorization after calling -`geqlf!` on `A`. Uses the output of `geqlf!`. `A` is overwritten by `Q`. -""" -orgql!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) - -""" - orgrq!(A, tau, k = length(tau)) - -Explicitly finds the matrix `Q` of a `RQ` factorization after calling -`gerqf!` on `A`. Uses the output of `gerqf!`. `A` is overwritten by `Q`. -""" -orgrq!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) - -""" - ormlq!(side, trans, A, tau, C) - -Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` -(`trans = C`) for `side = L` or the equivalent right-sided multiplication -for `side = R` using `Q` from a `LQ` factorization of `A` computed using -`gelqf!`. `C` is overwritten. -""" -ormlq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) - -""" - ormqr!(side, trans, A, tau, C) - -Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` -(`trans = C`) for `side = L` or the equivalent right-sided multiplication -for `side = R` using `Q` from a `QR` factorization of `A` computed using -`geqrf!`. `C` is overwritten. -""" -ormqr!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) - -""" - ormql!(side, trans, A, tau, C) - -Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` -(`trans = C`) for `side = L` or the equivalent right-sided multiplication -for `side = R` using `Q` from a `QL` factorization of `A` computed using -`geqlf!`. `C` is overwritten. -""" -ormql!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) - -""" - ormrq!(side, trans, A, tau, C) - -Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` -(`trans = C`) for `side = L` or the equivalent right-sided multiplication -for `side = R` using `Q` from a `RQ` factorization of `A` computed using -`gerqf!`. `C` is overwritten. -""" -ormrq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) - -""" - gemqrt!(side, trans, V, T, C) - -Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` -(`trans = C`) for `side = L` or the equivalent right-sided multiplication -for `side = R` using `Q` from a `QR` factorization of `A` computed using -`geqrt!`. `C` is overwritten. -""" -gemqrt!(side::AbstractChar, trans::AbstractChar, V::AbstractMatrix, T::AbstractMatrix, C::AbstractVecOrMat) - -# (PO) positive-definite symmetric matrices, -for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in - ((:dposv_,:dpotrf_,:dpotri_,:dpotrs_,:dpstrf_,:Float64,:Float64), - (:sposv_,:spotrf_,:spotri_,:spotrs_,:spstrf_,:Float32,:Float32), - (:zposv_,:zpotrf_,:zpotri_,:zpotrs_,:zpstrf_,:ComplexF64,:Float64), - (:cposv_,:cpotrf_,:cpotri_,:cpotrs_,:cpstrf_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DPOSV( UPLO, N, NRHS, A, LDA, B, LDB, INFO ) - #* .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function posv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - n = checksquare(A) - chkuplo(uplo) - if size(B,1) != n - throw(DimensionMismatch(lazy"first dimension of B, $(size(B,1)), and size of A, ($n,$n), must match!")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($posv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info, 1) - chkargsok(info[]) - chkposdef(info[]) - A, B - end - - # SUBROUTINE DPOTRF( UPLO, N, A, LDA, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ) - function potrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - checksquare(A) - chkuplo(uplo) - lda = max(1,stride(A,2)) - if lda == 0 - return A, 0 - end - info = Ref{BlasInt}() - ccall((@blasfunc($potrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, size(A,1), A, lda, info, 1) - chkargsok(info[]) - #info[] > 0 means the leading minor of order info[] is not positive definite - #ordinarily, throw Exception here, but return error code here - #this simplifies isposdef! and factorize - return A, info[] # info stored in Cholesky - end - - # SUBROUTINE DPOTRI( UPLO, N, A, LDA, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ) - function potri!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - chkuplo(uplo) - info = Ref{BlasInt}() - ccall((@blasfunc($potri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, size(A,1), A, max(1,stride(A,2)), info, 1) - chkargsok(info[]) - chknonsingular(info[]) - A - end - - # SUBROUTINE DPOTRS( UPLO, N, NRHS, A, LDA, B, LDB, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function potrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A, B) - n = checksquare(A) - chkuplo(uplo) - nrhs = size(B,2) - if size(B,1) != n - throw(DimensionMismatch(lazy"first dimension of B, $(size(B,1)), and size of A, ($n,$n), must match!")) - end - lda = max(1,stride(A,2)) - if lda == 0 || nrhs == 0 - return B - end - ldb = max(1,stride(B,2)) - info = Ref{BlasInt}() - ccall((@blasfunc($potrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, nrhs, A, - lda, B, ldb, info, 1) - chklapackerror(info[]) - return B - end - - # SUBROUTINE DPSTRF( UPLO, N, A, LDA, PIV, RANK, TOL, WORK, INFO ) - # .. Scalar Arguments .. - # DOUBLE PRECISION TOL - # INTEGER INFO, LDA, N, RANK - # CHARACTER UPLO - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), WORK( 2*N ) - # INTEGER PIV( N ) - function pstrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}, tol::Real) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - piv = similar(A, BlasInt, n) - rank = Vector{BlasInt}(undef, 1) - work = Vector{$rtyp}(undef, 2n) - info = Ref{BlasInt}() - ccall((@blasfunc($pstrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), piv, rank, tol, work, info, 1) - chkargsok(info[]) - A, piv, rank[1], info[] #Stored in CholeskyPivoted - end - end -end - -""" - posv!(uplo, A, B) -> (A, B) - -Finds the solution to `A * X = B` where `A` is a symmetric or Hermitian -positive definite matrix. If `uplo = U` the upper Cholesky decomposition -of `A` is computed. If `uplo = L` the lower Cholesky decomposition of `A` -is computed. `A` is overwritten by its Cholesky decomposition. `B` is -overwritten with the solution `X`. -""" -posv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -""" - potrf!(uplo, A) - -Computes the Cholesky (upper if `uplo = U`, lower if `uplo = L`) -decomposition of positive-definite matrix `A`. `A` is overwritten and -returned with an info code. -""" -potrf!(uplo::AbstractChar, A::AbstractMatrix) - -""" - potri!(uplo, A) - -Computes the inverse of positive-definite matrix `A` after calling -`potrf!` to find its (upper if `uplo = U`, lower if `uplo = L`) Cholesky -decomposition. - -`A` is overwritten by its inverse and returned. -""" -potri!(uplo::AbstractChar, A::AbstractMatrix) - -""" - potrs!(uplo, A, B) - -Finds the solution to `A * X = B` where `A` is a symmetric or Hermitian -positive definite matrix whose Cholesky decomposition was computed by -`potrf!`. If `uplo = U` the upper Cholesky decomposition of `A` was -computed. If `uplo = L` the lower Cholesky decomposition of `A` was -computed. `B` is overwritten with the solution `X`. -""" -potrs!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -""" - pstrf!(uplo, A, tol) -> (A, piv, rank, info) - -Computes the (upper if `uplo = U`, lower if `uplo = L`) pivoted Cholesky -decomposition of positive-definite matrix `A` with a user-set tolerance -`tol`. `A` is overwritten by its Cholesky decomposition. - -Returns `A`, the pivots `piv`, the rank of `A`, and an `info` code. If `info = 0`, -the factorization succeeded. If `info = i > 0 `, then `A` is indefinite or -rank-deficient. -""" -pstrf!(uplo::AbstractChar, A::AbstractMatrix, tol::Real) - -# (PT) positive-definite, symmetric, tri-diagonal matrices -# Direct solvers for general tridiagonal and symmetric positive-definite tridiagonal -for (ptsv, pttrf, elty, relty) in - ((:dptsv_,:dpttrf_,:Float64,:Float64), - (:sptsv_,:spttrf_,:Float32,:Float32), - (:zptsv_,:zpttrf_,:ComplexF64,:Float64), - (:cptsv_,:cpttrf_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DPTSV( N, NRHS, D, E, B, LDB, INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, LDB, N, NRHS - # .. Array Arguments .. - # DOUBLE PRECISION B( LDB, * ), D( * ), E( * ) - function ptsv!(D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(D, E, B) - chkstride1(B, D, E) - n = length(D) - if length(E) != n - 1 - throw(DimensionMismatch(lazy"E has length $(length(E)), but needs $(n - 1)")) - end - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)) but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($ptsv), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - n, size(B,2), D, E, B, max(1,stride(B,2)), info) - chklapackerror(info[]) - B - end - - # SUBROUTINE DPTTRF( N, D, E, INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, N - # .. Array Arguments .. - # DOUBLE PRECISION D( * ), E( * ) - function pttrf!(D::AbstractVector{$relty}, E::AbstractVector{$elty}) - require_one_based_indexing(D, E) - chkstride1(D, E) - n = length(D) - if length(E) != n - 1 - throw(DimensionMismatch(lazy"E has length $(length(E)), but needs $(n - 1)")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($pttrf), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), - n, D, E, info) - chklapackerror(info[]) - D, E - end - end -end - -""" - ptsv!(D, E, B) - -Solves `A * X = B` for positive-definite tridiagonal `A`. `D` is the -diagonal of `A` and `E` is the off-diagonal. `B` is overwritten with the -solution `X` and returned. -""" -ptsv!(D::AbstractVector, E::AbstractVector, B::AbstractVecOrMat) - -""" - pttrf!(D, E) - -Computes the LDLt factorization of a positive-definite tridiagonal matrix -with `D` as diagonal and `E` as off-diagonal. `D` and `E` are overwritten -and returned. -""" -pttrf!(D::AbstractVector, E::AbstractVector) - -for (pttrs, elty, relty) in - ((:dpttrs_,:Float64,:Float64), - (:spttrs_,:Float32,:Float32)) - @eval begin - # SUBROUTINE DPTTRS( N, NRHS, D, E, B, LDB, INFO ) - # .. Scalar Arguments .. - # INTEGER INFO, LDB, N, NRHS - # .. Array Arguments .. - # DOUBLE PRECISION B( LDB, * ), D( * ), E( * ) - function pttrs!(D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(D, E, B) - chkstride1(B, D, E) - n = length(D) - if length(E) != n - 1 - throw(DimensionMismatch(lazy"E has length $(length(E)), but needs $(n - 1)")) - end - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)) but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($pttrs), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - n, size(B,2), D, E, B, max(1,stride(B,2)), info) - chklapackerror(info[]) - B - end - end -end - -for (pttrs, elty, relty) in - ((:zpttrs_,:ComplexF64,:Float64), - (:cpttrs_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZPTTRS( UPLO, N, NRHS, D, E, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION D( * ) - # COMPLEX*16 B( LDB, * ), E( * ) - function pttrs!(uplo::AbstractChar, D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(D, E, B) - chkstride1(B, D, E) - chkuplo(uplo) - n = length(D) - if length(E) != n - 1 - throw(DimensionMismatch(lazy"E has length $(length(E)), but needs $(n - 1)")) - end - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)) but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($pttrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), D, E, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -""" - pttrs!(D, E, B) - -Solves `A * X = B` for positive-definite tridiagonal `A` with diagonal -`D` and off-diagonal `E` after computing `A`'s LDLt factorization using -`pttrf!`. `B` is overwritten with the solution `X`. -""" -pttrs!(D::AbstractVector, E::AbstractVector, B::AbstractVecOrMat) - -## (TR) triangular matrices: solver and inverse -for (trtri, trtrs, elty) in - ((:dtrtri_,:dtrtrs_,:Float64), - (:strtri_,:strtrs_,:Float32), - (:ztrtri_,:ztrtrs_,:ComplexF64), - (:ctrtri_,:ctrtrs_,:ComplexF32)) - @eval begin - # SUBROUTINE DTRTRI( UPLO, DIAG, N, A, LDA, INFO ) - #* .. Scalar Arguments .. - # CHARACTER DIAG, UPLO - # INTEGER INFO, LDA, N - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ) - function trtri!(uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - chkdiag(diag) - lda = max(1,stride(A, 2)) - info = Ref{BlasInt}() - ccall((@blasfunc($trtri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - uplo, diag, n, A, lda, info, 1, 1) - chklapackerror(info[]) - A - end - - # SUBROUTINE DTRTRS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER DIAG, TRANS, UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function trtrs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, - A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chktrans(trans) - chkdiag(diag) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)) but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($trtrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Clong, Clong, Clong), - uplo, trans, diag, n, size(B,2), A, max(1,stride(A,2)), - B, max(1,stride(B,2)), info, - 1, 1, 1) - chklapackerror(info[], trtrs!) - B - end - end -end -chklapackerror_positive(ret, ::typeof(trtrs!)) = chknonsingular(ret) - -""" - trtri!(uplo, diag, A) - -Finds the inverse of (upper if `uplo = U`, lower if `uplo = L`) -triangular matrix `A`. If `diag = N`, `A` has non-unit diagonal elements. -If `diag = U`, all diagonal elements of `A` are one. `A` is overwritten -with its inverse. -""" -trtri!(uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix) - -""" - trtrs!(uplo, trans, diag, A, B) - -Solves `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), or -`adjoint(A) * X = B` (`trans = C`) for (upper if `uplo = U`, lower if `uplo = L`) -triangular matrix `A`. If `diag = N`, `A` has non-unit diagonal elements. -If `diag = U`, all diagonal elements of `A` are one. `B` is overwritten -with the solution `X`. -""" -trtrs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -#Eigenvector computation and condition number estimation -for (trcon, trevc, trrfs, elty) in - ((:dtrcon_,:dtrevc_,:dtrrfs_,:Float64), - (:strcon_,:strevc_,:strrfs_,:Float32)) - @eval begin - # SUBROUTINE DTRCON( NORM, UPLO, DIAG, N, A, LDA, RCOND, WORK, - # IWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER DIAG, NORM, UPLO - # INTEGER INFO, LDA, N - # DOUBLE PRECISION RCOND - # .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - chkdiag(diag) - n = checksquare(A) - chkuplo(uplo) - @chkvalidparam 1 norm ('O', '1', 'I') - rcond = Ref{$elty}() - work = Vector{$elty}(undef, 3n) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trcon), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong, Clong), - norm, uplo, diag, n, - A, max(1,stride(A,2)), rcond, work, iwork, info, - 1, 1, 1) - chklapackerror(info[]) - rcond[] - end - - # SUBROUTINE DTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, - # LDVR, MM, M, WORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER HOWMNY, SIDE - # INTEGER INFO, LDT, LDVL, LDVR, M, MM, N - # .. - # .. Array Arguments .. - # LOGICAL SELECT( * ) - # DOUBLE PRECISION T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), - #$ WORK( * ) - Base.@constprop :aggressive function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, - VL::AbstractMatrix{$elty} = similar(T), - VR::AbstractMatrix{$elty} = similar(T)) - require_one_based_indexing(select, T, VL, VR) - # Extract - if side ∉ ('L','R','B') - throw(ArgumentError(lazy"side argument must be 'L' (left eigenvectors), 'R' (right eigenvectors), or 'B' (both), got $side")) - end - @chkvalidparam 2 howmny ('A', 'B', 'S') - n, mm = checksquare(T), size(VL, 2) - ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) - - # Check - chkstride1(T, select, VL, VR) - - # Allocate - m = Ref{BlasInt}() - work = Vector{$elty}(undef, 3n) - info = Ref{BlasInt}() - - ccall((@blasfunc($trevc), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - side, howmny, select, n, - T, ldt, VL, ldvl, - VR, ldvr, mm, m, - work, info, 1, 1) - chklapackerror(info[]) - - #Decide what exactly to return - if howmny == 'S' #compute selected eigenvectors - if side == 'L' #left eigenvectors only - return select, VL[:,1:m[]] - elseif side == 'R' #right eigenvectors only - return select, VR[:,1:m[]] - else #side == 'B' #both eigenvectors - return select, VL[:,1:m[]], VR[:,1:m[]] - end - else #compute all eigenvectors - if side == 'L' #left eigenvectors only - return VL[:,1:m[]] - elseif side == 'R' #right eigenvectors only - return VR[:,1:m[]] - else #side == 'B' #both eigenvectors - return VL[:,1:m[]], VR[:,1:m[]] - end - end - end - - # SUBROUTINE DTRRFS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, X, - # LDX, FERR, BERR, WORK, IWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER DIAG, TRANS, UPLO - # INTEGER INFO, LDA, LDB, LDX, N, NRHS - # .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), BERR( * ), FERR( * ), - #$ WORK( * ), X( LDX, * ) - function trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, - A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, X::AbstractVecOrMat{$elty}, - Ferr::AbstractVector{$elty} = similar(B, $elty, size(B,2)), - Berr::AbstractVector{$elty} = similar(B, $elty, size(B,2))) - require_one_based_indexing(A, B, X, Ferr, Berr) - chkstride1(A, B, X, Ferr, Berr) - chktrans(trans) - chkuplo(uplo) - chkdiag(diag) - n = size(A,2) - nrhs = size(B,2) - if nrhs != size(X,2) - throw(DimensionMismatch(lazy"second dimensions of B, $nrhs, and X, $(size(X,2)), must match")) - end - work = Vector{$elty}(undef, 3n) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), - uplo, trans, diag, n, - nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), - Ferr, Berr, work, iwork, info, 1, 1, 1) - chklapackerror(info[]) - Ferr, Berr - end - end -end - -for (trcon, trevc, trrfs, elty, relty) in - ((:ztrcon_,:ztrevc_,:ztrrfs_,:ComplexF64,:Float64), - (:ctrcon_,:ctrevc_,:ctrrfs_,:ComplexF32, :Float32)) - @eval begin - # SUBROUTINE ZTRCON( NORM, UPLO, DIAG, N, A, LDA, RCOND, WORK, - # RWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER DIAG, NORM, UPLO - # INTEGER INFO, LDA, N - # DOUBLE PRECISION RCOND - # .. Array Arguments .. - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - @chkvalidparam 1 norm ('O', '1', 'I') - chkuplo(uplo) - chkdiag(diag) - rcond = Ref{$relty}(1) - work = Vector{$elty}(undef, 2n) - rwork = Vector{$relty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trcon), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, - Clong, Clong, Clong), - norm, uplo, diag, n, - A, max(1,stride(A,2)), rcond, work, rwork, info, - 1, 1, 1) - chklapackerror(info[]) - rcond[] - end - - # SUBROUTINE ZTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, - # LDVR, MM, M, WORK, RWORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER HOWMNY, SIDE - # INTEGER INFO, LDT, LDVL, LDVR, M, MM, N - # .. - # .. Array Arguments .. - # LOGICAL SELECT( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), - #$ WORK( * ) - function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, - VL::AbstractMatrix{$elty} = similar(T), - VR::AbstractMatrix{$elty} = similar(T)) - require_one_based_indexing(select, T, VL, VR) - # Extract - n, mm = checksquare(T), size(VL, 2) - ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) - - # Check - chkstride1(T, select, VL, VR) - if side ∉ ('L','R','B') - throw(ArgumentError(lazy"side argument must be 'L' (left eigenvectors), 'R' (right eigenvectors), or 'B' (both), got $side")) - end - @chkvalidparam 2 howmny ('A', 'B', 'S') - - # Allocate - m = Ref{BlasInt}() - work = Vector{$elty}(undef, 2n) - rwork = Vector{$relty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trevc), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), - side, howmny, select, n, - T, ldt, VL, ldvl, - VR, ldvr, mm, m, - work, rwork, info, 1, 1) - chklapackerror(info[]) - - #Decide what exactly to return - if howmny == 'S' #compute selected eigenvectors - if side == 'L' #left eigenvectors only - return select, VL[:,1:m[]] - elseif side == 'R' #right eigenvectors only - return select, VR[:,1:m[]] - else #side=='B' #both eigenvectors - return select, VL[:,1:m[]], VR[:,1:m[]] - end - else #compute all eigenvectors - if side == 'L' #left eigenvectors only - return VL[:,1:m[]] - elseif side == 'R' #right eigenvectors only - return VR[:,1:m[]] - else #side=='B' #both eigenvectors - return VL[:,1:m[]], VR[:,1:m[]] - end - end - end - - # SUBROUTINE ZTRRFS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, X, - # LDX, FERR, BERR, WORK, IWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER DIAG, TRANS, UPLO - # INTEGER INFO, LDA, LDB, LDX, N, NRHS - # .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), BERR( * ), FERR( * ), - #$ WORK( * ), X( LDX, * ) - function trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, - A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, X::AbstractVecOrMat{$elty}, - Ferr::AbstractVector{$relty} = similar(B, $relty, size(B,2)), - Berr::AbstractVector{$relty} = similar(B, $relty, size(B,2))) - require_one_based_indexing(A, B, X, Ferr, Berr) - chkstride1(A, B, X, Ferr, Berr) - chktrans(trans) - chkuplo(uplo) - chkdiag(diag) - n = size(A,2) - nrhs = size(B,2) - if nrhs != size(X,2) - throw(DimensionMismatch(lazy"second dimensions of B, $nrhs, and X, $(size(X,2)), must match")) - end - work = Vector{$elty}(undef, 2n) - rwork = Vector{$relty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong), - uplo, trans, diag, n, - nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), - Ferr, Berr, work, rwork, info, 1, 1, 1) - chklapackerror(info[]) - Ferr, Berr - end - end -end - -""" - trcon!(norm, uplo, diag, A) - -Finds the reciprocal condition number of (upper if `uplo = U`, lower if -`uplo = L`) triangular matrix `A`. If `diag = N`, `A` has non-unit -diagonal elements. If `diag = U`, all diagonal elements of `A` are one. -If `norm = I`, the condition number is found in the infinity norm. If -`norm = O` or `1`, the condition number is found in the one norm. -""" -trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix) - -""" - trevc!(side, howmny, select, T, VL = similar(T), VR = similar(T)) - -Finds the eigensystem of an upper triangular matrix `T`. If `side = R`, -the right eigenvectors are computed. If `side = L`, the left -eigenvectors are computed. If `side = B`, both sets are computed. If -`howmny = A`, all eigenvectors are found. If `howmny = B`, all -eigenvectors are found and backtransformed using `VL` and `VR`. If -`howmny = S`, only the eigenvectors corresponding to the values in -`select` are computed. -""" -trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix, - VL::AbstractMatrix = similar(T), VR::AbstractMatrix = similar(T)) - -""" - trrfs!(uplo, trans, diag, A, B, X, Ferr, Berr) -> (Ferr, Berr) - -Estimates the error in the solution to `A * X = B` (`trans = N`), -`transpose(A) * X = B` (`trans = T`), `adjoint(A) * X = B` (`trans = C`) for `side = L`, -or the equivalent equations a right-handed `side = R` `X * A` after -computing `X` using `trtrs!`. If `uplo = U`, `A` is upper triangular. -If `uplo = L`, `A` is lower triangular. If `diag = N`, `A` has non-unit -diagonal elements. If `diag = U`, all diagonal elements of `A` are one. -`Ferr` and `Berr` are optional inputs. `Ferr` is the forward error and -`Berr` is the backward error, each component-wise. -""" -trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat, - X::AbstractVecOrMat, Ferr::AbstractVector, Berr::AbstractVector) - -## (ST) Symmetric tridiagonal - eigendecomposition -for (stev, stebz, stegr, stein, elty) in - ((:dstev_,:dstebz_,:dstegr_,:dstein_,:Float64), - (:sstev_,:sstebz_,:sstegr_,:sstein_,:Float32) -# , (:zstev_,:ComplexF64) Need to rewrite for ZHEEV, rwork, etc. -# , (:cstev_,:ComplexF32) - ) - @eval begin - function stev!(job::AbstractChar, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}) - require_one_based_indexing(dv, ev) - @chkvalidparam 1 job ('N', 'V') - chkstride1(dv, ev) - n = length(dv) - if length(ev) != n - 1 && length(ev) != n - throw(DimensionMismatch(lazy"ev has length $(length(ev)) but needs one less than or equal to dv's length, $n)")) - end - Zmat = similar(dv, $elty, (n, job != 'N' ? n : 0)) - work = Vector{$elty}(undef, max(1, 2n-2)) - info = Ref{BlasInt}() - ccall((@blasfunc($stev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - job, n, dv, ev, Zmat, n, work, info, 1) - chklapackerror(info[]) - dv, Zmat - end - - #* DSTEBZ computes the eigenvalues of a symmetric tridiagonal - #* matrix T. The user may ask for all eigenvalues, all eigenvalues - #* in the half-open interval (VL, VU], or the IL-th through IU-th - #* eigenvalues. - function stebz!(range::AbstractChar, order::AbstractChar, vl::$elty, vu::$elty, il::Integer, iu::Integer, abstol::Real, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}) - require_one_based_indexing(dv, ev) - @chkvalidparam 1 range ('A', 'V', 'I') - @chkvalidparam 2 order ('B', 'E') - chkstride1(dv, ev) - n = length(dv) - if length(ev) != n - 1 - throw(DimensionMismatch(lazy"ev has length $(length(ev)) but needs one less than dv's length, $n)")) - end - m = Ref{BlasInt}() - nsplit = Vector{BlasInt}(undef, 1) - w = similar(dv, $elty, n) - tmp = 0.0 - iblock = similar(dv, BlasInt,n) - isplit = similar(dv, BlasInt,n) - work = Vector{$elty}(undef, 4*n) - iwork = Vector{BlasInt}(undef, 3*n) - info = Ref{BlasInt}() - ccall((@blasfunc($stebz), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, - Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong), - range, order, n, vl, - vu, il, iu, abstol, - dv, ev, m, nsplit, - w, iblock, isplit, work, - iwork, info, 1, 1) - chklapackerror(info[]) - w[1:m[]], iblock[1:m[]], isplit[1:nsplit[1]] - end - - function stegr!(jobz::AbstractChar, range::AbstractChar, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}, vl::Real, vu::Real, il::Integer, iu::Integer) - require_one_based_indexing(dv, ev) - @chkvalidparam 1 jobz ('N', 'V') - @chkvalidparam 2 range ('A', 'V', 'I') - chkstride1(dv, ev) - n = length(dv) - ne = length(ev) - if ne == n - 1 - eev = [ev; zero($elty)] - elseif ne == n - eev = copy(ev) - eev[n] = zero($elty) - else - throw(DimensionMismatch(lazy"ev has length $ne but needs one less than or equal to dv's length, $n)")) - end - - abstol = Vector{$elty}(undef, 1) - m = Ref{BlasInt}() - w = similar(dv, $elty, n) - ldz = jobz == 'N' ? 1 : n - Z = similar(dv, $elty, ldz, range == 'I' ? iu-il+1 : n) - isuppz = similar(dv, BlasInt, 2*size(Z, 2)) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($stegr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - jobz, range, n, dv, - eev, vl, vu, il, - iu, abstol, m, w, - Z, ldz, isuppz, work, - lwork, iwork, liwork, info, - 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - m[] == length(w) ? w : w[1:m[]], m[] == size(Z, 2) ? Z : Z[:,1:m[]] - end - - function stein!(dv::AbstractVector{$elty}, ev_in::AbstractVector{$elty}, w_in::AbstractVector{$elty}, iblock_in::AbstractVector{BlasInt}, isplit_in::AbstractVector{BlasInt}) - require_one_based_indexing(dv, ev_in, w_in, iblock_in, isplit_in) - chkstride1(dv, ev_in, w_in, iblock_in, isplit_in) - n = length(dv) - ne = length(ev_in) - if ne == n - 1 - ev = [ev_in; zero($elty)] - elseif ne == n - ev = copy(ev_in) - ev[n] = zero($elty) - else - throw(DimensionMismatch(lazy"ev_in has length $ne but needs one less than or equal to dv's length, $n)")) - end - ldz = n #Leading dimension - #Number of eigenvalues to find - if !(1 <= length(w_in) <= n) - throw(DimensionMismatch(lazy"w_in has length $(length(w_in)), but needs to be between 1 and $n")) - end - m = length(w_in) - #If iblock and isplit are invalid input, assume worst-case block partitioning, - # i.e. set the block scheme to be the entire matrix - iblock = similar(dv, BlasInt,n) - isplit = similar(dv, BlasInt,n) - w = similar(dv, $elty,n) - if length(iblock_in) < m #Not enough block specifications - iblock[1:m] = fill(BlasInt(1), m) - w[1:m] = sort(w_in) - else - iblock[1:m] = iblock_in - w[1:m] = w_in #Assume user has sorted the eigenvalues properly - end - if length(isplit_in) < 1 #Not enough block specifications - isplit[1] = n - else - isplit[1:length(isplit_in)] = isplit_in - end - z = similar(dv, $elty,(n,m)) - work = Vector{$elty}(undef, 5*n) - iwork = Vector{BlasInt}(undef, n) - ifail = Vector{BlasInt}(undef, m) - info = Ref{BlasInt}() - ccall((@blasfunc($stein), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}), - n, dv, ev, m, w, iblock, isplit, z, ldz, work, iwork, ifail, info) - chklapackerror(info[]) - if any(ifail .!= 0) - # TODO: better error message / type - error("failed to converge eigenvectors:\n$(findall(!iszero, ifail))") - end - z - end - end -end -stegr!(jobz::AbstractChar, dv::AbstractVector, ev::AbstractVector) = stegr!(jobz, 'A', dv, ev, 0.0, 0.0, 0, 0) - -# Allow user to skip specification of iblock and isplit -stein!(dv::AbstractVector, ev::AbstractVector, w_in::AbstractVector) = stein!(dv, ev, w_in, zeros(BlasInt,0), zeros(BlasInt,0)) -# Allow user to specify just one eigenvector to get in stein! -stein!(dv::AbstractVector, ev::AbstractVector, eval::Real) = stein!(dv, ev, [eval], zeros(BlasInt,0), zeros(BlasInt,0)) - -""" - stev!(job, dv, ev) -> (dv, Zmat) - -Computes the eigensystem for a symmetric tridiagonal matrix with `dv` as -diagonal and `ev` as off-diagonal. If `job = N` only the eigenvalues are -found and returned in `dv`. If `job = V` then the eigenvectors are also found -and returned in `Zmat`. -""" -stev!(job::AbstractChar, dv::AbstractVector, ev::AbstractVector) - -""" - stebz!(range, order, vl, vu, il, iu, abstol, dv, ev) -> (dv, iblock, isplit) - -Computes the eigenvalues for a symmetric tridiagonal matrix with `dv` as -diagonal and `ev` as off-diagonal. If `range = A`, all the eigenvalues -are found. If `range = V`, the eigenvalues in the half-open interval -`(vl, vu]` are found. If `range = I`, the eigenvalues with indices between -`il` and `iu` are found. If `order = B`, eigvalues are ordered within a -block. If `order = E`, they are ordered across all the blocks. -`abstol` can be set as a tolerance for convergence. -""" -stebz!(range::AbstractChar, order::AbstractChar, vl, vu, il::Integer, iu::Integer, abstol::Real, dv::AbstractVector, ev::AbstractVector) - -""" - stegr!(jobz, range, dv, ev, vl, vu, il, iu) -> (w, Z) - -Computes the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors -(`jobz = V`) for a symmetric tridiagonal matrix with `dv` as diagonal -and `ev` as off-diagonal. If `range = A`, all the eigenvalues -are found. If `range = V`, the eigenvalues in the half-open interval -`(vl, vu]` are found. If `range = I`, the eigenvalues with indices between -`il` and `iu` are found. The eigenvalues are returned in `w` and the eigenvectors -in `Z`. -""" -stegr!(jobz::AbstractChar, range::AbstractChar, dv::AbstractVector, ev::AbstractVector, vl::Real, vu::Real, il::Integer, iu::Integer) - -""" - stein!(dv, ev_in, w_in, iblock_in, isplit_in) - -Computes the eigenvectors for a symmetric tridiagonal matrix with `dv` -as diagonal and `ev_in` as off-diagonal. `w_in` specifies the input -eigenvalues for which to find corresponding eigenvectors. `iblock_in` -specifies the submatrices corresponding to the eigenvalues in `w_in`. -`isplit_in` specifies the splitting points between the submatrix blocks. -""" -stein!(dv::AbstractVector, ev_in::AbstractVector, w_in::AbstractVector, iblock_in::AbstractVector{BlasInt}, isplit_in::AbstractVector{BlasInt}) - -## (SY) symmetric real matrices - Bunch-Kaufman decomposition, -## solvers (direct and factored) and inverse. -for (syconv, sysv, sytrf, sytri, sytrs, elty) in - ((:dsyconv_,:dsysv_,:dsytrf_,:dsytri_,:dsytrs_,:Float64), - (:ssyconv_,:ssysv_,:ssytrf_,:ssytri_,:ssytrs_,:Float32)) - @eval begin - # SUBROUTINE DSYCONV( UPLO, WAY, N, A, LDA, IPIV, WORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO, WAY - # INTEGER INFO, LDA, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function syconv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($syconv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) - chklapackerror(info[]) - A, work - end - - # SUBROUTINE DSYSV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # LWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) - function sysv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sysv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE DSYTRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - if n == 0 - return A, ipiv, zero(BlasInt) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - return A, ipiv, info[] - end - - function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkuplo(uplo) - n = checksquare(A) - ipiv = similar(A, BlasInt, n) - sytrf!(uplo, A, ipiv) - end - - # SUBROUTINE DSYTRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) -# function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) -# chkstride1(A) -# n = checksquare(A) -# chkuplo(uplo) -# work = Vector{$elty}(undef, 1) -# lwork = BlasInt(-1) -# info = Ref{BlasInt}() -# for i in 1:2 -# ccall((@blasfunc($sytri), libblastrampoline), Cvoid, -# (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), -# &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) -# @assertargsok -# chknonsingular(info[]) -# if lwork < 0 -# lwork = BlasInt(real(work[1])) -# work = Vector{$elty}(undef, lwork) -# end -# end -# A -# end - - # SUBROUTINE DSYTRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($sytri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - A - end - - # SUBROUTINE DSYTRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function sytrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkstride1(A,B,ipiv) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -# Rook-pivoting variants of symmetric-matrix algorithms -for (sysv, sytrf, sytri, sytrs, syconvf, elty) in - ((:dsysv_rook_,:dsytrf_rook_,:dsytri_rook_,:dsytrs_rook_,:dsyconvf_rook_,:Float64), - (:ssysv_rook_,:ssytrf_rook_,:ssytri_rook_,:ssytrs_rook_,:ssyconvf_rook_,:Float32)) - @eval begin - # SUBROUTINE DSYSV_ROOK(UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # LWORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) - function sysv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sysv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE DSYTRF_ROOK(UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function sytrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - ipiv = similar(A, BlasInt, n) - if n == 0 - return A, ipiv, zero(BlasInt) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - return A, ipiv, info[] - end - - # SUBROUTINE DSYTRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function sytri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($sytri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - A - end - - # SUBROUTINE DSYTRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - function sytrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkstride1(A,B,ipiv) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - - # SUBROUTINE DSYCONVF_ROOK( UPLO, WAY, N, A, LDA, IPIV, E, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER UPLO, WAY - # INTEGER INFO, LDA, N - # .. - # .. Array Arguments .. - # INTEGER IPIV( * ) - # DOUBLE PRECISION A( LDA, * ), E( * ) - function syconvf_rook!(uplo::AbstractChar, way::AbstractChar, - A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, - e::AbstractVector{$elty} = Vector{$elty}(undef, length(ipiv))) - require_one_based_indexing(A, ipiv, e) - # extract - n = checksquare(A) - lda = max(1, stride(A, 2)) - - # check - chkuplo(uplo) - if way != 'C' && way != 'R' - throw(ArgumentError("way must be C or R")) - end - if length(ipiv) != n - throw(ArgumentError(lazy"length of pivot vector was $(length(ipiv)) but should have been $n")) - end - if length(e) != n - throw(ArgumentError(lazy"length of e vector was $(length(e)) but should have been $n")) - end - - # allocate - info = Ref{BlasInt}() - - ccall((@blasfunc($syconvf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - uplo, way, n, A, - lda, e, ipiv, info, - 1, 1) - - chklapackerror(info[]) - return A, e - end - end -end - -## (SY) hermitian matrices - eigendecomposition, Bunch-Kaufman decomposition, -## solvers (direct and factored) and inverse. -for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in - ((:zsyconv_,:zhesv_,:zhetrf_,:zhetri_,:zhetrs_,:ComplexF64, :Float64), - (:csyconv_,:chesv_,:chetrf_,:chetri_,:chetrs_,:ComplexF32, :Float32)) - @eval begin - # SUBROUTINE ZSYCONV( UPLO, WAY, N, A, LDA, IPIV, WORK, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER UPLO, WAY - # INTEGER INFO, LDA, N - # .. - # .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function syconv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A,ipiv) - chkstride1(A,ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($syconv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), - uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) - chklapackerror(info[]) - A, work - end - - # SUBROUTINE ZHESV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function hesv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hesv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE ZHETRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function hetrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i in 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, ipiv, info[] - end - - function hetrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkuplo(uplo) - n = checksquare(A) - ipiv = similar(A, BlasInt, n) - hetrf!(uplo, A, ipiv) - end - -# SUBROUTINE ZHETRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) -# * .. Scalar Arguments .. -# CHARACTER UPLO -# INTEGER INFO, LDA, LWORK, N -# * .. -# * .. Array Arguments .. -# INTEGER IPIV( * ) -# COMPLEX*16 A( LDA, * ), WORK( * ) -# function hetri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) -# chkstride1(A) -# n = checksquare(A) -# chkuplo(uplo) -# work = Vector{$elty}(undef, 1) -# lwork = BlasInt(-1) -# info = Ref{BlasInt}() -# for i in 1:2 -# ccall((@blasfunc($hetri), libblastrampoline), Cvoid, -# (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), -# &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) -# chklapackerror(info[]) -# if lwork < 0 -# lwork = BlasInt(real(work[1])) -# work = Vector{$elty}(undef, lwork) -# end -# end -# A -# end - - - # SUBROUTINE ZHETRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function hetri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($hetri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chklapackerror(info[]) - A - end - - # SUBROUTINE ZHETRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ) - function hetrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkuplo(uplo) - chkstride1(A,B,ipiv) - n = checksquare(A) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -for (hesv, hetrf, hetri, hetrs, elty, relty) in - ((:zhesv_rook_,:zhetrf_rook_,:zhetri_rook_,:zhetrs_rook_,:ComplexF64, :Float64), - (:chesv_rook_,:chetrf_rook_,:chetri_rook_,:chetrs_rook_,:ComplexF32, :Float32)) - @eval begin - # SUBROUTINE ZHESV_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function hesv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hesv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE ZHETRF_ROOK( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function hetrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i in 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, ipiv, info[] - end - - # SUBROUTINE ZHETRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function hetri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A,ipiv) - chkstride1(A,ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($hetri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chklapackerror(info[]) - A - end - - # SUBROUTINE ZHETRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ) - function hetrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkstride1(A,B,ipiv) - chkuplo(uplo) - n = checksquare(A) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -for (sysv, sytrf, sytri, sytrs, elty, relty) in - ((:zsysv_,:zsytrf_,:zsytri_,:zsytrs_,:ComplexF64, :Float64), - (:csysv_,:csytrf_,:csytri_,:csytrs_,:ComplexF32, :Float32)) - @eval begin - # SUBROUTINE ZSYSV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # $ LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function sysv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sysv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE ZSYTRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - if n == 0 - return A, ipiv, zero(BlasInt) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, ipiv, info[] - end - - function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkuplo(uplo) - n = checksquare(A) - ipiv = similar(A, BlasInt, n) - sytrf!(uplo, A, ipiv) - end - -# SUBROUTINE ZSYTRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) -# * .. Scalar Arguments .. -# CHARACTER UPLO -# INTEGER INFO, LDA, LWORK, N -# * .. -# * .. Array Arguments .. -# INTEGER IPIV( * ) -# COMPLEX*16 A( LDA, * ), WORK( * ) -# function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) -# chkstride1(A) -# n = checksquare(A) -# chkuplo(uplo) -# work = Vector{$elty}(undef, 1) -# lwork = BlasInt(-1) -# info = Ref{BlasInt}() -# for i in 1:2 -# ccall((@blasfunc($sytri), libblastrampoline), Cvoid, -# (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, -# Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ref{BlasInt}, Clong), -# &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) -# chklapackerror(info[]) -# if lwork < 0 -# lwork = BlasInt(real(work[1])) -# work = Vector{$elty}(undef, lwork) -# end -# end -# A -# end - - # SUBROUTINE ZSYTRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($sytri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chklapackerror(info[]) - A - end - - # SUBROUTINE ZSYTRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ) - function sytrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkstride1(A,B,ipiv) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - end -end - -for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in - ((:zsysv_rook_,:zsytrf_rook_,:zsytri_rook_,:zsytrs_rook_,:zsyconvf_rook_,:ComplexF64, :Float64), - (:csysv_rook_,:csytrf_rook_,:csytri_rook_,:csytrs_rook_,:csyconvf_rook_,:ComplexF32, :Float32)) - @eval begin - # SUBROUTINE ZSYSV_ROOK(UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, - # $ LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, LWORK, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function sysv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, B) - chkstride1(A,B) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - ipiv = similar(A, BlasInt, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sysv), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), - work, lwork, info, 1) - chkargsok(info[]) - chknonsingular(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - B, A, ipiv - end - - # SUBROUTINE ZSYTRF_ROOK( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function sytrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplo(uplo) - ipiv = similar(A, BlasInt, n) - if n == 0 - return A, ipiv, zero(BlasInt) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, ipiv, info[] - end - - # SUBROUTINE ZSYTRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, N - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function sytri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) - require_one_based_indexing(A, ipiv) - chkstride1(A, ipiv) - n = checksquare(A) - chkuplo(uplo) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($sytri), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) - chklapackerror(info[]) - A - end - - # SUBROUTINE ZSYTRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LDB, N, NRHS - # * .. - # * .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ) - function sytrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, - ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) - require_one_based_indexing(A, ipiv, B) - chkstride1(A,B,ipiv) - n = checksquare(A) - chkuplo(uplo) - if n != size(B,1) - throw(DimensionMismatch(lazy"B has first dimension $(size(B,1)), but needs $n")) - end - info = Ref{BlasInt}() - ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) - chklapackerror(info[]) - B - end - - # SUBROUTINE ZSYCONVF_ROOK( UPLO, WAY, N, A, LDA, IPIV, E, INFO ) - # - # .. Scalar Arguments .. - # CHARACTER UPLO, WAY - # INTEGER INFO, LDA, N - # .. - # .. Array Arguments .. - # INTEGER IPIV( * ) - # COMPLEX*16 A( LDA, * ), E( * ) - function syconvf_rook!(uplo::AbstractChar, way::AbstractChar, - A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, - e::AbstractVector{$elty} = Vector{$elty}(undef, length(ipiv))) - require_one_based_indexing(A, ipiv, e) - chkstride1(A, ipiv, e) - - # extract - n = checksquare(A) - lda = stride(A, 2) - - # check - chkuplo(uplo) - if way != 'C' && way != 'R' - throw(ArgumentError(lazy"way must be 'C' or 'R'")) - end - if length(ipiv) != n - throw(ArgumentError(lazy"length of pivot vector was $(length(ipiv)) but should have been $n")) - end - if length(e) != n - throw(ArgumentError(lazy"length of e vector was $(length(e)) but should have been $n")) - end - - # allocate - info = Ref{BlasInt}() - - ccall((@blasfunc($syconvf), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - uplo, way, n, A, - max(1, lda), e, ipiv, info, - 1, 1) - - chklapackerror(info[]) - return A, e - end - end -end - -""" - syconv!(uplo, A, ipiv) -> (A, work) - -Converts a symmetric matrix `A` (which has been factorized into a -triangular matrix) into two matrices `L` and `D`. If `uplo = U`, `A` -is upper triangular. If `uplo = L`, it is lower triangular. `ipiv` is -the pivot vector from the triangular factorization. `A` is overwritten -by `L` and `D`. -""" -syconv!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -""" - sysv!(uplo, A, B) -> (B, A, ipiv) - -Finds the solution to `A * X = B` for symmetric matrix `A`. If `uplo = U`, -the upper half of `A` is stored. If `uplo = L`, the lower half is stored. -`B` is overwritten by the solution `X`. `A` is overwritten by its -Bunch-Kaufman factorization. `ipiv` contains pivoting information about the -factorization. -""" -sysv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -""" - sytrf!(uplo, A) -> (A, ipiv, info) - -Computes the Bunch-Kaufman factorization of a symmetric matrix `A`. If -`uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower -half is stored. - -Returns `A`, overwritten by the factorization, a pivot vector `ipiv`, and -the error code `info` which is a non-negative integer. If `info` is positive -the matrix is singular and the diagonal part of the factorization is exactly -zero at position `info`. -""" -sytrf!(uplo::AbstractChar, A::AbstractMatrix) - -""" - sytrf!(uplo, A, ipiv) -> (A, ipiv, info) - -Computes the Bunch-Kaufman factorization of a symmetric matrix `A`. If -`uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower -half is stored. - -Returns `A`, overwritten by the factorization, the pivot vector `ipiv`, and -the error code `info` which is a non-negative integer. If `info` is positive -the matrix is singular and the diagonal part of the factorization is exactly -zero at position `info`. -""" -sytrf!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -""" - sytri!(uplo, A, ipiv) - -Computes the inverse of a symmetric matrix `A` using the results of -`sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, -the lower half is stored. `A` is overwritten by its inverse. -""" -sytri!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -""" - sytrs!(uplo, A, ipiv, B) - -Solves the equation `A * X = B` for a symmetric matrix `A` using the -results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. -If `uplo = L`, the lower half is stored. `B` is overwritten by the -solution `X`. -""" -sytrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) - - -""" - hesv!(uplo, A, B) -> (B, A, ipiv) - -Finds the solution to `A * X = B` for Hermitian matrix `A`. If `uplo = U`, -the upper half of `A` is stored. If `uplo = L`, the lower half is stored. -`B` is overwritten by the solution `X`. `A` is overwritten by its -Bunch-Kaufman factorization. `ipiv` contains pivoting information about the -factorization. -""" -hesv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) - -""" - hetrf!(uplo, A) -> (A, ipiv, info) - -Computes the Bunch-Kaufman factorization of a Hermitian matrix `A`. If -`uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower -half is stored. - -Returns `A`, overwritten by the factorization, a pivot vector `ipiv`, and -the error code `info` which is a non-negative integer. If `info` is positive -the matrix is singular and the diagonal part of the factorization is exactly -zero at position `info`. -""" -hetrf!(uplo::AbstractChar, A::AbstractMatrix) - -""" - hetrf!(uplo, A, ipiv) -> (A, ipiv, info) - -Computes the Bunch-Kaufman factorization of a Hermitian matrix `A`. If -`uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower -half is stored. - -Returns `A`, overwritten by the factorization, the pivot vector `ipiv`, and -the error code `info` which is a non-negative integer. If `info` is positive -the matrix is singular and the diagonal part of the factorization is exactly -zero at position `info`. -""" -hetrf!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -""" - hetri!(uplo, A, ipiv) - -Computes the inverse of a Hermitian matrix `A` using the results of -`sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, -the lower half is stored. `A` is overwritten by its inverse. -""" -hetri!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) - -""" - hetrs!(uplo, A, ipiv, B) - -Solves the equation `A * X = B` for a Hermitian matrix `A` using the -results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. -If `uplo = L`, the lower half is stored. `B` is overwritten by the -solution `X`. -""" -hetrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) - -for f in (:syevd!, :syev!) - _f = Symbol(:_, f) - @eval function $f(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) - W, A = $_f(jobz, uplo, A) - jobz == 'V' ? (W, A) : W - end -end - -# Symmetric (real) eigensolvers -for (syev, syevr, syevd, sygvd, elty) in - ((:dsyev_,:dsyevr_,:dsyevd_,:dsygvd_,:Float64), - (:ssyev_,:ssyevr_,:ssyevd_,:ssygvd_,:Float32)) - @eval begin - # SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) - Base.@constprop :none function _syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - chkuplo(uplo) - chkstride1(A) - n = checksquare(A) - W = similar(A, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($syev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - jobz, uplo, n, A, max(1,stride(A,2)), W, work, lwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - W, A - end - - # SUBROUTINE DSYEVR( JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, - # $ ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, - # $ IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, RANGE, UPLO - # INTEGER IL, INFO, IU, LDA, LDZ, LIWORK, LWORK, M, N - # DOUBLE PRECISION ABSTOL, VL, VU - # * .. - # * .. Array Arguments .. - # INTEGER ISUPPZ( * ), IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ), Z( LDZ, * ) - function syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, - vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - @chkvalidparam 2 range ('A', 'V', 'I') - chkstride1(A) - n = checksquare(A) - if range == 'I' && !(1 <= il <= iu <= n) - throw(ArgumentError(lazy"illegal choice of eigenvalue indices (il = $il, iu = $iu), which must be between 1 and n = $n")) - end - if range == 'V' && vl >= vu - throw(ArgumentError(lazy"lower boundary, $vl, must be less than upper boundary, $vu")) - end - chkuplofinite(A, uplo) - lda = stride(A,2) - m = Ref{BlasInt}() - W = similar(A, $elty, n) - ldz = n - if jobz == 'N' - Z = similar(A, $elty, ldz, 0) - elseif jobz == 'V' - Z = similar(A, $elty, ldz, n) - end - isuppz = similar(A, BlasInt, 2*n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($syevr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong, Clong), - jobz, range, uplo, n, - A, max(1,lda), vl, vu, - il, iu, abstol, m, - W, Z, max(1,ldz), isuppz, - work, lwork, iwork, liwork, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] - end - syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = - syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) - - # SUBROUTINE DSYEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, - # $ IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, LDA, LIWORK, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) - Base.@constprop :none function _syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - chkstride1(A) - n = checksquare(A) - chkuplofinite(A, uplo) - lda = stride(A,2) - m = Ref{BlasInt}() - W = similar(A, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($syevd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - jobz, uplo, n, A, max(1,lda), - W, work, lwork, iwork, liwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - W, A - end - - # Generalized eigenproblem - # SUBROUTINE DSYGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, - # $ LWORK, IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, ITYPE, LDA, LDB, LIWORK, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), W( * ), WORK( * ) - function sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 itype 1:3 - @chkvalidparam 2 jobz ('N', 'V') - chkuplo(uplo) - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - w = similar(A, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($sygvd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - itype, jobz, uplo, n, - A, lda, B, ldb, - w, work, lwork, iwork, - liwork, info, 1, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(work[1]) - resize!(work, lwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - chkposdef(info[]) - w, A, B - end - end -end -# Hermitian eigensolvers -for (syev, syevr, syevd, sygvd, elty, relty) in - ((:zheev_,:zheevr_,:zheevd_,:zhegvd_,:ComplexF64,:Float64), - (:cheev_,:cheevr_,:cheevd_,:chegvd_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZHEEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION RWORK( * ), W( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - Base.@constprop :none function _syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - chkstride1(A) - chkuplofinite(A, uplo) - n = checksquare(A) - W = similar(A, $relty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, max(1, 3n-2)) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($syev), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, - Clong, Clong), - jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, info, - 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - W, A - end - - # SUBROUTINE ZHEEVR( JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, - # $ ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, - # $ RWORK, LRWORK, IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, RANGE, UPLO - # INTEGER IL, INFO, IU, LDA, LDZ, LIWORK, LRWORK, LWORK, - # $ M, N - # DOUBLE PRECISION ABSTOL, VL, VU - # * .. - # * .. Array Arguments .. - # INTEGER ISUPPZ( * ), IWORK( * ) - # DOUBLE PRECISION RWORK( * ), W( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ), Z( LDZ, * ) - function syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, - vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - @chkvalidparam 2 range ('A', 'V', 'I') - chkstride1(A) - chkuplofinite(A, uplo) - n = checksquare(A) - if range == 'I' && !(1 <= il <= iu <= n) - throw(ArgumentError(lazy"illegal choice of eigenvalue indices (il = $il, iu=$iu), which must be between 1 and n = $n")) - end - if range == 'V' && vl >= vu - throw(ArgumentError(lazy"lower boundary, $vl, must be less than upper boundary, $vu")) - end - lda = max(1,stride(A,2)) - m = Ref{BlasInt}() - W = similar(A, $relty, n) - if jobz == 'N' - ldz = 1 - Z = similar(A, $elty, ldz, 0) - elseif jobz == 'V' - ldz = n - Z = similar(A, $elty, ldz, n) - end - isuppz = similar(A, BlasInt, 2*n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 1) - lrwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] - ccall((@blasfunc($syevr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, - Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Clong, Clong, Clong), - jobz, range, uplo, n, - A, lda, vl, vu, - il, iu, abstol, m, - W, Z, ldz, isuppz, - work, lwork, rwork, lrwork, - iwork, liwork, info, - 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - lrwork = BlasInt(rwork[1]) - resize!(rwork, lrwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] - end - syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = - syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) - - # SUBROUTINE ZHEEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, - # $ LRWORK, IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, LDA, LIWORK, LRWORK, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - Base.@constprop :none function _syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobz ('N', 'V') - chkstride1(A) - chkuplofinite(A, uplo) - n = checksquare(A) - lda = max(1, stride(A,2)) - m = Ref{BlasInt}() - W = similar(A, $relty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 1) - lrwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] - ccall((@blasfunc($syevd), liblapack), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, - Ptr{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - jobz, uplo, n, A, stride(A,2), - W, work, lwork, rwork, lrwork, - iwork, liwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - lrwork = BlasInt(rwork[1]) - resize!(rwork, lrwork) - liwork = iwork[1] - resize!(iwork, liwork) - end - end - W, A - end - - # SUBROUTINE ZHEGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, - # $ LWORK, RWORK, LRWORK, IWORK, LIWORK, INFO ) - # * .. Scalar Arguments .. - # CHARACTER JOBZ, UPLO - # INTEGER INFO, ITYPE, LDA, LDB, LIWORK, LRWORK, LWORK, N - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION RWORK( * ), W( * ) - # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) - function sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 itype 1:3 - @chkvalidparam 2 jobz ('N', 'V') - chkstride1(A, B) - chkuplofinite(A, uplo) - chkuplofinite(B, uplo) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - w = similar(A, $relty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 1) - lrwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] - ccall((@blasfunc($sygvd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, - Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - itype, jobz, uplo, n, - A, lda, B, ldb, - w, work, lwork, rwork, - lrwork, iwork, liwork, info, - 1, 1) - chkargsok(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = iwork[1] - resize!(iwork, liwork) - lrwork = BlasInt(rwork[1]) - resize!(rwork, lrwork) - end - end - chkposdef(info[]) - w, A, B - end - end -end - -""" - syev!(jobz, uplo, A) - -Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors -(`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle -of `A` is used. If `uplo = L`, the lower triangle of `A` is used. -""" -syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) - -""" - syevr!(jobz, range, uplo, A, vl, vu, il, iu, abstol) -> (W, Z) - -Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors -(`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle -of `A` is used. If `uplo = L`, the lower triangle of `A` is used. If -`range = A`, all the eigenvalues are found. If `range = V`, the -eigenvalues in the half-open interval `(vl, vu]` are found. -If `range = I`, the eigenvalues with indices between `il` and `iu` are -found. `abstol` can be set as a tolerance for convergence. - -The eigenvalues are returned in `W` and the eigenvectors in `Z`. -""" -syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, - vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) - -""" - syevd!(jobz, uplo, A) - -Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors -(`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle -of `A` is used. If `uplo = L`, the lower triangle of `A` is used. -""" -syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) - -""" - sygvd!(itype, jobz, uplo, A, B) -> (w, A, B) - -Finds the generalized eigenvalues (`jobz = N`) or eigenvalues and -eigenvectors (`jobz = V`) of a symmetric matrix `A` and symmetric -positive-definite matrix `B`. If `uplo = U`, the upper triangles -of `A` and `B` are used. If `uplo = L`, the lower triangles of `A` and -`B` are used. If `itype = 1`, the problem to solve is -`A * x = lambda * B * x`. If `itype = 2`, the problem to solve is -`A * B * x = lambda * x`. If `itype = 3`, the problem to solve is -`B * A * x = lambda * x`. -""" -sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - -## (BD) Bidiagonal matrices - singular value decomposition -for (bdsqr, relty, elty) in - ((:dbdsqr_,:Float64,:Float64), - (:sbdsqr_,:Float32,:Float32), - (:zbdsqr_,:Float64,:ComplexF64), - (:cbdsqr_,:Float32,:ComplexF32)) - @eval begin - function bdsqr!(uplo::AbstractChar, d::AbstractVector{$relty}, e_::AbstractVector{$relty}, - Vt::AbstractMatrix{$elty}, U::AbstractMatrix{$elty}, C::AbstractMatrix{$elty}) - require_one_based_indexing(d, e_, Vt, U, C) - chkstride1(d, e_, Vt, U, C) - # Extract number - n = length(d) - ncvt, nru, ncc = size(Vt, 2), size(U, 1), size(C, 2) - ldvt, ldu, ldc = max(1, stride(Vt,2)), max(1, stride(U, 2)), max(1, stride(C,2)) - # Do checks - chkuplo(uplo) - if length(e_) != n - 1 - throw(DimensionMismatch(lazy"off-diagonal has length $(length(e_)) but should have length $(n - 1)")) - end - if ncvt > 0 && ldvt < n - throw(DimensionMismatch(lazy"leading dimension of Vt, $ldvt, must be at least $n")) - end - if ldu < nru - throw(DimensionMismatch(lazy"leading dimension of U, $ldu, must be at least $nru")) - end - if size(U, 2) != n - throw(DimensionMismatch(lazy"U must have $n columns but has $(size(U, 2))")) - end - if ncc > 0 && ldc < n - throw(DimensionMismatch(lazy"leading dimension of C, $ldc, must be at least $n")) - end - # Allocate - work = Vector{$relty}(undef, 4n) - info = Ref{BlasInt}() - ccall((@blasfunc($bdsqr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong), - uplo, n, ncvt, nru, - ncc, d, e_, Vt, - ldvt, U, ldu, C, - ldc, work, info, 1) - chklapackerror(info[]) - d, Vt, U, C #singular values in descending order, P**T * VT, U * Q, Q**T * C - end - end -end - -""" - bdsqr!(uplo, d, e_, Vt, U, C) -> (d, Vt, U, C) - -Computes the singular value decomposition of a bidiagonal matrix with -`d` on the diagonal and `e_` on the off-diagonal. If `uplo = U`, `e_` is -the superdiagonal. If `uplo = L`, `e_` is the subdiagonal. Can optionally also -compute the product `Q' * C`. - -Returns the singular values in `d`, and the matrix `C` overwritten with `Q' * C`. -""" -bdsqr!(uplo::AbstractChar, d::AbstractVector, e_::AbstractVector, Vt::AbstractMatrix, U::AbstractMatrix, C::AbstractMatrix) - -#Defined only for real types -for (bdsdc, elty) in - ((:dbdsdc_,:Float64), - (:sbdsdc_,:Float32)) - @eval begin - #* DBDSDC computes the singular value decomposition (SVD) of a real - #* N-by-N (upper or lower) bidiagonal matrix B: B = U * S * VT, - #* using a divide and conquer method - #* .. Scalar Arguments .. - # CHARACTER COMPQ, UPLO - # INTEGER INFO, LDU, LDVT, N - #* .. - #* .. Array Arguments .. - # INTEGER IQ( * ), IWORK( * ) - # DOUBLE PRECISION D( * ), E( * ), Q( * ), U( LDU, * ), - # $ VT( LDVT, * ), WORK( * ) - function bdsdc!(uplo::AbstractChar, compq::AbstractChar, d::AbstractVector{$elty}, e_::AbstractVector{$elty}) - require_one_based_indexing(d, e_) - chkstride1(d, e_) - n, ldiq, ldq, ldu, ldvt = length(d), 1, 1, 1, 1 - chkuplo(uplo) - if compq == 'N' - lwork = 6*n - elseif compq == 'P' - @warn "COMPQ='P' is not tested" - #TODO turn this into an actual LAPACK call - #smlsiz=ilaenv(9, $elty === :Float64 ? 'dbdsqr' : 'sbdsqr', string(uplo, compq), n,n,n,n) - smlsiz=100 #For now, completely overkill - ldq = n*(11+2*smlsiz+8*round(Int,log((n/(smlsiz+1)))/log(2))) - ldiq = n*(3+3*round(Int,log(n/(smlsiz+1))/log(2))) - lwork = 6*n - elseif compq == 'I' - ldvt=ldu=max(1, n) - lwork=3*n^2 + 4*n - else - throw(ArgumentError(lazy"COMPQ argument must be 'N', 'P' or 'I', got $(repr(compq))")) - end - u = similar(d, $elty, (ldu, n)) - vt = similar(d, $elty, (ldvt, n)) - q = similar(d, $elty, ldq) - iq = similar(d, BlasInt, ldiq) - work = Vector{$elty}(undef, lwork) - iwork = Vector{BlasInt}(undef, 8n) - info = Ref{BlasInt}() - ccall((@blasfunc($bdsdc), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Clong, Clong), - uplo, compq, n, d, e_, - u, ldu, vt, ldvt, - q, iq, work, iwork, info, - 1, 1) - chklapackerror(info[]) - d, e_, u, vt, q, iq - end - end -end - -""" - bdsdc!(uplo, compq, d, e_) -> (d, e, u, vt, q, iq) - -Computes the singular value decomposition of a bidiagonal matrix with `d` on the -diagonal and `e_` on the off-diagonal using a divide and conqueq method. -If `uplo = U`, `e_` is the superdiagonal. If `uplo = L`, `e_` is the subdiagonal. -If `compq = N`, only the singular values are found. If `compq = I`, the singular -values and vectors are found. If `compq = P`, the singular values -and vectors are found in compact form. Only works for real types. - -Returns the singular values in `d`, and if `compq = P`, the compact singular -vectors in `iq`. -""" -bdsdc!(uplo::AbstractChar, compq::AbstractChar, d::AbstractVector, e_::AbstractVector) - -for (gecon, elty) in - ((:dgecon_,:Float64), - (:sgecon_,:Float32)) - @eval begin - # SUBROUTINE DGECON( NORM, N, A, LDA, ANORM, RCOND, WORK, IWORK, - # $ INFO ) - # * .. Scalar Arguments .. - # CHARACTER NORM - # INTEGER INFO, LDA, N - # DOUBLE PRECISION ANORM, RCOND - # * .. - # * .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), WORK( * ) - function gecon!(normtype::AbstractChar, A::AbstractMatrix{$elty}, anorm::$elty) - require_one_based_indexing(A) - @chkvalidparam 1 normtype ('0', '1', 'I') - chkstride1(A) - n = checksquare(A) - lda = max(1, stride(A, 2)) - rcond = Ref{$elty}() - work = Vector{$elty}(undef, 4n) - iwork = Vector{BlasInt}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($gecon), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ref{BlasInt}, Clong), - normtype, n, A, lda, anorm, rcond, work, iwork, - info, 1) - chklapackerror(info[]) - rcond[] - end - end -end - -for (gecon, elty, relty) in - ((:zgecon_,:ComplexF64,:Float64), - (:cgecon_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE ZGECON( NORM, N, A, LDA, ANORM, RCOND, WORK, RWORK, - # $ INFO ) - # * .. Scalar Arguments .. - # CHARACTER NORM - # INTEGER INFO, LDA, N - # DOUBLE PRECISION ANORM, RCOND - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), WORK( * ) - function gecon!(normtype::AbstractChar, A::AbstractMatrix{$elty}, anorm::$relty) - require_one_based_indexing(A) - @chkvalidparam 1 normtype ('0', '1', 'I') - chkstride1(A) - n = checksquare(A) - lda = max(1, stride(A, 2)) - rcond = Ref{$relty}() - work = Vector{$elty}(undef, 2n) - rwork = Vector{$relty}(undef, 2n) - info = Ref{BlasInt}() - ccall((@blasfunc($gecon), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, - Ref{BlasInt}, Clong), - normtype, n, A, lda, anorm, rcond, work, rwork, - info, 1) - chklapackerror(info[]) - rcond[] - end - end -end - -""" - gecon!(normtype, A, anorm) - -Finds the reciprocal condition number of matrix `A`. If `normtype = I`, -the condition number is found in the infinity norm. If `normtype = O` or -`1`, the condition number is found in the one norm. `A` must be the -result of `getrf!` and `anorm` is the norm of `A` in the relevant norm. -""" -gecon!(normtype::AbstractChar, A::AbstractMatrix, anorm) - -for (gehrd, elty) in - ((:dgehrd_,:Float64), - (:sgehrd_,:Float32), - (:zgehrd_,:ComplexF64), - (:cgehrd_,:ComplexF32)) - @eval begin - - # .. Scalar Arguments .. - # INTEGER IHI, ILO, INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function gehrd!(ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkfinite(A) # balancing routines don't support NaNs and Infs - tau = similar(A, $elty, max(0,n - 1)) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gehrd), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - n, ilo, ihi, A, - max(1, stride(A, 2)), tau, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, tau - end - end -end -gehrd!(A::AbstractMatrix) = gehrd!(1, size(A, 1), A) - -""" - gehrd!(ilo, ihi, A) -> (A, tau) - -Converts a matrix `A` to Hessenberg form. If `A` is balanced with `gebal!` -then `ilo` and `ihi` are the outputs of `gebal!`. Otherwise they should be -`ilo = 1` and `ihi = size(A,2)`. `tau` contains the elementary reflectors of -the factorization. -""" -gehrd!(ilo::Integer, ihi::Integer, A::AbstractMatrix) - -for (orghr, elty) in - ((:dorghr_,:Float64), - (:sorghr_,:Float32), - (:zunghr_,:ComplexF64), - (:cunghr_,:ComplexF32)) - @eval begin - # * .. Scalar Arguments .. - # INTEGER IHI, ILO, INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orghr!(ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A, tau) - n = checksquare(A) - if n - length(tau) != 1 - throw(DimensionMismatch(lazy"tau has length $(length(tau)), needs $(n - 1)")) - end - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orghr), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - n, ilo, ihi, A, - max(1, stride(A, 2)), tau, work, lwork, - info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A - end - end -end - -""" - orghr!(ilo, ihi, A, tau) - -Explicitly finds `Q`, the orthogonal/unitary matrix from `gehrd!`. `ilo`, -`ihi`, `A`, and `tau` must correspond to the input/output to `gehrd!`. -""" -orghr!(ilo::Integer, ihi::Integer, A::AbstractMatrix, tau::AbstractVector) - -for (ormhr, elty) in - ((:dormhr_,:Float64), - (:sormhr_,:Float32), - (:zunmhr_,:ComplexF64), - (:cunmhr_,:ComplexF32)) - @eval begin - # .. Scalar Arguments .. - # CHARACTER side, trans - # INTEGER ihi, ilo, info, lda, ldc, lwork, m, n - # .. - # .. Array Arguments .. - # DOUBLE PRECISION a( lda, * ), c( ldc, * ), tau( * ), work( * ) - function ormhr!(side::AbstractChar, trans::AbstractChar, ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - - require_one_based_indexing(A, tau, C) - chkstride1(A, tau, C) - chkside(side) - chktrans(trans) - n = checksquare(A) - mC, nC = size(C, 1), size(C, 2) - - if n - length(tau) != 1 - throw(DimensionMismatch(lazy"tau has length $(length(tau)), needs $(n - 1)")) - end - if (side == 'L' && mC != n) || (side == 'R' && nC != n) - throw(DimensionMismatch("A and C matrices are not conformable")) - end - - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormhr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), - side, trans, mC, nC, - ilo, ihi, A, max(1, stride(A, 2)), - tau, C, max(1, stride(C, 2)), work, - lwork, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - end -end - -for (hseqr, elty) in - ((:zhseqr_,:ComplexF64), - (:chseqr_,:ComplexF32)) - @eval begin - # * .. Scalar Arguments .. - # CHARACTER JOB, COMPZ - # INTEGER N, ILO, IHI, LWORK, LDH, LDZ, INFO - # * .. - # * .. Array Arguments .. - # COMPLEX*16 H( LDH, * ), Z( LDZ, * ), WORK( * ) - function hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, - H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) - require_one_based_indexing(H, Z) - @chkvalidparam 1 job ('E', 'S') - @chkvalidparam 2 compz ('N', 'I', 'V') - chkstride1(H) - n = checksquare(H) - checksquare(Z) == n || throw(DimensionMismatch()) - ldh = max(1, stride(H, 2)) - ldz = max(1, stride(Z, 2)) - w = similar(H, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hseqr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - job, compz, n, ilo, ihi, - H, ldh, w, Z, ldz, work, - lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - H, Z, w - end - end -end - -for (hseqr, elty) in - ((:dhseqr_,:Float64), - (:shseqr_,:Float32)) - @eval begin - # * .. Scalar Arguments .. - # CHARACTER JOB, COMPZ - # INTEGER N, ILO, IHI, LWORK, LDH, LDZ, INFO - # * .. - # * .. Array Arguments .. - # COMPLEX*16 H( LDH, * ), Z( LDZ, * ), WORK( * ) - function hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, - H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) - require_one_based_indexing(H, Z) - @chkvalidparam 1 job ('E', 'S') - @chkvalidparam 2 compz ('N', 'I', 'V') - chkstride1(H) - n = checksquare(H) - checksquare(Z) == n || throw(DimensionMismatch()) - ldh = max(1, stride(H, 2)) - ldz = max(1, stride(Z, 2)) - wr = similar(H, $elty, n) - wi = similar(H, $elty, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hseqr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}), - job, compz, n, ilo, ihi, - H, ldh, wr, wi, Z, ldz, work, - lwork, info) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - H, Z, complex.(wr, wi) - end - end -end -hseqr!(H::StridedMatrix{T}, Z::StridedMatrix{T}) where {T<:BlasFloat} = hseqr!('S', 'V', 1, size(H, 1), H, Z) -hseqr!(H::StridedMatrix{T}) where {T<:BlasFloat} = hseqr!('S', 'I', 1, size(H, 1), H, similar(H)) - -""" - hseqr!(job, compz, ilo, ihi, H, Z) -> (H, Z, w) - -Computes all eigenvalues and (optionally) the Schur factorization of a matrix -reduced to Hessenberg form. If `H` is balanced with `gebal!` -then `ilo` and `ihi` are the outputs of `gebal!`. Otherwise they should be -`ilo = 1` and `ihi = size(H,2)`. `tau` contains the elementary reflectors of -the factorization. -""" -hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, H::AbstractMatrix, Z::AbstractMatrix) - -for (hetrd, elty) in - ((:dsytrd_,Float64), - (:ssytrd_,Float32), - (:zhetrd_,ComplexF64), - (:chetrd_,ComplexF32)) - relty = real(elty) - @eval begin - - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAU( * ), WORK( * ) - function hetrd!(uplo::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - chkstride1(A) - n = checksquare(A) - chkuplofinite(A, uplo) # balancing routines don't support NaNs and Infs - tau = similar(A, $elty, max(0,n - 1)) - d = Vector{$relty}(undef, n) - e = Vector{$relty}(undef, max(0,n - 1)) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($hetrd), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong), - uplo, n, A, max(1, stride(A, 2)), d, e, tau, work, lwork, info, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, tau, d, e - end - end -end - -""" - hetrd!(uplo, A) -> (A, tau, d, e) - -Converts a Hermitian matrix `A` to real-symmetric tridiagonal Hessenberg form. -If `uplo = U`, the upper half of `A` is stored; if `uplo = L`, the lower half is stored. -`tau` contains the elementary reflectors of the factorization, `d` contains the -diagonal and `e` contains the upper/lower diagonal. -""" -hetrd!(uplo::AbstractChar, A::AbstractMatrix) - -for (orgtr, elty) in - ((:dorgtr_,:Float64), - (:sorgtr_,:Float32), - (:zungtr_,:ComplexF64), - (:cungtr_,:ComplexF32)) - @eval begin - # * .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER INFO, LDA, LWORK, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) - function orgtr!(uplo::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) - require_one_based_indexing(A, tau) - chkstride1(A, tau) - n = checksquare(A) - if n - length(tau) != 1 - throw(DimensionMismatch(lazy"tau has length $(length(tau)), needs $(n - 1)")) - end - chkuplo(uplo) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($orgtr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong), - uplo, n, A, - max(1, stride(A, 2)), tau, work, lwork, - info, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A - end - end -end - -""" - orgtr!(uplo, A, tau) - -Explicitly finds `Q`, the orthogonal/unitary matrix from `hetrd!`. `uplo`, -`A`, and `tau` must correspond to the input/output to `hetrd!`. -""" -orgtr!(uplo::AbstractChar, A::AbstractMatrix, tau::AbstractVector) - -for (ormtr, elty) in - ((:dormtr_,:Float64), - (:sormtr_,:Float32), - (:zunmtr_,:ComplexF64), - (:cunmtr_,:ComplexF32)) - @eval begin - # .. Scalar Arguments .. - # CHARACTER side, trans, uplo - # INTEGER info, lda, ldc, lwork, m, n - # .. - # .. Array Arguments .. - # DOUBLE PRECISION a( lda, * ), c( ldc, * ), tau( * ), work( * ) - function ormtr!(side::AbstractChar, uplo::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, - tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) - - require_one_based_indexing(A, tau, C) - chkstride1(A, tau, C) - n = checksquare(A) - chkside(side) - chkuplo(uplo) - chktrans(trans) - mC, nC = size(C, 1), size(C, 2) - - if n - length(tau) != 1 - throw(DimensionMismatch(lazy"tau has length $(length(tau)), needs $(n - 1)")) - end - if (side == 'L' && mC != n) || (side == 'R' && nC != n) - throw(DimensionMismatch(lazy"A and C matrices are not conformable")) - end - - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($ormtr), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), - side, uplo, trans, mC, nC, - A, max(1, stride(A, 2)), - tau, C, max(1, stride(C, 2)), work, - lwork, info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - C - end - end -end - -for (gees, gges, gges3, elty) in - ((:dgees_,:dgges_,:dgges3_,:Float64), - (:sgees_,:sgges_,:sgges3_,:Float32)) - @eval begin - # .. Scalar Arguments .. - # CHARACTER JOBVS, SORT - # INTEGER INFO, LDA, LDVS, LWORK, N, SDIM - # .. - # .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION A( LDA, * ), VS( LDVS, * ), WI( * ), WORK( * ), - # $ WR( * ) - function gees!(jobvs::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobvs ('N', 'V') - chkstride1(A) - n = checksquare(A) - sdim = Vector{BlasInt}(undef, 1) - wr = similar(A, $elty, n) - wi = similar(A, $elty, n) - vs = similar(A, $elty, jobvs == 'V' ? n : 0, n) - ldvs = max(size(vs, 1), 1) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gees), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), - jobvs, 'N', C_NULL, n, - A, max(1, stride(A, 2)), sdim, wr, - wi, vs, ldvs, work, - lwork, C_NULL, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - iszero(wi) ? (A, vs, wr) : (A, vs, complex.(wr, wi)) - end - - # * .. Scalar Arguments .. - # CHARACTER JOBVSL, JOBVSR, SORT - # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM - # * .. - # * .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), - # $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), - # $ VSR( LDVSR, * ), WORK( * ) - function gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 jobvsl ('N', 'V') - @chkvalidparam 2 jobvsr ('N', 'V') - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - sdim = BlasInt(0) - alphar = similar(A, $elty, n) - alphai = similar(A, $elty, n) - beta = similar(A, $elty, n) - ldvsl = jobvsl == 'V' ? max(1, n) : 1 - vsl = similar(A, $elty, ldvsl, n) - ldvsr = jobvsr == 'V' ? max(1, n) : 1 - vsr = similar(A, $elty, ldvsr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gges), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, - Ref{BlasInt}, Clong, Clong, Clong), - jobvsl, jobvsr, 'N', C_NULL, - n, A, max(1,stride(A, 2)), B, - max(1,stride(B, 2)), sdim, alphar, alphai, - beta, vsl, ldvsl, vsr, - ldvsr, work, lwork, C_NULL, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, B, complex.(alphar, alphai), beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] - end - - # * .. Scalar Arguments .. - # CHARACTER JOBVSL, JOBVSR, SORT - # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM - # * .. - # * .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), - # $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), - # $ VSR( LDVSR, * ), WORK( * ) - function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 jobvsl ('N', 'V') - @chkvalidparam 2 jobvsr ('N', 'V') - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - sdim = BlasInt(0) - alphar = similar(A, $elty, n) - alphai = similar(A, $elty, n) - beta = similar(A, $elty, n) - ldvsl = jobvsl == 'V' ? max(1, n) : 1 - vsl = similar(A, $elty, ldvsl, n) - ldvsr = jobvsr == 'V' ? max(1, n) : 1 - vsr = similar(A, $elty, ldvsr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gges3), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, - Ref{BlasInt}, Clong, Clong, Clong), - jobvsl, jobvsr, 'N', C_NULL, - n, A, max(1,stride(A, 2)), B, - max(1,stride(B, 2)), sdim, alphar, alphai, - beta, vsl, ldvsl, vsr, - ldvsr, work, lwork, C_NULL, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, B, complex.(alphar, alphai), beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] - end - end -end - -for (gees, gges, gges3, elty, relty) in - ((:zgees_,:zgges_,:zgges3_,:ComplexF64,:Float64), - (:cgees_,:cgges_,:cgges3_,:ComplexF32,:Float32)) - @eval begin - # * .. Scalar Arguments .. - # CHARACTER JOBVS, SORT - # INTEGER INFO, LDA, LDVS, LWORK, N, SDIM - # * .. - # * .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), VS( LDVS, * ), W( * ), WORK( * ) - function gees!(jobvs::AbstractChar, A::AbstractMatrix{$elty}) - require_one_based_indexing(A) - @chkvalidparam 1 jobvs ('N', 'V') - chkstride1(A) - n = checksquare(A) - sort = 'N' - sdim = BlasInt(0) - w = similar(A, $elty, n) - vs = similar(A, $elty, jobvs == 'V' ? n : 1, n) - ldvs = max(size(vs, 1), 1) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gees), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), - jobvs, sort, C_NULL, n, - A, max(1, stride(A, 2)), sdim, w, - vs, ldvs, work, lwork, - rwork, C_NULL, info, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, vs, w - end - - # * .. Scalar Arguments .. - # CHARACTER JOBVSL, JOBVSR, SORT - # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM - # * .. - # * .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), - # $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), - # $ WORK( * ) - function gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 jobvsl ('N', 'V') - @chkvalidparam 2 jobvsr ('N', 'V') - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - sdim = BlasInt(0) - alpha = similar(A, $elty, n) - beta = similar(A, $elty, n) - ldvsl = jobvsl == 'V' ? max(1, n) : 1 - vsl = similar(A, $elty, ldvsl, n) - ldvsr = jobvsr == 'V' ? max(1, n) : 1 - vsr = similar(A, $elty, ldvsr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 8n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gges), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, - Ref{BlasInt}, Clong, Clong, Clong), - jobvsl, jobvsr, 'N', C_NULL, - n, A, max(1, stride(A, 2)), B, - max(1, stride(B, 2)), sdim, alpha, beta, - vsl, ldvsl, vsr, ldvsr, - work, lwork, rwork, C_NULL, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, B, alpha, beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] - end - - # * .. Scalar Arguments .. - # CHARACTER JOBVSL, JOBVSR, SORT - # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM - # * .. - # * .. Array Arguments .. - # LOGICAL BWORK( * ) - # DOUBLE PRECISION RWORK( * ) - # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), - # $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), - # $ WORK( * ) - function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) - require_one_based_indexing(A, B) - @chkvalidparam 1 jobvsl ('N', 'V') - @chkvalidparam 2 jobvsr ('N', 'V') - chkstride1(A, B) - n, m = checksquare(A, B) - if n != m - throw(DimensionMismatch(lazy"dimensions of A, ($n,$n), and B, ($m,$m), must match")) - end - sdim = BlasInt(0) - alpha = similar(A, $elty, n) - beta = similar(A, $elty, n) - ldvsl = jobvsl == 'V' ? max(1, n) : 1 - vsl = similar(A, $elty, ldvsl, n) - ldvsr = jobvsr == 'V' ? max(1, n) : 1 - vsr = similar(A, $elty, ldvsr, n) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - rwork = Vector{$relty}(undef, 8n) - info = Ref{BlasInt}() - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($gges3), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, - Ref{BlasInt}, Clong, Clong, Clong), - jobvsl, jobvsr, 'N', C_NULL, - n, A, max(1, stride(A, 2)), B, - max(1, stride(B, 2)), sdim, alpha, beta, - vsl, ldvsl, vsr, ldvsr, - work, lwork, rwork, C_NULL, - info, 1, 1, 1) - chklapackerror(info[]) - if i == 1 - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - A, B, alpha, beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] - end - end -end - -""" - gees!(jobvs, A) -> (A, vs, w) - -Computes the eigenvalues (`jobvs = N`) or the eigenvalues and Schur -vectors (`jobvs = V`) of matrix `A`. `A` is overwritten by its Schur form. - -Returns `A`, `vs` containing the Schur vectors, and `w`, containing the -eigenvalues. -""" -gees!(jobvs::AbstractChar, A::AbstractMatrix) - - -""" - gges!(jobvsl, jobvsr, A, B) -> (A, B, alpha, beta, vsl, vsr) - -Computes the generalized eigenvalues, generalized Schur form, left Schur -vectors (`jobsvl = V`), or right Schur vectors (`jobvsr = V`) of `A` and -`B`. - -The generalized eigenvalues are returned in `alpha` and `beta`. The left Schur -vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. -""" -gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - -""" - gges3!(jobvsl, jobvsr, A, B) -> (A, B, alpha, beta, vsl, vsr) - -Computes the generalized eigenvalues, generalized Schur form, left Schur -vectors (`jobsvl = V`), or right Schur vectors (`jobvsr = V`) of `A` and -`B` using a blocked algorithm. This function requires LAPACK 3.6.0. - -The generalized eigenvalues are returned in `alpha` and `beta`. The left Schur -vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. -""" -gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) - -for (trexc, trsen, tgsen, elty) in - ((:dtrexc_, :dtrsen_, :dtgsen_, :Float64), - (:strexc_, :strsen_, :stgsen_, :Float32)) - @eval begin - # * .. Scalar Arguments .. - # CHARACTER COMPQ - # INTEGER IFST, ILST, INFO, LDQ, LDT, N - # * .. - # * .. Array Arguments .. - # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WORK( * ) - function trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) - require_one_based_indexing(T, Q) - @chkvalidparam 1 compq ('V', 'N') - chkstride1(T, Q) - n = checksquare(T) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - work = Vector{$elty}(undef, n) - info = Ref{BlasInt}() - ccall((@blasfunc($trexc), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Clong), - compq, n, - T, ldt, Q, ldq, - ifst, ilst, - work, info, 1) - chklapackerror(info[]) - T, Q - end - trexc!(ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = - trexc!('V', ifst, ilst, T, Q) - - # * .. Scalar Arguments .. - # CHARACTER COMPQ, JOB - # INTEGER INFO, LDQ, LDT, LIWORK, LWORK, M, N - # DOUBLE PRECISION S, SEP - # * .. - # * .. Array Arguments .. - # LOGICAL SELECT( * ) - # INTEGER IWORK( * ) - # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WI( * ), WORK( * ), WR( * ) - function trsen!(job::AbstractChar, compq::AbstractChar, select::AbstractVector{BlasInt}, - T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) - require_one_based_indexing(T, Q, select) - @chkvalidparam 1 job ('N', 'E', 'V', 'B') - @chkvalidparam 2 compq ('V', 'N') - chkstride1(T, Q, select) - n = checksquare(T) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - wr = similar(T, $elty, n) - wi = similar(T, $elty, n) - m = sum(select) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - liwork = BlasInt(-1) - info = Ref{BlasInt}() - select = convert(Array{BlasInt}, select) - s = Ref{$elty}(zero($elty)) - sep = Ref{$elty}(zero($elty)) - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($trsen), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - job, compq, select, n, - T, ldt, Q, ldq, - wr, wi, m, s, sep, - work, lwork, iwork, liwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 # only estimated optimal lwork, liwork - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = BlasInt(real(iwork[1])) - resize!(iwork, liwork) - end - end - iszero(wi) ? (T, Q, wr, s[], sep[]) : (T, Q, complex.(wr, wi), s[], sep[]) - end - trsen!(select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = - trsen!('N', 'V', select, T, Q) - - # .. Scalar Arguments .. - # LOGICAL WANTQ, WANTZ - # INTEGER IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK, - # $ M, N - # DOUBLE PRECISION PL, PR - # .. - # .. Array Arguments .. - # LOGICAL SELECT( * ) - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), - # $ B( LDB, * ), BETA( * ), DIF( * ), Q( LDQ, * ), - # $ WORK( * ), Z( LDZ, * ) - # .. - function tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, - Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) - require_one_based_indexing(select, S, T, Q, Z) - chkstride1(select, S, T, Q, Z) - n, nt, nq, nz = checksquare(S, T, Q, Z) - if n != nt - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and T, ($nt,$nt), must match")) - end - if n != nq - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and Q, ($nq,$nq), must match")) - end - if n != nz - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and Z, ($nz,$nz), must match")) - end - lds = max(1, stride(S, 2)) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - ldz = max(1, stride(Z, 2)) - m = sum(select) - alphai = similar(T, $elty, n) - alphar = similar(T, $elty, n) - beta = similar(T, $elty, n) - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - liwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - info = Ref{BlasInt}() - select = convert(Array{BlasInt}, select) - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($tgsen), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}), - 0, 1, 1, select, - n, S, lds, T, - ldt, alphar, alphai, beta, - Q, ldq, Z, ldz, - m, C_NULL, C_NULL, C_NULL, - work, lwork, iwork, liwork, - info) - chklapackerror(info[]) - if i == 1 # only estimated optimal lwork, liwork - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = BlasInt(real(iwork[1])) - resize!(iwork, liwork) - end - end - S, T, complex.(alphar, alphai), beta, Q, Z - end - end -end - -for (trexc, trsen, tgsen, elty, relty) in - ((:ztrexc_, :ztrsen_, :ztgsen_, :ComplexF64, :Float64), - (:ctrexc_, :ctrsen_, :ctgsen_, :ComplexF32, :Float32)) - @eval begin - # .. Scalar Arguments .. - # CHARACTER COMPQ - # INTEGER IFST, ILST, INFO, LDQ, LDT, N - # .. - # .. Array Arguments .. - # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WORK( * ) - function trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) - require_one_based_indexing(T, Q) - @chkvalidparam 1 compq ('V', 'N') - chkstride1(T, Q) - n = checksquare(T) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - info = Ref{BlasInt}() - ccall((@blasfunc($trexc), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Clong), - compq, n, - T, ldt, Q, ldq, - ifst, ilst, - info, 1) - chklapackerror(info[]) - T, Q - end - trexc!(ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = - trexc!('V', ifst, ilst, T, Q) - - # .. Scalar Arguments .. - # CHARACTER COMPQ, JOB - # INTEGER INFO, LDQ, LDT, LWORK, M, N - # DOUBLE PRECISION S, SEP - # .. - # .. Array Arguments .. - # LOGICAL SELECT( * ) - # COMPLEX Q( LDQ, * ), T( LDT, * ), W( * ), WORK( * ) - function trsen!(job::AbstractChar, compq::AbstractChar, select::AbstractVector{BlasInt}, - T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) - require_one_based_indexing(select, T, Q) - @chkvalidparam 1 job ('N', 'E', 'V', 'B') - @chkvalidparam 2 compq ('N', 'V') - chkstride1(select, T, Q) - n = checksquare(T) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - w = similar(T, $elty, n) - m = sum(select) - work = Vector{$elty}(undef, 1) - lwork = BlasInt(-1) - info = Ref{BlasInt}() - select = convert(Array{BlasInt}, select) - s = Ref{$relty}(zero($relty)) - sep = Ref{$relty}(zero($relty)) - for i = 1:2 # first call returns lwork as work[1] - ccall((@blasfunc($trsen), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ref{$relty}, - Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Clong, Clong), - job, compq, select, n, - T, ldt, Q, ldq, - w, m, s, sep, - work, lwork, - info, 1, 1) - chklapackerror(info[]) - if i == 1 # only estimated optimal lwork, liwork - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - end - end - T, Q, w, s[], sep[] - end - trsen!(select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = - trsen!('N', 'V', select, T, Q) - - # .. Scalar Arguments .. - # LOGICAL WANTQ, WANTZ - # INTEGER IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK, - # $ M, N - # DOUBLE PRECISION PL, PR - # .. - # .. Array Arguments .. - # LOGICAL SELECT( * ) - # INTEGER IWORK( * ) - # DOUBLE PRECISION DIF( * ) - # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), - # $ BETA( * ), Q( LDQ, * ), WORK( * ), Z( LDZ, * ) - # .. - function tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, - Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) - require_one_based_indexing(select, S, T, Q, Z) - chkstride1(select, S, T, Q, Z) - n, nt, nq, nz = checksquare(S, T, Q, Z) - if n != nt - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and T, ($nt,$nt), must match")) - end - if n != nq - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and Q, ($nq,$nq), must match")) - end - if n != nz - throw(DimensionMismatch(lazy"dimensions of S, ($n,$n), and Z, ($nz,$nz), must match")) - end - lds = max(1, stride(S, 2)) - ldt = max(1, stride(T, 2)) - ldq = max(1, stride(Q, 2)) - ldz = max(1, stride(Z, 2)) - m = sum(select) - alpha = similar(T, $elty, n) - beta = similar(T, $elty, n) - lwork = BlasInt(-1) - work = Vector{$elty}(undef, 1) - liwork = BlasInt(-1) - iwork = Vector{BlasInt}(undef, 1) - info = Ref{BlasInt}() - select = convert(Array{BlasInt}, select) - for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall((@blasfunc($tgsen), libblastrampoline), Cvoid, - (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}), - 0, 1, 1, select, - n, S, lds, T, - ldt, alpha, beta, - Q, ldq, Z, ldz, - m, C_NULL, C_NULL, C_NULL, - work, lwork, iwork, liwork, - info) - chklapackerror(info[]) - if i == 1 # only estimated optimal lwork, liwork - lwork = BlasInt(real(work[1])) - resize!(work, lwork) - liwork = BlasInt(real(iwork[1])) - resize!(iwork, liwork) - end - end - S, T, alpha, beta, Q, Z - end - end -end - -""" - trexc!(compq, ifst, ilst, T, Q) -> (T, Q) - trexc!(ifst, ilst, T, Q) -> (T, Q) - -Reorder the Schur factorization `T` of a matrix, such that the diagonal block -of `T` with row index `ifst` is moved to row index `ilst`. If `compq = V`, the Schur -vectors `Q` are reordered. If `compq = N` they are not modified. The 4-arg method -calls the 5-arg method with `compq = V`. -""" -trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix, Q::AbstractMatrix) - -""" - trsen!(job, compq, select, T, Q) -> (T, Q, w, s, sep) - trsen!(select, T, Q) -> (T, Q, w, s, sep) - -Reorder the Schur factorization of a matrix and optionally finds reciprocal -condition numbers. If `job = N`, no condition numbers are found. If `job = E`, -only the condition number for this cluster of eigenvalues is found. If -`job = V`, only the condition number for the invariant subspace is found. -If `job = B` then the condition numbers for the cluster and subspace are -found. If `compq = V` the Schur vectors `Q` are updated. If `compq = N` -the Schur vectors are not modified. `select` determines which -eigenvalues are in the cluster. The 3-arg method calls the 5-arg method -with `job = N` and `compq = V`. - -Returns `T`, `Q`, reordered eigenvalues in `w`, the condition number of the -cluster of eigenvalues `s`, and the condition number of the invariant subspace -`sep`. -""" -trsen!(compq::AbstractChar, job::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix, Q::AbstractMatrix) - -""" - tgsen!(select, S, T, Q, Z) -> (S, T, alpha, beta, Q, Z) - -Reorders the vectors of a generalized Schur decomposition. `select` specifies -the eigenvalues in each cluster. -""" -tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix, T::AbstractMatrix, Q::AbstractMatrix, Z::AbstractMatrix) - -for (fn, elty, relty) in ((:dtrsyl_, :Float64, :Float64), - (:strsyl_, :Float32, :Float32), - (:ztrsyl_, :ComplexF64, :Float64), - (:ctrsyl_, :ComplexF32, :Float32)) - @eval begin - function trsyl!(transa::AbstractChar, transb::AbstractChar, A::AbstractMatrix{$elty}, - B::AbstractMatrix{$elty}, C::AbstractMatrix{$elty}, isgn::Int=1) - require_one_based_indexing(A, B, C) - chktrans(transa) - chktrans(transb) - chkstride1(A, B, C) - m, n = checksquare(A), checksquare(B) - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - m1, n1 = size(C) - if m != m1 || n != n1 - throw(DimensionMismatch(lazy"dimensions of A, ($m,$n), and C, ($m1,$n1), must match")) - end - ldc = max(1, stride(C, 2)) - scale = Ref{$relty}() - info = Ref{BlasInt}() - ccall((@blasfunc($fn), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ref{BlasInt}, Clong, Clong), - transa, transb, isgn, m, n, - A, lda, B, ldb, C, ldc, - scale, info, 1, 1) - chklapackerror(info[]) - C, scale[] - end - end -end - -""" - trsyl!(transa, transb, A, B, C, isgn=1) -> (C, scale) - -Solves the Sylvester matrix equation `A * X +/- X * B = scale*C` where `A` and -`B` are both quasi-upper triangular. If `transa = N`, `A` is not modified. -If `transa = T`, `A` is transposed. If `transa = C`, `A` is conjugate -transposed. Similarly for `transb` and `B`. If `isgn = 1`, the equation -`A * X + X * B = scale * C` is solved. If `isgn = -1`, the equation -`A * X - X * B = scale * C` is solved. - -Returns `X` (overwriting `C`) and `scale`. -""" -trsyl!(transa::AbstractChar, transb::AbstractChar, A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, isgn::Int=1) - -for (fn, elty) in ((:dlacpy_, :Float64), - (:slacpy_, :Float32), - (:zlacpy_, :ComplexF64), - (:clacpy_, :ComplexF32)) - @eval begin - # SUBROUTINE DLACPY( UPLO, M, N, A, LDA, B, LDB ) - # .. Scalar Arguments .. - # CHARACTER UPLO - # INTEGER LDA, LDB, M, N - # .. - # .. Array Arguments .. - # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) - # .. - function lacpy!(B::AbstractMatrix{$elty}, A::AbstractMatrix{$elty}, uplo::AbstractChar) - require_one_based_indexing(A, B) - chkstride1(A, B) - m, n = size(A) - m1, n1 = size(B) - if uplo == 'U' - lacpy_size_check((m1, n1), (n < m ? n : m, n)) - elseif uplo == 'L' - lacpy_size_check((m1, n1), (m, m < n ? m : n)) - else - lacpy_size_check((m1, n1), (m, n)) - end - lda = max(1, stride(A, 2)) - ldb = max(1, stride(B, 2)) - ccall((@blasfunc($fn), libblastrampoline), Cvoid, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), - uplo, m, n, A, lda, B, ldb, 1) - B - end - end -end - -# The noinline annotation reduces latency -@noinline lacpy_size_check((m1, n1), (m, n)) = (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) - -""" - lacpy!(B, A, uplo) -> B - -Copies all or part of a matrix `A` to another matrix `B`. -uplo specifies the part of the matrix `A` to be copied to `B`. -Set `uplo = 'L'` for the lower triangular part, `uplo = 'U'` -for the upper triangular part, any other character for all -the matrix `A`. - -# Examples -```jldoctest -julia> A = [1. 2. ; 3. 4.]; - -julia> B = [0. 0. ; 0. 0.]; - -julia> LAPACK.lacpy!(B, A, 'U') -2×2 Matrix{Float64}: - 1.0 2.0 - 0.0 4.0 -``` -""" -lacpy!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) - -end # module diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl deleted file mode 100644 index 81d10f930c8c5..0000000000000 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ /dev/null @@ -1,348 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## This file contains libblastrampoline-specific APIs - -# Keep these in sync with `src/libblastrampoline_internal.h` -struct lbt_library_info_t - libname::Cstring - handle::Ptr{Cvoid} - suffix::Cstring - active_forwards::Ptr{UInt8} - interface::Int32 - complex_retstyle::Int32 - f2c::Int32 - cblas::Int32 -end - -macro get_warn(map, key) - return quote - if !haskey($(esc(map)), $(esc(key))) - println(Core.stderr, string("Warning: [LBT] Unknown key into ", $(string(map)), ": ", $(esc(key)), ", defaulting to :unknown")) - # All the unknown values share a common value: `-1` - $(esc(map))[$(esc(LBT_INTERFACE_UNKNOWN))] - else - $(esc(map))[$(esc(key))] - end - end -end - -const LBT_INTERFACE_LP64 = 32 -const LBT_INTERFACE_ILP64 = 64 -const LBT_INTERFACE_UNKNOWN = -1 -const LBT_INTERFACE_MAP = Dict( - LBT_INTERFACE_LP64 => :lp64, - LBT_INTERFACE_ILP64 => :ilp64, - LBT_INTERFACE_UNKNOWN => :unknown, -) -const LBT_INV_INTERFACE_MAP = Dict(v => k for (k, v) in LBT_INTERFACE_MAP) - -const LBT_F2C_PLAIN = 0 -const LBT_F2C_REQUIRED = 1 -const LBT_F2C_UNKNOWN = -1 -const LBT_F2C_MAP = Dict( - LBT_F2C_PLAIN => :plain, - LBT_F2C_REQUIRED => :required, - LBT_F2C_UNKNOWN => :unknown, -) -const LBT_INV_F2C_MAP = Dict(v => k for (k, v) in LBT_F2C_MAP) - -const LBT_COMPLEX_RETSTYLE_NORMAL = 0 -const LBT_COMPLEX_RETSTYLE_ARGUMENT = 1 -const LBT_COMPLEX_RETSTYLE_FNDA = 2 -const LBT_COMPLEX_RETSTYLE_UNKNOWN = -1 -const LBT_COMPLEX_RETSTYLE_MAP = Dict( - LBT_COMPLEX_RETSTYLE_NORMAL => :normal, - LBT_COMPLEX_RETSTYLE_ARGUMENT => :argument, - LBT_COMPLEX_RETSTYLE_FNDA => :float_normal_double_argument, - LBT_COMPLEX_RETSTYLE_UNKNOWN => :unknown, -) -const LBT_INV_COMPLEX_RETSTYLE_MAP = Dict(v => k for (k, v) in LBT_COMPLEX_RETSTYLE_MAP) - -const LBT_CBLAS_CONFORMANT = 0 -const LBT_CBLAS_DIVERGENT = 1 -const LBT_CBLAS_UNKNOWN = -1 -const LBT_CBLAS_MAP = Dict( - LBT_CBLAS_CONFORMANT => :conformant, - LBT_CBLAS_DIVERGENT => :divergent, - LBT_CBLAS_UNKNOWN => :unknown, -) -const LBT_INV_CBLAS_MAP = Dict(v => k for (k, v) in LBT_CBLAS_MAP) - -struct LBTLibraryInfo - libname::String - handle::Ptr{Cvoid} - suffix::String - active_forwards::Vector{UInt8} - interface::Symbol - complex_retstyle::Symbol - f2c::Symbol - cblas::Symbol - - function LBTLibraryInfo(lib_info::lbt_library_info_t, num_exported_symbols::UInt32) - return new( - unsafe_string(lib_info.libname), - lib_info.handle, - unsafe_string(lib_info.suffix), - unsafe_wrap(Vector{UInt8}, lib_info.active_forwards, div(num_exported_symbols,8)+1), - @get_warn(LBT_INTERFACE_MAP, lib_info.interface), - @get_warn(LBT_COMPLEX_RETSTYLE_MAP, lib_info.complex_retstyle), - @get_warn(LBT_F2C_MAP, lib_info.f2c), - @get_warn(LBT_CBLAS_MAP, lib_info.cblas), - ) - end -end - -struct lbt_config_t - loaded_libs::Ptr{Ptr{lbt_library_info_t}} - build_flags::UInt32 - exported_symbols::Ptr{Cstring} - num_exported_symbols::UInt32 -end -const LBT_BUILDFLAGS_DEEPBINDLESS = 0x01 -const LBT_BUILDFLAGS_F2C_CAPABLE = 0x02 -const LBT_BUILDFLAGS_CBLAS_DIVERGENCE = 0x04 -const LBT_BUILDFLAGS_COMPLEX_RETSTYLE = 0x08 -const LBT_BUILDFLAGS_SYMBOL_TRIMMING = 0x10 -const LBT_BUILDFLAGS_MAP = Dict( - LBT_BUILDFLAGS_DEEPBINDLESS => :deepbindless, - LBT_BUILDFLAGS_F2C_CAPABLE => :f2c_capable, - LBT_BUILDFLAGS_CBLAS_DIVERGENCE => :cblas_divergence, - LBT_BUILDFLAGS_COMPLEX_RETSTYLE => :complex_retstyle, - LBT_BUILDFLAGS_SYMBOL_TRIMMING => :symbol_trimming, -) - -struct LBTConfig - loaded_libs::Vector{LBTLibraryInfo} - build_flags::Vector{Symbol} - exported_symbols::Vector{String} - - function LBTConfig(config::lbt_config_t) - # Decode OR'ed flags into a list of names - build_flag_names = Symbol[] - for (flag, name) in LBT_BUILDFLAGS_MAP - if config.build_flags & flag != 0x00 - push!(build_flag_names, name) - end - end - - # Load all exported symbol names - exported_symbols = String[] - for sym_idx in 1:config.num_exported_symbols - str_ptr = unsafe_load(config.exported_symbols, sym_idx) - if str_ptr != C_NULL - push!(exported_symbols, unsafe_string(str_ptr)) - else - println(Core.stderr, "Error: NULL string in lbt_config.exported_symbols[$(sym_idx)]") - end - end - - # Unpack library info structures - libs = LBTLibraryInfo[] - idx = 1 - lib_ptr = unsafe_load(config.loaded_libs, idx) - while lib_ptr != C_NULL - push!(libs, LBTLibraryInfo(unsafe_load(lib_ptr), config.num_exported_symbols)) - - idx += 1 - lib_ptr = unsafe_load(config.loaded_libs, idx) - end - return new( - libs, - build_flag_names, - exported_symbols, - ) - end -end - -Base.show(io::IO, lbt::LBTLibraryInfo) = print(io, "LBTLibraryInfo(", basename(lbt.libname), ", ", lbt.interface, ")") -function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, lbt::LBTLibraryInfo) - summary(io, lbt); println(io) - println(io, "├ Library: ", basename(lbt.libname)) - println(io, "├ Interface: ", lbt.interface) - println(io, "├ Complex return style: ", lbt.complex_retstyle) - println(io, "├ F2C: ", lbt.f2c) - print(io, "└ CBLAS: ", lbt.cblas) -end - -function Base.show(io::IO, lbt::LBTConfig) - if length(lbt.loaded_libs) <= 3 - print(io, "LBTConfig(") - gen = (string("[", uppercase(string(l.interface)), "] ", - basename(l.libname)) for l in lbt.loaded_libs) - print(io, join(gen, ", ")) - print(io, ")") - else - print(io, "LBTConfig(...)") - end -end -function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, lbt::LBTConfig) - summary(io, lbt); println(io) - println(io, "Libraries: ") - for (i,l) in enumerate(lbt.loaded_libs) - char = i == length(lbt.loaded_libs) ? "└" : "├" - interface_str = if l.interface === :ilp64 - "ILP64" - elseif l.interface === :lp64 - " LP64" - else - "UNKWN" - end - print(io, char, " [", interface_str,"] ", basename(l.libname)) - i !== length(lbt.loaded_libs) && println() - end -end - -mutable struct ConfigCache - @atomic config::Union{Nothing,LBTConfig} - lock::ReentrantLock -end - -# In the event that users want to call `lbt_get_config()` multiple times (e.g. for -# runtime checks of which BLAS vendor is providing a symbol), let's cache the value -# and clear it only when someone calls something that would cause it to change. -const _CACHED_CONFIG = ConfigCache(nothing, ReentrantLock()) - -function lbt_get_config() - config = @atomic :acquire _CACHED_CONFIG.config - config === nothing || return config - return lock(_CACHED_CONFIG.lock) do - local config = @atomic :monotonic _CACHED_CONFIG.config - config === nothing || return config - config_ptr = ccall((:lbt_get_config, libblastrampoline), Ptr{lbt_config_t}, ()) - @atomic :release _CACHED_CONFIG.config = LBTConfig(unsafe_load(config_ptr)) - end -end - -function _clear_config_with(f) - lock(_CACHED_CONFIG.lock) do - @atomic :release _CACHED_CONFIG.config = nothing - f() - end -end - -function lbt_get_num_threads() - return ccall((:lbt_get_num_threads, libblastrampoline), Int32, ()) -end - -function lbt_set_num_threads(nthreads) - return ccall((:lbt_set_num_threads, libblastrampoline), Cvoid, (Int32,), nthreads) -end - -function lbt_forward(path::AbstractString; clear::Bool = false, verbose::Bool = false, suffix_hint::Union{String,Nothing} = nothing) - _clear_config_with() do - return ccall((:lbt_forward, libblastrampoline), Int32, (Cstring, Int32, Int32, Cstring), - path, clear ? 1 : 0, verbose ? 1 : 0, something(suffix_hint, C_NULL)) - end -end - -function lbt_set_default_func(addr) - _clear_config_with() do - return ccall((:lbt_set_default_func, libblastrampoline), Cvoid, (Ptr{Cvoid},), addr) - end -end - -function lbt_get_default_func() - return ccall((:lbt_get_default_func, libblastrampoline), Ptr{Cvoid}, ()) -end - -""" - lbt_find_backing_library(symbol_name, interface; config::LBTConfig = lbt_get_config()) - -Return the `LBTLibraryInfo` that represents the backing library for the given symbol -exported from libblastrampoline. This allows us to discover which library will service -a particular BLAS call from Julia code. This method returns `nothing` if either of the -following conditions are met: - - * No loaded library exports the desired symbol (the default function will be called) - * The symbol was set via `lbt_set_forward()`, which does not track library provenance. - -If the given `symbol_name` is not contained within the list of exported symbols, an -`ArgumentError` will be thrown. -""" -function lbt_find_backing_library(symbol_name, interface::Symbol; - config::LBTConfig = lbt_get_config()) - if interface ∉ (:ilp64, :lp64) - throw(ArgumentError(lazy"Invalid interface specification: '$(interface)'")) - end - symbol_idx = findfirst(s -> s == symbol_name, config.exported_symbols) - if symbol_idx === nothing - throw(ArgumentError(lazy"Invalid exported symbol name '$(symbol_name)'")) - end - # Convert to zero-indexed - symbol_idx -= 1 - - forward_byte_offset = div(symbol_idx, 8) - forward_byte_mask = 1 << mod(symbol_idx, 8) - for lib in filter(l -> l.interface == interface, config.loaded_libs) - if lib.active_forwards[forward_byte_offset+1] & forward_byte_mask != 0x00 - return lib - end - end - - # No backing library was found - return nothing -end - - -""" - lbt_forwarded_funcs(config::LBTConfig, lib::LBTLibraryInfo) - -Given a backing library `lib`, return the list of all functions that are -forwarded to that library, as a vector of `String`s. -""" -function lbt_forwarded_funcs(config::LBTConfig, lib::LBTLibraryInfo) - forwarded_funcs = String[] - for (symbol_idx, symbol) in enumerate(config.exported_symbols) - forward_byte_offset = div(symbol_idx - 1, 8) - forward_byte_mask = 1 << mod(symbol_idx - 1, 8) - if lib.active_forwards[forward_byte_offset+1] & forward_byte_mask != 0x00 - push!(forwarded_funcs, symbol) - end - end - return forwarded_funcs -end - - -## NOTE: Manually setting forwards is referred to as the 'footgun API'. It allows truly -## bizarre and complex setups to be created. If you run into strange errors while using -## it, the first thing you should ask yourself is whether you've set things up properly. -function lbt_set_forward(symbol_name, addr, interface, - complex_retstyle = LBT_COMPLEX_RETSTYLE_NORMAL, - f2c = LBT_F2C_PLAIN; verbose::Bool = false) - _clear_config_with() do - return ccall( - (:lbt_set_forward, libblastrampoline), - Int32, - (Cstring, Ptr{Cvoid}, Int32, Int32, Int32, Int32), - string(symbol_name), - addr, - Int32(interface), - Int32(complex_retstyle), - Int32(f2c), - verbose ? Int32(1) : Int32(0), - ) - end -end -function lbt_set_forward(symbol_name, addr, interface::Symbol, - complex_retstyle::Symbol = :normal, - f2c::Symbol = :plain; kwargs...) - return lbt_set_forward(symbol_name, addr, - LBT_INV_INTERFACE_MAP[interface], - LBT_INV_COMPLEX_RETSTYLE_MAP[complex_retstyle], - LBT_INV_F2C_MAP[f2c]; - kwargs...) -end - -function lbt_get_forward(symbol_name, interface, f2c = LBT_F2C_PLAIN) - return ccall( - (:lbt_get_forward, libblastrampoline), - Ptr{Cvoid}, - (Cstring, Int32, Int32), - string(symbol_name), - Int32(interface), - Int32(f2c), - ) -end -function lbt_get_forward(symbol_name, interface::Symbol, f2c::Symbol = :plain) - return lbt_get_forward(symbol_name, LBT_INV_INTERFACE_MAP[interface], LBT_INV_F2C_MAP[f2c]) -end diff --git a/stdlib/LinearAlgebra/src/ldlt.jl b/stdlib/LinearAlgebra/src/ldlt.jl deleted file mode 100644 index 89e57d0dd27eb..0000000000000 --- a/stdlib/LinearAlgebra/src/ldlt.jl +++ /dev/null @@ -1,224 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - LDLt <: Factorization - -Matrix factorization type of the `LDLt` factorization of a real [`SymTridiagonal`](@ref) -matrix `S` such that `S = L*Diagonal(d)*L'`, where `L` is a [`UnitLowerTriangular`](@ref) -matrix and `d` is a vector. The main use of an `LDLt` factorization `F = ldlt(S)` -is to solve the linear system of equations `Sx = b` with `F\\b`. This is the -return type of [`ldlt`](@ref), the corresponding matrix factorization function. - -The individual components of the factorization `F::LDLt` can be accessed via `getproperty`: - -| Component | Description | -|:---------:|:--------------------------------------------| -| `F.L` | `L` (unit lower triangular) part of `LDLt` | -| `F.D` | `D` (diagonal) part of `LDLt` | -| `F.Lt` | `Lt` (unit upper triangular) part of `LDLt` | -| `F.d` | diagonal values of `D` as a `Vector` | - -# Examples -```jldoctest -julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 3.0 1.0 ⋅ - 1.0 4.0 2.0 - ⋅ 2.0 5.0 - -julia> F = ldlt(S) -LDLt{Float64, SymTridiagonal{Float64, Vector{Float64}}} -L factor: -3×3 UnitLowerTriangular{Float64, SymTridiagonal{Float64, Vector{Float64}}}: - 1.0 ⋅ ⋅ - 0.333333 1.0 ⋅ - 0.0 0.545455 1.0 -D factor: -3×3 Diagonal{Float64, Vector{Float64}}: - 3.0 ⋅ ⋅ - ⋅ 3.66667 ⋅ - ⋅ ⋅ 3.90909 -``` -""" -struct LDLt{T,S<:AbstractMatrix{T}} <: Factorization{T} - data::S - - function LDLt{T,S}(data) where {T,S<:AbstractMatrix{T}} - require_one_based_indexing(data) - new{T,S}(data) - end -end -LDLt(data::AbstractMatrix{T}) where {T} = LDLt{T,typeof(data)}(data) -LDLt{T}(data::AbstractMatrix) where {T} = LDLt(convert(AbstractMatrix{T}, data)::AbstractMatrix{T}) - -size(S::LDLt) = size(S.data) -size(S::LDLt, i::Integer) = size(S.data, i) - -LDLt{T,S}(F::LDLt{T,S}) where {T,S<:AbstractMatrix{T}} = F -LDLt{T,S}(F::LDLt) where {T,S<:AbstractMatrix{T}} = LDLt{T,S}(convert(S, F.data)::S) -LDLt{T}(F::LDLt{T}) where {T} = F -LDLt{T}(F::LDLt) where {T} = LDLt(convert(AbstractMatrix{T}, F.data)::AbstractMatrix{T}) - -Factorization{T}(F::LDLt{T}) where {T} = F -Factorization{T}(F::LDLt) where {T} = LDLt{T}(F) - -function getproperty(F::LDLt{<:Any, <:SymTridiagonal}, d::Symbol) - Fdata = getfield(F, :data) - if d === :d - return Fdata.dv - elseif d === :D - return Diagonal(Fdata.dv) - elseif d === :L - return UnitLowerTriangular(Fdata) - elseif d === :Lt - return UnitUpperTriangular(Fdata) - else - return getfield(F, d) - end -end - -adjoint(F::LDLt{<:Real,<:SymTridiagonal}) = F -adjoint(F::LDLt) = LDLt(copy(adjoint(F.data))) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LDLt) - summary(io, F); println(io) - println(io, "L factor:") - show(io, mime, F.L) - println(io, "\nD factor:") - show(io, mime, F.D) -end - -# SymTridiagonal -""" - ldlt!(S::SymTridiagonal) -> LDLt - -Same as [`ldlt`](@ref), but saves space by overwriting the input `S`, instead of creating a copy. - -# Examples -```jldoctest -julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 3.0 1.0 ⋅ - 1.0 4.0 2.0 - ⋅ 2.0 5.0 - -julia> ldltS = ldlt!(S); - -julia> ldltS === S -false - -julia> S -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 3.0 0.333333 ⋅ - 0.333333 3.66667 0.545455 - ⋅ 0.545455 3.90909 -``` -""" -function ldlt!(S::SymTridiagonal{T,V}) where {T,V} - n = size(S,1) - d = S.dv - e = S.ev - @inbounds for i in 1:n-1 - iszero(d[i]) && throw(ZeroPivotException(i)) - e[i] /= d[i] - d[i+1] -= e[i]^2*d[i] - end - return LDLt{T,SymTridiagonal{T,V}}(S) -end - -""" - ldlt(S::SymTridiagonal) -> LDLt - -Compute an `LDLt` (i.e., ``LDL^T``) factorization of the real symmetric tridiagonal matrix `S` such that `S = L*Diagonal(d)*L'` -where `L` is a unit lower triangular matrix and `d` is a vector. The main use of an `LDLt` -factorization `F = ldlt(S)` is to solve the linear system of equations `Sx = b` with `F\\b`. - -See also [`bunchkaufman`](@ref) for a similar, but pivoted, factorization of arbitrary symmetric or Hermitian matrices. - -# Examples -```jldoctest -julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 3.0 1.0 ⋅ - 1.0 4.0 2.0 - ⋅ 2.0 5.0 - -julia> ldltS = ldlt(S); - -julia> b = [6., 7., 8.]; - -julia> ldltS \\ b -3-element Vector{Float64}: - 1.7906976744186047 - 0.627906976744186 - 1.3488372093023255 - -julia> S \\ b -3-element Vector{Float64}: - 1.7906976744186047 - 0.627906976744186 - 1.3488372093023255 -``` -""" -function ldlt(M::SymTridiagonal{T}; shift::Number=false) where T - S = typeof((zero(T)+shift)/one(T)) - Mₛ = SymTridiagonal{S}(copymutable_oftype(M.dv, S), copymutable_oftype(M.ev, S)) - if !iszero(shift) - Mₛ.dv .+= shift - end - return ldlt!(Mₛ) -end - -factorize(S::SymTridiagonal) = ldlt(S) - -function ldiv!(S::LDLt{<:Any,<:SymTridiagonal}, B::AbstractVecOrMat) - require_one_based_indexing(B) - n, nrhs = size(B, 1), size(B, 2) - if size(S,1) != n - throw(DimensionMismatch(lazy"Matrix has dimensions $(size(S)) but right hand side has first dimension $n")) - end - d = S.data.dv - l = S.data.ev - @inbounds begin - for i = 2:n - li1 = l[i-1] - @simd for j = 1:nrhs - B[i,j] -= li1*B[i-1,j] - end - end - dn = d[n] - @simd for j = 1:nrhs - B[n,j] /= dn - end - for i = n-1:-1:1 - di = d[i] - li = l[i] - @simd for j = 1:nrhs - B[i,j] /= di - B[i,j] -= li*B[i+1,j] - end - end - end - return B -end - -rdiv!(B::AbstractVecOrMat, S::LDLt{<:Any,<:SymTridiagonal}) = - transpose(ldiv!(S, transpose(B))) - -function logabsdet(F::LDLt{<:Any,<:SymTridiagonal}) - it = (F.data[i,i] for i in 1:size(F, 1)) - return sum(log∘abs, it), prod(sign, it) -end - -# Conversion methods -function SymTridiagonal(F::LDLt{<:Any, <:SymTridiagonal}) - e = copy(F.data.ev) - d = copy(F.data.dv) - e .*= d[1:end-1] - d[2:end] += e .* F.data.ev - SymTridiagonal(d, e) -end -AbstractMatrix(F::LDLt) = SymTridiagonal(F) -AbstractArray(F::LDLt) = AbstractMatrix(F) -Matrix(F::LDLt) = Array(AbstractArray(F)) -Array(F::LDLt) = Matrix(F) diff --git a/stdlib/LinearAlgebra/src/lq.jl b/stdlib/LinearAlgebra/src/lq.jl deleted file mode 100644 index 07d918c4374a5..0000000000000 --- a/stdlib/LinearAlgebra/src/lq.jl +++ /dev/null @@ -1,203 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# LQ Factorizations -""" - LQ <: Factorization - -Matrix factorization type of the `LQ` factorization of a matrix `A`. The `LQ` -decomposition is the [`QR`](@ref) decomposition of `transpose(A)`. This is the return -type of [`lq`](@ref), the corresponding matrix factorization function. - -If `S::LQ` is the factorization object, the lower triangular component can be -obtained via `S.L`, and the orthogonal/unitary component via `S.Q`, such that -`A ≈ S.L*S.Q`. - -Iterating the decomposition produces the components `S.L` and `S.Q`. - -# Examples -```jldoctest -julia> A = [5. 7.; -2. -4.] -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> S = lq(A) -LQ{Float64, Matrix{Float64}, Vector{Float64}} -L factor: -2×2 Matrix{Float64}: - -8.60233 0.0 - 4.41741 -0.697486 -Q factor: 2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} - -julia> S.L * S.Q -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> l, q = S; # destructuring via iteration - -julia> l == S.L && q == S.Q -true -``` -""" -struct LQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: Factorization{T} - factors::S - τ::C - - function LQ{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} - require_one_based_indexing(factors) - new{T,S,C}(factors, τ) - end -end -LQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = - LQ{T,typeof(factors),typeof(τ)}(factors, τ) -LQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} = - LQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(LQ{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S}, - LQ{T,S,typeof(τ)}(factors, τ), false) - -# iteration for destructuring into components -Base.iterate(S::LQ) = (S.L, Val(:Q)) -Base.iterate(S::LQ, ::Val{:Q}) = (S.Q, Val(:done)) -Base.iterate(S::LQ, ::Val{:done}) = nothing - -""" - lq!(A) -> LQ - -Compute the [`LQ`](@ref) factorization of `A`, using the input -matrix as a workspace. See also [`lq`](@ref). -""" -lq!(A::StridedMatrix{<:BlasFloat}) = LQ(LAPACK.gelqf!(A)...) - -""" - lq(A) -> S::LQ - -Compute the LQ decomposition of `A`. The decomposition's lower triangular -component can be obtained from the [`LQ`](@ref) object `S` via `S.L`, and the -orthogonal/unitary component via `S.Q`, such that `A ≈ S.L*S.Q`. - -Iterating the decomposition produces the components `S.L` and `S.Q`. - -The LQ decomposition is the QR decomposition of `transpose(A)`, and it is useful -in order to compute the minimum-norm solution `lq(A) \\ b` to an underdetermined -system of equations (`A` has more columns than rows, but has full row rank). - -# Examples -```jldoctest -julia> A = [5. 7.; -2. -4.] -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> S = lq(A) -LQ{Float64, Matrix{Float64}, Vector{Float64}} -L factor: -2×2 Matrix{Float64}: - -8.60233 0.0 - 4.41741 -0.697486 -Q factor: 2×2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} - -julia> S.L * S.Q -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> l, q = S; # destructuring via iteration - -julia> l == S.L && q == S.Q -true -``` -""" -lq(A::AbstractMatrix{T}) where {T} = lq!(copy_similar(A, lq_eltype(T))) -lq(x::Number) = lq!(fill(convert(lq_eltype(typeof(x)), x), 1, 1)) - -lq_eltype(::Type{T}) where {T} = typeof(zero(T) / sqrt(abs2(one(T)))) - -copy(A::LQ) = LQ(copy(A.factors), copy(A.τ)) - -LQ{T}(A::LQ) where {T} = LQ(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ)) -Factorization{T}(A::LQ) where {T} = LQ{T}(A) - -AbstractMatrix(A::LQ) = A.L*A.Q -AbstractArray(A::LQ) = AbstractMatrix(A) -Matrix(A::LQ) = Array(AbstractArray(A)) -Array(A::LQ) = Matrix(A) - -transpose(F::LQ{<:Real}) = F' -transpose(::LQ) = - throw(ArgumentError("transpose of LQ decomposition is not supported, consider using adjoint")) - -Base.copy(F::AdjointFactorization{T,<:LQ{T}}) where {T} = - QR{T,typeof(F.parent.factors),typeof(F.parent.τ)}(copy(adjoint(F.parent.factors)), copy(F.parent.τ)) - -function getproperty(F::LQ, d::Symbol) - m, n = size(F) - if d === :L - return tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) - elseif d === :Q - return LQPackedQ(getfield(F, :factors), getfield(F, :τ)) - else - return getfield(F, d) - end -end - -Base.propertynames(F::LQ, private::Bool=false) = - (:L, :Q, (private ? fieldnames(typeof(F)) : ())...) - -# getindex(A::LQPackedQ, i::Integer, j::Integer) = -# lmul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i] - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LQ) - summary(io, F); println(io) - println(io, "L factor:") - show(io, mime, F.L) - print(io, "\nQ factor: ") - show(io, mime, F.Q) -end - -size(F::LQ, dim::Integer) = size(getfield(F, :factors), dim) -size(F::LQ) = size(getfield(F, :factors)) - -## Multiplication by LQ -function lmul!(A::LQ, B::AbstractVecOrMat) - lmul!(LowerTriangular(A.L), view(lmul!(A.Q, B), 1:size(A,1), axes(B,2))) - return B -end -function *(A::LQ{TA}, B::AbstractVecOrMat{TB}) where {TA,TB} - TAB = promote_type(TA, TB) - _cut_B(lmul!(convert(Factorization{TAB}, A), copy_similar(B, TAB)), 1:size(A,1)) -end - -# With a real lhs and complex rhs with the same precision, we can reinterpret -# the complex rhs as a real rhs with twice the number of columns -function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal - require_one_based_indexing(B) - X = zeros(T, size(F,2), 2*size(B,2)) - X[1:size(B,1), 1:size(B,2)] .= real.(B) - X[1:size(B,1), size(B,2)+1:size(X,2)] .= imag.(B) - ldiv!(F, X) - return reshape(copy(reinterpret(Complex{T}, copy(transpose(reshape(X, div(length(X), 2), 2))))), - isa(B, AbstractVector) ? (size(F,2),) : (size(F,2), size(B,2))) -end - - -function ldiv!(A::LQ, B::AbstractVecOrMat) - require_one_based_indexing(B) - m, n = size(A) - m ≤ n || throw(DimensionMismatch("LQ solver does not support overdetermined systems (more rows than columns)")) - - ldiv!(LowerTriangular(A.L), view(B, 1:size(A,1), axes(B,2))) - return lmul!(adjoint(A.Q), B) -end - -function ldiv!(Fadj::AdjointFactorization{<:Any,<:LQ}, B::AbstractVecOrMat) - require_one_based_indexing(B) - m, n = size(Fadj) - m >= n || throw(DimensionMismatch("solver does not support underdetermined systems (more columns than rows)")) - - F = parent(Fadj) - lmul!(F.Q, B) - ldiv!(UpperTriangular(adjoint(F.L)), view(B, 1:size(F,1), axes(B,2))) - return B -end diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl deleted file mode 100644 index 0837ac08e74ea..0000000000000 --- a/stdlib/LinearAlgebra/src/lu.jl +++ /dev/null @@ -1,834 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -#################### -# LU Factorization # -#################### -""" - LU <: Factorization - -Matrix factorization type of the `LU` factorization of a square matrix `A`. This -is the return type of [`lu`](@ref), the corresponding matrix factorization function. - -The individual components of the factorization `F::LU` can be accessed via [`getproperty`](@ref): - -| Component | Description | -|:----------|:-----------------------------------------| -| `F.L` | `L` (unit lower triangular) part of `LU` | -| `F.U` | `U` (upper triangular) part of `LU` | -| `F.p` | (right) permutation `Vector` | -| `F.P` | (right) permutation `Matrix` | - -Iterating the factorization produces the components `F.L`, `F.U`, and `F.p`. - -# Examples - -```jldoctest -julia> A = [4 3; 6 3] -2×2 Matrix{Int64}: - 4 3 - 6 3 - -julia> F = lu(A) -LU{Float64, Matrix{Float64}, Vector{Int64}} -L factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 0.666667 1.0 -U factor: -2×2 Matrix{Float64}: - 6.0 3.0 - 0.0 1.0 - -julia> F.L * F.U == A[F.p, :] -true - -julia> l, u, p = lu(A); # destructuring via iteration - -julia> l == F.L && u == F.U && p == F.p -true -``` -""" -struct LU{T,S<:AbstractMatrix{T},P<:AbstractVector{<:Integer}} <: Factorization{T} - factors::S - ipiv::P - info::BlasInt # Can be negative to indicate failed unpivoted factorization - - function LU{T,S,P}(factors, ipiv, info) where {T, S<:AbstractMatrix{T}, P<:AbstractVector{<:Integer}} - require_one_based_indexing(factors) - new{T,S,P}(factors, ipiv, info) - end -end -LU(factors::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, info::BlasInt) where {T} = - LU{T,typeof(factors),typeof(ipiv)}(factors, ipiv, info) -LU{T}(factors::AbstractMatrix, ipiv::AbstractVector{<:Integer}, info::Integer) where {T} = - LU(convert(AbstractMatrix{T}, factors), ipiv, BlasInt(info)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(LU{T,S}(factors::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, - info::BlasInt) where {T,S}, - LU{T,S,typeof(ipiv)}(factors, ipiv, info), false) - -# iteration for destructuring into components -Base.iterate(S::LU) = (S.L, Val(:U)) -Base.iterate(S::LU, ::Val{:U}) = (S.U, Val(:p)) -Base.iterate(S::LU, ::Val{:p}) = (S.p, Val(:done)) -Base.iterate(S::LU, ::Val{:done}) = nothing - -# LU prefers transpose over adjoint in the real case, override the generic fallback -adjoint(F::LU{<:Real}) = TransposeFactorization(F) -transpose(F::LU{<:Real}) = TransposeFactorization(F) - -function _check_lu_success(info, allowsingular) - if info < 0 # zero pivot error from unpivoted LU - checknozeropivot(-info) - else - allowsingular || checknonsingular(info) - end -end - -# the following method is meant to catch calls to lu!(A::LAPACKArray) without a pivoting strategy -lu!(A::StridedMatrix{<:BlasFloat}; check::Bool = true, allowsingular::Bool = false) = lu!(A, RowMaximum(); check, allowsingular) -function lu!(A::StridedMatrix{T}, ::RowMaximum; check::Bool = true, allowsingular::Bool = false) where {T<:BlasFloat} - lpt = LAPACK.getrf!(A; check) - check && _check_lu_success(lpt[3], allowsingular) - return LU{T,typeof(lpt[1]),typeof(lpt[2])}(lpt[1], lpt[2], lpt[3]) -end -function lu!(A::HermOrSym{T}, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(T); - check::Bool = true, allowsingular::Bool = false) where {T} - copytri!(A.data, A.uplo, isa(A, Hermitian)) - @inbounds if isa(A, Hermitian) # realify diagonal - for i in axes(A, 1) - A.data[i,i] = A[i,i] - end - end - lu!(A.data, pivot; check, allowsingular) -end -# for backward compatibility -# TODO: remove towards Julia v2 -@deprecate lu!(A::Union{StridedMatrix,HermOrSym,Tridiagonal}, ::Val{true}; check::Bool = true) lu!(A, RowMaximum(); check=check) -@deprecate lu!(A::Union{StridedMatrix,HermOrSym,Tridiagonal}, ::Val{false}; check::Bool = true) lu!(A, NoPivot(); check=check) - -""" - lu!(A, pivot = RowMaximum(); check = true, allowsingular = false) -> LU - -`lu!` is the same as [`lu`](@ref), but saves space by overwriting the -input `A`, instead of creating a copy. An [`InexactError`](@ref) -exception is thrown if the factorization produces a number not representable by the -element type of `A`, e.g. for integer types. - -!!! compat "Julia 1.11" - The `allowsingular` keyword argument was added in Julia 1.11. - -# Examples -```jldoctest -julia> A = [4. 3.; 6. 3.] -2×2 Matrix{Float64}: - 4.0 3.0 - 6.0 3.0 - -julia> F = lu!(A) -LU{Float64, Matrix{Float64}, Vector{Int64}} -L factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 0.666667 1.0 -U factor: -2×2 Matrix{Float64}: - 6.0 3.0 - 0.0 1.0 - -julia> iA = [4 3; 6 3] -2×2 Matrix{Int64}: - 4 3 - 6 3 - -julia> lu!(iA) -ERROR: InexactError: Int64(0.6666666666666666) -Stacktrace: -[...] -``` -""" -lu!(A::AbstractMatrix, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(eltype(A)); - check::Bool = true, allowsingular::Bool = false) = generic_lufact!(A, pivot; check, allowsingular) -function generic_lufact!(A::AbstractMatrix{T}, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(T); - check::Bool = true, allowsingular::Bool = false) where {T} - check && LAPACK.chkfinite(A) - # Extract values - m, n = size(A) - minmn = min(m,n) - - # Initialize variables - info = 0 - ipiv = Vector{BlasInt}(undef, minmn) - @inbounds begin - for k = 1:minmn - # find index max - kp = k - if pivot === RowMaximum() && k < m - amax = abs(A[k, k]) - for i = k+1:m - absi = abs(A[i,k]) - if absi > amax - kp = i - amax = absi - end - end - elseif pivot === RowNonZero() - for i = k:m - if !iszero(A[i,k]) - kp = i - break - end - end - end - ipiv[k] = kp - if !iszero(A[kp,k]) - if k != kp - # Interchange - for i = 1:n - tmp = A[k,i] - A[k,i] = A[kp,i] - A[kp,i] = tmp - end - end - # Scale first column - Akkinv = inv(A[k,k]) - for i = k+1:m - A[i,k] *= Akkinv - end - elseif info == 0 - info = k - end - # Update the rest - for j = k+1:n - for i = k+1:m - A[i,j] -= A[i,k]*A[k,j] - end - end - end - end - if pivot === NoPivot() - # Use a negative value to distinguish a failed factorization (zero in pivot - # position during unpivoted LU) from a valid but rank-deficient factorization - info = -info - end - check && _check_lu_success(info, allowsingular) - return LU{T,typeof(A),typeof(ipiv)}(A, ipiv, convert(BlasInt, info)) -end - -function lutype(T::Type) - # In generic_lufact!, the elements of the lower part of the matrix are - # obtained using the division of two matrix elements. Hence their type can - # be different (e.g. the division of two types with the same unit is a type - # without unit). - # The elements of the upper part are obtained by U - U * L - # where U is an upper part element and L is a lower part element. - # Therefore, the types LT, UT should be invariant under the map: - # (LT, UT) -> begin - # L = oneunit(UT) / oneunit(UT) - # U = oneunit(UT) - oneunit(UT) * L - # typeof(L), typeof(U) - # end - # The following should handle most cases - UT = typeof(oneunit(T) - oneunit(T) * (oneunit(T) / (oneunit(T) + zero(T)))) - LT = typeof(oneunit(UT) / oneunit(UT)) - S = promote_type(T, LT, UT) -end - -lupivottype(::Type{T}) where {T} = RowMaximum() - -# for all other types we must promote to a type which is stable under division -""" - lu(A, pivot = RowMaximum(); check = true, allowsingular = false) -> F::LU - -Compute the LU factorization of `A`. - -When `check = true`, an error is thrown if the decomposition fails. -When `check = false`, responsibility for checking the decomposition's -validity (via [`issuccess`](@ref)) lies with the user. - -By default, with `check = true`, an error is also thrown when the decomposition -produces valid factors, but the upper-triangular factor `U` is rank-deficient. This may be changed by -passing `allowsingular = true`. - -In most cases, if `A` is a subtype `S` of `AbstractMatrix{T}` with an element -type `T` supporting `+`, `-`, `*` and `/`, the return type is `LU{T,S{T}}`. - -In general, LU factorization involves a permutation of the rows of the matrix -(corresponding to the `F.p` output described below), known as "pivoting" (because it -corresponds to choosing which row contains the "pivot", the diagonal entry of `F.U`). -One of the following pivoting strategies can be selected via the optional `pivot` argument: - -* `RowMaximum()` (default): the standard pivoting strategy; the pivot corresponds - to the element of maximum absolute value among the remaining, to be factorized rows. - This pivoting strategy requires the element type to also support [`abs`](@ref) and - [`<`](@ref). (This is generally the only numerically stable option for floating-point - matrices.) -* `RowNonZero()`: the pivot corresponds to the first non-zero element among the remaining, - to be factorized rows. (This corresponds to the typical choice in hand calculations, and - is also useful for more general algebraic number types that support [`iszero`](@ref) but - not `abs` or `<`.) -* `NoPivot()`: pivoting turned off (will fail if a zero entry is encountered in - a pivot position, even when `allowsingular = true`). - -The individual components of the factorization `F` can be accessed via [`getproperty`](@ref): - -| Component | Description | -|:----------|:------------------------------------| -| `F.L` | `L` (lower triangular) part of `LU` | -| `F.U` | `U` (upper triangular) part of `LU` | -| `F.p` | (right) permutation `Vector` | -| `F.P` | (right) permutation `Matrix` | - -Iterating the factorization produces the components `F.L`, `F.U`, and `F.p`. - -The relationship between `F` and `A` is - -`F.L*F.U == A[F.p, :]` - -`F` further supports the following functions: - -| Supported function | `LU` | `LU{T,Tridiagonal{T}}` | -|:---------------------------------|:-----|:-----------------------| -| [`/`](@ref) | ✓ | | -| [`\\`](@ref) | ✓ | ✓ | -| [`inv`](@ref) | ✓ | ✓ | -| [`det`](@ref) | ✓ | ✓ | -| [`logdet`](@ref) | ✓ | ✓ | -| [`logabsdet`](@ref) | ✓ | ✓ | -| [`size`](@ref) | ✓ | ✓ | - -!!! compat "Julia 1.11" - The `allowsingular` keyword argument was added in Julia 1.11. - -# Examples -```jldoctest -julia> A = [4 3; 6 3] -2×2 Matrix{Int64}: - 4 3 - 6 3 - -julia> F = lu(A) -LU{Float64, Matrix{Float64}, Vector{Int64}} -L factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 0.666667 1.0 -U factor: -2×2 Matrix{Float64}: - 6.0 3.0 - 0.0 1.0 - -julia> F.L * F.U == A[F.p, :] -true - -julia> l, u, p = lu(A); # destructuring via iteration - -julia> l == F.L && u == F.U && p == F.p -true - -julia> lu([1 2; 1 2], allowsingular = true) -LU{Float64, Matrix{Float64}, Vector{Int64}} -L factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 1.0 1.0 -U factor (rank-deficient): -2×2 Matrix{Float64}: - 1.0 2.0 - 0.0 0.0 -``` -""" -lu(A::AbstractMatrix{T}, args...; kwargs...) where {T} = - _lu(_lucopy(A, lutype(T)), args...; kwargs...) -# TODO: remove for Julia v2.0 -@deprecate lu(A::AbstractMatrix, ::Val{true}; check::Bool = true) lu(A, RowMaximum(); check=check) -@deprecate lu(A::AbstractMatrix, ::Val{false}; check::Bool = true) lu(A, NoPivot(); check=check) -# allow packages like SparseArrays.jl to interfere here and call their own `lu` -_lu(A::AbstractMatrix, args...; kwargs...) = lu!(A, args...; kwargs...) - -_lucopy(A::AbstractMatrix, T) = copy_similar(A, T) -_lucopy(A::HermOrSym, T) = copymutable_oftype(A, T) -_lucopy(A::Tridiagonal, T) = copymutable_oftype(A, T) - -lu(S::LU) = S -function lu(x::Number; check::Bool=true, allowsingular::Bool=false) - info = x == 0 ? one(BlasInt) : zero(BlasInt) - check && _check_lu_success(info, allowsingular) - return LU(fill(x, 1, 1), BlasInt[1], info) -end - -function LU{T}(F::LU) where T - M = convert(AbstractMatrix{T}, F.factors) - LU{T,typeof(M),typeof(F.ipiv)}(M, F.ipiv, F.info) -end -LU{T,S,P}(F::LU) where {T,S,P} = LU{T,S,P}(convert(S, F.factors), convert(P, F.ipiv), F.info) -Factorization{T}(F::LU{T}) where {T} = F -Factorization{T}(F::LU) where {T} = LU{T}(F) - -copy(A::LU{T,S,P}) where {T,S,P} = LU{T,S,P}(copy(A.factors), copy(A.ipiv), A.info) - -size(A::LU) = size(getfield(A, :factors)) -size(A::LU, i::Integer) = size(getfield(A, :factors), i) - -function ipiv2perm(v::AbstractVector{T}, maxi::Integer) where T - require_one_based_indexing(v) - p = T[1:maxi;] - @inbounds for i in 1:length(v) - p[i], p[v[i]] = p[v[i]], p[i] - end - return p -end - -function getproperty(F::LU{T}, d::Symbol) where T - m, n = size(F) - if d === :L - L = tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) - for i = 1:min(m,n); L[i,i] = one(T); end - return L - elseif d === :U - return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d === :p - return ipiv2perm(getfield(F, :ipiv), m) - elseif d === :P - return Matrix{T}(I, m, m)[:,invperm(F.p)] - else - getfield(F, d) - end -end - -Base.propertynames(F::LU, private::Bool=false) = - (:L, :U, :p, :P, (private ? fieldnames(typeof(F)) : ())...) - - -""" - issuccess(F::LU; allowsingular = false) - -Test that the LU factorization of a matrix succeeded. By default a -factorization that produces a valid but rank-deficient U factor is considered a -failure. This can be changed by passing `allowsingular = true`. - -!!! compat "Julia 1.11" - The `allowsingular` keyword argument was added in Julia 1.11. - -# Examples - -```jldoctest -julia> F = lu([1 2; 1 2], check = false); - -julia> issuccess(F) -false - -julia> issuccess(F, allowsingular = true) -true -``` -""" -function issuccess(F::LU; allowsingular::Bool=false) - # A negative info is always a failure, a positive info indicates a valid but rank-deficient U factor - F.info == 0 || (allowsingular && F.info > 0) -end - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LU) - if F.info < 0 - print(io, "Failed factorization of type $(typeof(F))") - else - summary(io, F); println(io) - println(io, "L factor:") - show(io, mime, F.L) - if F.info > 0 - println(io, "\nU factor (rank-deficient):") - else - println(io, "\nU factor:") - end - show(io, mime, F.U) - end -end - -_apply_ipiv_rows!(A::LU, B::AbstractVecOrMat) = _ipiv_rows!(A, 1 : length(A.ipiv), B) -_apply_inverse_ipiv_rows!(A::LU, B::AbstractVecOrMat) = _ipiv_rows!(A, length(A.ipiv) : -1 : 1, B) - -function _ipiv_rows!(A::LU, order::OrdinalRange, B::AbstractVecOrMat) - for i = order - if i != A.ipiv[i] - _swap_rows!(B, i, A.ipiv[i]) - end - end - B -end - -function _swap_rows!(B::AbstractVector, i::Integer, j::Integer) - B[i], B[j] = B[j], B[i] - B -end - -function _swap_rows!(B::AbstractMatrix, i::Integer, j::Integer) - for col = 1 : size(B, 2) - B[i,col], B[j,col] = B[j,col], B[i,col] - end - B -end - -_apply_ipiv_cols!(A::LU, B::AbstractVecOrMat) = _ipiv_cols!(A, 1 : length(A.ipiv), B) -_apply_inverse_ipiv_cols!(A::LU, B::AbstractVecOrMat) = _ipiv_cols!(A, length(A.ipiv) : -1 : 1, B) - -function _ipiv_cols!(A::LU, order::OrdinalRange, B::AbstractVecOrMat) - for i = order - if i != A.ipiv[i] - _swap_cols!(B, i, A.ipiv[i]) - end - end - B -end - -function _swap_cols!(B::AbstractVector, i::Integer, j::Integer) - _swap_rows!(B, i, j) -end - -function _swap_cols!(B::AbstractMatrix, i::Integer, j::Integer) - for row = 1 : size(B, 1) - B[row,i], B[row,j] = B[row,j], B[row,i] - end - B -end - -function rdiv!(A::AbstractVecOrMat, B::LU) - rdiv!(rdiv!(A, UpperTriangular(B.factors)), UnitLowerTriangular(B.factors)) - _apply_inverse_ipiv_cols!(B, A) -end - -ldiv!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.getrs!('N', A.factors, A.ipiv, B) - -function ldiv!(A::LU, B::AbstractVecOrMat) - _apply_ipiv_rows!(A, B) - ldiv!(UpperTriangular(A.factors), ldiv!(UnitLowerTriangular(A.factors), B)) -end - -ldiv!(transA::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - (A = transA.parent; LAPACK.getrs!('T', A.factors, A.ipiv, B)) - -function ldiv!(transA::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) - A = transA.parent - ldiv!(transpose(UnitLowerTriangular(A.factors)), ldiv!(transpose(UpperTriangular(A.factors)), B)) - _apply_inverse_ipiv_rows!(A, B) -end - -ldiv!(adjA::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - (A = adjA.parent; LAPACK.getrs!('C', A.factors, A.ipiv, B)) - -function ldiv!(adjA::AdjointFactorization{<:Any,<:LU}, B::AbstractVecOrMat) - A = adjA.parent - ldiv!(adjoint(UnitLowerTriangular(A.factors)), ldiv!(adjoint(UpperTriangular(A.factors)), B)) - _apply_inverse_ipiv_rows!(A, B) -end - -(\)(A::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::Adjoint{T,<:StridedVecOrMat{T}}) where {T<:BlasComplex} = - LAPACK.getrs!('C', A.parent.factors, A.parent.ipiv, copy(B)) -(\)(A::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::Transpose{T,<:StridedVecOrMat{T}}) where {T<:BlasFloat} = - LAPACK.getrs!('T', A.parent.factors, A.parent.ipiv, copy(B)) - -function det(F::LU{T}) where T - n = checksquare(F) - issuccess(F) || return zero(T) - P = one(T) - c = 0 - @inbounds for i = 1:n - P *= F.factors[i,i] - if F.ipiv[i] != i - c += 1 - end - end - s = (isodd(c) ? -one(T) : one(T)) - return P * s -end - -function logabsdet(F::LU{T}) where T # return log(abs(det)) and sign(det) - n = checksquare(F) - issuccess(F) || return log(zero(real(T))), log(one(T)) - c = 0 - P = one(T) - abs_det = zero(real(T)) - @inbounds for i = 1:n - dg_ii = F.factors[i,i] - P *= sign(dg_ii) - if F.ipiv[i] != i - c += 1 - end - abs_det += log(abs(dg_ii)) - end - s = ifelse(isodd(c), -one(real(T)), one(real(T))) * P - abs_det, s -end - -inv!(A::LU{<:BlasFloat,<:StridedMatrix}) = - LAPACK.getri!(A.factors, A.ipiv) -inv!(A::LU{T,<:StridedMatrix}) where {T} = - ldiv!(A.factors, copy(A), Matrix{T}(I, size(A, 1), size(A, 1))) -inv(A::LU{<:BlasFloat,<:StridedMatrix}) = inv!(copy(A)) - -# Tridiagonal -function lu!(A::Tridiagonal{T,V}, pivot::Union{RowMaximum,NoPivot} = RowMaximum(); - check::Bool = true, allowsingular::Bool = false) where {T,V} - n = size(A, 1) - has_du2_defined = isdefined(A, :du2) && length(A.du2) == max(0, n-2) - if has_du2_defined - du2 = A.du2::V - else - du2 = similar(A.d, max(0, n-2))::V - end - _lu_tridiag!(A.dl, A.d, A.du, du2, Vector{BlasInt}(undef, n), pivot, check, allowsingular) -end -function lu!(F::LU{<:Any,<:Tridiagonal}, A::Tridiagonal, pivot::Union{RowMaximum,NoPivot} = RowMaximum(); - check::Bool = true, allowsingular::Bool = false) - B = F.factors - size(B) == size(A) || throw(DimensionMismatch()) - copyto!(B, A) - _lu_tridiag!(B.dl, B.d, B.du, B.du2, F.ipiv, pivot, check, allowsingular) -end -# See dgttrf.f -@inline function _lu_tridiag!(dl, d, du, du2, ipiv, pivot, check, allowsingular) - T = eltype(d) - V = typeof(d) - - # Extract values - n = length(d) - - # Initialize variables - info = 0 - fill!(du2, 0) - - @inbounds begin - for i = 1:n - ipiv[i] = i - end - for i = 1:n-2 - # pivot or not? - if pivot === NoPivot() || abs(d[i]) >= abs(dl[i]) - # No interchange - if d[i] != 0 - fact = dl[i]/d[i] - dl[i] = fact - d[i+1] -= fact*du[i] - du2[i] = 0 - end - else - # Interchange - fact = d[i]/dl[i] - d[i] = dl[i] - dl[i] = fact - tmp = du[i] - du[i] = d[i+1] - d[i+1] = tmp - fact*d[i+1] - du2[i] = du[i+1] - du[i+1] = -fact*du[i+1] - ipiv[i] = i+1 - end - end - if n > 1 - i = n-1 - if pivot === NoPivot() || abs(d[i]) >= abs(dl[i]) - if d[i] != 0 - fact = dl[i]/d[i] - dl[i] = fact - d[i+1] -= fact*du[i] - end - else - fact = d[i]/dl[i] - d[i] = dl[i] - dl[i] = fact - tmp = du[i] - du[i] = d[i+1] - d[i+1] = tmp - fact*d[i+1] - ipiv[i] = i+1 - end - end - # check for a zero on the diagonal of U - for i = 1:n - if d[i] == 0 - info = i - break - end - end - end - check && _check_lu_success(info, allowsingular) - return LU{T,Tridiagonal{T,V},typeof(ipiv)}(Tridiagonal{T,V}(dl, d, du, du2), ipiv, convert(BlasInt, info)) -end - -factorize(A::Tridiagonal) = lu(A) - -function getproperty(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} - m, n = size(F) - if d === :L - dl = getfield(getfield(F, :factors), :dl) - L = Array(Bidiagonal(fill!(similar(dl, n), one(T)), dl, d)) - for i = 2:n - tmp = L[getfield(F, :ipiv)[i], 1:i - 1] - L[getfield(F, :ipiv)[i], 1:i - 1] = L[i, 1:i - 1] - L[i, 1:i - 1] = tmp - end - return L - elseif d === :U - U = Array(Bidiagonal(getfield(getfield(F, :factors), :d), getfield(getfield(F, :factors), :du), d)) - for i = 1:n - 2 - U[i,i + 2] = getfield(getfield(F, :factors), :du2)[i] - end - return U - elseif d === :p - return ipiv2perm(getfield(F, :ipiv), m) - elseif d === :P - return Matrix{T}(I, m, m)[:,invperm(F.p)] - end - return getfield(F, d) -end - -# See dgtts2.f -function ldiv!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} - require_one_based_indexing(B) - n = size(A,1) - if n != size(B,1) - throw(DimensionMismatch(lazy"matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) - end - nrhs = size(B,2) - dl = A.factors.dl - d = A.factors.d - du = A.factors.du - du2 = A.factors.du2 - ipiv = A.ipiv - @inbounds begin - for j = 1:nrhs - for i = 1:n-1 - ip = ipiv[i] - tmp = B[i+1-ip+i,j] - dl[i]*B[ip,j] - B[i,j] = B[ip,j] - B[i+1,j] = tmp - end - B[n,j] /= d[n] - if n > 1 - B[n-1,j] = (B[n-1,j] - du[n-1]*B[n,j])/d[n-1] - end - for i = n-2:-1:1 - B[i,j] = (B[i,j] - du[i]*B[i+1,j] - du2[i]*B[i+2,j])/d[i] - end - end - end - return B -end - -function ldiv!(transA::TransposeFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} - require_one_based_indexing(B) - A = transA.parent - n = size(A,1) - if n != size(B,1) - throw(DimensionMismatch(lazy"matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) - end - nrhs = size(B,2) - dl = A.factors.dl - d = A.factors.d - du = A.factors.du - du2 = A.factors.du2 - ipiv = A.ipiv - @inbounds begin - for j = 1:nrhs - B[1,j] /= d[1] - if n > 1 - B[2,j] = (B[2,j] - du[1]*B[1,j])/d[2] - end - for i = 3:n - B[i,j] = (B[i,j] - du[i-1]*B[i-1,j] - du2[i-2]*B[i-2,j])/d[i] - end - for i = n-1:-1:1 - if ipiv[i] == i - B[i,j] = B[i,j] - dl[i]*B[i+1,j] - else - tmp = B[i+1,j] - B[i+1,j] = B[i,j] - dl[i]*tmp - B[i,j] = tmp - end - end - end - end - return B -end - -# Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) -function ldiv!(adjA::AdjointFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} - require_one_based_indexing(B) - A = adjA.parent - n = size(A,1) - if n != size(B,1) - throw(DimensionMismatch(lazy"matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) - end - nrhs = size(B,2) - dl = A.factors.dl - d = A.factors.d - du = A.factors.du - du2 = A.factors.du2 - ipiv = A.ipiv - @inbounds begin - for j = 1:nrhs - B[1,j] /= conj(d[1]) - if n > 1 - B[2,j] = (B[2,j] - conj(du[1])*B[1,j])/conj(d[2]) - end - for i = 3:n - B[i,j] = (B[i,j] - conj(du[i-1])*B[i-1,j] - conj(du2[i-2])*B[i-2,j])/conj(d[i]) - end - for i = n-1:-1:1 - if ipiv[i] == i - B[i,j] = B[i,j] - conj(dl[i])*B[i+1,j] - else - tmp = B[i+1,j] - B[i+1,j] = B[i,j] - conj(dl[i])*tmp - B[i,j] = tmp - end - end - end - end - return B -end - -rdiv!(B::AbstractMatrix, A::LU{T,Tridiagonal{T,V}}) where {T,V} = transpose(ldiv!(transpose(A), transpose(B))) - -# Conversions -AbstractMatrix(F::LU) = (F.L * F.U)[invperm(F.p),:] -AbstractArray(F::LU) = AbstractMatrix(F) -Matrix(F::LU) = Array(AbstractArray(F)) -Array(F::LU) = Matrix(F) - -function Tridiagonal(F::LU{T,Tridiagonal{T,V}}) where {T,V} - n = size(F, 1) - - dl = copy(F.factors.dl) - d = copy(F.factors.d) - du = copy(F.factors.du) - du2 = copy(F.factors.du2) - - for i = n - 1:-1:1 - li = dl[i] - dl[i] = li*d[i] - d[i + 1] += li*du[i] - if i < n - 1 - du[i + 1] += li*du2[i] - end - - if F.ipiv[i] != i - tmp = dl[i] - dl[i] = d[i] - d[i] = tmp - - tmp = d[i + 1] - d[i + 1] = du[i] - du[i] = tmp - - if i < n - 1 - tmp = du[i + 1] - du[i + 1] = du2[i] - du2[i] = tmp - end - end - end - return Tridiagonal(dl, d, du) -end -AbstractMatrix(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Tridiagonal(F) -AbstractArray(F::LU{T,Tridiagonal{T,V}}) where {T,V} = AbstractMatrix(F) -Matrix(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Array(AbstractArray(F)) -Array(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Matrix(F) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl deleted file mode 100644 index e22b6dce4bb03..0000000000000 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ /dev/null @@ -1,1339 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# matmul.jl: Everything to do with dense matrix multiplication - -# unused internal constant, here for legacy reasons -const tilebufsize = 10800 # Approximately 32k/3 - -# Matrix-matrix multiplication - -AdjOrTransStridedMat{T} = Union{Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} -StridedMaybeAdjOrTransMat{T} = Union{StridedMatrix{T}, Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} -StridedMaybeAdjOrTransVecOrMat{T} = Union{StridedVecOrMat{T}, AdjOrTrans{<:Any, <:StridedVecOrMat{T}}} - -matprod(x, y) = x*y + x*y - -# dot products - -dot(x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasReal} = BLAS.dot(x, y) -dot(x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasComplex} = BLAS.dotc(x, y) - -function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasReal,TI<:Integer} - if length(rx) != length(ry) - throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) - end - if minimum(rx) < 1 || maximum(rx) > length(x) - throw(BoundsError(x, rx)) - end - if minimum(ry) < 1 || maximum(ry) > length(y) - throw(BoundsError(y, ry)) - end - GC.@preserve x y BLAS.dot(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry)) -end - -function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasComplex,TI<:Integer} - if length(rx) != length(ry) - throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) - end - if minimum(rx) < 1 || maximum(rx) > length(x) - throw(BoundsError(x, rx)) - end - if minimum(ry) < 1 || maximum(ry) > length(y) - throw(BoundsError(y, ry)) - end - GC.@preserve x y BLAS.dotc(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry)) -end - -function *(transx::Transpose{<:Any,<:StridedVector{T}}, y::StridedVector{T}) where {T<:BlasComplex} - x = transx.parent - return BLAS.dotu(x, y) -end - -# Matrix-vector multiplication -function (*)(A::StridedMaybeAdjOrTransMat{T}, x::StridedVector{S}) where {T<:BlasFloat,S<:Real} - TS = promote_op(matprod, T, S) - y = isconcretetype(TS) ? convert(AbstractVector{TS}, x) : x - mul!(similar(x, TS, size(A,1)), A, y) -end -function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S} - TS = promote_op(matprod, T, S) - mul!(similar(x, TS, axes(A,1)), A, x) -end - -# these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case): -function (*)(a::AbstractVector, B::AbstractMatrix) - require_one_based_indexing(a) - reshape(a, length(a), 1) * B -end - -# Add a level of indirection and specialize _mul! to avoid ambiguities in mul! -@inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, - alpha::Number, beta::Number) = _mul!(y, A, x, alpha, beta) - -_mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, - alpha::Number, beta::Number) = - generic_matvecmul!(y, wrapper_char(A), _unwrap(A), x, alpha, beta) -# BLAS cases -# equal eltypes -generic_matvecmul!(y::StridedVector{T}, tA, A::StridedVecOrMat{T}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} = - gemv!(y, tA, A, x, alpha, beta) - -# Real (possibly transposed) matrix times complex vector. -# Multiply the matrix with the real and imaginary parts separately -generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemv!(y, tA, A, x, alpha, beta) - -# Complex matrix times real vector. -# Reinterpret the matrix as a real matrix and do real matvec computation. -# works only in cooperation with BLAS when A is untransposed (tA == 'N') -# but that check is included in gemv! anyway -generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, - alpha::Number, beta::Number) where {T<:BlasReal} = - gemv!(y, tA, A, x, alpha, beta) - -# Vector-Matrix multiplication -(*)(x::AdjointAbsVec, A::AbstractMatrix) = (A'*x')' -(*)(x::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A)*transpose(x)) - -# Matrix-matrix multiplication -""" - *(A::AbstractMatrix, B::AbstractMatrix) - -Matrix multiplication. - -# Examples -```jldoctest -julia> [1 1; 0 1] * [1 0; 1 1] -2×2 Matrix{Int64}: - 2 1 - 1 1 -``` -""" -function (*)(A::AbstractMatrix, B::AbstractMatrix) - TS = promote_op(matprod, eltype(A), eltype(B)) - mul!(matprod_dest(A, B, TS), A, B) -end - -""" - matprod_dest(A, B, T) - -Return an appropriate `AbstractArray` with element type `T` that may be used to store the result of `A * B`. - -!!! compat - This function requires at least Julia 1.11 -""" -matprod_dest(A, B, T) = similar(B, T, (size(A, 1), size(B, 2))) - -# optimization for dispatching to BLAS, e.g. *(::Matrix{Float32}, ::Matrix{Float64}) -# but avoiding the case *(::Matrix{<:BlasComplex}, ::Matrix{<:BlasReal}) -# which is better handled by reinterpreting rather than promotion -function (*)(A::StridedMaybeAdjOrTransMat{<:BlasReal}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) - TS = promote_type(eltype(A), eltype(B)) - mul!(similar(B, TS, (size(A, 1), size(B, 2))), - wrapperop(A)(convert(AbstractArray{TS}, _unwrap(A))), - wrapperop(B)(convert(AbstractArray{TS}, _unwrap(B)))) -end -function (*)(A::StridedMaybeAdjOrTransMat{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasComplex}) - TS = promote_type(eltype(A), eltype(B)) - mul!(similar(B, TS, (size(A, 1), size(B, 2))), - wrapperop(A)(convert(AbstractArray{TS}, _unwrap(A))), - wrapperop(B)(convert(AbstractArray{TS}, _unwrap(B)))) -end - -# Complex Matrix times real matrix: We use that it is generally faster to reinterpret the -# first matrix as a real matrix and carry out real matrix matrix multiply -function (*)(A::StridedMatrix{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) - TS = promote_type(eltype(A), eltype(B)) - mul!(similar(B, TS, (size(A, 1), size(B, 2))), - convert(AbstractArray{TS}, A), - wrapperop(B)(convert(AbstractArray{real(TS)}, _unwrap(B)))) -end -function (*)(A::AdjOrTransStridedMat{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) - TS = promote_type(eltype(A), eltype(B)) - mul!(similar(B, TS, (size(A, 1), size(B, 2))), - copymutable_oftype(A, TS), # remove AdjOrTrans to use reinterpret trick below - wrapperop(B)(convert(AbstractArray{real(TS)}, _unwrap(B)))) -end -# the following case doesn't seem to benefit from the translation A*B = (B' * A')' -function (*)(A::StridedMatrix{<:BlasReal}, B::StridedMatrix{<:BlasComplex}) - temp = real(B) - R = A * temp - temp .= imag.(B) - I = A * temp - Complex.(R, I) -end -(*)(A::AdjOrTransStridedMat{<:BlasReal}, B::StridedMatrix{<:BlasComplex}) = copy(transpose(transpose(B) * parent(A))) -(*)(A::StridedMaybeAdjOrTransMat{<:BlasReal}, B::AdjOrTransStridedMat{<:BlasComplex}) = copy(wrapperop(B)(parent(B) * transpose(A))) - -""" - muladd(A, y, z) - -Combined multiply-add, `A*y .+ z`, for matrix-matrix or matrix-vector multiplication. -The result is always the same size as `A*y`, but `z` may be smaller, or a scalar. - -!!! compat "Julia 1.6" - These methods require Julia 1.6 or later. - -# Examples -```jldoctest -julia> A=[1.0 2.0; 3.0 4.0]; B=[1.0 1.0; 1.0 1.0]; z=[0, 100]; - -julia> muladd(A, B, z) -2×2 Matrix{Float64}: - 3.0 3.0 - 107.0 107.0 -``` -""" -function Base.muladd(A::AbstractMatrix, y::AbstractVecOrMat, z::Union{Number, AbstractArray}) - Ay = A * y - for d in 1:ndims(Ay) - # Same error as Ay .+= z would give, to match StridedMatrix method: - size(z,d) > size(Ay,d) && throw(DimensionMismatch("array could not be broadcast to match destination")) - end - for d in ndims(Ay)+1:ndims(z) - # Similar error to what Ay + z would give, to match (Any,Any,Any) method: - size(z,d) > 1 && throw(DimensionMismatch(string("z has dims ", - axes(z), ", must have singleton at dim ", d))) - end - Ay .+ z -end - -function Base.muladd(u::AbstractVector, v::AdjOrTransAbsVec, z::Union{Number, AbstractArray}) - if size(z,1) > length(u) || size(z,2) > length(v) - # Same error as (u*v) .+= z: - throw(DimensionMismatch("array could not be broadcast to match destination")) - end - for d in 3:ndims(z) - # Similar error to (u*v) + z: - size(z,d) > 1 && throw(DimensionMismatch(string("z has dims ", - axes(z), ", must have singleton at dim ", d))) - end - (u .* v) .+ z -end - -Base.muladd(x::AdjointAbsVec, A::AbstractMatrix, z::Union{Number, AbstractVecOrMat}) = - muladd(A', x', z')' -Base.muladd(x::TransposeAbsVec, A::AbstractMatrix, z::Union{Number, AbstractVecOrMat}) = - transpose(muladd(transpose(A), transpose(x), transpose(z))) - -function Base.muladd(A::StridedMaybeAdjOrTransMat{<:Number}, y::AbstractVector{<:Number}, z::Union{Number, AbstractVector}) - T = promote_type(eltype(A), eltype(y), eltype(z)) - C = similar(A, T, axes(A,1)) - C .= z - mul!(C, A, y, true, true) -end - -function Base.muladd(A::StridedMaybeAdjOrTransMat{<:Number}, B::StridedMaybeAdjOrTransMat{<:Number}, z::Union{Number, AbstractVecOrMat}) - T = promote_type(eltype(A), eltype(B), eltype(z)) - C = similar(A, T, axes(A,1), axes(B,2)) - C .= z - mul!(C, A, B, true, true) -end - -""" - mul!(Y, A, B) -> Y - -Calculates the matrix-matrix or matrix-vector product ``A B`` and stores the result in `Y`, -overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or -`B`. - -# Examples -```jldoctest -julia> A = [1.0 2.0; 3.0 4.0]; B = [1.0 1.0; 1.0 1.0]; Y = similar(B); - -julia> mul!(Y, A, B) === Y -true - -julia> Y -2×2 Matrix{Float64}: - 3.0 3.0 - 7.0 7.0 - -julia> Y == A * B -true -``` - -# Implementation -For custom matrix and vector types, it is recommended to implement -5-argument `mul!` rather than implementing 3-argument `mul!` directly -if possible. -""" -mul!(C, A, B) = mul!(C, A, B, true, false) - -""" - mul!(C, A, B, α, β) -> C - -Combined inplace matrix-matrix or matrix-vector multiply-add ``A B α + C β``. -The result is stored in `C` by overwriting it. Note that `C` must not be -aliased with either `A` or `B`. - -!!! compat "Julia 1.3" - Five-argument `mul!` requires at least Julia 1.3. - -# Examples -```jldoctest -julia> A = [1.0 2.0; 3.0 4.0]; B = [1.0 1.0; 1.0 1.0]; C = [1.0 2.0; 3.0 4.0]; - -julia> α, β = 100.0, 10.0; - -julia> mul!(C, A, B, α, β) === C -true - -julia> C -2×2 Matrix{Float64}: - 310.0 320.0 - 730.0 740.0 - -julia> C_original = [1.0 2.0; 3.0 4.0]; # A copy of the original value of C - -julia> C == A * B * α + C_original * β -true -``` -""" -@inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) = _mul!(C, A, B, α, β) -# Add a level of indirection and specialize _mul! to avoid ambiguities in mul! -module BlasFlag -@enum BlasFunction SYRK HERK GEMM SYMM HEMM NONE -const SyrkHerkGemm = Union{Val{SYRK}, Val{HERK}, Val{GEMM}} -const SymmHemmGeneric = Union{Val{SYMM}, Val{HEMM}, Val{NONE}} -end -@inline function _mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, α::Number, β::Number) - tA = wrapper_char(A) - tB = wrapper_char(B) - tA_uc = uppercase(tA) - tB_uc = uppercase(tB) - isntc = wrapper_char_NTC(A) & wrapper_char_NTC(B) - blasfn = if isntc - if (tA_uc == 'T' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'T') - BlasFlag.SYRK - elseif (tA_uc == 'C' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'C') - BlasFlag.HERK - else isntc - BlasFlag.GEMM - end - else - if (tA_uc == 'S' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'S') - BlasFlag.SYMM - elseif (tA_uc == 'H' && tB_uc == 'N') || (tA_uc == 'N' && tB_uc == 'H') - BlasFlag.HEMM - else - BlasFlag.NONE - end - end - - generic_matmatmul_wrapper!( - C, - tA, - tB, - _unwrap(A), - _unwrap(B), - α, β, - Val(blasfn), - ) -end - -# this indirection allows is to specialize on the types of the wrappers of A and B to some extent, -# even though the wrappers are stripped off in mul! -# By default, we ignore the wrapper info and forward the arguments to generic_matmatmul! -function generic_matmatmul_wrapper!(C, tA, tB, A, B, α, β, @nospecialize(val)) - generic_matmatmul!(C, tA, tB, A, B, α, β) -end - - -""" - rmul!(A, B) - -Calculate the matrix-matrix product ``AB``, overwriting `A`, and return the result. -Here, `B` must be of special matrix type, like, e.g., [`Diagonal`](@ref), -[`UpperTriangular`](@ref) or [`LowerTriangular`](@ref), or of some orthogonal type, -see [`QR`](@ref). - -# Examples -```jldoctest -julia> A = [0 1; 1 0]; - -julia> B = UpperTriangular([1 2; 0 3]); - -julia> rmul!(A, B); - -julia> A -2×2 Matrix{Int64}: - 0 3 - 1 2 - -julia> A = [1.0 2.0; 3.0 4.0]; - -julia> F = qr([0 1; -1 0]); - -julia> rmul!(A, F.Q) -2×2 Matrix{Float64}: - 2.0 1.0 - 4.0 3.0 -``` -""" -rmul!(A, B) - -""" - lmul!(A, B) - -Calculate the matrix-matrix product ``AB``, overwriting `B`, and return the result. -Here, `A` must be of special matrix type, like, e.g., [`Diagonal`](@ref), -[`UpperTriangular`](@ref) or [`LowerTriangular`](@ref), or of some orthogonal type, -see [`QR`](@ref). - -# Examples -```jldoctest -julia> B = [0 1; 1 0]; - -julia> A = UpperTriangular([1 2; 0 3]); - -julia> lmul!(A, B); - -julia> B -2×2 Matrix{Int64}: - 2 1 - 3 0 - -julia> B = [1.0 2.0; 3.0 4.0]; - -julia> F = qr([0 1; -1 0]); - -julia> lmul!(F.Q, B) -2×2 Matrix{Float64}: - 3.0 4.0 - 1.0 2.0 -``` -""" -lmul!(A, B) - -# We may inline the matmul2x2! and matmul3x3! calls for `α == true` -# to simplify the @stable_muladdmul branches -function matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α, β) - if size(C) == size(A) == size(B) == (2,2) - matmul2x2!(C, tA, tB, A, B, α, β) - return true - end - if size(C) == size(A) == size(B) == (3,3) - matmul3x3!(C, tA, tB, A, B, α, β) - return true - end - return false -end -function matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α::Bool, β) - if size(C) == size(A) == size(B) == (2,2) - Aelements, Belements = _matmul2x2_elements(C, tA, tB, A, B) - @stable_muladdmul _modify2x2!(Aelements, Belements, C, MulAddMul(true, β)) - return true - end - if size(C) == size(A) == size(B) == (3,3) - Aelements, Belements = _matmul3x3_elements(C, tA, tB, A, B) - @stable_muladdmul _modify3x3!(Aelements, Belements, C, MulAddMul(true, β)) - return true - end - return false -end - -# THE one big BLAS dispatch. This is split into two methods to improve latency -Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number, val::BlasFlag.SyrkHerkGemm) where {T<:BlasFloat} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - if any(iszero, size(A)) || any(iszero, size(B)) || iszero(α) - if size(C) != (mA, nB) - throw(DimensionMismatch(lazy"C has dimensions $(size(C)), should have ($mA,$nB)")) - end - return _rmul_or_fill!(C, β) - end - matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α, β) && return C - _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, val) - return C -end -Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.SYRK}) - if A === B - tA_uc = uppercase(tA) # potentially strip a WrapperChar - return syrk_wrapper!(C, tA_uc, A, α, β) - else - return gemm_wrapper!(C, tA, tB, A, B, α, β) - end -end -Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.HERK}) - if A === B - tA_uc = uppercase(tA) # potentially strip a WrapperChar - return herk_wrapper!(C, tA_uc, A, α, β) - else - return gemm_wrapper!(C, tA, tB, A, B, α, β) - end -end -Base.@constprop :aggressive function _syrk_herk_gemm_wrapper!(C, tA, tB, A, B, α, β, ::Val{BlasFlag.GEMM}) - return gemm_wrapper!(C, tA, tB, A, B, α, β) -end -_valtypeparam(v::Val{T}) where {T} = T -Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number, val::BlasFlag.SymmHemmGeneric) where {T<:BlasFloat} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - if any(iszero, size(A)) || any(iszero, size(B)) || iszero(α) - if size(C) != (mA, nB) - throw(DimensionMismatch(lazy"C has dimensions $(size(C)), should have ($mA,$nB)")) - end - return _rmul_or_fill!(C, β) - end - matmul2x2or3x3_nonzeroalpha!(C, tA, tB, A, B, α, β) && return C - alpha, beta = promote(α, β, zero(T)) - blasfn = _valtypeparam(val) - if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && blasfn ∈ (BlasFlag.SYMM, BlasFlag.HEMM) - _blasfn = blasfn - αβ = (alpha, beta) - else - _blasfn = BlasFlag.NONE - αβ = (α, β) - end - _symm_hemm_generic!(C, tA, tB, A, B, αβ..., Val(_blasfn)) - return C -end -Base.@constprop :aggressive function _lrchar_ulchar(tA, tB) - if uppercase(tA) == 'N' - lrchar = 'R' - ulchar = isuppercase(tB) ? 'U' : 'L' - else - lrchar = 'L' - ulchar = isuppercase(tA) ? 'U' : 'L' - end - return lrchar, ulchar -end -function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.SYMM}) - lrchar, ulchar = _lrchar_ulchar(tA, tB) - if lrchar == 'L' - BLAS.symm!(lrchar, ulchar, alpha, A, B, beta, C) - else - BLAS.symm!(lrchar, ulchar, alpha, B, A, beta, C) - end -end -function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.HEMM}) - lrchar, ulchar = _lrchar_ulchar(tA, tB) - if lrchar == 'L' - BLAS.hemm!(lrchar, ulchar, alpha, A, B, beta, C) - else - BLAS.hemm!(lrchar, ulchar, alpha, B, A, beta, C) - end -end -Base.@constprop :aggressive function _symm_hemm_generic!(C, tA, tB, A, B, alpha, beta, ::Val{BlasFlag.NONE}) - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) -end - -# legacy method -Base.@constprop :aggressive generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - _add::MulAddMul = MulAddMul()) where {T<:BlasFloat} = - generic_matmatmul!(C, tA, tB, A, B, _add.alpha, _add.beta) - -function generic_matmatmul_wrapper!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - α::Number, β::Number, ::Val{true}) where {T<:BlasReal} - gemm_wrapper!(C, tA, tB, A, B, α, β) -end -Base.@constprop :aggressive function generic_matmatmul_wrapper!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - alpha::Number, beta::Number, ::Val{false}) where {T<:BlasReal} - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) -end -# legacy method -Base.@constprop :aggressive generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - _add::MulAddMul = MulAddMul()) where {T<:BlasReal} = - generic_matmatmul!(C, tA, tB, A, B, _add.alpha, _add.beta) - -# Supporting functions for matrix multiplication - -# copy transposed(adjoint) of upper(lower) side-diagonals. Optionally include diagonal. -@inline function copytri!(A::AbstractMatrix, uplo::AbstractChar, conjugate::Bool=false, diag::Bool=false) - n = checksquare(A) - off = diag ? 0 : 1 - if uplo == 'U' - for i = 1:n, j = (i+off):n - A[j,i] = conjugate ? adjoint(A[i,j]) : transpose(A[i,j]) - end - elseif uplo == 'L' - for i = 1:n, j = (i+off):n - A[i,j] = conjugate ? adjoint(A[j,i]) : transpose(A[j,i]) - end - else - throw(ArgumentError(lazy"uplo argument must be 'U' (upper) or 'L' (lower), got $uplo")) - end - A -end - -_fullstride2(A, f=identity) = f(stride(A, 2)) >= size(A, 1) -# for some standard StridedArrays, the _fullstride2 condition is known to hold at compile-time -# We specialize the function for certain StridedArray subtypes -_fullstride2(A::StridedArrayStdSubArray, ::typeof(abs)) = true -_fullstride2(A::StridedArrayStdSubArrayIncr, ::typeof(identity)) = true - -Base.@constprop :aggressive function gemv!(y::StridedVector{T}, tA::AbstractChar, - A::StridedVecOrMat{T}, x::StridedVector{T}, - α::Number=true, β::Number=false) where {T<:BlasFloat} - mA, nA = lapack_size(tA, A) - nA != length(x) && - throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) - mA != length(y) && - throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) - mA == 0 && return y - nA == 0 && return _rmul_or_fill!(y, β) - alpha, beta = promote(α, β, zero(T)) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && - stride(A, 1) == 1 && _fullstride2(A, abs) && - !iszero(stride(x, 1)) && # We only check input's stride here. - if tA_uc in ('N', 'T', 'C') - return BLAS.gemv!(tA, alpha, A, x, beta, y) - elseif tA_uc == 'S' - return BLAS.symv!(tA == 'S' ? 'U' : 'L', alpha, A, x, beta, y) - elseif tA_uc == 'H' - return BLAS.hemv!(tA == 'H' ? 'U' : 'L', alpha, A, x, beta, y) - end - end - if tA_uc in ('S', 'H') - # re-wrap again and use plain ('N') matvec mul algorithm, - # because _generic_matvecmul! can't handle the HermOrSym cases specifically - return _generic_matvecmul!(y, 'N', wrap(A, tA), x, α, β) - else - return _generic_matvecmul!(y, tA, A, x, α, β) - end -end - -Base.@constprop :aggressive function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, - α::Number = true, β::Number = false) where {T<:BlasReal} - mA, nA = lapack_size(tA, A) - nA != length(x) && - throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) - mA != length(y) && - throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) - mA == 0 && return y - nA == 0 && return _rmul_or_fill!(y, β) - alpha, beta = promote(α, β, zero(T)) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && - stride(A, 1) == 1 && _fullstride2(A, abs) && - stride(y, 1) == 1 && tA_uc == 'N' && # reinterpret-based optimization is valid only for contiguous `y` - !iszero(stride(x, 1)) - BLAS.gemv!(tA, alpha, reinterpret(T, A), x, beta, reinterpret(T, y)) - return y - else - Anew, ta = tA_uc in ('S', 'H') ? (wrap(A, tA), oftype(tA, 'N')) : (A, tA) - return _generic_matvecmul!(y, ta, Anew, x, α, β) - end -end - -Base.@constprop :aggressive function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, - A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, - α::Number = true, β::Number = false) where {T<:BlasReal} - mA, nA = lapack_size(tA, A) - nA != length(x) && - throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) - mA != length(y) && - throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) - mA == 0 && return y - nA == 0 && return _rmul_or_fill!(y, β) - alpha, beta = promote(α, β, zero(T)) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - @views if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && - stride(A, 1) == 1 && _fullstride2(A, abs) && - !iszero(stride(x, 1)) && tA_uc in ('N', 'T', 'C') - xfl = reinterpret(reshape, T, x) # Use reshape here. - yfl = reinterpret(reshape, T, y) - BLAS.gemv!(tA, alpha, A, xfl[1, :], beta, yfl[1, :]) - BLAS.gemv!(tA, alpha, A, xfl[2, :], beta, yfl[2, :]) - return y - elseif tA_uc in ('S', 'H') - # re-wrap again and use plain ('N') matvec mul algorithm, - # because _generic_matvecmul! can't handle the HermOrSym cases specifically - return _generic_matvecmul!(y, 'N', wrap(A, tA), x, α, β) - else - return _generic_matvecmul!(y, tA, A, x, α, β) - end -end - -# the aggressive constprop pushes tA and tB into gemm_wrapper!, which is needed for wrap calls within it -# to be concretely inferred -Base.@constprop :aggressive function syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat{T}, - alpha::Number, beta::Number) where {T<:BlasFloat} - nC = checksquare(C) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - if tA_uc == 'T' - (nA, mA) = size(A,1), size(A,2) - tAt = 'N' - else - (mA, nA) = size(A,1), size(A,2) - tAt = 'T' - end - if nC != mA - throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) - end - - # BLAS.syrk! only updates symmetric C - # alternatively, make non-zero β a show-stopper for BLAS.syrk! - if iszero(beta) || issymmetric(C) - α, β = promote(alpha, beta, zero(T)) - if (alpha isa Union{Bool,T} && - beta isa Union{Bool,T} && - stride(A, 1) == stride(C, 1) == 1 && - _fullstride2(A) && _fullstride2(C)) - return copytri!(BLAS.syrk!('U', tA, alpha, A, beta, C), 'U') - end - end - return gemm_wrapper!(C, tA, tAt, A, A, alpha, beta) -end -# legacy method -syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat{T}, _add::MulAddMul = MulAddMul()) where {T<:BlasFloat} = - syrk_wrapper!(C, tA, A, _add.alpha, _add.beta) - -# the aggressive constprop pushes tA and tB into gemm_wrapper!, which is needed for wrap calls within it -# to be concretely inferred -Base.@constprop :aggressive function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA::AbstractChar, A::Union{StridedVecOrMat{T}, StridedVecOrMat{Complex{T}}}, - α::Number, β::Number) where {T<:BlasReal} - nC = checksquare(C) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - if tA_uc == 'C' - (nA, mA) = size(A,1), size(A,2) - tAt = 'N' - else - (mA, nA) = size(A,1), size(A,2) - tAt = 'C' - end - if nC != mA - throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) - end - - # Result array does not need to be initialized as long as beta==0 - # C = Matrix{T}(undef, mA, mA) - - if iszero(β) || issymmetric(C) - alpha, beta = promote(α, β, zero(T)) - if (alpha isa Union{Bool,T} && - beta isa Union{Bool,T} && - stride(A, 1) == stride(C, 1) == 1 && - _fullstride2(A) && _fullstride2(C)) - return copytri!(BLAS.herk!('U', tA, alpha, A, beta, C), 'U', true) - end - end - return gemm_wrapper!(C, tA, tAt, A, A, α, β) -end -# legacy method -herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA::AbstractChar, A::Union{StridedVecOrMat{T}, StridedVecOrMat{Complex{T}}}, - _add::MulAddMul = MulAddMul()) where {T<:BlasReal} = - herk_wrapper!(C, tA, A, _add.alpha, _add.beta) - -# Aggressive constprop helps propagate the values of tA and tB into wrap, which -# makes the calls concretely inferred -Base.@constprop :aggressive function gemm_wrapper(tA::AbstractChar, tB::AbstractChar, - A::StridedVecOrMat{T}, - B::StridedVecOrMat{T}) where {T<:BlasFloat} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - C = similar(B, T, mA, nB) - # We convert the chars to uppercase to potentially unwrap a WrapperChar, - # and extract the char corresponding to the wrapper type - tA_uc, tB_uc = uppercase(tA), uppercase(tB) - # the map in all ensures constprop by acting on tA and tB individually, instead of looping over them. - if all(map(in(('N', 'T', 'C')), (tA_uc, tB_uc))) - gemm_wrapper!(C, tA, tB, A, B, true, false) - else - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), true, false) - end -end - -# Aggressive constprop helps propagate the values of tA and tB into wrap, which -# makes the calls concretely inferred -Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, - A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number) where {T<:BlasFloat} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - - if nA != mB - throw(DimensionMismatch(lazy"A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) - end - - if C === A || B === C - throw(ArgumentError("output matrix must not be aliased with input matrix")) - end - - alpha, beta = promote(α, β, zero(T)) - if (alpha isa Union{Bool,T} && - beta isa Union{Bool,T} && - stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && - _fullstride2(A) && _fullstride2(B) && _fullstride2(C)) - return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) - end - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), α, β) -end -# legacy method -gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, - A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add::MulAddMul = MulAddMul()) where {T<:BlasFloat} = - gemm_wrapper!(C, tA, tB, A, B, _add.alpha, _add.beta) - -# Aggressive constprop helps propagate the values of tA and tB into wrap, which -# makes the calls concretely inferred -Base.@constprop :aggressive function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, - A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, - α::Number, β::Number) where {T<:BlasReal} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - - if nA != mB - throw(DimensionMismatch(lazy"A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) - end - - if C === A || B === C - throw(ArgumentError("output matrix must not be aliased with input matrix")) - end - - alpha, beta = promote(α, β, zero(T)) - - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - - # Make-sure reinterpret-based optimization is BLAS-compatible. - if (alpha isa Union{Bool,T} && - beta isa Union{Bool,T} && - stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && - _fullstride2(A) && _fullstride2(B) && _fullstride2(C) && tA_uc == 'N') - BLAS.gemm!(tA, tB, alpha, reinterpret(T, A), B, beta, reinterpret(T, C)) - return C - end - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), α, β) -end -# legacy method -gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, - A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, _add::MulAddMul = MulAddMul()) where {T<:BlasReal} = - gemm_wrapper!(C, tA, tB, A, B, _add.alpha, _add.beta) - -# blas.jl defines matmul for floats; other integer and mixed precision -# cases are handled here - -lapack_size(t::AbstractChar, M::AbstractVecOrMat) = (size(M, t=='N' ? 1 : 2), size(M, t=='N' ? 2 : 1)) - -""" - copyto!(B::AbstractMatrix, ir_dest::AbstractUnitRange, jr_dest::AbstractUnitRange, - tM::AbstractChar, - M::AbstractVecOrMat, ir_src::AbstractUnitRange, jr_src::AbstractUnitRange) -> B - -Efficiently copy elements of matrix `M` to `B` conditioned on the character -parameter `tM` as follows: - -| `tM` | Destination | Source | -| --- | :--- | :--- | -| `'N'` | `B[ir_dest, jr_dest]` | `M[ir_src, jr_src]` | -| `'T'` | `B[ir_dest, jr_dest]` | `transpose(M)[ir_src, jr_src]` | -| `'C'` | `B[ir_dest, jr_dest]` | `adjoint(M)[ir_src, jr_src]` | - -The elements `B[ir_dest, jr_dest]` are overwritten. Furthermore, the index range -parameters must satisfy `length(ir_dest) == length(ir_src)` and -`length(jr_dest) == length(jr_src)`. - -See also [`copy_transpose!`](@ref) and [`copy_adjoint!`](@ref). -""" -function copyto!(B::AbstractVecOrMat, ir_dest::AbstractUnitRange{Int}, jr_dest::AbstractUnitRange{Int}, tM::AbstractChar, M::AbstractVecOrMat, ir_src::AbstractUnitRange{Int}, jr_src::AbstractUnitRange{Int}) - tM_uc = uppercase(tM) # potentially convert a WrapperChar to a Char - if tM_uc == 'N' - copyto!(B, ir_dest, jr_dest, M, ir_src, jr_src) - elseif tM_uc == 'T' - copy_transpose!(B, ir_dest, jr_dest, M, jr_src, ir_src) - else - copy_adjoint!(B, ir_dest, jr_dest, M, jr_src, ir_src) - end - B -end - -""" - copy_transpose!(B::AbstractMatrix, ir_dest::AbstractUnitRange, jr_dest::AbstractUnitRange, - tM::AbstractChar, - M::AbstractVecOrMat, ir_src::AbstractUnitRange, jr_src::AbstractUnitRange) -> B - -Efficiently copy elements of matrix `M` to `B` conditioned on the character -parameter `tM` as follows: - -| `tM` | Destination | Source | -| --- | :--- | :--- | -| `'N'` | `B[ir_dest, jr_dest]` | `transpose(M)[jr_src, ir_src]` | -| `'T'` | `B[ir_dest, jr_dest]` | `M[jr_src, ir_src]` | -| `'C'` | `B[ir_dest, jr_dest]` | `conj(M)[jr_src, ir_src]` | - -The elements `B[ir_dest, jr_dest]` are overwritten. Furthermore, the index -range parameters must satisfy `length(ir_dest) == length(jr_src)` and -`length(jr_dest) == length(ir_src)`. - -See also [`copyto!`](@ref) and [`copy_adjoint!`](@ref). -""" -function copy_transpose!(B::AbstractMatrix, ir_dest::AbstractUnitRange{Int}, jr_dest::AbstractUnitRange{Int}, tM::AbstractChar, M::AbstractVecOrMat, ir_src::AbstractUnitRange{Int}, jr_src::AbstractUnitRange{Int}) - tM_uc = uppercase(tM) # potentially convert a WrapperChar to a Char - if tM_uc == 'N' - copy_transpose!(B, ir_dest, jr_dest, M, ir_src, jr_src) - else - copyto!(B, ir_dest, jr_dest, M, jr_src, ir_src) - tM_uc == 'C' && conj!(@view B[ir_dest, jr_dest]) - end - B -end - -# TODO: It will be faster for large matrices to convert to float, -# call BLAS, and convert back to required type. - -# NOTE: the generic version is also called as fallback for -# strides != 1 cases - -# legacy method, retained for backward compatibility -generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) = - generic_matvecmul!(C, tA, A, B, _add.alpha, _add.beta) -@inline function generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, - alpha::Number, beta::Number) - tA_uc = uppercase(tA) # potentially convert a WrapperChar to a Char - Anew, ta = tA_uc in ('S', 'H') ? (wrap(A, tA), oftype(tA, 'N')) : (A, tA) - return _generic_matvecmul!(C, ta, Anew, B, alpha, beta) -end - -# legacy method, retained for backward compatibility -_generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) = - _generic_matvecmul!(C, tA, A, B, _add.alpha, _add.beta) -function __generic_matvecmul!(f::F, C::AbstractVector, A::AbstractVecOrMat, B::AbstractVector, - alpha::Number, beta::Number) where {F} - Astride = size(A, 1) - @inbounds begin - if length(B) == 0 - for k = eachindex(C) - @stable_muladdmul _modify!(MulAddMul(alpha,beta), false, C, k) - end - else - for k = eachindex(C) - aoffs = (k-1)*Astride - firstterm = f(A[aoffs + 1]) * B[1] - s = zero(firstterm + firstterm) - for i = eachindex(B) - s += f(A[aoffs+i]) * B[i] - end - @stable_muladdmul _modify!(MulAddMul(alpha,beta), s, C, k) - end - end - end -end -function __generic_matvecmul!(::typeof(identity), C::AbstractVector, A::AbstractVecOrMat, B::AbstractVector, - alpha::Number, beta::Number) - Astride = size(A, 1) - @inbounds begin - for i = eachindex(C) - if !iszero(beta) - C[i] *= beta - elseif length(B) == 0 - C[i] = false - else - C[i] = zero(A[i]*B[1] + A[i]*B[1]) - end - end - for k = eachindex(B) - aoffs = (k-1)*Astride - b = @stable_muladdmul MulAddMul(alpha,beta)(B[k]) - for i = eachindex(C) - C[i] += A[aoffs + i] * b - end - end - end - return C -end -function _generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, - alpha::Number, beta::Number) - require_one_based_indexing(C, A, B) - @assert tA in ('N', 'T', 'C') - mB = length(B) - mA, nA = lapack_size(tA, A) - if mB != nA - throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), vector B has length $mB")) - end - if mA != length(C) - throw(DimensionMismatch(lazy"result C has length $(length(C)), needs length $mA")) - end - - if tA == 'T' # fastest case - __generic_matvecmul!(transpose, C, A, B, alpha, beta) - elseif tA == 'C' - __generic_matvecmul!(adjoint, C, A, B, alpha, beta) - else # tA == 'N' - __generic_matvecmul!(identity, C, A, B, alpha, beta) - end - C -end - -function generic_matmatmul(tA, tB, A::AbstractVecOrMat{T}, B::AbstractMatrix{S}) where {T,S} - mA, nA = lapack_size(tA, A) - mB, nB = lapack_size(tB, B) - C = similar(B, promote_op(matprod, T, S), mA, nB) - generic_matmatmul!(C, tA, tB, A, B, true, false) -end - -# aggressive const prop makes mixed eltype mul!(C, A, B) invoke _generic_matmatmul! directly -# legacy method -Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul = MulAddMul()) = - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), _add.alpha, _add.beta) -Base.@constprop :aggressive generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, alpha::Number, beta::Number) = - _generic_matmatmul!(C, wrap(A, tA), wrap(B, tB), alpha, beta) - -# legacy method -_generic_matmatmul!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul) = - _generic_matmatmul!(C, A, B, _add.alpha, _add.beta) - -@noinline function _generic_matmatmul!(C::AbstractVecOrMat{R}, A::AbstractVecOrMat, B::AbstractVecOrMat, - alpha::Number, beta::Number) where {R} - AxM = axes(A, 1) - AxK = axes(A, 2) # we use two `axes` calls in case of `AbstractVector` - BxK = axes(B, 1) - BxN = axes(B, 2) - CxM = axes(C, 1) - CxN = axes(C, 2) - if AxM != CxM - throw(DimensionMismatch(lazy"matrix A has axes ($AxM,$AxK), matrix C has axes ($CxM,$CxN)")) - end - if AxK != BxK - throw(DimensionMismatch(lazy"matrix A has axes ($AxM,$AxK), matrix B has axes ($BxK,$CxN)")) - end - if BxN != CxN - throw(DimensionMismatch(lazy"matrix B has axes ($BxK,$BxN), matrix C has axes ($CxM,$CxN)")) - end - __generic_matmatmul!(C, A, B, alpha, beta, Val(isbitstype(R) && sizeof(R) ≤ 16)) - return C -end -__generic_matmatmul!(C, A::Adjoint, B::Adjoint, alpha, beta, ::Val{true}) = _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) -__generic_matmatmul!(C, A::Transpose, B::Transpose, alpha, beta, ::Val{true}) = _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) -__generic_matmatmul!(C, A::Union{Adjoint, Transpose}, B, alpha, beta, ::Val{true}) = _generic_matmatmul_generic!(C, A, B, alpha, beta) -__generic_matmatmul!(C, A, B, alpha, beta, ::Val{true}) = _generic_matmatmul_nonadjtrans!(C, A, B, alpha, beta) -__generic_matmatmul!(C, A, B, alpha, beta, ::Val{false}) = _generic_matmatmul_generic!(C, A, B, alpha, beta) - -function _generic_matmatmul_nonadjtrans!(C, A, B, alpha, beta) - _rmul_or_fill!(C, beta) - (iszero(alpha) || isempty(A) || isempty(B)) && return C - @inbounds for n in axes(B, 2), k in axes(B, 1) - # Balpha = B[k,n] * alpha, but we skip the multiplication in case isone(alpha) - Balpha = @stable_muladdmul MulAddMul(alpha, false)(B[k,n]) - @simd for m in axes(A, 1) - C[m,n] = muladd(A[m,k], Balpha, C[m,n]) - end - end - C -end -function _generic_matmatmul_adjtrans!(C, A, B, alpha, beta) - _rmul_or_fill!(C, beta) - (iszero(alpha) || isempty(A) || isempty(B)) && return C - t = wrapperop(A) - pB = parent(B) - pA = parent(A) - tmp = similar(C, axes(C, 2)) - ci = firstindex(C, 1) - ta = t(alpha) - for i in axes(A, 1) - mul!(tmp, pB, view(pA, :, i)) - @views C[ci,:] .+= t.(ta .* tmp) - ci += 1 - end - C -end -function _generic_matmatmul_generic!(C, A, B, alpha, beta) - if iszero(alpha) || isempty(A) || isempty(B) - return _rmul_or_fill!(C, beta) - end - a1 = firstindex(A, 2) - b1 = firstindex(B, 1) - @inbounds for i in axes(A, 1), j in axes(B, 2) - z2 = zero(A[i, a1]*B[b1, j] + A[i, a1]*B[b1, j]) - Ctmp = convert(promote_type(eltype(C), typeof(z2)), z2) - @simd for k in axes(A, 2) - Ctmp = muladd(A[i, k], B[k, j], Ctmp) - end - @stable_muladdmul _modify!(MulAddMul(alpha,beta), Ctmp, C, (i,j)) - end - C -end - -# multiply 2x2 matrices -function matmul2x2(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} - matmul2x2!(similar(B, promote_op(matprod, T, S), 2, 2), tA, tB, A, B) -end - -function __matmul_checks(C, A, B, sz) - require_one_based_indexing(C, A, B) - if C === A || B === C - throw(ArgumentError("output matrix must not be aliased with input matrix")) - end - if !(size(A) == size(B) == size(C) == sz) - throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) - end - return nothing -end - -# separate function with the core of matmul2x2! that doesn't depend on a MulAddMul -function _matmul2x2_elements(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix) - __matmul_checks(C, A, B, (2,2)) - __matmul2x2_elements(tA, tB, A, B) -end -function __matmul2x2_elements(tA, A::AbstractMatrix) - @inbounds begin - tA_uc = uppercase(tA) # possibly unwrap a WrapperChar - if tA_uc == 'N' - A11 = A[1,1]; A12 = A[1,2]; A21 = A[2,1]; A22 = A[2,2] - elseif tA_uc == 'T' - # TODO making these lazy could improve perf - A11 = copy(transpose(A[1,1])); A12 = copy(transpose(A[2,1])) - A21 = copy(transpose(A[1,2])); A22 = copy(transpose(A[2,2])) - elseif tA_uc == 'C' - # TODO making these lazy could improve perf - A11 = copy(A[1,1]'); A12 = copy(A[2,1]') - A21 = copy(A[1,2]'); A22 = copy(A[2,2]') - elseif tA_uc == 'S' - if isuppercase(tA) # tA == 'S' - A11 = symmetric(A[1,1], :U); A12 = A[1,2] - A21 = copy(transpose(A[1,2])); A22 = symmetric(A[2,2], :U) - else - A11 = symmetric(A[1,1], :L); A12 = copy(transpose(A[2,1])) - A21 = A[2,1]; A22 = symmetric(A[2,2], :L) - end - elseif tA_uc == 'H' - if isuppercase(tA) # tA == 'H' - A11 = hermitian(A[1,1], :U); A12 = A[1,2] - A21 = copy(adjoint(A[1,2])); A22 = hermitian(A[2,2], :U) - else # if tA == 'h' - A11 = hermitian(A[1,1], :L); A12 = copy(adjoint(A[2,1])) - A21 = A[2,1]; A22 = hermitian(A[2,2], :L) - end - end - end # inbounds - A11, A12, A21, A22 -end -__matmul2x2_elements(tA, tB, A, B) = __matmul2x2_elements(tA, A), __matmul2x2_elements(tB, B) - -function _modify2x2!(Aelements, Belements, C, _add) - (A11, A12, A21, A22), (B11, B12, B21, B22) = Aelements, Belements - @inbounds begin - _modify!(_add, A11*B11 + A12*B21, C, (1,1)) - _modify!(_add, A21*B11 + A22*B21, C, (2,1)) - _modify!(_add, A11*B12 + A12*B22, C, (1,2)) - _modify!(_add, A21*B12 + A22*B22, C, (2,2)) - end # inbounds - C -end -function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, - α = true, β = false) - Aelements, Belements = _matmul2x2_elements(C, tA, tB, A, B) - @stable_muladdmul _modify2x2!(Aelements, Belements, C, MulAddMul(α, β)) - C -end - -# Multiply 3x3 matrices -function matmul3x3(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} - matmul3x3!(similar(B, promote_op(matprod, T, S), 3, 3), tA, tB, A, B) -end - -# separate function with the core of matmul3x3! that doesn't depend on a MulAddMul -function _matmul3x3_elements(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix) - __matmul_checks(C, A, B, (3,3)) - __matmul3x3_elements(tA, tB, A, B) -end -function __matmul3x3_elements(tA, A::AbstractMatrix) - @inbounds begin - tA_uc = uppercase(tA) # possibly unwrap a WrapperChar - if tA_uc == 'N' - A11 = A[1,1]; A12 = A[1,2]; A13 = A[1,3] - A21 = A[2,1]; A22 = A[2,2]; A23 = A[2,3] - A31 = A[3,1]; A32 = A[3,2]; A33 = A[3,3] - elseif tA_uc == 'T' - # TODO making these lazy could improve perf - A11 = copy(transpose(A[1,1])); A12 = copy(transpose(A[2,1])); A13 = copy(transpose(A[3,1])) - A21 = copy(transpose(A[1,2])); A22 = copy(transpose(A[2,2])); A23 = copy(transpose(A[3,2])) - A31 = copy(transpose(A[1,3])); A32 = copy(transpose(A[2,3])); A33 = copy(transpose(A[3,3])) - elseif tA_uc == 'C' - # TODO making these lazy could improve perf - A11 = copy(A[1,1]'); A12 = copy(A[2,1]'); A13 = copy(A[3,1]') - A21 = copy(A[1,2]'); A22 = copy(A[2,2]'); A23 = copy(A[3,2]') - A31 = copy(A[1,3]'); A32 = copy(A[2,3]'); A33 = copy(A[3,3]') - elseif tA_uc == 'S' - if isuppercase(tA) # tA == 'S' - A11 = symmetric(A[1,1], :U); A12 = A[1,2]; A13 = A[1,3] - A21 = copy(transpose(A[1,2])); A22 = symmetric(A[2,2], :U); A23 = A[2,3] - A31 = copy(transpose(A[1,3])); A32 = copy(transpose(A[2,3])); A33 = symmetric(A[3,3], :U) - else - A11 = symmetric(A[1,1], :L); A12 = copy(transpose(A[2,1])); A13 = copy(transpose(A[3,1])) - A21 = A[2,1]; A22 = symmetric(A[2,2], :L); A23 = copy(transpose(A[3,2])) - A31 = A[3,1]; A32 = A[3,2]; A33 = symmetric(A[3,3], :L) - end - elseif tA_uc == 'H' - if isuppercase(tA) # tA == 'H' - A11 = hermitian(A[1,1], :U); A12 = A[1,2]; A13 = A[1,3] - A21 = copy(adjoint(A[1,2])); A22 = hermitian(A[2,2], :U); A23 = A[2,3] - A31 = copy(adjoint(A[1,3])); A32 = copy(adjoint(A[2,3])); A33 = hermitian(A[3,3], :U) - else # if tA == 'h' - A11 = hermitian(A[1,1], :L); A12 = copy(adjoint(A[2,1])); A13 = copy(adjoint(A[3,1])) - A21 = A[2,1]; A22 = hermitian(A[2,2], :L); A23 = copy(adjoint(A[3,2])) - A31 = A[3,1]; A32 = A[3,2]; A33 = hermitian(A[3,3], :L) - end - end - end # inbounds - A11, A12, A13, A21, A22, A23, A31, A32, A33 -end -__matmul3x3_elements(tA, tB, A, B) = __matmul3x3_elements(tA, A), __matmul3x3_elements(tB, B) - -function _modify3x3!(Aelements, Belements, C, _add) - (A11, A12, A13, A21, A22, A23, A31, A32, A33), - (B11, B12, B13, B21, B22, B23, B31, B32, B33) = Aelements, Belements - @inbounds begin - _modify!(_add, A11*B11 + A12*B21 + A13*B31, C, (1,1)) - _modify!(_add, A21*B11 + A22*B21 + A23*B31, C, (2,1)) - _modify!(_add, A31*B11 + A32*B21 + A33*B31, C, (3,1)) - - _modify!(_add, A11*B12 + A12*B22 + A13*B32, C, (1,2)) - _modify!(_add, A21*B12 + A22*B22 + A23*B32, C, (2,2)) - _modify!(_add, A31*B12 + A32*B22 + A33*B32, C, (3,2)) - - _modify!(_add, A11*B13 + A12*B23 + A13*B33, C, (1,3)) - _modify!(_add, A21*B13 + A22*B23 + A23*B33, C, (2,3)) - _modify!(_add, A31*B13 + A32*B23 + A33*B33, C, (3,3)) - end # inbounds - C -end -function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, - α = true, β = false) - - Aelements, Belements = _matmul3x3_elements(C, tA, tB, A, B) - @stable_muladdmul _modify3x3!(Aelements, Belements, C, MulAddMul(α, β)) - C -end - -const RealOrComplex = Union{Real,Complex} - -# Three-argument * -""" - *(A, B::AbstractMatrix, C) - A * B * C * D - -Chained multiplication of 3 or 4 matrices is done in the most efficient sequence, -based on the sizes of the arrays. That is, the number of scalar multiplications needed -for `(A * B) * C` (with 3 dense matrices) is compared to that for `A * (B * C)` -to choose which of these to execute. - -If the last factor is a vector, or the first a transposed vector, then it is efficient -to deal with these first. In particular `x' * B * y` means `(x' * B) * y` -for an ordinary column-major `B::Matrix`. Unlike `dot(x, B, y)`, this -allocates an intermediate array. - -If the first or last factor is a number, this will be fused with the matrix -multiplication, using 5-arg [`mul!`](@ref). - -See also [`muladd`](@ref), [`dot`](@ref). - -!!! compat "Julia 1.7" - These optimisations require at least Julia 1.7. -""" -*(A::AbstractMatrix, B::AbstractMatrix, x::AbstractVector) = A * (B*x) - -*(tu::AdjOrTransAbsVec, B::AbstractMatrix, v::AbstractVector) = (tu*B) * v -*(tu::AdjOrTransAbsVec, B::AdjOrTransAbsMat, v::AbstractVector) = tu * (B*v) - -*(A::AbstractMatrix, x::AbstractVector, γ::Number) = mat_vec_scalar(A,x,γ) -*(A::AbstractMatrix, B::AbstractMatrix, γ::Number) = mat_mat_scalar(A,B,γ) -*(α::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractVector{<:RealOrComplex}) = - mat_vec_scalar(B,C,α) -*(α::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}) = - mat_mat_scalar(B,C,α) - -*(α::Number, u::AbstractVector, tv::AdjOrTransAbsVec) = broadcast(*, α, u, tv) -*(u::AbstractVector, tv::AdjOrTransAbsVec, γ::Number) = broadcast(*, u, tv, γ) -*(u::AbstractVector, tv::AdjOrTransAbsVec, C::AbstractMatrix) = u * (tv*C) - -*(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix) = _tri_matmul(A,B,C) -*(tv::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix) = (tv*B) * C - -function _tri_matmul(A,B,C,δ=nothing) - n,m = size(A) - # m,k == size(B) - k,l = size(C) - costAB_C = n*m*k + n*k*l # multiplications, allocations n*k + n*l - costA_BC = m*k*l + n*m*l # m*l + n*l - if costA_BC < costAB_C - isnothing(δ) ? A * (B*C) : A * mat_mat_scalar(B,C,δ) - else - isnothing(δ) ? (A*B) * C : mat_mat_scalar(A*B, C, δ) - end -end - -# Fast path for two arrays * one scalar is opt-in, via mat_vec_scalar and mat_mat_scalar. - -mat_vec_scalar(A, x, γ) = A * (x * γ) # fallback -mat_vec_scalar(A::StridedMaybeAdjOrTransMat, x::StridedVector, γ) = _mat_vec_scalar(A, x, γ) -mat_vec_scalar(A::AdjOrTransAbsVec, x::StridedVector, γ) = (A * x) * γ - -function _mat_vec_scalar(A, x, γ) - T = promote_type(eltype(A), eltype(x), typeof(γ)) - C = similar(A, T, axes(A,1)) - mul!(C, A, x, γ, false) -end - -mat_mat_scalar(A, B, γ) = (A*B) * γ # fallback -mat_mat_scalar(A::StridedMaybeAdjOrTransMat, B::StridedMaybeAdjOrTransMat, γ) = - _mat_mat_scalar(A, B, γ) - -function _mat_mat_scalar(A, B, γ) - T = promote_type(eltype(A), eltype(B), typeof(γ)) - C = similar(A, T, axes(A,1), axes(B,2)) - mul!(C, A, B, γ, false) -end - -mat_mat_scalar(A::AdjointAbsVec, B, γ) = (γ' * (A * B)')' # preserving order, adjoint reverses -mat_mat_scalar(A::AdjointAbsVec{<:RealOrComplex}, B::StridedMaybeAdjOrTransMat{<:RealOrComplex}, γ::RealOrComplex) = - mat_vec_scalar(B', A', γ')' - -mat_mat_scalar(A::TransposeAbsVec, B, γ) = transpose(γ * transpose(A * B)) -mat_mat_scalar(A::TransposeAbsVec{<:RealOrComplex}, B::StridedMaybeAdjOrTransMat{<:RealOrComplex}, γ::RealOrComplex) = - transpose(mat_vec_scalar(transpose(B), transpose(A), γ)) - - -# Four-argument *, by type -*(α::Number, β::Number, C::AbstractMatrix, x::AbstractVector) = (α*β) * C * x -*(α::Number, β::Number, C::AbstractMatrix, D::AbstractMatrix) = (α*β) * C * D -*(α::Number, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = α * B * (C*x) -*(α::Number, vt::AdjOrTransAbsVec, C::AbstractMatrix, x::AbstractVector) = α * (vt*C*x) -*(α::RealOrComplex, vt::AdjOrTransAbsVec{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}, D::AbstractMatrix{<:RealOrComplex}) = - (α*vt*C) * D # solves an ambiguity - -*(A::AbstractMatrix, x::AbstractVector, γ::Number, δ::Number) = A * x * (γ*δ) -*(A::AbstractMatrix, B::AbstractMatrix, γ::Number, δ::Number) = A * B * (γ*δ) -*(A::AbstractMatrix, B::AbstractMatrix, x::AbstractVector, δ::Number, ) = A * (B*x*δ) -*(vt::AdjOrTransAbsVec, B::AbstractMatrix, x::AbstractVector, δ::Number) = (vt*B*x) * δ -*(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, δ::Number) = (vt*B) * C * δ - -*(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = A * B * (C*x) -*(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix) = (vt*B) * C * D -*(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = vt * B * (C*x) - -# Four-argument *, by size -*(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, δ::Number) = _tri_matmul(A,B,C,δ) -*(α::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}, D::AbstractMatrix{<:RealOrComplex}) = - _tri_matmul(B,C,D,α) -*(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix) = - _quad_matmul(A,B,C,D) - -function _quad_matmul(A,B,C,D) - c1 = _mul_cost((A,B),(C,D)) - c2 = _mul_cost(((A,B),C),D) - c3 = _mul_cost(A,(B,(C,D))) - c4 = _mul_cost((A,(B,C)),D) - c5 = _mul_cost(A,((B,C),D)) - cmin = min(c1,c2,c3,c4,c5) - if c1 == cmin - (A*B) * (C*D) - elseif c2 == cmin - ((A*B) * C) * D - elseif c3 == cmin - A * (B * (C*D)) - elseif c4 == cmin - (A * (B*C)) * D - else - A * ((B*C) * D) - end -end -@inline _mul_cost(A::AbstractMatrix) = 0 -@inline _mul_cost((A,B)::Tuple) = _mul_cost(A,B) -@inline _mul_cost(A,B) = _mul_cost(A) + _mul_cost(B) + *(_mul_sizes(A)..., last(_mul_sizes(B))) -@inline _mul_sizes(A::AbstractMatrix) = size(A) -@inline _mul_sizes((A,B)::Tuple) = first(_mul_sizes(A)), last(_mul_sizes(B)) diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl deleted file mode 100644 index 9a89e58372d08..0000000000000 --- a/stdlib/LinearAlgebra/src/qr.jl +++ /dev/null @@ -1,769 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# QR Factorization -""" - QR <: Factorization - -A QR matrix factorization stored in a packed format, typically obtained from -[`qr`](@ref). If ``A`` is an `m`×`n` matrix, then - -```math -A = Q R -``` - -where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. -The matrix ``Q`` is stored as a sequence of Householder reflectors ``v_i`` -and coefficients ``\\tau_i`` where: - -```math -Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T). -``` - -Iterating the decomposition produces the components `Q` and `R`. - -The object has two fields: - -* `factors` is an `m`×`n` matrix. - - - The upper triangular part contains the elements of ``R``, that is `R = - triu(F.factors)` for a `QR` object `F`. - - - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where - ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`. - -* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``. -""" -struct QR{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: Factorization{T} - factors::S - τ::C - - function QR{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} - require_one_based_indexing(factors) - new{T,S,C}(factors, τ) - end -end -QR(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = - QR{T,typeof(factors),typeof(τ)}(factors, τ) -QR{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} = - QR(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QR{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S}, - QR{T,S,typeof(τ)}(factors, τ), false) - -# iteration for destructuring into components -Base.iterate(S::QR) = (S.Q, Val(:R)) -Base.iterate(S::QR, ::Val{:R}) = (S.R, Val(:done)) -Base.iterate(S::QR, ::Val{:done}) = nothing - -# Note. For QRCompactWY factorization without pivoting, the WY representation based method introduced in LAPACK 3.4 -""" - QRCompactWY <: Factorization - -A QR matrix factorization stored in a compact blocked format, typically obtained from -[`qr`](@ref). If ``A`` is an `m`×`n` matrix, then - -```math -A = Q R -``` - -where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. It is similar -to the [`QR`](@ref) format except that the orthogonal/unitary matrix ``Q`` is stored in -*Compact WY* format [^Schreiber1989]. For the block size ``n_b``, it is stored as -a `m`×`n` lower trapezoidal matrix ``V`` and a matrix ``T = (T_1 \\; T_2 \\; ... \\; -T_{b-1} \\; T_b')`` composed of ``b = \\lceil \\min(m,n) / n_b \\rceil`` upper triangular -matrices ``T_j`` of size ``n_b``×``n_b`` (``j = 1, ..., b-1``) and an upper trapezoidal -``n_b``×``\\min(m,n) - (b-1) n_b`` matrix ``T_b'`` (``j=b``) whose upper square part -denoted with ``T_b`` satisfying - -```math -Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T) -= \\prod_{j=1}^{b} (I - V_j T_j V_j^T) -``` - -such that ``v_i`` is the ``i``th column of ``V``, ``\\tau_i`` is the ``i``th element -of `[diag(T_1); diag(T_2); …; diag(T_b)]`, and ``(V_1 \\; V_2 \\; ... \\; V_b)`` -is the left `m`×`min(m, n)` block of ``V``. When constructed using [`qr`](@ref), -the block size is given by ``n_b = \\min(m, n, 36)``. - -Iterating the decomposition produces the components `Q` and `R`. - -The object has two fields: - -* `factors`, as in the [`QR`](@ref) type, is an `m`×`n` matrix. - - - The upper triangular part contains the elements of ``R``, that is `R = - triu(F.factors)` for a `QR` object `F`. - - - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format such - that `V = I + tril(F.factors, -1)`. - -* `T` is a ``n_b``-by-``\\min(m,n)`` matrix as described above. The subdiagonal elements - for each triangular matrix ``T_j`` are ignored. - -!!! note - - This format should not to be confused with the older *WY* representation - [^Bischof1987]. - - -[^Bischof1987]: C Bischof and C Van Loan, "The WY representation for products of Householder matrices", SIAM J Sci Stat Comput 8 (1987), s2-s13. [doi:10.1137/0908009](https://doi.org/10.1137/0908009) - -[^Schreiber1989]: R Schreiber and C Van Loan, "A storage-efficient WY representation for products of Householder transformations", SIAM J Sci Stat Comput 10 (1989), 53-57. [doi:10.1137/0910005](https://doi.org/10.1137/0910005) -""" -struct QRCompactWY{S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} <: Factorization{S} - factors::M - T::C - - function QRCompactWY{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} - require_one_based_indexing(factors) - new{S,M,C}(factors, T) - end -end -QRCompactWY(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = - QRCompactWY{S,typeof(factors),typeof(T)}(factors, T) -QRCompactWY{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = - QRCompactWY(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRCompactWY{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, - QRCompactWY{S,M,typeof(T)}(factors, T), false) - -# iteration for destructuring into components -Base.iterate(S::QRCompactWY) = (S.Q, Val(:R)) -Base.iterate(S::QRCompactWY, ::Val{:R}) = (S.R, Val(:done)) -Base.iterate(S::QRCompactWY, ::Val{:done}) = nothing - -# returns upper triangular views of all non-undef values of `qr(A).T`: -# -# julia> sparse(qr(A).T .== qr(A).T) -# 36×100 SparseMatrixCSC{Bool, Int64} with 1767 stored entries: -# ⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ -# ⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ -# ⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿ -# ⠀⠀⠀⠀⠀⠂⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿ -# ⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⢀⠐⠙⢿⣿⣿⣿⣿ -# ⠀⠀⠐⠀⠀⠀⠀⠀⠀⢀⢙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠁⠀⡀⠀⠙⢿⣿⣿ -# ⠀⠀⠐⠀⠀⠀⠀⠀⠀⠀⠄⠀⠙⢿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⡀⠀⠀⢀⠀⠀⠙⢿ -# ⠀⡀⠀⠀⠀⠀⠀⠀⠂⠒⠒⠀⠀⠀⠙⢿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⡀⠀⠀ -# ⠀⠀⠀⠀⠀⠀⠀⠀⣈⡀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠂⠀⢀⠀ -# -function _triuppers_qr(T) - blocksize, cols = size(T) - return Iterators.map(0:div(cols - 1, blocksize)) do i - n = min(blocksize, cols - i * blocksize) - return UpperTriangular(view(T, 1:n, (1:n) .+ i * blocksize)) - end -end - -function Base.hash(F::QRCompactWY, h::UInt) - return hash(F.factors, foldr(hash, _triuppers_qr(F.T); init=hash(QRCompactWY, h))) -end -function Base.:(==)(A::QRCompactWY, B::QRCompactWY) - return A.factors == B.factors && all(splat(==), zip(_triuppers_qr.((A.T, B.T))...)) -end -function Base.isequal(A::QRCompactWY, B::QRCompactWY) - return isequal(A.factors, B.factors) && all(zip(_triuppers_qr.((A.T, B.T))...)) do (a, b) - isequal(a, b)::Bool - end -end - -""" - QRPivoted <: Factorization - -A QR matrix factorization with column pivoting in a packed format, typically obtained from -[`qr`](@ref). If ``A`` is an `m`×`n` matrix, then - -```math -A P = Q R -``` - -where ``P`` is a permutation matrix, ``Q`` is an orthogonal/unitary matrix and ``R`` is -upper triangular. The matrix ``Q`` is stored as a sequence of Householder reflectors: - -```math -Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T). -``` - -Iterating the decomposition produces the components `Q`, `R`, and `p`. - -The object has three fields: - -* `factors` is an `m`×`n` matrix. - - - The upper triangular part contains the elements of ``R``, that is `R = - triu(F.factors)` for a `QR` object `F`. - - - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where - ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`. - -* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``. - -* `jpvt` is an integer vector of length `n` corresponding to the permutation ``P``. -""" -struct QRPivoted{T,S<:AbstractMatrix{T},C<:AbstractVector{T},P<:AbstractVector{<:Integer}} <: Factorization{T} - factors::S - τ::C - jpvt::P - - function QRPivoted{T,S,C,P}(factors, τ, jpvt) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T},P<:AbstractVector{<:Integer}} - require_one_based_indexing(factors, τ, jpvt) - new{T,S,C,P}(factors, τ, jpvt) - end -end -QRPivoted(factors::AbstractMatrix{T}, τ::AbstractVector{T}, - jpvt::AbstractVector{<:Integer}) where {T} = - QRPivoted{T,typeof(factors),typeof(τ),typeof(jpvt)}(factors, τ, jpvt) -QRPivoted{T}(factors::AbstractMatrix, τ::AbstractVector, - jpvt::AbstractVector{<:Integer}) where {T} = - QRPivoted(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ), jpvt) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(QRPivoted{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}, - jpvt::AbstractVector{<:Integer}) where {T,S}, - QRPivoted{T,S,typeof(τ),typeof(jpvt)}(factors, τ, jpvt), false) - -# iteration for destructuring into components -Base.iterate(S::QRPivoted) = (S.Q, Val(:R)) -Base.iterate(S::QRPivoted, ::Val{:R}) = (S.R, Val(:p)) -Base.iterate(S::QRPivoted, ::Val{:p}) = (S.p, Val(:done)) -Base.iterate(S::QRPivoted, ::Val{:done}) = nothing - -function qrfactUnblocked!(A::AbstractMatrix{T}) where {T} - require_one_based_indexing(A) - m, n = size(A) - τ = zeros(T, min(m,n)) - for k = 1:min(m - 1 + !(T<:Real), n) - x = view(A, k:m, k) - τk = reflector!(x) - τ[k] = τk - reflectorApply!(x, τk, view(A, k:m, k + 1:n)) - end - QR(A, τ) -end - -# Find index for columns with largest two norm -function indmaxcolumn(A::AbstractMatrix) - mm = norm(view(A, :, 1)) - ii = 1 - for i = 2:size(A, 2) - mi = norm(view(A, :, i)) - if abs(mi) > mm - mm = mi - ii = i - end - end - return ii -end - -function qrfactPivotedUnblocked!(A::AbstractMatrix) - m, n = size(A) - piv = Vector(UnitRange{BlasInt}(1,n)) - τ = Vector{eltype(A)}(undef, min(m,n)) - for j = 1:min(m,n) - - # Find column with maximum norm in trailing submatrix - jm = indmaxcolumn(view(A, j:m, j:n)) + j - 1 - - if jm != j - # Flip elements in pivoting vector - tmpp = piv[jm] - piv[jm] = piv[j] - piv[j] = tmpp - - # Update matrix with - for i = 1:m - tmp = A[i,jm] - A[i,jm] = A[i,j] - A[i,j] = tmp - end - end - - # Compute reflector of columns j - x = view(A, j:m, j) - τj = reflector!(x) - τ[j] = τj - - # Update trailing submatrix with reflector - reflectorApply!(x, τj, view(A, j:m, j+1:n)) - end - return QRPivoted{eltype(A), typeof(A), typeof(τ), typeof(piv)}(A, τ, piv) -end - -# LAPACK version -qr!(A::StridedMatrix{<:BlasFloat}, ::NoPivot; blocksize=36) = - QRCompactWY(LAPACK.geqrt!(A, min(min(size(A)...), blocksize))...) -qr!(A::StridedMatrix{<:BlasFloat}, ::ColumnNorm) = QRPivoted(LAPACK.geqp3!(A)...) - -# Generic fallbacks - -""" - qr!(A, pivot = NoPivot(); blocksize) - -`qr!` is the same as [`qr`](@ref) when `A` is a subtype of [`AbstractMatrix`](@ref), -but saves space by overwriting the input `A`, instead of creating a copy. -An [`InexactError`](@ref) exception is thrown if the factorization produces a number not -representable by the element type of `A`, e.g. for integer types. - -!!! compat "Julia 1.4" - The `blocksize` keyword argument requires Julia 1.4 or later. - -# Examples -```jldoctest -julia> a = [1. 2.; 3. 4.] -2×2 Matrix{Float64}: - 1.0 2.0 - 3.0 4.0 - -julia> qr!(a) -LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} -Q factor: 2×2 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} -R factor: -2×2 Matrix{Float64}: - -3.16228 -4.42719 - 0.0 -0.632456 - -julia> a = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> qr!(a) -ERROR: InexactError: Int64(3.1622776601683795) -Stacktrace: -[...] -``` -""" -qr!(A::AbstractMatrix, ::NoPivot) = qrfactUnblocked!(A) -qr!(A::AbstractMatrix, ::ColumnNorm) = qrfactPivotedUnblocked!(A) -qr!(A::AbstractMatrix) = qr!(A, NoPivot()) -# TODO: Remove in Julia v2.0 -@deprecate qr!(A::AbstractMatrix, ::Val{true}) qr!(A, ColumnNorm()) -@deprecate qr!(A::AbstractMatrix, ::Val{false}) qr!(A, NoPivot()) - -_qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T)))) - -""" - qr(A, pivot = NoPivot(); blocksize) -> F - -Compute the QR factorization of the matrix `A`: an orthogonal (or unitary if `A` is -complex-valued) matrix `Q`, and an upper triangular matrix `R` such that - -```math -A = Q R -``` - -The returned object `F` stores the factorization in a packed format: - - - if `pivot == ColumnNorm()` then `F` is a [`QRPivoted`](@ref) object, - - - otherwise if the element type of `A` is a BLAS type ([`Float32`](@ref), [`Float64`](@ref), - `ComplexF32` or `ComplexF64`), then `F` is a [`QRCompactWY`](@ref) object, - - - otherwise `F` is a [`QR`](@ref) object. - -The individual components of the decomposition `F` can be retrieved via property accessors: - - - `F.Q`: the orthogonal/unitary matrix `Q` - - `F.R`: the upper triangular matrix `R` - - `F.p`: the permutation vector of the pivot ([`QRPivoted`](@ref) only) - - `F.P`: the permutation matrix of the pivot ([`QRPivoted`](@ref) only) - -!!! note - Each reference to the upper triangular factor via `F.R` allocates a new array. - It is therefore advisable to cache that array, say, by `R = F.R` and continue working - with `R`. - -Iterating the decomposition produces the components `Q`, `R`, and if extant `p`. - -The following functions are available for the `QR` objects: [`inv`](@ref), [`size`](@ref), -and [`\\`](@ref). When `A` is rectangular, `\\` will return a least squares -solution and if the solution is not unique, the one with smallest norm is returned. When -`A` is not full rank, factorization with (column) pivoting is required to obtain a minimum -norm solution. - -Multiplication with respect to either full/square or non-full/square `Q` is allowed, i.e. both `F.Q*F.R` -and `F.Q*A` are supported. A `Q` matrix can be converted into a regular matrix with -[`Matrix`](@ref). This operation returns the "thin" Q factor, i.e., if `A` is `m`×`n` with `m>=n`, then -`Matrix(F.Q)` yields an `m`×`n` matrix with orthonormal columns. To retrieve the "full" Q factor, an -`m`×`m` orthogonal matrix, use `F.Q*I` or `collect(F.Q)`. If `m<=n`, then `Matrix(F.Q)` yields an `m`×`m` -orthogonal matrix. - -The block size for QR decomposition can be specified by keyword argument -`blocksize :: Integer` when `pivot == NoPivot()` and `A isa StridedMatrix{<:BlasFloat}`. -It is ignored when `blocksize > minimum(size(A))`. See [`QRCompactWY`](@ref). - -!!! compat "Julia 1.4" - The `blocksize` keyword argument requires Julia 1.4 or later. - -# Examples -```jldoctest -julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0] -3×2 Matrix{Float64}: - 3.0 -6.0 - 4.0 -8.0 - 0.0 1.0 - -julia> F = qr(A) -LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} -Q factor: 3×3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} -R factor: -2×2 Matrix{Float64}: - -5.0 10.0 - 0.0 -1.0 - -julia> F.Q * F.R == A -true -``` - -!!! note - `qr` returns multiple types because LAPACK uses several representations - that minimize the memory storage requirements of products of Householder - elementary reflectors, so that the `Q` and `R` matrices can be stored - compactly rather than two separate dense matrices. -""" -function qr(A::AbstractMatrix{T}, arg...; kwargs...) where T - require_one_based_indexing(A) - AA = copy_similar(A, _qreltype(T)) - return _qr(AA, arg...; kwargs...) -end -# TODO: remove in Julia v2.0 -@deprecate qr(A::AbstractMatrix, ::Val{false}; kwargs...) qr(A, NoPivot(); kwargs...) -@deprecate qr(A::AbstractMatrix, ::Val{true}; kwargs...) qr(A, ColumnNorm(); kwargs...) - -# allow packages like SparseArrays.jl to hook into here and redirect to out-of-place `qr` -_qr(A::AbstractMatrix, args...; kwargs...) = qr!(A, args...; kwargs...) - -qr(x::Number) = qr(fill(x,1,1)) -function qr(v::AbstractVector) - require_one_based_indexing(v) - qr(reshape(v, (length(v), 1))) -end - -# Conversions -QR{T}(A::QR) where {T} = QR(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ)) -Factorization{T}(A::QR{T}) where {T} = A -Factorization{T}(A::QR) where {T} = QR{T}(A) -QRCompactWY{T}(A::QRCompactWY) where {T} = QRCompactWY(convert(AbstractMatrix{T}, A.factors), convert(AbstractMatrix{T}, A.T)) -Factorization{T}(A::QRCompactWY{T}) where {T} = A -Factorization{T}(A::QRCompactWY) where {T} = QRCompactWY{T}(A) -AbstractMatrix(F::Union{QR,QRCompactWY}) = F.Q * F.R -AbstractArray(F::Union{QR,QRCompactWY}) = AbstractMatrix(F) -Matrix(F::Union{QR,QRCompactWY}) = Array(AbstractArray(F)) -Array(F::Union{QR,QRCompactWY}) = Matrix(F) -QRPivoted{T}(A::QRPivoted) where {T} = QRPivoted(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ), A.jpvt) -Factorization{T}(A::QRPivoted{T}) where {T} = A -Factorization{T}(A::QRPivoted) where {T} = QRPivoted{T}(A) -AbstractMatrix(F::QRPivoted) = (F.Q * F.R)[:,invperm(F.p)] -AbstractArray(F::QRPivoted) = AbstractMatrix(F) -Matrix(F::QRPivoted) = Array(AbstractArray(F)) -Array(F::QRPivoted) = Matrix(F) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Union{QR, QRCompactWY, QRPivoted}) - summary(io, F); println(io) - print(io, "Q factor: ") - show(io, mime, F.Q) - println(io, "\nR factor:") - show(io, mime, F.R) - if F isa QRPivoted - println(io, "\npermutation:") - show(io, mime, F.p) - end -end - -function getproperty(F::QR, d::Symbol) - m, n = size(F) - if d === :R - return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d === :Q - return QRPackedQ(getfield(F, :factors), F.τ) - else - getfield(F, d) - end -end -function getproperty(F::QRCompactWY, d::Symbol) - m, n = size(F) - if d === :R - return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d === :Q - return QRCompactWYQ(getfield(F, :factors), F.T) - else - getfield(F, d) - end -end -Base.propertynames(F::Union{QR,QRCompactWY}, private::Bool=false) = - (:R, :Q, (private ? fieldnames(typeof(F)) : ())...) - -function getproperty(F::QRPivoted{T}, d::Symbol) where T - m, n = size(F) - if d === :R - return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d === :Q - return QRPackedQ(getfield(F, :factors), F.τ) - elseif d === :p - return getfield(F, :jpvt) - elseif d === :P - p = F.p - n = length(p) - P = zeros(T, n, n) - for i in 1:n - P[p[i],i] = one(T) - end - return P - else - getfield(F, d) - end -end -Base.propertynames(F::QRPivoted, private::Bool=false) = - (:R, :Q, :p, :P, (private ? fieldnames(typeof(F)) : ())...) - -transpose(F::Union{QR{<:Real},QRPivoted{<:Real},QRCompactWY{<:Real}}) = F' -transpose(::Union{QR,QRPivoted,QRCompactWY}) = - throw(ArgumentError("transpose of QR decomposition is not supported, consider using adjoint")) - -size(F::Union{QR,QRCompactWY,QRPivoted}) = size(getfield(F, :factors)) -size(F::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(getfield(F, :factors), dim) - - -function ldiv!(A::QRCompactWY{T}, b::AbstractVector{T}) where {T} - require_one_based_indexing(b) - m, n = size(A) - ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), b), 1:size(A, 2))) - return b -end -function ldiv!(A::QRCompactWY{T}, B::AbstractMatrix{T}) where {T} - require_one_based_indexing(B) - m, n = size(A) - ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), B), 1:size(A, 2), 1:size(B, 2))) - return B -end - -function rank(A::QRPivoted; atol::Real=0, rtol::Real=min(size(A)...) * eps(real(float(one(eltype(A.Q))))) * iszero(atol)) - m = min(size(A)...) - m == 0 && return 0 - tol = max(atol, rtol*abs(A.R[1,1])) - return something(findfirst(i -> abs(A.R[i,i]) <= tol, 1:m), m+1) - 1 -end - -# Julia implementation similar to xgelsy -function ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}, rcond::Real) where {T<:BlasFloat} - require_one_based_indexing(B) - m, n = size(A) - - if m > size(B, 1) || n > size(B, 1) - throw(DimensionMismatch(lazy"B has leading dimension $(size(B, 1)) but needs at least $(max(m, n))")) - end - - if length(A.factors) == 0 || length(B) == 0 - return B, 0 - end - - @inbounds begin - smin = smax = abs(A.factors[1]) - - if smax == 0 - return fill!(B, 0), 0 - end - - mn = min(m, n) - - # allocate temporary work space - tmp = Vector{T}(undef, 2mn) - wmin = view(tmp, 1:mn) - wmax = view(tmp, mn+1:2mn) - - rnk = 1 - wmin[1] = 1 - wmax[1] = 1 - - while rnk < mn - i = rnk + 1 - - smin, s1, c1 = LAPACK.laic1!(2, view(wmin, 1:rnk), smin, view(A.factors, 1:rnk, i), A.factors[i,i]) - smax, s2, c2 = LAPACK.laic1!(1, view(wmax, 1:rnk), smax, view(A.factors, 1:rnk, i), A.factors[i,i]) - - if smax*rcond > smin - break - end - - for j in 1:rnk - wmin[j] *= s1 - wmax[j] *= s2 - end - wmin[i] = c1 - wmax[i] = c2 - - rnk += 1 - end - - if rnk < n - C, τ = LAPACK.tzrzf!(A.factors[1:rnk, :]) - work = vec(C) - else - C, τ = A.factors, A.τ - work = resize!(tmp, n) - end - - lmul!(adjoint(A.Q), view(B, 1:m, :)) - ldiv!(UpperTriangular(view(C, 1:rnk, 1:rnk)), view(B, 1:rnk, :)) - - if rnk < n - B[rnk+1:n,:] .= zero(T) - LAPACK.ormrz!('L', T <: Complex ? 'C' : 'T', C, τ, view(B, 1:n, :)) - end - - for j in axes(B, 2) - for i in 1:n - work[A.p[i]] = B[i,j] - end - for i in 1:n - B[i,j] = work[i] - end - end - end - - return B, rnk -end - -ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractVector{T}) where {T<:BlasFloat} = - vec(ldiv!(A, reshape(B, length(B), 1))) -ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}) where {T<:BlasFloat} = - ldiv!(A, B, min(size(A)...)*eps(real(T)))[1] - -function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T - m, n = size(A) - minmn = min(m,n) - mB, nB = size(B) - lmul!(adjoint(A.Q), view(B, 1:m, :)) - R = A.R # makes a copy, used as a buffer below - @inbounds begin - if n > m # minimum norm solution - τ = zeros(T,m) - for k = m:-1:1 # Trapezoid to triangular by elementary operation - x = view(R, k, [k; m + 1:n]) - τk = reflector!(x) - τ[k] = conj(τk) - for i = 1:k - 1 - vRi = R[i,k] - for j = m + 1:n - vRi += R[i,j]*x[j - m + 1]' - end - vRi *= τk - R[i,k] -= vRi - for j = m + 1:n - R[i,j] -= vRi*x[j - m + 1] - end - end - end - end - ldiv!(UpperTriangular(view(R, :, 1:minmn)), view(B, 1:minmn, :)) - if n > m # Apply elementary transformation to solution - B[m + 1:mB,1:nB] .= zero(T) - for j = 1:nB - for k = 1:m - vBj = B[k,j]' - for i = m + 1:n - vBj += B[i,j]'*R[k,i]' - end - vBj *= τ[k] - B[k,j] -= vBj' - for i = m + 1:n - B[i,j] -= R[k,i]'*vBj' - end - end - end - end - end - return B -end - - -function ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T - m, n = size(A) - m < n && return _wide_qr_ldiv!(A, B) - - lmul!(adjoint(A.Q), view(B, 1:m, :)) - R = A.factors - ldiv!(UpperTriangular(view(R,1:n,:)), view(B, 1:n, :)) - return B -end -function ldiv!(A::QR, B::AbstractVector) - ldiv!(A, reshape(B, length(B), 1)) - return B -end - -function ldiv!(A::QRPivoted, b::AbstractVector) - ldiv!(QR(A.factors,A.τ), b) - b[1:size(A.factors, 2)] = view(b, 1:size(A.factors, 2))[invperm(A.jpvt)] - b -end -function ldiv!(A::QRPivoted, B::AbstractMatrix) - ldiv!(QR(A.factors, A.τ), B) - B[1:size(A.factors, 2),:] = view(B, 1:size(A.factors, 2), :)[invperm(A.jpvt),:] - B -end - -function _apply_permutation!(F::QRPivoted, B::AbstractVecOrMat) - # Apply permutation but only to the top part of the solution vector since - # it's padded with zeros for underdetermined problems - B[1:length(F.p), :] = B[F.p, :] - return B -end -_apply_permutation!(::Factorization, B::AbstractVecOrMat) = B - -function ldiv!(Fadj::AdjointFactorization{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat) - require_one_based_indexing(B) - m, n = size(Fadj) - - # We don't allow solutions overdetermined systems - if m > n - throw(DimensionMismatch("overdetermined systems are not supported")) - end - if n != size(B, 1) - throw(DimensionMismatch("inputs should have the same number of rows")) - end - F = parent(Fadj) - - B = _apply_permutation!(F, B) - - # For underdetermined system, the triangular solve should only be applied to the top - # part of B that contains the rhs. For square problems, the view corresponds to B itself - ldiv!(LowerTriangular(adjoint(F.R)), view(B, 1:size(F.R, 2), :)) - lmul!(F.Q, B) - - return B -end - -# With a real lhs and complex rhs with the same precision, we can reinterpret the complex -# rhs as a real rhs with twice the number of columns. - -# convenience methods to compute the return size correctly for vectors and matrices -_ret_size(A::Factorization, b::AbstractVector) = (max(size(A, 2), length(b)),) -_ret_size(A::Factorization, B::AbstractMatrix) = (max(size(A, 2), size(B, 1)), size(B, 2)) - -function (\)(A::Union{QR{T},QRCompactWY{T},QRPivoted{T}}, BIn::VecOrMat{Complex{T}}) where T<:BlasReal - require_one_based_indexing(BIn) - m, n = size(A) - m == size(BIn, 1) || throw(DimensionMismatch(lazy"left hand side has $m rows, but right hand side has $(size(BIn,1)) rows")) - -# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3| -# |z2|z4| -> |y1|y2|y3|y4| -> |x2|y2| -> |x2|y2|x4|y4| -# |x3|y3| -# |x4|y4| - B = reshape(copy(transpose(reinterpret(T, reshape(BIn, (1, length(BIn)))))), size(BIn, 1), 2*size(BIn, 2)) - - X = _zeros(T, B, n) - X[1:size(B, 1), :] = B - - ldiv!(A, X) - -# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3| -# |z2|z4| <- |y1|y2|y3|y4| <- |x2|y2| <- |x2|y2|x4|y4| -# |x3|y3| -# |x4|y4| - XX = reshape(collect(reinterpret(Complex{T}, copy(transpose(reshape(X, div(length(X), 2), 2))))), _ret_size(A, BIn)) - return _cut_B(XX, 1:n) -end - -##TODO: Add methods for rank(A::QRP{T}) and adjust the (\) method accordingly -## Add rcond methods for Cholesky, LU, QR and QRP types -## Lower priority: Add LQ, QL and RQ factorizations - -# FIXME! Should add balancing option through xgebal diff --git a/stdlib/LinearAlgebra/src/schur.jl b/stdlib/LinearAlgebra/src/schur.jl deleted file mode 100644 index 7257544ff872e..0000000000000 --- a/stdlib/LinearAlgebra/src/schur.jl +++ /dev/null @@ -1,449 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Schur decomposition -""" - Schur <: Factorization - -Matrix factorization type of the Schur factorization of a matrix `A`. This is the -return type of [`schur(_)`](@ref), the corresponding matrix factorization function. - -If `F::Schur` is the factorization object, the (quasi) triangular Schur factor can -be obtained via either `F.Schur` or `F.T` and the orthogonal/unitary Schur vectors -via `F.vectors` or `F.Z` such that `A = F.vectors * F.Schur * F.vectors'`. The -eigenvalues of `A` can be obtained with `F.values`. - -Iterating the decomposition produces the components `F.T`, `F.Z`, and `F.values`. - -# Examples -```jldoctest -julia> A = [5. 7.; -2. -4.] -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> F = schur(A) -Schur{Float64, Matrix{Float64}, Vector{Float64}} -T factor: -2×2 Matrix{Float64}: - 3.0 9.0 - 0.0 -2.0 -Z factor: -2×2 Matrix{Float64}: - 0.961524 0.274721 - -0.274721 0.961524 -eigenvalues: -2-element Vector{Float64}: - 3.0 - -2.0 - -julia> F.vectors * F.Schur * F.vectors' -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> t, z, vals = F; # destructuring via iteration - -julia> t == F.T && z == F.Z && vals == F.values -true -``` -""" -struct Schur{Ty,S<:AbstractMatrix,C<:AbstractVector} <: Factorization{Ty} - T::S - Z::S - values::C - Schur{Ty,S,C}(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, - values::AbstractVector) where {Ty,S,C} = new(T, Z, values) -end -Schur(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::AbstractVector) where {Ty} = - Schur{Ty, typeof(T), typeof(values)}(T, Z, values) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(Schur{Ty,S}(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, - values::AbstractVector) where {Ty,S}, - Schur{Ty,S,typeof(values)}(T, Z, values)) - -# iteration for destructuring into components -Base.iterate(S::Schur) = (S.T, Val(:Z)) -Base.iterate(S::Schur, ::Val{:Z}) = (S.Z, Val(:values)) -Base.iterate(S::Schur, ::Val{:values}) = (S.values, Val(:done)) -Base.iterate(S::Schur, ::Val{:done}) = nothing - -""" - schur!(A) -> F::Schur - -Same as [`schur`](@ref) but uses the input argument `A` as workspace. - -# Examples -```jldoctest -julia> A = [5. 7.; -2. -4.] -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> F = schur!(A) -Schur{Float64, Matrix{Float64}, Vector{Float64}} -T factor: -2×2 Matrix{Float64}: - 3.0 9.0 - 0.0 -2.0 -Z factor: -2×2 Matrix{Float64}: - 0.961524 0.274721 - -0.274721 0.961524 -eigenvalues: -2-element Vector{Float64}: - 3.0 - -2.0 - -julia> A -2×2 Matrix{Float64}: - 3.0 9.0 - 0.0 -2.0 -``` -""" -schur!(A::StridedMatrix{<:BlasFloat}) = Schur(LinearAlgebra.LAPACK.gees!('V', A)...) - -schur!(A::UpperHessenberg{T}) where {T<:BlasFloat} = Schur(LinearAlgebra.LAPACK.hseqr!(parent(A))...) - -""" - schur(A) -> F::Schur - -Computes the Schur factorization of the matrix `A`. The (quasi) triangular Schur factor can -be obtained from the `Schur` object `F` with either `F.Schur` or `F.T` and the -orthogonal/unitary Schur vectors can be obtained with `F.vectors` or `F.Z` such that -`A = F.vectors * F.Schur * F.vectors'`. The eigenvalues of `A` can be obtained with `F.values`. - -For real `A`, the Schur factorization is "quasitriangular", which means that it -is upper-triangular except with 2×2 diagonal blocks for any conjugate pair -of complex eigenvalues; this allows the factorization to be purely real even -when there are complex eigenvalues. To obtain the (complex) purely upper-triangular -Schur factorization from a real quasitriangular factorization, you can use -`Schur{Complex}(schur(A))`. - -Iterating the decomposition produces the components `F.T`, `F.Z`, and `F.values`. - -# Examples -```jldoctest -julia> A = [5. 7.; -2. -4.] -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> F = schur(A) -Schur{Float64, Matrix{Float64}, Vector{Float64}} -T factor: -2×2 Matrix{Float64}: - 3.0 9.0 - 0.0 -2.0 -Z factor: -2×2 Matrix{Float64}: - 0.961524 0.274721 - -0.274721 0.961524 -eigenvalues: -2-element Vector{Float64}: - 3.0 - -2.0 - -julia> F.vectors * F.Schur * F.vectors' -2×2 Matrix{Float64}: - 5.0 7.0 - -2.0 -4.0 - -julia> t, z, vals = F; # destructuring via iteration - -julia> t == F.T && z == F.Z && vals == F.values -true -``` -""" -schur(A::AbstractMatrix{T}) where {T} = schur!(copy_similar(A, eigtype(T))) -schur(A::UpperHessenberg{T}) where {T} = schur!(copy_similar(A, eigtype(T))) -function schur(A::RealHermSymComplexHerm) - F = eigen(A; sortby=nothing) - return Schur(typeof(F.vectors)(Diagonal(F.values)), F.vectors, F.values) -end -function schur(A::Union{UnitUpperTriangular{T},UpperTriangular{T}}) where {T} - t = eigtype(T) - Z = copy_similar(A, t) - return Schur(Z, Matrix{t}(I, size(A)), convert(Vector{t}, diag(A))) -end -function schur(A::Union{UnitLowerTriangular{T},LowerTriangular{T}}) where {T} - t = eigtype(T) - # double flip the matrix A - Z = copy_similar(A, t) - reverse!(reshape(Z, :)) - # construct "reverse" identity - n = size(A, 1) - J = zeros(t, n, n) - for i in axes(J, 2) - J[n+1-i, i] = oneunit(t) - end - return Schur(Z, J, convert(Vector{t}, diag(A))) -end -function schur(A::Bidiagonal{T}) where {T} - t = eigtype(T) - if A.uplo == 'U' - return Schur(Matrix{t}(A), Matrix{t}(I, size(A)), Vector{t}(A.dv)) - else # A.uplo == 'L' - # construct "reverse" identity - n = size(A, 1) - J = zeros(t, n, n) - for i in axes(J, 2) - J[n+1-i, i] = oneunit(t) - end - dv = reverse!(Vector{t}(A.dv)) - ev = reverse!(Vector{t}(A.ev)) - return Schur(Matrix{t}(Bidiagonal(dv, ev, 'U')), J, dv) - end -end - -function getproperty(F::Schur, d::Symbol) - if d === :Schur - return getfield(F, :T) - elseif d === :vectors - return getfield(F, :Z) - else - getfield(F, d) - end -end - -Base.propertynames(F::Schur) = - (:Schur, :vectors, fieldnames(typeof(F))...) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Schur) - summary(io, F); println(io) - println(io, "T factor:") - show(io, mime, F.T) - println(io, "\nZ factor:") - show(io, mime, F.Z) - println(io, "\neigenvalues:") - show(io, mime, F.values) -end - -# convert a (standard-form) quasi-triangular real Schur factorization into a -# triangular complex Schur factorization. -# -# Based on the "triangularize" function from GenericSchur.jl, -# released under the MIT "Expat" license by @RalphAS -function Schur{CT}(S::Schur{<:Real}) where {CT<:Complex} - Tr = S.T - T = CT.(Tr) - Z = CT.(S.Z) - n = size(T,1) - for j=n:-1:2 - if !iszero(Tr[j,j-1]) - # We want a unitary similarity transform from - # ┌ ┐ ┌ ┐ - # │a b│ │w₁ x│ - # │c a│ into │0 w₂│ where bc < 0 (a,b,c real) - # └ ┘ └ ┘ - # If we write it as - # ┌ ┐ - # │u v'│ - # │-v u'│ - # └ ┘ - # and make the Ansatz that u is real (so v is imaginary), - # we arrive at a Givens rotation: - # θ = atan(sqrt(-Tr[j,j-1]/Tr[j-1,j])) - # s,c = sin(θ), cos(θ) - s = sqrt(abs(Tr[j,j-1])) - c = sqrt(abs(Tr[j-1,j])) - r = hypot(s,c) - G = Givens(j-1,j,complex(c/r),im*(-s/r)) - lmul!(G,T) - rmul!(T,G') - rmul!(Z,G') - end - end - return Schur(triu!(T),Z,diag(T)) -end - -Schur{Complex}(S::Schur{<:Complex}) = S -Schur{T}(S::Schur{T}) where {T} = S -Schur{T}(S::Schur) where {T} = Schur(T.(S.T), T.(S.Z), T <: Real && !(eltype(S.values) <: Real) ? complex(T).(S.values) : T.(S.values)) - -""" - ordschur!(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur - -Same as [`ordschur`](@ref) but overwrites the factorization `F`. -""" -function ordschur!(schur::Schur, select::Union{Vector{Bool},BitVector}) - _, _, vals = _ordschur!(schur.T, schur.Z, select) - schur.values[:] = vals - return schur -end - -_ordschur(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = - _ordschur!(copy(T), copy(Z), select) - -_ordschur!(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = - LinearAlgebra.LAPACK.trsen!(convert(Vector{BlasInt}, select), T, Z)[1:3] - -""" - ordschur(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur - -Reorders the Schur factorization `F` of a matrix `A = Z*T*Z'` according to the logical array -`select` returning the reordered factorization `F` object. The selected eigenvalues appear -in the leading diagonal of `F.Schur` and the corresponding leading columns of -`F.vectors` form an orthogonal/unitary basis of the corresponding right invariant -subspace. In the real case, a complex conjugate pair of eigenvalues must be either both -included or both excluded via `select`. -""" -ordschur(schur::Schur, select::Union{Vector{Bool},BitVector}) = - Schur(_ordschur(schur.T, schur.Z, select)...) - -""" - GeneralizedSchur <: Factorization - -Matrix factorization type of the generalized Schur factorization of two matrices -`A` and `B`. This is the return type of [`schur(_, _)`](@ref), the corresponding -matrix factorization function. - -If `F::GeneralizedSchur` is the factorization object, the (quasi) triangular Schur -factors can be obtained via `F.S` and `F.T`, the left unitary/orthogonal Schur -vectors via `F.left` or `F.Q`, and the right unitary/orthogonal Schur vectors can -be obtained with `F.right` or `F.Z` such that `A=F.left*F.S*F.right'` and -`B=F.left*F.T*F.right'`. The generalized eigenvalues of `A` and `B` can be obtained -with `F.α./F.β`. - -Iterating the decomposition produces the components `F.S`, `F.T`, `F.Q`, `F.Z`, -`F.α`, and `F.β`. -""" -struct GeneralizedSchur{Ty,M<:AbstractMatrix,A<:AbstractVector,B<:AbstractVector{Ty}} <: Factorization{Ty} - S::M - T::M - α::A - β::B - Q::M - Z::M - function GeneralizedSchur{Ty,M,A,B}(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, - alpha::AbstractVector, beta::AbstractVector{Ty}, - Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where {Ty,M,A,B} - new{Ty,M,A,B}(S, T, alpha, beta, Q, Z) - end -end -function GeneralizedSchur(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, - alpha::AbstractVector, beta::AbstractVector{Ty}, - Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where Ty - GeneralizedSchur{Ty, typeof(S), typeof(alpha), typeof(beta)}(S, T, alpha, beta, Q, Z) -end -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(GeneralizedSchur{Ty,M}(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, - alpha::AbstractVector, beta::AbstractVector{Ty}, - Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where {Ty,M}, - GeneralizedSchur{Ty,M,typeof(alpha),typeof(beta)}(S, T, alpha, beta, Q, Z)) - -# iteration for destructuring into components -Base.iterate(S::GeneralizedSchur) = (S.S, Val(:T)) -Base.iterate(S::GeneralizedSchur, ::Val{:T}) = (S.T, Val(:Q)) -Base.iterate(S::GeneralizedSchur, ::Val{:Q}) = (S.Q, Val(:Z)) -Base.iterate(S::GeneralizedSchur, ::Val{:Z}) = (S.Z, Val(:α)) -Base.iterate(S::GeneralizedSchur, ::Val{:α}) = (S.α, Val(:β)) -Base.iterate(S::GeneralizedSchur, ::Val{:β}) = (S.β, Val(:done)) -Base.iterate(S::GeneralizedSchur, ::Val{:done}) = nothing - -""" - schur!(A::StridedMatrix, B::StridedMatrix) -> F::GeneralizedSchur - -Same as [`schur`](@ref) but uses the input matrices `A` and `B` as workspace. -""" -function schur!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} - if LAPACK.version() < v"3.6.0" - GeneralizedSchur(LinearAlgebra.LAPACK.gges!('V', 'V', A, B)...) - else - GeneralizedSchur(LinearAlgebra.LAPACK.gges3!('V', 'V', A, B)...) - end -end - -""" - schur(A, B) -> F::GeneralizedSchur - -Computes the Generalized Schur (or QZ) factorization of the matrices `A` and `B`. The -(quasi) triangular Schur factors can be obtained from the `Schur` object `F` with `F.S` -and `F.T`, the left unitary/orthogonal Schur vectors can be obtained with `F.left` or -`F.Q` and the right unitary/orthogonal Schur vectors can be obtained with `F.right` or -`F.Z` such that `A=F.left*F.S*F.right'` and `B=F.left*F.T*F.right'`. The -generalized eigenvalues of `A` and `B` can be obtained with `F.α./F.β`. - -Iterating the decomposition produces the components `F.S`, `F.T`, `F.Q`, `F.Z`, -`F.α`, and `F.β`. -""" -function schur(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} - S = promote_type(eigtype(TA), TB) - return schur!(copy_similar(A, S), copy_similar(B, S)) -end - -""" - ordschur!(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur - -Same as `ordschur` but overwrites the factorization `F`. -""" -function ordschur!(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) - _, _, α, β, _, _ = _ordschur!(gschur.S, gschur.T, gschur.Q, gschur.Z, select) - gschur.α[:] = α - gschur.β[:] = β - return gschur -end - -_ordschur(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty}, - Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = - _ordschur!(copy(S), copy(T), copy(Q), copy(Z), select) - -_ordschur!(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty}, - Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = - LinearAlgebra.LAPACK.tgsen!(convert(Vector{BlasInt}, select), S, T, Q, Z) - -""" - ordschur(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur - -Reorders the Generalized Schur factorization `F` of a matrix pair `(A, B) = (Q*S*Z', Q*T*Z')` -according to the logical array `select` and returns a GeneralizedSchur object `F`. The -selected eigenvalues appear in the leading diagonal of both `F.S` and `F.T`, and the -left and right orthogonal/unitary Schur vectors are also reordered such that -`(A, B) = F.Q*(F.S, F.T)*F.Z'` still holds and the generalized eigenvalues of `A` -and `B` can still be obtained with `F.α./F.β`. -""" -ordschur(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) = - GeneralizedSchur(_ordschur(gschur.S, gschur.T, gschur.Q, gschur.Z, select)...) - -function getproperty(F::GeneralizedSchur, d::Symbol) - if d === :values - return getfield(F, :α) ./ getfield(F, :β) - elseif d === :alpha - return getfield(F, :α) - elseif d === :beta - return getfield(F, :β) - elseif d === :left - return getfield(F, :Q) - elseif d === :right - return getfield(F, :Z) - else - getfield(F, d) - end -end - -Base.propertynames(F::GeneralizedSchur) = - (:values, :left, :right, fieldnames(typeof(F))...) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::GeneralizedSchur) - summary(io, F); println(io) - println(io, "S factor:") - show(io, mime, F.S) - println(io, "\nT factor:") - show(io, mime, F.T) - println(io, "\nQ factor:") - show(io, mime, F.Q) - println(io, "\nZ factor:") - show(io, mime, F.Z) - println(io, "\nα:") - show(io, mime, F.α) - println(io, "\nβ:") - show(io, mime, F.β) -end - -# Conversion -AbstractMatrix(F::Schur) = (F.Z * F.T) * F.Z' -AbstractArray(F::Schur) = AbstractMatrix(F) -Matrix(F::Schur) = Array(AbstractArray(F)) -Array(F::Schur) = Matrix(F) - -copy(F::Schur) = Schur(copy(F.T), copy(F.Z), copy(F.values)) -copy(F::GeneralizedSchur) = GeneralizedSchur(copy(F.S), copy(F.T), copy(F.α), copy(F.β), copy(F.Q), copy(F.Z)) diff --git a/stdlib/LinearAlgebra/src/special.jl b/stdlib/LinearAlgebra/src/special.jl deleted file mode 100644 index c61586a810140..0000000000000 --- a/stdlib/LinearAlgebra/src/special.jl +++ /dev/null @@ -1,595 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Methods operating on different special matrix types - -# Interconversion between special matrix types - -# conversions from Diagonal to other special matrix types -Bidiagonal(A::Diagonal) = Bidiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0), :U) -SymTridiagonal(A::Diagonal) = SymTridiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0)) -Tridiagonal(A::Diagonal) = Tridiagonal(fill!(similar(A.diag, length(A.diag)-1), 0), A.diag, - fill!(similar(A.diag, length(A.diag)-1), 0)) - -# conversions from Bidiagonal to other special matrix types -Diagonal(A::Bidiagonal) = Diagonal(A.dv) -SymTridiagonal(A::Bidiagonal) = - iszero(A.ev) ? SymTridiagonal(A.dv, A.ev) : - throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) -function Tridiagonal(A::Bidiagonal) - # ensure that the types are identical, even if zero returns a different type - z = oftype(A.ev, zero(A.ev)) - Tridiagonal(A.uplo == 'U' ? z : A.ev, A.dv, A.uplo == 'U' ? A.ev : z) -end - -_diagview(S::SymTridiagonal{<:Number}) = S.dv -_diagview(S::SymTridiagonal) = diagview(S) - -# conversions from SymTridiagonal to other special matrix types -Diagonal(A::SymTridiagonal) = Diagonal(_diagview(A)) - -# These can fail when ev has the same length as dv -# TODO: Revisit when a good solution for #42477 is found -Bidiagonal(A::SymTridiagonal{<:Number}) = - iszero(A.ev) ? Bidiagonal(A.dv, A.ev, :U) : - throw(ArgumentError("matrix cannot be represented as Bidiagonal")) -Tridiagonal(A::SymTridiagonal{<:Number}) = - Tridiagonal(A.ev, A.dv, A.ev) - -# conversions from Tridiagonal to other special matrix types -Diagonal(A::Tridiagonal) = Diagonal(A.d) -Bidiagonal(A::Tridiagonal) = - iszero(A.dl) ? Bidiagonal(A.d, A.du, :U) : - iszero(A.du) ? Bidiagonal(A.d, A.dl, :L) : - throw(ArgumentError("matrix cannot be represented as Bidiagonal")) - -# conversions from AbstractTriangular to special matrix types -Bidiagonal(A::AbstractTriangular) = - isbanded(A, 0, 1) ? Bidiagonal(diag(A, 0), diag(A, 1), :U) : # is upper bidiagonal - isbanded(A, -1, 0) ? Bidiagonal(diag(A, 0), diag(A, -1), :L) : # is lower bidiagonal - throw(ArgumentError("matrix cannot be represented as Bidiagonal")) - -_lucopy(A::Bidiagonal, T) = copymutable_oftype(Tridiagonal(A), T) -_lucopy(A::Diagonal, T) = copymutable_oftype(Tridiagonal(A), T) -function _lucopy(A::SymTridiagonal, T) - du = copy_similar(_evview(A), T) - dl = copy.(transpose.(du)) - d = copy_similar(A.dv, T) - return Tridiagonal(dl, d, du) -end - -const ConvertibleSpecialMatrix = Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,AbstractTriangular} -const PossibleTriangularMatrix = Union{Diagonal, Bidiagonal, AbstractTriangular} - -convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:Diagonal} = m isa T ? m : - isdiag(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as Diagonal")) -convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:SymTridiagonal} = m isa T ? m : - issymmetric(m) && isbanded(m, -1, 1) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) -convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:Tridiagonal} = m isa T ? m : - isbanded(m, -1, 1) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as Tridiagonal")) - -convert(::Type{T}, m::Union{LowerTriangular,UnitLowerTriangular}) where {T<:LowerTriangular} = m isa T ? m : T(m)::T -convert(::Type{T}, m::Union{UpperTriangular,UnitUpperTriangular}) where {T<:UpperTriangular} = m isa T ? m : T(m)::T - -convert(::Type{T}, m::PossibleTriangularMatrix) where {T<:LowerTriangular} = m isa T ? m : - istril(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as LowerTriangular")) -convert(::Type{T}, m::PossibleTriangularMatrix) where {T<:UpperTriangular} = m isa T ? m : - istriu(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as UpperTriangular")) - -# Constructs two method definitions taking into account (assumed) commutativity -# e.g. @commutative f(x::S, y::T) where {S,T} = x+y is the same is defining -# f(x::S, y::T) where {S,T} = x+y -# f(y::T, x::S) where {S,T} = f(x, y) -macro commutative(myexpr) - @assert Base.is_function_def(myexpr) # Make sure it is a function definition - y = copy(myexpr.args[1].args[2:end]) - reverse!(y) - reversed_call = Expr(:(=), Expr(:call,myexpr.args[1].args[1],y...), myexpr.args[1]) - esc(Expr(:block, myexpr, reversed_call)) -end - -for op in (:+, :-) - for (matrixtype, uplo, converttype) in ((:UpperTriangular, 'U', :UpperTriangular), - (:UnitUpperTriangular, 'U', :UpperTriangular), - (:LowerTriangular, 'L', :LowerTriangular), - (:UnitLowerTriangular, 'L', :LowerTriangular)) - @eval begin - function ($op)(A::$matrixtype, B::Bidiagonal) - if B.uplo == $uplo - ($op)(A, convert($converttype, B)) - else - ($op).(A, B) - end - end - - function ($op)(A::Bidiagonal, B::$matrixtype) - if A.uplo == $uplo - ($op)(convert($converttype, A), B) - else - ($op).(A, B) - end - end - end - end -end - -(*)(Da::Diagonal, A::BandedMatrix, Db::Diagonal) = _tri_matmul(Da, A, Db) - -# disambiguation between triangular and banded matrices, banded ones "dominate" -_mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) -_mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractTriangular, alpha::Number, beta::Number) = - @stable_muladdmul _mul!(C, A, B, MulAddMul(alpha, beta)) - -function *(H::UpperHessenberg, B::Bidiagonal) - T = promote_op(matprod, eltype(H), eltype(B)) - A = mul!(similar(H, T, size(H)), H, B) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end -function *(B::Bidiagonal, H::UpperHessenberg) - T = promote_op(matprod, eltype(B), eltype(H)) - A = mul!(similar(H, T, size(H)), B, H) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - -function /(H::UpperHessenberg, B::Bidiagonal) - T = typeof(oneunit(eltype(H))/oneunit(eltype(B))) - A = _rdiv!(similar(H, T, size(H)), H, B) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - -function \(B::Bidiagonal, H::UpperHessenberg) - T = typeof(oneunit(eltype(B))\oneunit(eltype(H))) - A = ldiv!(similar(H, T, size(H)), B, H) - return B.uplo == 'U' ? UpperHessenberg(A) : A -end - -# specialized +/- for structured matrices. If these are removed, it falls -# back to broadcasting which has ~2-10x speed regressions. -# For the other structure matrix pairs, broadcasting works well. - -# For structured matrix types with different non-zero diagonals the underlying -# representations must be promoted to the same type. -# For example, in Diagonal + Bidiagonal only the main diagonal is touched so -# the off diagonal could be a different type after the operation resulting in -# an error. See issue #28994 - -@commutative function (+)(A::Bidiagonal, B::Diagonal) - newdv = A.dv + B.diag - Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) -end - -function (-)(A::Bidiagonal, B::Diagonal) - newdv = A.dv - B.diag - Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) -end - -function (-)(A::Diagonal, B::Bidiagonal) - newdv = A.diag - B.dv - Bidiagonal(newdv, typeof(newdv)(-B.ev), B.uplo) -end - -# Return a SymTridiagonal if the elements of `newdv` are -# statically known to be symmetric. Return a Tridiagonal otherwise -function _symtri_or_tri(dl, d, du) - new_du = oftype(d, du) - new_dl = oftype(d, dl) - if symmetric_type(eltype(d)) == eltype(d) - SymTridiagonal(d, new_du) - else - Tridiagonal(new_dl, d, new_du) - end -end - -@commutative function (+)(A::Diagonal, B::SymTridiagonal) - newdv = A.diag + _diagview(B) - _symtri_or_tri(_evview_transposed(B), newdv, _evview(B)) -end - -function (-)(A::Diagonal, B::SymTridiagonal) - newdv = A.diag - _diagview(B) - _symtri_or_tri(-_evview_transposed(B), newdv, -_evview(B)) -end - -function (-)(A::SymTridiagonal, B::Diagonal) - newdv = _diagview(A) - B.diag - _symtri_or_tri(_evview_transposed(A), newdv, _evview(A)) -end - -# this set doesn't have the aforementioned problem -_evview_transposed(S::SymTridiagonal{<:Number}) = _evview(S) -_evview_transposed(S::SymTridiagonal) = transpose.(_evview(S)) -@commutative function (+)(A::Tridiagonal, B::SymTridiagonal) - Tridiagonal(A.dl+_evview_transposed(B), A.d+_diagview(B), A.du+_evview(B)) -end -function -(A::Tridiagonal, B::SymTridiagonal) - Tridiagonal(A.dl-_evview_transposed(B), A.d-_diagview(B), A.du-_evview(B)) -end -function -(A::SymTridiagonal, B::Tridiagonal) - Tridiagonal(_evview_transposed(A)-B.dl, _diagview(A)-B.d, _evview(A)-B.du) -end - -@commutative function (+)(A::Diagonal, B::Tridiagonal) - newdv = A.diag + B.d - Tridiagonal(typeof(newdv)(B.dl), newdv, typeof(newdv)(B.du)) -end - -function (-)(A::Diagonal, B::Tridiagonal) - newdv = A.diag - B.d - Tridiagonal(typeof(newdv)(-B.dl), newdv, typeof(newdv)(-B.du)) -end - -function (-)(A::Tridiagonal, B::Diagonal) - newdv = A.d - B.diag - Tridiagonal(typeof(newdv)(A.dl), newdv, typeof(newdv)(A.du)) -end - -@commutative function (+)(A::Bidiagonal, B::Tridiagonal) - newdv = A.dv + B.d - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(B.dl), newdv, A.ev+B.du) : (A.ev+B.dl, newdv, typeof(newdv)(B.du)))...) -end - -function (-)(A::Bidiagonal, B::Tridiagonal) - newdv = A.dv - B.d - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-B.dl), newdv, A.ev-B.du) : (A.ev-B.dl, newdv, typeof(newdv)(-B.du)))...) -end - -function (-)(A::Tridiagonal, B::Bidiagonal) - newdv = A.d - B.dv - Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(A.dl), newdv, A.du-B.ev) : (A.dl-B.ev, newdv, typeof(newdv)(A.du)))...) -end - -@commutative function (+)(A::Bidiagonal, B::SymTridiagonal) - newdv = A.dv + _diagview(B) - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(_evview_transposed(B)), newdv, A.ev+_evview(B)) : (A.ev+_evview_transposed(B), newdv, typeof(newdv)(_evview(B))))...) -end - -function (-)(A::Bidiagonal, B::SymTridiagonal) - newdv = A.dv - _diagview(B) - Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-_evview_transposed(B)), newdv, A.ev-_evview(B)) : (A.ev-_evview_transposed(B), newdv, typeof(newdv)(-_evview(B))))...) -end - -function (-)(A::SymTridiagonal, B::Bidiagonal) - newdv = _diagview(A) - B.dv - Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(_evview_transposed(A)), newdv, _evview(A)-B.ev) : (_evview_transposed(A)-B.ev, newdv, typeof(newdv)(_evview(A))))...) -end - -@commutative function (+)(A::Tridiagonal, B::UniformScaling) - newd = A.d .+ Ref(B) - Tridiagonal(typeof(newd)(A.dl), newd, typeof(newd)(A.du)) -end - -@commutative function (+)(A::SymTridiagonal, B::UniformScaling) - newdv = A.dv .+ Ref(B) - SymTridiagonal(newdv, typeof(newdv)(A.ev)) -end - -@commutative function (+)(A::Bidiagonal, B::UniformScaling) - newdv = A.dv .+ Ref(B) - Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) -end - -@commutative function (+)(A::Diagonal, B::UniformScaling) - Diagonal(A.diag .+ Ref(B)) -end - -# StructuredMatrix - UniformScaling = StructuredMatrix + (-UniformScaling) => -# no need to define reversed order -function (-)(A::UniformScaling, B::Tridiagonal) - d = Ref(A) .- B.d - Tridiagonal(convert(typeof(d), -B.dl), d, convert(typeof(d), -B.du)) -end -function (-)(A::UniformScaling, B::SymTridiagonal) - dv = Ref(A) .- B.dv - SymTridiagonal(dv, convert(typeof(dv), -_evview(B))) -end -function (-)(A::UniformScaling, B::Bidiagonal) - dv = Ref(A) .- B.dv - Bidiagonal(dv, convert(typeof(dv), -B.ev), B.uplo) -end -function (-)(A::UniformScaling, B::Diagonal) - Diagonal(Ref(A) .- B.diag) -end - -for f in (:+, :-) - @eval function $f(D::Diagonal{<:Number}, S::Symmetric) - uplo = sym_uplo(S.uplo) - return Symmetric(parentof_applytri($f, Symmetric(D, uplo), S), uplo) - end - @eval function $f(S::Symmetric, D::Diagonal{<:Number}) - uplo = sym_uplo(S.uplo) - return Symmetric(parentof_applytri($f, S, Symmetric(D, uplo)), uplo) - end - @eval function $f(D::Diagonal{<:Real}, H::Hermitian) - uplo = sym_uplo(H.uplo) - return Hermitian(parentof_applytri($f, Hermitian(D, uplo), H), uplo) - end - @eval function $f(H::Hermitian, D::Diagonal{<:Real}) - uplo = sym_uplo(H.uplo) - return Hermitian(parentof_applytri($f, H, Hermitian(D, uplo)), uplo) - end -end - -## Diagonal construction from UniformScaling -Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.λ), m)) -Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) - -Base.muladd(A::Union{Diagonal, UniformScaling}, B::Union{Diagonal, UniformScaling}, z::Union{Diagonal, UniformScaling}) = - Diagonal(_diag_or_value(A) .* _diag_or_value(B) .+ _diag_or_value(z)) - -_diag_or_value(A::Diagonal) = A.diag -_diag_or_value(A::UniformScaling) = A.λ - -# fill[stored]! methods -fillstored!(A::Diagonal, x) = (fill!(A.diag, x); A) -fillstored!(A::Bidiagonal, x) = (fill!(A.dv, x); fill!(A.ev, x); A) -fillstored!(A::Tridiagonal, x) = (fill!(A.dl, x); fill!(A.d, x); fill!(A.du, x); A) -fillstored!(A::SymTridiagonal, x) = (fill!(A.dv, x); fill!(A.ev, x); A) - -_small_enough(A::Union{Diagonal, Bidiagonal}) = size(A, 1) <= 1 -_small_enough(A::Tridiagonal) = size(A, 1) <= 2 -_small_enough(A::SymTridiagonal) = size(A, 1) <= 2 - -function fill!(A::Union{Diagonal,Bidiagonal,Tridiagonal}, x) - xT = convert(eltype(A), x) - (iszero(xT) || _small_enough(A)) && return fillstored!(A, xT) - throw(ArgumentError(lazy"array of type $(typeof(A)) and size $(size(A)) can - not be filled with $x, since some of its entries are constrained.")) -end -function fill!(A::SymTridiagonal, x) - issymmetric(x) || throw(ArgumentError("cannot fill a SymTridiagonal with an asymmetric value")) - xT = convert(eltype(A), x) - (iszero(xT) || _small_enough(A)) && return fillstored!(A, xT) - throw(ArgumentError(lazy"array of type $(typeof(A)) and size $(size(A)) can - not be filled with $x, since some of its entries are constrained.")) -end - -one(D::Diagonal) = Diagonal(one.(D.diag)) -one(A::Bidiagonal{T}) where T = Bidiagonal(fill!(similar(A.dv, typeof(one(T))), one(T)), fill!(similar(A.ev, typeof(one(T))), zero(one(T))), A.uplo) -one(A::Tridiagonal{T}) where T = Tridiagonal(fill!(similar(A.du, typeof(one(T))), zero(one(T))), fill!(similar(A.d, typeof(one(T))), one(T)), fill!(similar(A.dl, typeof(one(T))), zero(one(T)))) -one(A::SymTridiagonal{T}) where T = SymTridiagonal(fill!(similar(A.dv, typeof(one(T))), one(T)), fill!(similar(A.ev, typeof(one(T))), zero(one(T)))) -for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular) - @eval one(A::$t) = $t(one(parent(A))) - @eval oneunit(A::$t) = $t(oneunit(parent(A))) -end - -zero(D::Diagonal) = Diagonal(zero.(D.diag)) -oneunit(D::Diagonal) = Diagonal(oneunit.(D.diag)) - -isdiag(A::HermOrSym{<:Any,<:Diagonal}) = isdiag(parent(A)) -dot(x::AbstractVector, A::RealHermSymComplexSym{<:Real,<:Diagonal}, y::AbstractVector) = - dot(x, A.data, y) - -# O(N) implementations using the banded structure -function copyto!(dest::BandedMatrix, src::BandedMatrix) - if axes(dest) == axes(src) - _copyto_banded!(dest, src) - else - @invoke copyto!(dest::AbstractMatrix, src::AbstractMatrix) - end - return dest -end -function _copyto_banded!(T::Tridiagonal, D::Diagonal) - T.d .= D.diag - T.dl .= diagview(D, -1) - T.du .= diagview(D, 1) - return T -end -function _copyto_banded!(SymT::SymTridiagonal, D::Diagonal) - issymmetric(D) || throw(ArgumentError("cannot copy a non-symmetric Diagonal matrix to a SymTridiagonal")) - SymT.dv .= D.diag - _ev = _evview(SymT) - _ev .= diagview(D, 1) - return SymT -end -function _copyto_banded!(B::Bidiagonal, D::Diagonal) - B.dv .= D.diag - B.ev .= diagview(D, _offdiagind(B.uplo)) - return B -end -function _copyto_banded!(D::Diagonal, B::Bidiagonal) - isdiag(B) || - throw(ArgumentError("cannot copy a Bidiagonal with a non-zero off-diagonal band to a Diagonal")) - D.diag .= B.dv - return D -end -function _copyto_banded!(D::Diagonal, T::Tridiagonal) - isdiag(T) || - throw(ArgumentError("cannot copy a Tridiagonal with a non-zero off-diagonal band to a Diagonal")) - D.diag .= T.d - return D -end -function _copyto_banded!(D::Diagonal, SymT::SymTridiagonal) - isdiag(SymT) || - throw(ArgumentError("cannot copy a SymTridiagonal with a non-zero off-diagonal band to a Diagonal")) - # we broadcast identity for numbers using the fact that symmetric(x::Number) = x - # this potentially allows us to access faster copyto! paths - _symmetric = eltype(SymT) <: Number ? identity : symmetric - D.diag .= _symmetric.(SymT.dv) - return D -end -function _copyto_banded!(T::Tridiagonal, B::Bidiagonal) - T.d .= B.dv - if B.uplo == 'U' - T.du .= B.ev - T.dl .= diagview(B,-1) - else - T.dl .= B.ev - T.du .= diagview(B, 1) - end - return T -end -function _copyto_banded!(SymT::SymTridiagonal, B::Bidiagonal) - issymmetric(B) || throw(ArgumentError("cannot copy a non-symmetric Bidiagonal matrix to a SymTridiagonal")) - SymT.dv .= B.dv - _ev = _evview(SymT) - _ev .= B.ev - return SymT -end -function _copyto_banded!(B::Bidiagonal, T::Tridiagonal) - if B.uplo == 'U' && !iszero(T.dl) - throw(ArgumentError("cannot copy a Tridiagonal with a non-zero subdiagonal to a Bidiagonal with uplo=:U")) - elseif B.uplo == 'L' && !iszero(T.du) - throw(ArgumentError("cannot copy a Tridiagonal with a non-zero superdiagonal to a Bidiagonal with uplo=:L")) - end - B.dv .= T.d - B.ev .= B.uplo == 'U' ? T.du : T.dl - return B -end -function _copyto_banded!(B::Bidiagonal, SymT::SymTridiagonal) - isdiag(SymT) || - throw(ArgumentError("cannot copy a SymTridiagonal with a non-zero off-diagonal band to a Bidiagonal")) - # we broadcast identity for numbers using the fact that symmetric(x::Number) = x - # this potentially allows us to access faster copyto! paths - _symmetric = eltype(SymT) <: Number ? identity : symmetric - B.dv .= _symmetric.(SymT.dv) - return B -end - -# equals and approx equals methods for structured matrices -# SymTridiagonal == Tridiagonal is already defined in tridiag.jl - -==(A::Diagonal, B::Bidiagonal) = iszero(B.ev) && A.diag == B.dv -==(A::Diagonal, B::SymTridiagonal) = iszero(_evview(B)) && A.diag == _diagview(B) -==(B::Bidiagonal, A::Diagonal) = A == B -==(A::Diagonal, B::Tridiagonal) = iszero(B.dl) && iszero(B.du) && A.diag == B.d -==(B::Tridiagonal, A::Diagonal) = A == B - -function ==(A::Bidiagonal, B::Tridiagonal) - if A.uplo == 'U' - return iszero(B.dl) && A.dv == B.d && A.ev == B.du - else - return iszero(B.du) && A.dv == B.d && A.ev == B.dl - end -end -==(B::Tridiagonal, A::Bidiagonal) = A == B - -==(A::Bidiagonal, B::SymTridiagonal) = iszero(_evview(B)) && iszero(A.ev) && A.dv == _diagview(B) -==(B::SymTridiagonal, A::Bidiagonal) = A == B - -# TODO: remove these deprecations (used by SparseArrays in the past) -const _DenseConcatGroup = Union{} -const _SpecialArrays = Union{} - -promote_to_array_type(::Tuple) = Matrix - -# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices -# in A to matrices of type T and sizes given by n[k:end]. n is an array -# 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} = Matrix(J, n, n) -promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A -promote_to_arrays_(n::Int, ::Type, A::AbstractQ) = collect(A) -promote_to_arrays(n,k, ::Type) = () -promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),) -promote_to_arrays(n,k, ::Type{T}, A, B) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B)) -promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C)) -promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = - (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) - -_us2number(A) = A -_us2number(J::UniformScaling) = J.λ - -for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) - @eval begin - @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling}...) = $_f(A...) - # if there's a Number present, J::UniformScaling must be 1x1-dimensional - @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = $f(map(_us2number, A)...) - function $_f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A)) - n = -1 - for a in A - if !isa(a, UniformScaling) - require_one_based_indexing(a) - na = size(a,$dim) - n >= 0 && n != na && - throw(DimensionMismatch(string("number of ", $name, - " of each array must match (got ", n, " and ", na, ")"))) - n = na - 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, array_type, A...)..., dims=Val(3-$dim)) - end - end -end - -hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling}...) = _hvcat(rows, A...) -hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = _hvcat(rows, A...) -function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,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")) - n = fill(-1, length(A)) - needcols = false # whether we also need to infer some sizes from the column count - j = 0 - for i = 1:nr # infer UniformScaling sizes from row counts, if possible: - ni = -1 # number of rows in this block-row, -1 indicates unknown - for k = 1:rows[i] - if !isa(A[j+k], UniformScaling) - na = size(A[j+k], 1) - ni >= 0 && ni != na && - throw(DimensionMismatch("mismatch in number of rows")) - ni = na - end - end - if ni >= 0 - for k = 1:rows[i] - n[j+k] = ni - end - else # row consisted only of UniformScaling objects - needcols = true - end - j += rows[i] - end - if needcols # some sizes still unknown, try to infer from column count - nc = -1 - j = 0 - for i = 1:nr - nci = 0 - rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue) - for k = 1:rows[i] - nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2) - end - nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns")) - nc = nci - j += rows[i] - end - nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred")) - j = 0 - for i = 1:nr - if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings - nci, r = divrem(nc, rows[i]) - r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes")) - for k = 1:rows[i] - n[j+k] = nci - end - end - j += rows[i] - end - end - 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 array_type == Matrix - return typed_hvcat(promote_eltype(Amat...), rows, Amat...) - else - return hvcat(rows, Amat...) - end -end - -# factorizations -function cholesky(S::RealHermSymComplexHerm{<:Real,<:SymTridiagonal}, ::NoPivot = NoPivot(); check::Bool = true) - T = choltype(S) - B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo(S.uplo)) - cholesky!(Hermitian(B, sym_uplo(S.uplo)), NoPivot(); check = check) -end - -# istriu/istril for triangular wrappers of structured matrices -_istril(A::LowerTriangular{<:Any, <:BandedMatrix}, k) = istril(parent(A), k) -_istriu(A::UpperTriangular{<:Any, <:BandedMatrix}, k) = istriu(parent(A), k) -_istriu(A::UpperHessenberg{<:Any, <:BandedMatrix}, k) = istriu(parent(A), k) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl deleted file mode 100644 index 9a4d55fd58bf0..0000000000000 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ /dev/null @@ -1,297 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## Broadcast styles -import Base.Broadcast -using Base.Broadcast: DefaultArrayStyle, Broadcasted - -struct StructuredMatrixStyle{T} <: Broadcast.AbstractArrayStyle{2} end -StructuredMatrixStyle{T}(::Val{2}) where {T} = StructuredMatrixStyle{T}() -StructuredMatrixStyle{T}(::Val{N}) where {T,N} = Broadcast.DefaultArrayStyle{N}() - -const StructuredMatrix{T} = Union{Diagonal{T},Bidiagonal{T},SymTridiagonal{T},Tridiagonal{T},LowerTriangular{T},UnitLowerTriangular{T},UpperTriangular{T},UnitUpperTriangular{T}} -for ST in (Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,LowerTriangular,UnitLowerTriangular,UpperTriangular,UnitUpperTriangular) - @eval Broadcast.BroadcastStyle(::Type{<:$ST}) = $(StructuredMatrixStyle{ST}()) -end - -# Promotion of broadcasts between structured matrices. This is slightly unusual -# as we define them symmetrically. This allows us to have a fallback to DefaultArrayStyle{2}(). -# Diagonal can cavort with all the other structured matrix types. -# Bidiagonal doesn't know if it's upper or lower, so it becomes Tridiagonal -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Diagonal}) = - StructuredMatrixStyle{Diagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Bidiagonal}) = - StructuredMatrixStyle{Bidiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{SymTridiagonal,Tridiagonal}}) = - StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = - StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = - StructuredMatrixStyle{UpperTriangular}() - -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{Diagonal}) = - StructuredMatrixStyle{Bidiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{<:Union{Bidiagonal,SymTridiagonal,Tridiagonal}}) = - StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{SymTridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = - StructuredMatrixStyle{Tridiagonal}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{Tridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = - StructuredMatrixStyle{Tridiagonal}() - -Broadcast.BroadcastStyle(::StructuredMatrixStyle{LowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = - StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{UpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = - StructuredMatrixStyle{UpperTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitLowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = - StructuredMatrixStyle{LowerTriangular}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = - StructuredMatrixStyle{UpperTriangular}() - -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = - StructuredMatrixStyle{Matrix}() -Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = - StructuredMatrixStyle{Matrix}() - -# Make sure that `StructuredMatrixStyle{Matrix}` doesn't ever end up falling -# through and give back `DefaultArrayStyle{2}` -Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle) = T -Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{Matrix}) = T -Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle{Matrix}) = T - -# All other combinations fall back to the default style -Broadcast.BroadcastStyle(::StructuredMatrixStyle, ::StructuredMatrixStyle) = DefaultArrayStyle{2}() - -# And a definition akin to similar using the structured type: -structured_broadcast_alloc(bc, ::Type{Diagonal}, ::Type{ElType}, n) where {ElType} = - Diagonal(Array{ElType}(undef, n)) -# Bidiagonal is tricky as we need to know if it's upper or lower. The promotion -# system will return Tridiagonal when there's more than one Bidiagonal, but when -# there's only one, we need to make figure out upper or lower -merge_uplos(::Nothing, ::Nothing) = nothing -merge_uplos(a, ::Nothing) = a -merge_uplos(::Nothing, b) = b -merge_uplos(a, b) = a == b ? a : 'T' - -find_uplo(a::Bidiagonal) = a.uplo -find_uplo(a) = nothing -find_uplo(bc::Broadcasted) = mapfoldl(find_uplo, merge_uplos, Broadcast.cat_nested(bc), init=nothing) - -function structured_broadcast_alloc(bc, ::Type{Bidiagonal}, ::Type{ElType}, n) where {ElType} - uplo = n > 0 ? find_uplo(bc) : 'U' - n1 = max(n - 1, 0) - if count_structedmatrix(Bidiagonal, bc) > 1 && uplo == 'T' - return Tridiagonal(Array{ElType}(undef, n1), Array{ElType}(undef, n), Array{ElType}(undef, n1)) - end - return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n1), uplo) -end -structured_broadcast_alloc(bc, ::Type{SymTridiagonal}, ::Type{ElType}, n) where {ElType} = - SymTridiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1)) -structured_broadcast_alloc(bc, ::Type{Tridiagonal}, ::Type{ElType}, n) where {ElType} = - Tridiagonal(Array{ElType}(undef, n-1),Array{ElType}(undef, n),Array{ElType}(undef, n-1)) -structured_broadcast_alloc(bc, ::Type{LowerTriangular}, ::Type{ElType}, n) where {ElType} = - LowerTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{UpperTriangular}, ::Type{ElType}, n) where {ElType} = - UpperTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{UnitLowerTriangular}, ::Type{ElType}, n) where {ElType} = - UnitLowerTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = - UnitUpperTriangular(Array{ElType}(undef, n, n)) -structured_broadcast_alloc(bc, ::Type{Matrix}, ::Type{ElType}, n) where {ElType} = - Array{ElType}(undef, n, n) - -# A _very_ limited list of structure-preserving functions known at compile-time. This list is -# derived from the formerly-implemented `broadcast` methods in 0.6. Note that this must -# preserve both zeros and ones (for Unit***erTriangular) and symmetry (for SymTridiagonal) -const TypeFuncs = Union{typeof(round),typeof(trunc),typeof(floor),typeof(ceil)} -isstructurepreserving(bc::Broadcasted) = isstructurepreserving(bc.f, bc.args...) -isstructurepreserving(::Union{typeof(abs),typeof(big)}, ::StructuredMatrix) = true -isstructurepreserving(::TypeFuncs, ::StructuredMatrix) = true -isstructurepreserving(::TypeFuncs, ::Ref{<:Type}, ::StructuredMatrix) = true -function isstructurepreserving(::typeof(Base.literal_pow), ::Ref{typeof(^)}, ::StructuredMatrix, ::Ref{Val{N}}) where N - return N isa Integer && N > 0 -end -isstructurepreserving(f, args...) = false - -""" - iszerodefined(T::Type) - -Return a `Bool` indicating whether `iszero` is well-defined for objects of type -`T`. By default, this function returns `false` unless `T <: Number`. Note that -this function may return `true` even if `zero(::T)` is not defined as long as -`iszero(::T)` has a method that does not requires `zero(::T)`. - -This function is used to determine if mapping the elements of an array with -a specific structure of nonzero elements preserve this structure. -For instance, it is used to determine whether the output of -`tuple.(Diagonal([1, 2]))` is `Diagonal([(1,), (2,)])` or -`[(1,) (0,); (0,) (2,)]`. For this, we need to determine whether `(0,)` is -considered to be zero. `iszero((0,))` falls back to `(0,) == zero((0,))` which -fails as `zero(::Tuple{Int})` is not defined. However, -`iszerodefined(::Tuple{Int})` is `false` hence we falls back to the comparison -`(0,) == 0` which returns `false` and decides that the correct output is -`[(1,) (0,); (0,) (2,)]`. -""" -iszerodefined(::Type) = false -iszerodefined(::Type{<:Number}) = true -iszerodefined(::Type{<:AbstractArray{T}}) where T = iszerodefined(T) -iszerodefined(::Type{<:UniformScaling{T}}) where T = iszerodefined(T) - -count_structedmatrix(T, bc::Broadcasted) = sum(Base.Fix2(isa, T), Broadcast.cat_nested(bc); init = 0) - -""" - fzeropreserving(bc) -> Bool - -Return true if the broadcasted function call evaluates to zero for structural zeros of the -structured arguments. - -For trivial broadcasted values such as `bc::Number`, this reduces to `iszero(bc)`. -""" -function fzeropreserving(bc) - v = fzero(bc) - isnothing(v) && return false - v2 = something(v) - iszerodefined(typeof(v2)) ? iszero(v2) : isequal(v2, 0) -end - -# Like sparse matrices, we assume that the zero-preservation property of a broadcasted -# expression is stable. We can test the zero-preservability by applying the function -# in cases where all other arguments are known scalars against a zero from the structured -# matrix. If any non-structured matrix argument is not a known scalar, we give up. -fzero(x::Number) = Some(x) -fzero(::Type{T}) where T = Some(T) -fzero(r::Ref) = Some(r[]) -fzero(t::Tuple{Any}) = Some(only(t)) -fzero(S::StructuredMatrix) = Some(zero(eltype(S))) -fzero(::StructuredMatrix{<:AbstractMatrix{T}}) where {T<:Number} = Some(haszero(T) ? zero(T)*I : nothing) -fzero(x) = nothing -function fzero(bc::Broadcast.Broadcasted) - args = map(fzero, bc.args) - return any(isnothing, args) ? nothing : Some(bc.f(map(something, args)...)) -end - -function Base.similar(bc::Broadcasted{StructuredMatrixStyle{T}}, ::Type{ElType}) where {T,ElType} - inds = axes(bc) - fzerobc = fzeropreserving(bc) - if isstructurepreserving(bc) || (fzerobc && !(T <: Union{UnitLowerTriangular,UnitUpperTriangular})) - return structured_broadcast_alloc(bc, T, ElType, length(inds[1])) - elseif fzerobc && T <: UnitLowerTriangular - return similar(convert(Broadcasted{StructuredMatrixStyle{LowerTriangular}}, bc), ElType) - elseif fzerobc && T <: UnitUpperTriangular - return similar(convert(Broadcasted{StructuredMatrixStyle{UpperTriangular}}, bc), ElType) - end - return similar(convert(Broadcasted{DefaultArrayStyle{ndims(bc)}}, bc), ElType) -end - -isvalidstructbc(dest, bc::Broadcasted{T}) where {T<:StructuredMatrixStyle} = - Broadcast.combine_styles(dest, bc) === Broadcast.combine_styles(dest) && - (isstructurepreserving(bc) || fzeropreserving(bc)) - -isvalidstructbc(dest::Bidiagonal, bc::Broadcasted{StructuredMatrixStyle{Bidiagonal}}) = - (size(dest, 1) < 2 || find_uplo(bc) == dest.uplo) && - (isstructurepreserving(bc) || fzeropreserving(bc)) - -@inline function getindex(bc::Broadcasted, b::BandIndex) - @boundscheck checkbounds(bc, b) - @inbounds Broadcast._broadcast_getindex(bc, b) -end - -function Broadcast.newindex(A::StructuredMatrix, b::BandIndex) - # we use the fact that a StructuredMatrix is square, - # and we apply newindex to both the axes at once to obtain the result - size(A,1) > 1 ? b : BandIndex(0, 1) -end -# All structured matrices are square, and therefore they only broadcast out if they are size (1, 1) -Broadcast.newindex(D::StructuredMatrix, I::CartesianIndex{2}) = size(D) == (1,1) ? CartesianIndex(1,1) : I - -function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for i in axs[1] - dest.diag[i] = @inbounds bc[BandIndex(0, i)] - end - return dest -end - -function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for i in axs[1] - dest.dv[i] = @inbounds bc[BandIndex(0, i)] - end - if dest.uplo == 'U' - for i = 1:size(dest, 1)-1 - dest.ev[i] = @inbounds bc[BandIndex(1, i)] - end - else - for i = 1:size(dest, 1)-1 - dest.ev[i] = @inbounds bc[BandIndex(-1, i)] - end - end - return dest -end - -function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for i in axs[1] - dest.dv[i] = @inbounds bc[BandIndex(0, i)] - end - for i = 1:size(dest, 1)-1 - v = @inbounds bc[BandIndex(1, i)] - v == transpose(@inbounds bc[BandIndex(-1, i)]) || - throw(ArgumentError(lazy"broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) - dest.ev[i] = v - end - return dest -end - -function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for i in axs[1] - dest.d[i] = @inbounds bc[BandIndex(0, i)] - end - for i = 1:size(dest, 1)-1 - dest.du[i] = @inbounds bc[BandIndex(1, i)] - end - for i = 1:size(dest, 1)-1 - dest.dl[i] = @inbounds bc[BandIndex(-1, i)] - end - return dest -end - -function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for j in axs[2] - for i in j:axs[1][end] - @inbounds dest.data[i,j] = bc[CartesianIndex(i, j)] - end - end - return dest -end - -function copyto!(dest::UpperTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) - isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) - axs = axes(dest) - axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) - for j in axs[2] - for i in 1:j - @inbounds dest.data[i,j] = bc[CartesianIndex(i, j)] - end - end - return dest -end - -# We can also implement `map` and its promotion in terms of broadcast with a stricter dimension check -function map(f, A::StructuredMatrix, Bs::StructuredMatrix...) - sz = size(A) - for B in Bs - size(B) == sz || Base.throw_promote_shape_mismatch(sz, size(B)) - end - return f.(A, Bs...) -end diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl deleted file mode 100644 index 7a88c4a6e14c4..0000000000000 --- a/stdlib/LinearAlgebra/src/svd.jl +++ /dev/null @@ -1,578 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Singular Value Decomposition -""" - SVD <: Factorization - -Matrix factorization type of the singular value decomposition (SVD) of a matrix `A`. -This is the return type of [`svd(_)`](@ref), the corresponding matrix factorization function. - -If `F::SVD` is the factorization object, `U`, `S`, `V` and `Vt` can be obtained -via `F.U`, `F.S`, `F.V` and `F.Vt`, such that `A = U * Diagonal(S) * Vt`. -The singular values in `S` are sorted in descending order. - -Iterating the decomposition produces the components `U`, `S`, and `V`. - -# Examples -```jldoctest -julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] -4×5 Matrix{Float64}: - 1.0 0.0 0.0 0.0 2.0 - 0.0 0.0 3.0 0.0 0.0 - 0.0 0.0 0.0 0.0 0.0 - 0.0 2.0 0.0 0.0 0.0 - -julia> F = svd(A) -SVD{Float64, Float64, Matrix{Float64}, Vector{Float64}} -U factor: -4×4 Matrix{Float64}: - 0.0 1.0 0.0 0.0 - 1.0 0.0 0.0 0.0 - 0.0 0.0 0.0 1.0 - 0.0 0.0 -1.0 0.0 -singular values: -4-element Vector{Float64}: - 3.0 - 2.23606797749979 - 2.0 - 0.0 -Vt factor: -4×5 Matrix{Float64}: - -0.0 0.0 1.0 -0.0 0.0 - 0.447214 0.0 0.0 0.0 0.894427 - 0.0 -1.0 0.0 0.0 0.0 - 0.0 0.0 0.0 1.0 0.0 - -julia> F.U * Diagonal(F.S) * F.Vt -4×5 Matrix{Float64}: - 1.0 0.0 0.0 0.0 2.0 - 0.0 0.0 3.0 0.0 0.0 - 0.0 0.0 0.0 0.0 0.0 - 0.0 2.0 0.0 0.0 0.0 - -julia> u, s, v = F; # destructuring via iteration - -julia> u == F.U && s == F.S && v == F.V -true -``` -""" -struct SVD{T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr}} <: Factorization{T} - U::M - S::C - Vt::M - function SVD{T,Tr,M,C}(U, S, Vt) where {T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr}} - require_one_based_indexing(U, S, Vt) - new{T,Tr,M,C}(U, S, Vt) - end -end -SVD(U::AbstractArray{T}, S::AbstractVector{Tr}, Vt::AbstractArray{T}) where {T,Tr} = - SVD{T,Tr,typeof(U),typeof(S)}(U, S, Vt) -SVD{T}(U::AbstractArray, S::AbstractVector{Tr}, Vt::AbstractArray) where {T,Tr} = - SVD(convert(AbstractArray{T}, U), - convert(AbstractVector{Tr}, S), - convert(AbstractArray{T}, Vt)) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(SVD{T,Tr,M}(U::AbstractArray{T}, S::AbstractVector{Tr}, Vt::AbstractArray{T}) where {T,Tr,M}, - SVD{T,Tr,M,typeof(S)}(U, S, Vt)) - -SVD{T}(F::SVD) where {T} = SVD( - convert(AbstractMatrix{T}, F.U), - convert(AbstractVector{real(T)}, F.S), - convert(AbstractMatrix{T}, F.Vt)) -Factorization{T}(F::SVD) where {T} = SVD{T}(F) - -# iteration for destructuring into components -Base.iterate(S::SVD) = (S.U, Val(:S)) -Base.iterate(S::SVD, ::Val{:S}) = (S.S, Val(:V)) -Base.iterate(S::SVD, ::Val{:V}) = (S.V, Val(:done)) -Base.iterate(S::SVD, ::Val{:done}) = nothing - - -default_svd_alg(A) = DivideAndConquer() - - -""" - svd!(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> SVD - -`svd!` is the same as [`svd`](@ref), but saves space by -overwriting the input `A`, instead of creating a copy. See documentation of [`svd`](@ref) for details. -""" -function svd!(A::StridedMatrix{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T<:BlasFloat} - m, n = size(A) - if m == 0 || n == 0 - u, s, vt = (Matrix{T}(I, m, full ? m : n), real(zeros(T,0)), Matrix{T}(I, n, n)) - else - u, s, vt = _svd!(A, full, alg) - end - SVD(u, s, vt) -end -function svd!(A::StridedVector{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T<:BlasFloat} - m = length(A) - normA = norm(A) - if iszero(normA) - return SVD(Matrix{T}(I, m, full ? m : 1), [normA], ones(T, 1, 1)) - elseif !full - normalize!(A) - return SVD(reshape(A, (m, 1)), [normA], ones(T, 1, 1)) - else - u, s, vt = _svd!(reshape(A, (m, 1)), full, alg) - return SVD(u, s, vt) - end -end - -_svd!(A::StridedMatrix{T}, full::Bool, alg::Algorithm) where {T<:BlasFloat} = - throw(ArgumentError("Unsupported value for `alg` keyword.")) -_svd!(A::StridedMatrix{T}, full::Bool, alg::DivideAndConquer) where {T<:BlasFloat} = - LAPACK.gesdd!(full ? 'A' : 'S', A) -function _svd!(A::StridedMatrix{T}, full::Bool, alg::QRIteration) where {T<:BlasFloat} - c = full ? 'A' : 'S' - u, s, vt = LAPACK.gesvd!(c, c, A) -end - - - -""" - svd(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> SVD - -Compute the singular value decomposition (SVD) of `A` and return an `SVD` object. - -`U`, `S`, `V` and `Vt` can be obtained from the factorization `F` with `F.U`, -`F.S`, `F.V` and `F.Vt`, such that `A = U * Diagonal(S) * Vt`. -The algorithm produces `Vt` and hence `Vt` is more efficient to extract than `V`. -The singular values in `S` are sorted in descending order. - -Iterating the decomposition produces the components `U`, `S`, and `V`. - -If `full = false` (default), a "thin" SVD is returned. For an ``M -\\times N`` matrix `A`, in the full factorization `U` is ``M \\times M`` -and `V` is ``N \\times N``, while in the thin factorization `U` is ``M -\\times K`` and `V` is ``N \\times K``, where ``K = \\min(M,N)`` is the -number of singular values. - -`alg` specifies which algorithm and LAPACK method to use for SVD: -- `alg = DivideAndConquer()` (default): Calls `LAPACK.gesdd!`. -- `alg = QRIteration()`: Calls `LAPACK.gesvd!` (typically slower but more accurate) . - -!!! compat "Julia 1.3" - The `alg` keyword argument requires Julia 1.3 or later. - -# Examples -```jldoctest -julia> A = rand(4,3); - -julia> F = svd(A); # Store the Factorization Object - -julia> A ≈ F.U * Diagonal(F.S) * F.Vt -true - -julia> U, S, V = F; # destructuring via iteration - -julia> A ≈ U * Diagonal(S) * V' -true - -julia> Uonly, = svd(A); # Store U only - -julia> Uonly == U -true -``` -""" -function svd(A::AbstractVecOrMat{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T} - svd!(eigencopy_oftype(A, eigtype(T)), full = full, alg = alg) -end -function svd(A::AbstractVecOrMat{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T <: Union{Float16,Complex{Float16}}} - A = svd!(eigencopy_oftype(A, eigtype(T)), full = full, alg = alg) - return SVD{T}(A) -end -function svd(x::Number; full::Bool = false, alg::Algorithm = default_svd_alg(x)) - SVD(x == 0 ? fill(one(x), 1, 1) : fill(x/abs(x), 1, 1), [abs(x)], fill(one(x), 1, 1)) -end -function svd(x::Integer; full::Bool = false, alg::Algorithm = default_svd_alg(x)) - svd(float(x), full = full, alg = alg) -end -function svd(A::Adjoint; full::Bool = false, alg::Algorithm = default_svd_alg(A)) - s = svd(A.parent, full = full, alg = alg) - return SVD(s.Vt', s.S, s.U') -end -function svd(A::Transpose; full::Bool = false, alg::Algorithm = default_svd_alg(A)) - s = svd(A.parent, full = full, alg = alg) - return SVD(transpose(s.Vt), s.S, transpose(s.U)) -end - -function getproperty(F::SVD, d::Symbol) - if d === :V - return getfield(F, :Vt)' - else - return getfield(F, d) - end -end - -Base.propertynames(F::SVD, private::Bool=false) = - private ? (:V, fieldnames(typeof(F))...) : (:U, :S, :V, :Vt) - -""" - svdvals!(A) - -Return the singular values of `A`, saving space by overwriting the input. -See also [`svdvals`](@ref) and [`svd`](@ref). -""" -svdvals!(A::StridedMatrix{T}) where {T<:BlasFloat} = isempty(A) ? zeros(real(T), 0) : LAPACK.gesdd!('N', A)[2] -svdvals!(A::StridedVector{T}) where {T<:BlasFloat} = svdvals!(reshape(A, (length(A), 1))) - -""" - svdvals(A) - -Return the singular values of `A` in descending order. - -# Examples -```jldoctest -julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] -4×5 Matrix{Float64}: - 1.0 0.0 0.0 0.0 2.0 - 0.0 0.0 3.0 0.0 0.0 - 0.0 0.0 0.0 0.0 0.0 - 0.0 2.0 0.0 0.0 0.0 - -julia> svdvals(A) -4-element Vector{Float64}: - 3.0 - 2.23606797749979 - 2.0 - 0.0 -``` -""" -svdvals(A::AbstractMatrix{T}) where {T} = svdvals!(eigencopy_oftype(A, eigtype(T))) -svdvals(A::AbstractVector{T}) where {T} = [convert(eigtype(T), norm(A))] -svdvals(x::Number) = abs(x) -svdvals(S::SVD{<:Any,T}) where {T} = (S.S)::Vector{T} - -### SVD least squares ### -function ldiv!(A::SVD{T}, B::AbstractVecOrMat) where T - m, n = size(A) - k = searchsortedlast(A.S, eps(real(T))*A.S[1], rev=true) - mul!(view(B, 1:n, :), view(A.Vt, 1:k, :)', view(A.S, 1:k) .\ (view(A.U, :, 1:k)' * _cut_B(B, 1:m))) - return B -end - -function inv(F::SVD{T}) where T - @inbounds for i in eachindex(F.S) - iszero(F.S[i]) && throw(SingularException(i)) - end - k = searchsortedlast(F.S, eps(real(T))*F.S[1], rev=true) - @views (F.S[1:k] .\ F.Vt[1:k, :])' * F.U[:,1:k]' -end - -size(A::SVD, dim::Integer) = dim == 1 ? size(A.U, dim) : size(A.Vt, dim) -size(A::SVD) = (size(A, 1), size(A, 2)) - -function adjoint(F::SVD) - return SVD(F.Vt', F.S, F.U') -end - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::SVD{<:Any,<:Any,<:AbstractArray,<:AbstractVector}) - summary(io, F); println(io) - println(io, "U factor:") - show(io, mime, F.U) - println(io, "\nsingular values:") - show(io, mime, F.S) - println(io, "\nVt factor:") - show(io, mime, F.Vt) -end - -# Generalized svd -""" - GeneralizedSVD <: Factorization - -Matrix factorization type of the generalized singular value decomposition (SVD) -of two matrices `A` and `B`, such that `A = F.U*F.D1*F.R0*F.Q'` and -`B = F.V*F.D2*F.R0*F.Q'`. This is the return type of [`svd(_, _)`](@ref), the -corresponding matrix factorization function. - -For an M-by-N matrix `A` and P-by-N matrix `B`, - -- `U` is a M-by-M orthogonal matrix, -- `V` is a P-by-P orthogonal matrix, -- `Q` is a N-by-N orthogonal matrix, -- `D1` is a M-by-(K+L) diagonal matrix with 1s in the first K entries, -- `D2` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal, -- `R0` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is - nonsingular upper block triangular, - -`K+L` is the effective numerical rank of the matrix `[A; B]`. - -Iterating the decomposition produces the components `U`, `V`, `Q`, `D1`, `D2`, and `R0`. - -The entries of `F.D1` and `F.D2` are related, as explained in the LAPACK -documentation for the -[generalized SVD](https://www.netlib.org/lapack/lug/node36.html) and the -[xGGSVD3](https://www.netlib.org/lapack/explore-html/d6/db3/dggsvd3_8f.html) -routine which is called underneath (in LAPACK 3.6.0 and newer). - -# Examples -```jldoctest -julia> A = [1. 0.; 0. -1.] -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 -1.0 - -julia> B = [0. 1.; 1. 0.] -2×2 Matrix{Float64}: - 0.0 1.0 - 1.0 0.0 - -julia> F = svd(A, B) -GeneralizedSVD{Float64, Matrix{Float64}, Float64, Vector{Float64}} -U factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 1.0 -V factor: -2×2 Matrix{Float64}: - -0.0 -1.0 - 1.0 0.0 -Q factor: -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 1.0 -D1 factor: -2×2 Matrix{Float64}: - 0.707107 0.0 - 0.0 0.707107 -D2 factor: -2×2 Matrix{Float64}: - 0.707107 0.0 - 0.0 0.707107 -R0 factor: -2×2 Matrix{Float64}: - 1.41421 0.0 - 0.0 -1.41421 - -julia> F.U*F.D1*F.R0*F.Q' -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 -1.0 - -julia> F.V*F.D2*F.R0*F.Q' -2×2 Matrix{Float64}: - -0.0 1.0 - 1.0 0.0 -``` -""" -struct GeneralizedSVD{T,S<:AbstractMatrix,Tr,C<:AbstractVector{Tr}} <: Factorization{T} - U::S - V::S - Q::S - a::C - b::C - k::Int - l::Int - R::S - function GeneralizedSVD{T,S,Tr,C}(U, V, Q, a, b, k, l, R) where {T,S<:AbstractMatrix{T},Tr,C<:AbstractVector{Tr}} - new{T,S,Tr,C}(U, V, Q, a, b, k, l, R) - end -end -GeneralizedSVD(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T}, - a::AbstractVector{Tr}, b::AbstractVector{Tr}, k::Int, l::Int, - R::AbstractMatrix{T}) where {T, Tr} = - GeneralizedSVD{T,typeof(U),Tr,typeof(a)}(U, V, Q, a, b, k, l, R) -# backwards-compatible constructors (remove with Julia 2.0) -@deprecate(GeneralizedSVD{T,S}(U, V, Q, a, b, k, l, R) where {T, S}, - GeneralizedSVD{T,S,real(T),typeof(a)}(U, V, Q, a, b, k, l, R)) - -# iteration for destructuring into components -Base.iterate(S::GeneralizedSVD) = (S.U, Val(:V)) -Base.iterate(S::GeneralizedSVD, ::Val{:V}) = (S.V, Val(:Q)) -Base.iterate(S::GeneralizedSVD, ::Val{:Q}) = (S.Q, Val(:D1)) -Base.iterate(S::GeneralizedSVD, ::Val{:D1}) = (S.D1, Val(:D2)) -Base.iterate(S::GeneralizedSVD, ::Val{:D2}) = (S.D2, Val(:R0)) -Base.iterate(S::GeneralizedSVD, ::Val{:R0}) = (S.R0, Val(:done)) -Base.iterate(S::GeneralizedSVD, ::Val{:done}) = nothing - -""" - svd!(A, B) -> GeneralizedSVD - -`svd!` is the same as [`svd`](@ref), but modifies the arguments -`A` and `B` in-place, instead of making copies. See documentation of [`svd`](@ref) for details. -""" -function svd!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat - # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.version() < v"3.6.0" - U, V, Q, a, b, k, l, R = LAPACK.ggsvd!('U', 'V', 'Q', A, B) - else - U, V, Q, a, b, k, l, R = LAPACK.ggsvd3!('U', 'V', 'Q', A, B) - end - GeneralizedSVD(U, V, Q, a, b, Int(k), Int(l), R) -end -svd(A::AbstractMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} = - svd!(copy_similar(A, T), copy_similar(B, T)) - -""" - - svd(A, B) -> GeneralizedSVD - -Compute the generalized SVD of `A` and `B`, returning a `GeneralizedSVD` factorization -object `F` such that `[A;B] = [F.U * F.D1; F.V * F.D2] * F.R0 * F.Q'` - -- `U` is a M-by-M orthogonal matrix, -- `V` is a P-by-P orthogonal matrix, -- `Q` is a N-by-N orthogonal matrix, -- `D1` is a M-by-(K+L) diagonal matrix with 1s in the first K entries, -- `D2` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal, -- `R0` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is - nonsingular upper block triangular, - -`K+L` is the effective numerical rank of the matrix `[A; B]`. - -Iterating the decomposition produces the components `U`, `V`, `Q`, `D1`, `D2`, and `R0`. - -The generalized SVD is used in applications such as when one wants to compare how much belongs -to `A` vs. how much belongs to `B`, as in human vs yeast genome, or signal vs noise, or between -clusters vs within clusters. (See Edelman and Wang for discussion: https://arxiv.org/abs/1901.00485) - -It decomposes `[A; B]` into `[UC; VS]H`, where `[UC; VS]` is a natural orthogonal basis for the -column space of `[A; B]`, and `H = RQ'` is a natural non-orthogonal basis for the rowspace of `[A;B]`, -where the top rows are most closely attributed to the `A` matrix, and the bottom to the `B` matrix. -The multi-cosine/sine matrices `C` and `S` provide a multi-measure of how much `A` vs how much `B`, -and `U` and `V` provide directions in which these are measured. - -# Examples -```jldoctest -julia> A = randn(3,2); B=randn(4,2); - -julia> F = svd(A, B); - -julia> U,V,Q,C,S,R = F; - -julia> H = R*Q'; - -julia> [A; B] ≈ [U*C; V*S]*H -true - -julia> [A; B] ≈ [F.U*F.D1; F.V*F.D2]*F.R0*F.Q' -true - -julia> Uonly, = svd(A,B); - -julia> U == Uonly -true -``` -""" -function svd(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} - S = promote_type(eigtype(TA),TB) - return svd!(copy_similar(A, S), copy_similar(B, S)) -end -# This method can be heavily optimized but it is probably not critical -# and might introduce bugs or inconsistencies relative to the 1x1 matrix -# version -svd(x::Number, y::Number) = svd(fill(x, 1, 1), fill(y, 1, 1)) - -@inline function getproperty(F::GeneralizedSVD{T}, d::Symbol) where T - Fa = getfield(F, :a) - Fb = getfield(F, :b) - Fk = getfield(F, :k) - Fl = getfield(F, :l) - FU = getfield(F, :U) - FV = getfield(F, :V) - FQ = getfield(F, :Q) - FR = getfield(F, :R) - if d === :alpha - return Fa - elseif d === :beta - return Fb - elseif d === :vals || d === :S - return Fa[1:Fk + Fl] ./ Fb[1:Fk + Fl] - elseif d === :D1 - m = size(FU, 1) - if m - Fk - Fl >= 0 - return [Matrix{T}(I, Fk, Fk) zeros(T, Fk, Fl) ; - zeros(T, Fl, Fk) Diagonal(Fa[Fk + 1:Fk + Fl]); - zeros(T, m - Fk - Fl, Fk + Fl) ] - else - return [Matrix{T}(I, m, Fk) [zeros(T, Fk, m - Fk); Diagonal(Fa[Fk + 1:m])] zeros(T, m, Fk + Fl - m)] - end - elseif d === :D2 - m = size(FU, 1) - p = size(FV, 1) - if m - Fk - Fl >= 0 - return [zeros(T, Fl, Fk) Diagonal(Fb[Fk + 1:Fk + Fl]); zeros(T, p - Fl, Fk + Fl)] - else - return [zeros(T, p, Fk) [Diagonal(Fb[Fk + 1:m]); zeros(T, Fk + p - m, m - Fk)] [zeros(T, m - Fk, Fk + Fl - m); Matrix{T}(I, Fk + p - m, Fk + Fl - m)]] - end - elseif d === :R0 - n = size(FQ, 1) - return [zeros(T, Fk + Fl, n - Fk - Fl) FR] - else - getfield(F, d) - end -end - -Base.propertynames(F::GeneralizedSVD) = - (:alpha, :beta, :vals, :S, :D1, :D2, :R0, fieldnames(typeof(F))...) - -function show(io::IO, mime::MIME{Symbol("text/plain")}, F::GeneralizedSVD{<:Any,<:AbstractArray}) - summary(io, F); println(io) - println(io, "U factor:") - show(io, mime, F.U) - println(io, "\nV factor:") - show(io, mime, F.V) - println(io, "\nQ factor:") - show(io, mime, F.Q) - println(io, "\nD1 factor:") - show(io, mime, F.D1) - println(io, "\nD2 factor:") - show(io, mime, F.D2) - println(io, "\nR0 factor:") - show(io, mime, F.R0) -end - -""" - svdvals!(A, B) - -Return the generalized singular values from the generalized singular value -decomposition of `A` and `B`, saving space by overwriting `A` and `B`. -See also [`svd`](@ref) and [`svdvals`](@ref). -""" -function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat - # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.version() < v"3.6.0" - _, _, _, a, b, k, l, _ = LAPACK.ggsvd!('N', 'N', 'N', A, B) - else - _, _, _, a, b, k, l, _ = LAPACK.ggsvd3!('N', 'N', 'N', A, B) - end - a[1:k + l] ./ b[1:k + l] -end - -""" - svdvals(A, B) - -Return the generalized singular values from the generalized singular value -decomposition of `A` and `B`. See also [`svd`](@ref). - -# Examples -```jldoctest -julia> A = [1. 0.; 0. -1.] -2×2 Matrix{Float64}: - 1.0 0.0 - 0.0 -1.0 - -julia> B = [0. 1.; 1. 0.] -2×2 Matrix{Float64}: - 0.0 1.0 - 1.0 0.0 - -julia> svdvals(A, B) -2-element Vector{Float64}: - 1.0 - 1.0 -``` -""" -function svdvals(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} - S = promote_type(eigtype(TA), TB) - return svdvals!(copy_similar(A, S), copy_similar(B, S)) -end -svdvals(x::Number, y::Number) = abs(x/y) - -# Conversion -AbstractMatrix(F::SVD) = (F.U * Diagonal(F.S)) * F.Vt -AbstractArray(F::SVD) = AbstractMatrix(F) -Matrix(F::SVD) = Array(AbstractArray(F)) -Array(F::SVD) = Matrix(F) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl deleted file mode 100644 index b059f31737b55..0000000000000 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ /dev/null @@ -1,1064 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Symmetric and Hermitian matrices -struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} - data::S - uplo::Char - - function Symmetric{T,S}(data, uplo::Char) where {T,S<:AbstractMatrix{<:T}} - require_one_based_indexing(data) - (uplo != 'U' && uplo != 'L') && throw_uplo() - new{T,S}(data, uplo) - end -end -""" - Symmetric(A::AbstractMatrix, uplo::Symbol=:U) - -Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) -triangle of the matrix `A`. - -`Symmetric` views are mainly useful for real-symmetric matrices, for which -specialized algorithms (e.g. for eigenproblems) are enabled for `Symmetric` types. -More generally, see also [`Hermitian(A)`](@ref) for Hermitian matrices `A == A'`, which -is effectively equivalent to `Symmetric` for real matrices but is also useful for -complex matrices. (Whereas complex `Symmetric` matrices are supported but have few -if any specialized algorithms.) - -To compute the symmetric part of a real matrix, or more generally the Hermitian part `(A + A') / 2` of -a real or complex matrix `A`, use [`hermitianpart`](@ref). - -# Examples -```jldoctest -julia> A = [1 2 3; 4 5 6; 7 8 9] -3×3 Matrix{Int64}: - 1 2 3 - 4 5 6 - 7 8 9 - -julia> Supper = Symmetric(A) -3×3 Symmetric{Int64, Matrix{Int64}}: - 1 2 3 - 2 5 6 - 3 6 9 - -julia> Slower = Symmetric(A, :L) -3×3 Symmetric{Int64, Matrix{Int64}}: - 1 4 7 - 4 5 8 - 7 8 9 - -julia> hermitianpart(A) -3×3 Hermitian{Float64, Matrix{Float64}}: - 1.0 3.0 5.0 - 3.0 5.0 7.0 - 5.0 7.0 9.0 -``` - -Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric (e.g. if -`A == transpose(A)`). -""" -function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) - checksquare(A) - return symmetric_type(typeof(A))(A, char_uplo(uplo)) -end - -""" - symmetric(A, uplo::Symbol=:U) - -Construct a symmetric view of `A`. If `A` is a matrix, `uplo` controls whether the upper -(if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the -other one. If `A` is a `Number`, it is returned as is. - -If a symmetric view of a matrix is to be constructed of which the elements are neither -matrices nor numbers, an appropriate method of `symmetric` has to be implemented. In that -case, `symmetric_type` has to be implemented, too. -""" -symmetric(A::AbstractMatrix, uplo::Symbol=:U) = Symmetric(A, uplo) -symmetric(A::Number, ::Symbol=:U) = A - -""" - symmetric_type(T::Type) - -The type of the object returned by `symmetric(::T, ::Symbol)`. For matrices, this is an -appropriately typed `Symmetric`, for `Number`s, it is the original type. If `symmetric` is -implemented for a custom type, so should be `symmetric_type`, and vice versa. -""" -function symmetric_type(::Type{T}) where {S, T<:AbstractMatrix{S}} - return Symmetric{Union{S, promote_op(transpose, S), symmetric_type(S)}, T} -end -function symmetric_type(::Type{T}) where {S<:Number, T<:AbstractMatrix{S}} - return Symmetric{S, T} -end -function symmetric_type(::Type{T}) where {S<:AbstractMatrix, T<:AbstractMatrix{S}} - return Symmetric{AbstractMatrix, T} -end -symmetric_type(::Type{T}) where {T<:Number} = T - -struct Hermitian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} - data::S - uplo::Char - - function Hermitian{T,S}(data, uplo::Char) where {T,S<:AbstractMatrix{<:T}} - require_one_based_indexing(data) - (uplo != 'U' && uplo != 'L') && throw_uplo() - new{T,S}(data, uplo) - end -end -""" - Hermitian(A::AbstractMatrix, uplo::Symbol=:U) - -Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) -triangle of the matrix `A`. - -To compute the Hermitian part of `A`, use [`hermitianpart`](@ref). - -# Examples -```jldoctest -julia> A = [1 2+2im 3-3im; 4 5 6-6im; 7 8+8im 9] -3×3 Matrix{Complex{Int64}}: - 1+0im 2+2im 3-3im - 4+0im 5+0im 6-6im - 7+0im 8+8im 9+0im - -julia> Hupper = Hermitian(A) -3×3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: - 1+0im 2+2im 3-3im - 2-2im 5+0im 6-6im - 3+3im 6+6im 9+0im - -julia> Hlower = Hermitian(A, :L) -3×3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: - 1+0im 4+0im 7+0im - 4+0im 5+0im 8-8im - 7+0im 8+8im 9+0im - -julia> hermitianpart(A) -3×3 Hermitian{ComplexF64, Matrix{ComplexF64}}: - 1.0+0.0im 3.0+1.0im 5.0-1.5im - 3.0-1.0im 5.0+0.0im 7.0-7.0im - 5.0+1.5im 7.0+7.0im 9.0+0.0im -``` - -Note that `Hupper` will not be equal to `Hlower` unless `A` is itself Hermitian (e.g. if `A == adjoint(A)`). - -All non-real parts of the diagonal will be ignored. - -```julia -Hermitian(fill(complex(1,1), 1, 1)) == fill(1, 1, 1) -``` -""" -function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) - n = checksquare(A) - return hermitian_type(typeof(A))(A, char_uplo(uplo)) -end - -""" - hermitian(A, uplo::Symbol=:U) - -Construct a hermitian view of `A`. If `A` is a matrix, `uplo` controls whether the upper -(if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the -other one. If `A` is a `Number`, its real part is returned converted back to the input -type. - -If a hermitian view of a matrix is to be constructed of which the elements are neither -matrices nor numbers, an appropriate method of `hermitian` has to be implemented. In that -case, `hermitian_type` has to be implemented, too. -""" -hermitian(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(A, uplo) -hermitian(A::Number, ::Symbol=:U) = convert(typeof(A), real(A)) - -""" - hermitian_type(T::Type) - -The type of the object returned by `hermitian(::T, ::Symbol)`. For matrices, this is an -appropriately typed `Hermitian`, for `Number`s, it is the original type. If `hermitian` is -implemented for a custom type, so should be `hermitian_type`, and vice versa. -""" -function hermitian_type(::Type{T}) where {S, T<:AbstractMatrix{S}} - return Hermitian{Union{S, promote_op(adjoint, S), hermitian_type(S)}, T} -end -function hermitian_type(::Type{T}) where {S<:Number, T<:AbstractMatrix{S}} - return Hermitian{S, T} -end -function hermitian_type(::Type{T}) where {S<:AbstractMatrix, T<:AbstractMatrix{S}} - return Hermitian{AbstractMatrix, T} -end -hermitian_type(::Type{T}) where {T<:Number} = T - -_unwrap(A::Hermitian) = parent(A) -_unwrap(A::Symmetric) = parent(A) - -for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) - @eval begin - $S(A::$S) = A - function $S(A::$S, uplo::Symbol) - if A.uplo == char_uplo(uplo) - return A - else - throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) - end - end - $S(A::$H) = $S(A, sym_uplo(A.uplo)) - function $S(A::$H, uplo::Symbol) - if A.uplo == char_uplo(uplo) - if $H === Hermitian && !(eltype(A) <: Real) && - any(!isreal, A.data[i] for i in diagind(A.data, IndexStyle(A.data))) - - throw(ArgumentError("Cannot construct $($S)($($H))); diagonal contains complex values")) - end - return $S(A.data, sym_uplo(A.uplo)) - else - throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) - end - end - end -end - -convert(::Type{T}, m::Union{Symmetric,Hermitian}) where {T<:Symmetric} = m isa T ? m : T(m)::T -convert(::Type{T}, m::Union{Symmetric,Hermitian}) where {T<:Hermitian} = m isa T ? m : T(m)::T - -const HermOrSym{T, S} = Union{Hermitian{T,S}, Symmetric{T,S}} -const RealHermSym{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}} -const SymSymTri{T} = Union{Symmetric{T}, SymTridiagonal{T}} -const RealHermSymSymTri{T<:Real} = Union{RealHermSym{T}, SymTridiagonal{T}} -const RealHermSymComplexHerm{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Hermitian{Complex{T},S}} -const RealHermSymComplexSym{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Symmetric{Complex{T},S}} -const RealHermSymSymTriComplexHerm{T<:Real} = Union{RealHermSymComplexSym{T}, SymTridiagonal{T}} -const SelfAdjoint = Union{Symmetric{<:Real}, Hermitian{<:Number}} - -wrappertype(::Union{Symmetric, SymTridiagonal}) = Symmetric -wrappertype(::Hermitian) = Hermitian - -size(A::HermOrSym) = size(A.data) -axes(A::HermOrSym) = axes(A.data) -@inline function Base.isassigned(A::HermOrSym, i::Int, j::Int) - @boundscheck checkbounds(Bool, A, i, j) || return false - @inbounds if i == j || ((A.uplo == 'U') == (i < j)) - return isassigned(A.data, i, j) - else - return isassigned(A.data, j, i) - end -end - -@inline function getindex(A::Symmetric, i::Int, j::Int) - @boundscheck checkbounds(A, i, j) - @inbounds if i == j - return symmetric(A.data[i, j], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) - elseif (A.uplo == 'U') == (i < j) - return A.data[i, j] - else - return transpose(A.data[j, i]) - end -end -@inline function getindex(A::Hermitian, i::Int, j::Int) - @boundscheck checkbounds(A, i, j) - @inbounds if i == j - return hermitian(A.data[i, j], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) - elseif (A.uplo == 'U') == (i < j) - return A.data[i, j] - else - return adjoint(A.data[j, i]) - end -end - -Base._reverse(A::Symmetric, dims::Integer) = reverse!(Matrix(A); dims) -Base._reverse(A::Symmetric, ::Colon) = Symmetric(reverse(A.data), A.uplo == 'U' ? :L : :U) - -@propagate_inbounds function setindex!(A::Symmetric, v, i::Integer, j::Integer) - i == j || throw(ArgumentError("Cannot set a non-diagonal index in a symmetric matrix")) - setindex!(A.data, v, i, j) - return A -end - -Base._reverse(A::Hermitian, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::Hermitian, ::Colon) = Hermitian(reverse(A.data), A.uplo == 'U' ? :L : :U) - -@propagate_inbounds function setindex!(A::Hermitian, v, i::Integer, j::Integer) - if i != j - throw(ArgumentError("Cannot set a non-diagonal index in a Hermitian matrix")) - elseif !isreal(v) - throw(ArgumentError("Cannot set a diagonal entry in a Hermitian matrix to a nonreal value")) - else - setindex!(A.data, v, i, j) - end - return A -end - -Base.dataids(A::HermOrSym) = Base.dataids(parent(A)) -Base.unaliascopy(A::Hermitian) = Hermitian(Base.unaliascopy(parent(A)), sym_uplo(A.uplo)) -Base.unaliascopy(A::Symmetric) = Symmetric(Base.unaliascopy(parent(A)), sym_uplo(A.uplo)) - -_conjugation(::Symmetric) = transpose -_conjugation(::Hermitian) = adjoint - -diag(A::Symmetric) = symmetric.(diag(parent(A)), sym_uplo(A.uplo)) -diag(A::Hermitian) = hermitian.(diag(parent(A)), sym_uplo(A.uplo)) - -function applytri(f, A::HermOrSym) - if A.uplo == 'U' - f(uppertriangular(A.data)) - else - f(lowertriangular(A.data)) - end -end - -function applytri(f, A::HermOrSym, B::HermOrSym) - if A.uplo == B.uplo == 'U' - f(uppertriangular(A.data), uppertriangular(B.data)) - elseif A.uplo == B.uplo == 'L' - f(lowertriangular(A.data), lowertriangular(B.data)) - elseif A.uplo == 'U' - f(uppertriangular(A.data), uppertriangular(_conjugation(B)(B.data))) - else # A.uplo == 'L' - f(uppertriangular(_conjugation(A)(A.data)), uppertriangular(B.data)) - end -end -_parent_tri(U::UpperOrLowerTriangular) = parent(U) -_parent_tri(U) = U -parentof_applytri(f, args...) = _parent_tri(applytri(f, args...)) - -isdiag(A::HermOrSym) = applytri(isdiag, A) - -# For A<:Union{Symmetric,Hermitian}, similar(A[, neweltype]) should yield a matrix with the same -# symmetry type, uplo flag, and underlying storage type as A. The following methods cover these cases. -similar(A::Symmetric, ::Type{T}) where {T} = Symmetric(similar(parent(A), T), ifelse(A.uplo == 'U', :U, :L)) -# If the Hermitian constructor's check ascertaining that the wrapped matrix's -# diagonal is strictly real is removed, the following method can be simplified. -function similar(A::Hermitian, ::Type{T}) where T - B = similar(parent(A), T) - for i in 1:size(B, 1) B[i, i] = 0 end - return Hermitian(B, ifelse(A.uplo == 'U', :U, :L)) -end -# On the other hand, similar(A, [neweltype,] shape...) should yield a matrix of the underlying -# storage type of A (not wrapped in a symmetry type). The following method covers these cases. -similar(A::Union{Symmetric,Hermitian}, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(A), T, dims) - -parent(A::HermOrSym) = A.data -Symmetric{T,S}(A::Symmetric{T,S}) where {T,S<:AbstractMatrix{T}} = A -Symmetric{T,S}(A::Symmetric) where {T,S<:AbstractMatrix{T}} = Symmetric{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) -AbstractMatrix{T}(A::Symmetric{T}) where {T} = copy(A) -Hermitian{T,S}(A::Hermitian{T,S}) where {T,S<:AbstractMatrix{T}} = A -Hermitian{T,S}(A::Hermitian) where {T,S<:AbstractMatrix{T}} = Hermitian{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) -AbstractMatrix{T}(A::Hermitian{T}) where {T} = copy(A) - -copy(A::Symmetric) = (Symmetric(parentof_applytri(copy, A), sym_uplo(A.uplo))) -copy(A::Hermitian) = (Hermitian(parentof_applytri(copy, A), sym_uplo(A.uplo))) - -function copyto!(dest::Symmetric, src::Symmetric) - if axes(dest) != axes(src) - @invoke copyto!(dest::AbstractMatrix, src::AbstractMatrix) - elseif src.uplo == dest.uplo - copytrito!(dest.data, src.data, src.uplo) - else - transpose!(dest.data, Base.unalias(dest.data, src.data)) - end - return dest -end - -function copyto!(dest::Hermitian, src::Hermitian) - if axes(dest) != axes(src) - @invoke copyto!(dest::AbstractMatrix, src::AbstractMatrix) - elseif src.uplo == dest.uplo - copytrito!(dest.data, src.data, src.uplo) - else - adjoint!(dest.data, Base.unalias(dest.data, src.data)) - end - return dest -end - -@propagate_inbounds function copyto!(dest::StridedMatrix, A::HermOrSym) - if axes(dest) != axes(A) - @invoke copyto!(dest::StridedMatrix, A::AbstractMatrix) - else - _copyto!(dest, Base.unalias(dest, A)) - end - return dest -end -@propagate_inbounds function _copyto!(dest::StridedMatrix, A::HermOrSym) - copytrito!(dest, parent(A), A.uplo) - conjugate = A isa Hermitian - copytri!(dest, A.uplo, conjugate) - _symmetrize_diagonal!(dest, A) - return dest -end -@inline function _symmetrize_diagonal!(B, A::Symmetric) - for i = 1:size(A, 1) - B[i,i] = symmetric(A[i,i], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) - end - return B -end -@inline function _symmetrize_diagonal!(B, A::Hermitian) - for i = 1:size(A, 1) - B[i,i] = hermitian(A[i,i], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) - end - return B -end - -# fill[stored]! -fill!(A::HermOrSym, x) = fillstored!(A, x) -function fillstored!(A::HermOrSym{T}, x) where T - xT = convert(T, x) - if isa(A, Hermitian) - isreal(xT) || throw(ArgumentError("cannot fill Hermitian matrix with a nonreal value")) - end - if A.uplo == 'U' - fillband!(A.data, xT, 0, size(A,2)-1) - else # A.uplo == 'L' - fillband!(A.data, xT, 1-size(A,1), 0) - end - return A -end - -Base.isreal(A::HermOrSym{<:Real}) = true -function Base.isreal(A::HermOrSym) - n = size(A, 1) - @inbounds if A.uplo == 'U' - for j in 1:n - for i in 1:(j - (A isa Hermitian)) - if !isreal(A.data[i,j]) - return false - end - end - end - else - for j in 1:n - for i in (j + (A isa Hermitian)):n - if !isreal(A.data[i,j]) - return false - end - end - end - end - return true -end - -ishermitian(A::Hermitian) = true -ishermitian(A::Symmetric{<:Real}) = true -ishermitian(A::Symmetric{<:Complex}) = isreal(A) -issymmetric(A::Hermitian{<:Real}) = true -issymmetric(A::Hermitian{<:Complex}) = isreal(A) -issymmetric(A::Symmetric) = true - -adjoint(A::Hermitian) = A -transpose(A::Symmetric) = A -adjoint(A::Symmetric{<:Real}) = A -transpose(A::Hermitian{<:Real}) = A -adjoint(A::Symmetric) = Adjoint(A) -transpose(A::Hermitian) = Transpose(A) - -real(A::Symmetric{<:Real}) = A -real(A::Hermitian{<:Real}) = A -real(A::Symmetric) = Symmetric(parentof_applytri(real, A), sym_uplo(A.uplo)) -real(A::Hermitian) = Hermitian(parentof_applytri(real, A), sym_uplo(A.uplo)) -imag(A::Symmetric) = Symmetric(parentof_applytri(imag, A), sym_uplo(A.uplo)) - -Base.copy(A::Adjoint{<:Any,<:Symmetric}) = - Symmetric(copy(adjoint(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) -Base.copy(A::Transpose{<:Any,<:Hermitian}) = - Hermitian(copy(transpose(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) - -tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) -tr(A::Hermitian{<:Number}) = real(tr(A.data)) - -Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo(A.uplo)) -Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo(A.uplo)) -Base.conj!(A::HermOrSym) = typeof(A)(parentof_applytri(conj!, A), A.uplo) - -# tril/triu -function tril(A::Hermitian, k::Integer=0) - if A.uplo == 'U' && k <= 0 - return tril!(copy(A.data'),k) - elseif A.uplo == 'U' && k > 0 - return tril!(copy(A.data'),-1) + tril!(triu(A.data),k) - elseif A.uplo == 'L' && k <= 0 - return tril(A.data,k) - else - return tril(A.data,-1) + tril!(triu!(copy(A.data')),k) - end -end - -function tril(A::Symmetric, k::Integer=0) - if A.uplo == 'U' && k <= 0 - return tril!(copy(transpose(A.data)),k) - elseif A.uplo == 'U' && k > 0 - return tril!(copy(transpose(A.data)),-1) + tril!(triu(A.data),k) - elseif A.uplo == 'L' && k <= 0 - return tril(A.data,k) - else - return tril(A.data,-1) + tril!(triu!(copy(transpose(A.data))),k) - end -end - -function triu(A::Hermitian, k::Integer=0) - if A.uplo == 'U' && k >= 0 - return triu(A.data,k) - elseif A.uplo == 'U' && k < 0 - return triu(A.data,1) + triu!(tril!(copy(A.data')),k) - elseif A.uplo == 'L' && k >= 0 - return triu!(copy(A.data'),k) - else - return triu!(copy(A.data'),1) + triu!(tril(A.data),k) - end -end - -function triu(A::Symmetric, k::Integer=0) - if A.uplo == 'U' && k >= 0 - return triu(A.data,k) - elseif A.uplo == 'U' && k < 0 - return triu(A.data,1) + triu!(tril!(copy(transpose(A.data))),k) - elseif A.uplo == 'L' && k >= 0 - return triu!(copy(transpose(A.data)),k) - else - return triu!(copy(transpose(A.data)),1) + triu!(tril(A.data),k) - end -end - -for (T, trans, real) in [(:Symmetric, :transpose, :identity), (:(Hermitian{<:Union{Real,Complex}}), :adjoint, :real)] - @eval begin - function dot(A::$T, B::$T) - n = size(A, 2) - if n != size(B, 2) - throw(DimensionMismatch(lazy"A has dimensions $(size(A)) but B has dimensions $(size(B))")) - end - - dotprod = $real(zero(dot(first(A), first(B)))) - @inbounds if A.uplo == 'U' && B.uplo == 'U' - for j in 1:n - for i in 1:(j - 1) - dotprod += 2 * $real(dot(A.data[i, j], B.data[i, j])) - end - dotprod += $real(dot(A[j, j], B[j, j])) - end - elseif A.uplo == 'L' && B.uplo == 'L' - for j in 1:n - dotprod += $real(dot(A[j, j], B[j, j])) - for i in (j + 1):n - dotprod += 2 * $real(dot(A.data[i, j], B.data[i, j])) - end - end - elseif A.uplo == 'U' && B.uplo == 'L' - for j in 1:n - for i in 1:(j - 1) - dotprod += 2 * $real(dot(A.data[i, j], $trans(B.data[j, i]))) - end - dotprod += $real(dot(A[j, j], B[j, j])) - end - else - for j in 1:n - dotprod += $real(dot(A[j, j], B[j, j])) - for i in (j + 1):n - dotprod += 2 * $real(dot(A.data[i, j], $trans(B.data[j, i]))) - end - end - end - return dotprod - end - end -end - -function kron(A::Hermitian{<:Union{Real,Complex},<:StridedMatrix}, B::Hermitian{<:Union{Real,Complex},<:StridedMatrix}) - resultuplo = A.uplo == 'U' || B.uplo == 'U' ? :U : :L - C = Hermitian(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)), resultuplo) - return kron!(C, A, B) -end -function kron(A::Symmetric{<:Number,<:StridedMatrix}, B::Symmetric{<:Number,<:StridedMatrix}) - resultuplo = A.uplo == 'U' || B.uplo == 'U' ? :U : :L - C = Symmetric(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)), resultuplo) - return kron!(C, A, B) -end - -function kron!(C::Hermitian{<:Union{Real,Complex},<:StridedMatrix}, A::Hermitian{<:Union{Real,Complex},<:StridedMatrix}, B::Hermitian{<:Union{Real,Complex},<:StridedMatrix}) - size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) - if ((A.uplo == 'U' || B.uplo == 'U') && C.uplo != 'U') || ((A.uplo == 'L' && B.uplo == 'L') && C.uplo != 'L') - throw(ArgumentError("C.uplo must match A.uplo and B.uplo, got $(C.uplo) $(A.uplo) $(B.uplo)")) - end - _hermkron!(C.data, A.data, B.data, conj, real, A.uplo, B.uplo) - return C -end -function kron!(C::Symmetric{<:Number,<:StridedMatrix}, A::Symmetric{<:Number,<:StridedMatrix}, B::Symmetric{<:Number,<:StridedMatrix}) - size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) - if ((A.uplo == 'U' || B.uplo == 'U') && C.uplo != 'U') || ((A.uplo == 'L' && B.uplo == 'L') && C.uplo != 'L') - throw(ArgumentError("C.uplo must match A.uplo and B.uplo, got $(C.uplo) $(A.uplo) $(B.uplo)")) - end - _hermkron!(C.data, A.data, B.data, identity, identity, A.uplo, B.uplo) - return C -end - -function _hermkron!(C, A, B, conj, real, Auplo, Buplo) - n_A = size(A, 1) - n_B = size(B, 1) - @inbounds if Auplo == 'U' && Buplo == 'U' - for j = 1:n_A - jnB = (j - 1) * n_B - for i = 1:(j-1) - Aij = A[i, j] - inB = (i - 1) * n_B - for l = 1:n_B - for k = 1:(l-1) - C[inB+k, jnB+l] = Aij * B[k, l] - C[inB+l, jnB+k] = Aij * conj(B[k, l]) - end - C[inB+l, jnB+l] = Aij * real(B[l, l]) - end - end - Ajj = real(A[j, j]) - for l = 1:n_B - for k = 1:(l-1) - C[jnB+k, jnB+l] = Ajj * B[k, l] - end - C[jnB+l, jnB+l] = Ajj * real(B[l, l]) - end - end - elseif Auplo == 'U' && Buplo == 'L' - for j = 1:n_A - jnB = (j - 1) * n_B - for i = 1:(j-1) - Aij = A[i, j] - inB = (i - 1) * n_B - for l = 1:n_B - C[inB+l, jnB+l] = Aij * real(B[l, l]) - for k = (l+1):n_B - C[inB+l, jnB+k] = Aij * conj(B[k, l]) - C[inB+k, jnB+l] = Aij * B[k, l] - end - end - end - Ajj = real(A[j, j]) - for l = 1:n_B - C[jnB+l, jnB+l] = Ajj * real(B[l, l]) - for k = (l+1):n_B - C[jnB+l, jnB+k] = Ajj * conj(B[k, l]) - end - end - end - elseif Auplo == 'L' && Buplo == 'U' - for j = 1:n_A - jnB = (j - 1) * n_B - Ajj = real(A[j, j]) - for l = 1:n_B - for k = 1:(l-1) - C[jnB+k, jnB+l] = Ajj * B[k, l] - end - C[jnB+l, jnB+l] = Ajj * real(B[l, l]) - end - for i = (j+1):n_A - conjAij = conj(A[i, j]) - inB = (i - 1) * n_B - for l = 1:n_B - for k = 1:(l-1) - C[jnB+k, inB+l] = conjAij * B[k, l] - C[jnB+l, inB+k] = conjAij * conj(B[k, l]) - end - C[jnB+l, inB+l] = conjAij * real(B[l, l]) - end - end - end - else #if Auplo == 'L' && Buplo == 'L' - for j = 1:n_A - jnB = (j - 1) * n_B - Ajj = real(A[j, j]) - for l = 1:n_B - C[jnB+l, jnB+l] = Ajj * real(B[l, l]) - for k = (l+1):n_B - C[jnB+k, jnB+l] = Ajj * B[k, l] - end - end - for i = (j+1):n_A - Aij = A[i, j] - inB = (i - 1) * n_B - for l = 1:n_B - C[inB+l, jnB+l] = Aij * real(B[l, l]) - for k = (l+1):n_B - C[inB+k, jnB+l] = Aij * B[k, l] - C[inB+l, jnB+k] = Aij * conj(B[k, l]) - end - end - end - end - end -end - -(-)(A::Symmetric) = Symmetric(parentof_applytri(-, A), sym_uplo(A.uplo)) -(-)(A::Hermitian) = Hermitian(parentof_applytri(-, A), sym_uplo(A.uplo)) - -## Addition/subtraction -for f ∈ (:+, :-), Wrapper ∈ (:Hermitian, :Symmetric) - @eval function $f(A::$Wrapper, B::$Wrapper) - uplo = A.uplo == B.uplo ? sym_uplo(A.uplo) : (:U) - $Wrapper(parentof_applytri($f, A, B), uplo) - end -end - -for f in (:+, :-) - @eval begin - $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo(B.uplo))) - $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo(A.uplo)), B) - $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, sym_uplo(B.uplo)), B) - $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, sym_uplo(A.uplo))) - $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, sym_uplo(B.uplo)), B) - $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, sym_uplo(A.uplo))) - end -end - -*(A::HermOrSym, B::HermOrSym) = A * copyto!(similar(parent(B)), B) - -function dot(x::AbstractVector, A::RealHermSymComplexHerm, y::AbstractVector) - require_one_based_indexing(x, y) - n = length(x) - (n == length(y) == size(A, 1)) || throw(DimensionMismatch()) - data = A.data - r = dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - iszero(n) && return r - if A.uplo == 'U' - @inbounds for j = 1:length(y) - r += dot(x[j], real(data[j,j]), y[j]) - @simd for i = 1:j-1 - Aij = data[i,j] - r += dot(x[i], Aij, y[j]) + dot(x[j], adjoint(Aij), y[i]) - end - end - else # A.uplo == 'L' - @inbounds for j = 1:length(y) - r += dot(x[j], real(data[j,j]), y[j]) - @simd for i = j+1:length(y) - Aij = data[i,j] - r += dot(x[i], Aij, y[j]) + dot(x[j], adjoint(Aij), y[i]) - end - end - end - return r -end - -# Scaling with Number -*(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y * x, A), sym_uplo(A.uplo)) -*(x::Number, A::Symmetric) = Symmetric(parentof_applytri(y -> x * y, A), sym_uplo(A.uplo)) -*(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y * x, A), sym_uplo(A.uplo)) -*(x::Real, A::Hermitian) = Hermitian(parentof_applytri(y -> x * y, A), sym_uplo(A.uplo)) -/(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y/x, A), sym_uplo(A.uplo)) -/(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y/x, A), sym_uplo(A.uplo)) - -factorize(A::HermOrSym) = _factorize(A) -function _factorize(A::HermOrSym{T}; check::Bool=true) where T - TT = typeof(sqrt(oneunit(T))) - if isdiag(A) - return Diagonal(A) - elseif TT <: BlasFloat - return bunchkaufman(A; check=check) - else # fallback - return lu(A; check=check) - end -end - -logabsdet(A::RealHermSymComplexHerm) = ((l, s) = logabsdet(_factorize(A; check=false)); return real(l), s) -logabsdet(A::Symmetric{<:Real}) = logabsdet(_factorize(A; check=false)) -logabsdet(A::Symmetric) = logabsdet(_factorize(A; check=false)) -logdet(A::RealHermSymComplexHerm) = real(logdet(_factorize(A; check=false))) -logdet(A::Symmetric{<:Real}) = logdet(_factorize(A; check=false)) -logdet(A::Symmetric) = logdet(_factorize(A; check=false)) -det(A::RealHermSymComplexHerm) = real(det(_factorize(A; check=false))) -det(A::Symmetric{<:Real}) = det(_factorize(A; check=false)) -det(A::Symmetric) = det(_factorize(A; check=false)) - -\(A::HermOrSym, B::AbstractVector) = \(factorize(A), B) -# Bunch-Kaufman solves can not utilize BLAS-3 for multiple right hand sides -# so using LU is faster for AbstractMatrix right hand side -\(A::HermOrSym, B::AbstractMatrix) = \(isdiag(A) ? Diagonal(A) : lu(A), B) - -function _inv(A::HermOrSym) - n = checksquare(A) - B = inv!(lu(A)) - conjugate = isa(A, Hermitian) - # symmetrize - if A.uplo == 'U' # add to upper triangle - @inbounds for i = 1:n, j = i:n - B[i,j] = conjugate ? (B[i,j] + conj(B[j,i])) / 2 : (B[i,j] + B[j,i]) / 2 - end - else # A.uplo == 'L', add to lower triangle - @inbounds for i = 1:n, j = i:n - B[j,i] = conjugate ? (B[j,i] + conj(B[i,j])) / 2 : (B[j,i] + B[i,j]) / 2 - end - end - B -end -# StridedMatrix restriction seems necessary due to inv! call in _inv above -inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), sym_uplo(A.uplo)) -inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), sym_uplo(A.uplo)) - -function svd(A::RealHermSymComplexHerm; full::Bool=false) - vals, vecs = eigen(A) - I = sortperm(vals; by=abs, rev=true) - permute!(vals, I) - Base.permutecols!!(vecs, I) # left-singular vectors - V = copy(vecs) # right-singular vectors - # shifting -1 from singular values to right-singular vectors - @inbounds for i = 1:length(vals) - if vals[i] < 0 - vals[i] = -vals[i] - for j = 1:size(V,1); V[j,i] = -V[j,i]; end - end - end - return SVD(vecs, vals, V') -end -function svd(A::RealHermSymComplexHerm{Float16}; full::Bool = false) - T = eltype(A) - F = svd(eigencopy_oftype(A, eigtype(T)); full) - return SVD{T}(F) -end - -function svdvals!(A::RealHermSymComplexHerm) - vals = eigvals!(A) - for i = 1:length(vals) - vals[i] = abs(vals[i]) - end - return sort!(vals, rev = true) -end - -# Matrix functions -^(A::Symmetric{<:Real}, p::Integer) = sympow(A, p) -^(A::Symmetric{<:Complex}, p::Integer) = sympow(A, p) -^(A::SymTridiagonal{<:Real}, p::Integer) = sympow(A, p) -^(A::SymTridiagonal{<:Complex}, p::Integer) = sympow(A, p) -function sympow(A::SymSymTri, p::Integer) - if p < 0 - return Symmetric(Base.power_by_squaring(inv(A), -p)) - else - return Symmetric(Base.power_by_squaring(A, p)) - end -end -for hermtype in (:Symmetric, :SymTridiagonal) - @eval begin - function ^(A::$hermtype{<:Real}, p::Real) - isinteger(p) && return integerpow(A, p) - F = eigen(A) - if all(λ -> λ ≥ 0, F.values) - return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors') - else - return Symmetric((F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors') - end - end - function ^(A::$hermtype{<:Complex}, p::Real) - isinteger(p) && return integerpow(A, p) - return Symmetric(schurpow(A, p)) - end - end -end -function ^(A::Hermitian, p::Integer) - if p < 0 - retmat = Base.power_by_squaring(inv(A), -p) - else - retmat = Base.power_by_squaring(A, p) - end - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) -end -function ^(A::Hermitian{T}, p::Real) where T - isinteger(p) && return integerpow(A, p) - F = eigen(A) - if all(λ -> λ ≥ 0, F.values) - retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors' - if T <: Real - return Hermitian(retmat) - else - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) - end - else - retmat = (F.vectors * Diagonal((complex.(F.values).^p))) * F.vectors' - if T <: Real - return Symmetric(retmat) - else - return retmat - end - end -end - -for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh, :cbrt) - @eval begin - function ($func)(A::RealHermSymSymTri) - F = eigen(A) - return wrappertype(A)((F.vectors * Diagonal(($func).(F.values))) * F.vectors') - end - function ($func)(A::Hermitian{<:Complex}) - F = eigen(A) - retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) - end - end -end - -function cis(A::RealHermSymSymTri) - F = eigen(A) - return Symmetric(F.vectors .* cis.(F.values') * F.vectors') -end -function cis(A::Hermitian{<:Complex}) - F = eigen(A) - return F.vectors .* cis.(F.values') * F.vectors' -end - - -for func in (:acos, :asin) - @eval begin - function ($func)(A::RealHermSymSymTri) - F = eigen(A) - if all(λ -> -1 ≤ λ ≤ 1, F.values) - return wrappertype(A)((F.vectors * Diagonal(($func).(F.values))) * F.vectors') - else - return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors') - end - end - function ($func)(A::Hermitian{<:Complex}) - F = eigen(A) - if all(λ -> -1 ≤ λ ≤ 1, F.values) - retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) - else - return (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' - end - end - end -end - -function acosh(A::RealHermSymSymTri) - F = eigen(A) - if all(λ -> λ ≥ 1, F.values) - return wrappertype(A)((F.vectors * Diagonal(acosh.(F.values))) * F.vectors') - else - return Symmetric((F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors') - end -end -function acosh(A::Hermitian{<:Complex}) - F = eigen(A) - if all(λ -> λ ≥ 1, F.values) - retmat = (F.vectors * Diagonal(acosh.(F.values))) * F.vectors' - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) - else - return (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors' - end -end - -function sincos(A::RealHermSymSymTri) - n = checksquare(A) - F = eigen(A) - T = float(eltype(F.values)) - S, C = Diagonal(similar(A, T, (n,))), Diagonal(similar(A, T, (n,))) - for i in eachindex(S.diag, C.diag, F.values) - S.diag[i], C.diag[i] = sincos(F.values[i]) - end - return wrappertype(A)((F.vectors * S) * F.vectors'), wrappertype(A)((F.vectors * C) * F.vectors') -end -function sincos(A::Hermitian{<:Complex}) - n = checksquare(A) - F = eigen(A) - T = float(eltype(F.values)) - S, C = Diagonal(similar(A, T, (n,))), Diagonal(similar(A, T, (n,))) - for i in eachindex(S.diag, C.diag, F.values) - S.diag[i], C.diag[i] = sincos(F.values[i]) - end - retmatS, retmatC = (F.vectors * S) * F.vectors', (F.vectors * C) * F.vectors' - for i in diagind(retmatS, IndexStyle(retmatS)) - retmatS[i] = real(retmatS[i]) - retmatC[i] = real(retmatC[i]) - end - return Hermitian(retmatS), Hermitian(retmatC) -end - - -for func in (:log, :sqrt) - # sqrt has rtol arg to handle matrices that are semidefinite up to roundoff errors - rtolarg = func === :sqrt ? Any[Expr(:kw, :(rtol::Real), :(eps(real(float(one(T))))*size(A,1)))] : Any[] - rtolval = func === :sqrt ? :(-maximum(abs, F.values) * rtol) : 0 - @eval begin - function ($func)(A::RealHermSymSymTri{T}; $(rtolarg...)) where {T<:Real} - F = eigen(A) - λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff - if all(λ -> λ ≥ λ₀, F.values) - return wrappertype(A)((F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors') - else - return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors') - end - end - function ($func)(A::Hermitian{T}; $(rtolarg...)) where {T<:Complex} - n = checksquare(A) - F = eigen(A) - λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff - if all(λ -> λ ≥ λ₀, F.values) - retmat = (F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors' - for i in diagind(retmat, IndexStyle(retmat)) - retmat[i] = real(retmat[i]) - end - return Hermitian(retmat) - else - retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' - return retmat - end - end - end -end - -""" - hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian - -Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a -[`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part -of `A`; it is also sometimes called the "operator real part". The optional argument `uplo` controls the corresponding argument of the -[`Hermitian`](@ref) view. For real matrices, the latter is equivalent to a -[`Symmetric`](@ref) view. - -See also [`hermitianpart!`](@ref) for the corresponding in-place operation. - -!!! compat "Julia 1.10" - This function requires Julia 1.10 or later. -""" -hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) - -""" - hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) -> Hermitian - -Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return -[`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric -part of `A`. - -See also [`hermitianpart`](@ref) for the corresponding out-of-place operation. - -!!! compat "Julia 1.10" - This function requires Julia 1.10 or later. -""" -hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart!(A), uplo) - -_hermitianpart(A::AbstractMatrix) = _hermitianpart!(copy_similar(A, Base.promote_op(/, eltype(A), Int))) -_hermitianpart(a::Number) = real(a) - -function _hermitianpart!(A::AbstractMatrix) - require_one_based_indexing(A) - n = checksquare(A) - @inbounds for j in 1:n - A[j, j] = _hermitianpart(A[j, j]) - for i in 1:j-1 - A[i, j] = val = (A[i, j] + adjoint(A[j, i])) / 2 - A[j, i] = adjoint(val) - end - end - return A -end - -## structured matrix printing ## -function Base.replace_in_print_matrix(A::HermOrSym,i::Integer,j::Integer,s::AbstractString) - ijminmax = minmax(i, j) - inds = A.uplo == 'U' ? ijminmax : reverse(ijminmax) - Base.replace_in_print_matrix(parent(A), inds..., s) -end diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl deleted file mode 100644 index 68a1b29f5dbc7..0000000000000 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ /dev/null @@ -1,410 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# preserve HermOrSym wrapper -# Call `copytrito!` instead of `copy_similar` to only copy the matching triangular half -eigencopy_oftype(A::Hermitian, S) = Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) -eigencopy_oftype(A::Symmetric, S) = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) -eigencopy_oftype(A::Symmetric{<:Complex}, S) = copyto!(similar(parent(A), S), A) - -default_eigen_alg(A) = DivideAndConquer() - -# Eigensolvers for symmetric and Hermitian matrices -function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) - if alg === DivideAndConquer() - Eigen(sorteig!(LAPACK.syevd!('V', A.uplo, A.data)..., sortby)...) - elseif alg === QRIteration() - Eigen(sorteig!(LAPACK.syev!('V', A.uplo, A.data)..., sortby)...) - elseif alg === RobustRepresentations() - Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) - else - throw(ArgumentError("Unsupported value for `alg` keyword.")) - end -end - -""" - eigen(A::Union{Hermitian, Symmetric}, alg::Algorithm = default_eigen_alg(A)) -> Eigen - -Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` -which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the -matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -`alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: -- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd!`. -- `alg = QRIteration()`: Calls `LAPACK.syev!`. -- `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. - -See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for -a comparison of the accuracy and performance of different algorithms. - -The default `alg` used may change in the future. - -!!! compat "Julia 1.12" - The `alg` keyword argument requires Julia 1.12 or later. - -The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). -""" -function eigen(A::RealHermSymComplexHerm, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) - _eigen(A, alg; sortby) -end - -# we dispatch on the eltype in an internal method to avoid ambiguities -function _eigen(A::RealHermSymComplexHerm, alg::Algorithm; sortby) - S = eigtype(eltype(A)) - eigen!(eigencopy_oftype(A, S), alg; sortby) -end - -function _eigen(A::RealHermSymComplexHerm{Float16}, alg::Algorithm; sortby::Union{Function,Nothing}=nothing) - S = eigtype(eltype(A)) - E = eigen!(eigencopy_oftype(A, S), alg, sortby=sortby) - values = convert(AbstractVector{Float16}, E.values) - vectors = convert(AbstractMatrix{isreal(E.vectors) ? Float16 : Complex{Float16}}, E.vectors) - return Eigen(values, vectors) -end - -eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = - Eigen(LAPACK.syevr!('V', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)...) - -""" - eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> Eigen - -Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` -which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the -matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). - -The [`UnitRange`](@ref) `irange` specifies indices of the sorted eigenvalues to search for. - -!!! note - If `irange` is not `1:n`, where `n` is the dimension of `A`, then the returned factorization - will be a *truncated* factorization. -""" -function eigen(A::RealHermSymComplexHerm, irange::UnitRange) - S = eigtype(eltype(A)) - eigen!(eigencopy_oftype(A, S), irange) -end - -eigen!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} = - Eigen(LAPACK.syevr!('V', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)...) - -""" - eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> Eigen - -Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` -which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the -matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) - -Iterating the decomposition produces the components `F.values` and `F.vectors`. - -The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). - -`vl` is the lower bound of the window of eigenvalues to search for, and `vu` is the upper bound. - -!!! note - If [`vl`, `vu`] does not contain all eigenvalues of `A`, then the returned factorization - will be a *truncated* factorization. -""" -function eigen(A::RealHermSymComplexHerm, vl::Real, vh::Real) - S = eigtype(eltype(A)) - eigen!(eigencopy_oftype(A, S), vl, vh) -end - - -function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) - vals::Vector{real(eltype(A))} = if alg === DivideAndConquer() - LAPACK.syevd!('N', A.uplo, A.data) - elseif alg === QRIteration() - LAPACK.syev!('N', A.uplo, A.data) - elseif alg === RobustRepresentations() - LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] - else - throw(ArgumentError("Unsupported value for `alg` keyword.")) - end - !isnothing(sortby) && sort!(vals, by=sortby) - return vals -end - -""" - eigvals(A::Union{Hermitian, Symmetric}, alg::Algorithm = default_eigen_alg(A))) -> values - -Return the eigenvalues of `A`. - -`alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: -- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd!`. -- `alg = QRIteration()`: Calls `LAPACK.syev!`. -- `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. - -See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for -a comparison of the accuracy and performance of different methods. - -The default `alg` used may change in the future. -""" -function eigvals(A::RealHermSymComplexHerm, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) - S = eigtype(eltype(A)) - eigvals!(eigencopy_oftype(A, S), alg; sortby) -end - - -""" - eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values - -Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. -`irange` is a range of eigenvalue *indices* to search for - for instance, the 2nd to 8th eigenvalues. -""" -eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = - LAPACK.syevr!('N', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)[1] - -""" - eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values - -Return the eigenvalues of `A`. It is possible to calculate only a subset of the -eigenvalues by specifying a [`UnitRange`](@ref) `irange` covering indices of the sorted eigenvalues, -e.g. the 2nd to 8th eigenvalues. - -# Examples -```jldoctest -julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 1.0 2.0 ⋅ - 2.0 2.0 3.0 - ⋅ 3.0 1.0 - -julia> eigvals(A, 2:2) -1-element Vector{Float64}: - 0.9999999999999996 - -julia> eigvals(A) -3-element Vector{Float64}: - -2.1400549446402604 - 1.0000000000000002 - 5.140054944640259 -``` -""" -function eigvals(A::RealHermSymComplexHerm, irange::UnitRange) - S = eigtype(eltype(A)) - eigvals!(eigencopy_oftype(A, S), irange) -end - -""" - eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values - -Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. -`vl` is the lower bound of the interval to search for eigenvalues, and `vu` is the upper bound. -""" -eigvals!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} = - LAPACK.syevr!('N', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)[1] - -""" - eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values - -Return the eigenvalues of `A`. It is possible to calculate only a subset of the eigenvalues -by specifying a pair `vl` and `vu` for the lower and upper boundaries of the eigenvalues. - -# Examples -```jldoctest -julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 1.0 2.0 ⋅ - 2.0 2.0 3.0 - ⋅ 3.0 1.0 - -julia> eigvals(A, -1, 2) -1-element Vector{Float64}: - 1.0000000000000009 - -julia> eigvals(A) -3-element Vector{Float64}: - -2.1400549446402604 - 1.0000000000000002 - 5.140054944640259 -``` -""" -function eigvals(A::RealHermSymComplexHerm, vl::Real, vh::Real) - S = eigtype(eltype(A)) - eigvals!(eigencopy_oftype(A, S), vl, vh) -end - -eigmax(A::RealHermSymComplexHerm{<:Real}) = eigvals(A, size(A, 1):size(A, 1))[1] -eigmin(A::RealHermSymComplexHerm{<:Real}) = eigvals(A, 1:1)[1] - -function eigen(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} - S = promote_type(eigtype(TA), TB) - return eigen!(eigencopy_oftype(A, S), eigencopy_oftype(B, S); kws...) -end - -function eigen!(A::HermOrSym{T,S}, B::HermOrSym{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasReal,S<:StridedMatrix} - vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data')) - GeneralizedEigen(sorteig!(vals, vecs, sortby)...) -end -function eigen!(A::Hermitian{T,S}, B::Hermitian{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasComplex,S<:StridedMatrix} - vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data')) - GeneralizedEigen(sorteig!(vals, vecs, sortby)...) -end - -function eigen(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) - if ishermitian(A) - eigen!(eigencopy_oftype(Hermitian(A), eigtype(eltype(A))), C; sortby) - else - eigen!(copy_similar(A, eigtype(eltype(A))), C; sortby) - end -end -function eigen!(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) - # Cholesky decomposition based eigenvalues and eigenvectors - vals, w = eigen!(UtiAUi!(A, C.U)) - vecs = C.U \ w - GeneralizedEigen(sorteig!(vals, vecs, sortby)...) -end - -# Bunch-Kaufmann (LDLT) based solution for generalized eigenvalues and eigenvectors -function eigen(A::StridedMatrix{T}, B::BunchKaufman{T,<:AbstractMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasFloat} - eigen!(copy(A), copy(B); sortby) -end -function eigen!(A::StridedMatrix{T}, B::BunchKaufman{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasFloat} - M, TD, p = getproperties!(B) - # Compute generalized eigenvalues of equivalent matrix: - # A' = inv(Tridiagonal(dl,d,du))*inv(M)*P*A*P'*inv(M') - # See: https://github.com/JuliaLang/julia/pull/50471#issuecomment-1627836781 - permutecols!(A, p) - permuterows!(A, p) - ldiv!(M, A) - rdiv!(A, M') - ldiv!(TD, A) - vals, vecs = eigen!(A; sortby) - # Compute generalized eigenvectors from 'vecs': - # vecs = P'*inv(M')*vecs - # See: https://github.com/JuliaLang/julia/pull/50471#issuecomment-1627836781 - M = B.uplo == 'U' ? UnitUpperTriangular{eltype(vecs)}(M) : UnitLowerTriangular{eltype(vecs)}(M) ; - ldiv!(M', vecs) - invpermuterows!(vecs, p) - GeneralizedEigen(sorteig!(vals, vecs, sortby)...) -end - -# LU based solution for generalized eigenvalues and eigenvectors -function eigen(A::StridedMatrix{T}, F::LU{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T} - return eigen!(copy(A), copy(F); sortby) -end -function eigen!(A::StridedMatrix{T}, F::LU{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T} - L = UnitLowerTriangular(F.L) - U = UpperTriangular(F.U) - permuterows!(A, F.p) - ldiv!(L, A) - rdiv!(A, U) - vals, vecs = eigen!(A; sortby) - # Compute generalized eigenvectors from 'vecs': - # vecs = P'*inv(M')*vecs - # See: https://github.com/JuliaLang/julia/pull/50471#issuecomment-1627836781 - U = UpperTriangular{eltype(vecs)}(U) - ldiv!(U, vecs) - GeneralizedEigen(sorteig!(vals, vecs, sortby)...) -end - -# Perform U' \ A / U in-place, where U::Union{UpperTriangular,Diagonal} -UtiAUi!(A, U) = _UtiAUi!(A, U) -UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), sym_uplo(A.uplo)) -UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), sym_uplo(A.uplo)) -_UtiAUi!(A, U) = rdiv!(ldiv!(U', A), U) - -function eigvals(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} - S = promote_type(eigtype(TA), TB) - return eigvals!(eigencopy_oftype(A, S), eigencopy_oftype(B, S); kws...) -end - -function eigvals!(A::HermOrSym{T,S}, B::HermOrSym{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasReal,S<:StridedMatrix} - vals = LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data'))[1] - isnothing(sortby) || sort!(vals, by=sortby) - return vals -end -function eigvals!(A::Hermitian{T,S}, B::Hermitian{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasComplex,S<:StridedMatrix} - vals = LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data'))[1] - isnothing(sortby) || sort!(vals, by=sortby) - return vals -end -eigvecs(A::HermOrSym) = eigvecs(eigen(A)) - -function eigvals(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) - if ishermitian(A) - eigvals!(eigencopy_oftype(Hermitian(A), eigtype(eltype(A))), C; sortby) - else - eigvals!(copy_similar(A, eigtype(eltype(A))), C; sortby) - end -end -function eigvals!(A::AbstractMatrix{T}, C::Cholesky{T, <:AbstractMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:Number} - # Cholesky decomposition based eigenvalues - return eigvals!(UtiAUi!(A, C.U); sortby) -end - -# Bunch-Kaufmann (LDLT) based solution for generalized eigenvalues -function eigvals(A::StridedMatrix{T}, B::BunchKaufman{T,<:AbstractMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasFloat} - eigvals!(copy(A), copy(B); sortby) -end -function eigvals!(A::StridedMatrix{T}, B::BunchKaufman{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasFloat} - M, TD, p = getproperties!(B) - # Compute generalized eigenvalues of equivalent matrix: - # A' = inv(Tridiagonal(dl,d,du))*inv(M)*P*A*P'*inv(M') - # See: https://github.com/JuliaLang/julia/pull/50471#issuecomment-1627836781 - permutecols!(A, p) - permuterows!(A, p) - ldiv!(M, A) - rdiv!(A, M') - ldiv!(TD, A) - return eigvals!(A; sortby) -end - -# LU based solution for generalized eigenvalues -function eigvals(A::StridedMatrix{T}, F::LU{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T} - return eigvals!(copy(A), copy(F); sortby) -end -function eigvals!(A::StridedMatrix{T}, F::LU{T,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) where {T} - L = UnitLowerTriangular(F.L) - U = UpperTriangular(F.U) - # Compute generalized eigenvalues of equivalent matrix: - # A' = inv(L)*(P*A)*inv(U) - # See: https://github.com/JuliaLang/julia/pull/50471#issuecomment-1627836781 - permuterows!(A, F.p) - ldiv!(L, A) - rdiv!(A, U) - return eigvals!(A; sortby) -end - -eigen(A::Hermitian{<:Complex, <:Tridiagonal}; kwargs...) = - _eigenhermtridiag(A; kwargs...) -# disambiguation -function eigen(A::Hermitian{Complex{Float16}, <:Tridiagonal}; kwargs...) - E = _eigenhermtridiag(A; kwargs...) - values = convert(AbstractVector{Float16}, E.values) - vectors = convert(AbstractMatrix{ComplexF16}, E.vectors) - return Eigen(values, vectors) -end -function _eigenhermtridiag(A::Hermitian{<:Complex,<:Tridiagonal}; kwargs...) - (; dl, d, du) = parent(A) - N = length(d) - if N <= 1 - eigen(parent(A); kwargs...) - else - if A.uplo == 'U' - E = du' - Er = abs.(du) - else - E = dl - Er = abs.(E) - end - S = Vector{eigtype(eltype(A))}(undef, N) - S[1] = 1 - for i ∈ 1:N-1 - S[i+1] = iszero(Er[i]) ? oneunit(eltype(S)) : S[i] * sign(E[i]) - end - B = SymTridiagonal(float.(real.(d)), Er) - Λ, Φ = eigen(B; kwargs...) - return Eigen(Λ, Diagonal(S) * Φ) - end -end - -function eigvals(A::Hermitian{Complex{T}, <:Tridiagonal}; kwargs...) where {T} - (; dl, d, du) = parent(A) - Er = A.uplo == 'U' ? abs.(du) : abs.(dl) - eigvals(SymTridiagonal(float.(real.(d)), Er); kwargs...) -end diff --git a/stdlib/LinearAlgebra/src/transpose.jl b/stdlib/LinearAlgebra/src/transpose.jl deleted file mode 100644 index a36919b2e557a..0000000000000 --- a/stdlib/LinearAlgebra/src/transpose.jl +++ /dev/null @@ -1,257 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -adjoint(a::AbstractArray) = error("adjoint not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") -transpose(a::AbstractArray) = error("transpose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") - -## Matrix transposition ## - -""" - transpose!(dest,src) - -Transpose array `src` and store the result in the preallocated array `dest`, which should -have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is -supported and unexpected results will happen if `src` and `dest` have overlapping memory -regions. - -# Examples -```jldoctest -julia> A = [3+2im 9+2im; 8+7im 4+6im] -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 8+7im 4+6im - -julia> B = zeros(Complex{Int64}, 2, 2) -2×2 Matrix{Complex{Int64}}: - 0+0im 0+0im - 0+0im 0+0im - -julia> transpose!(B, A); - -julia> B -2×2 Matrix{Complex{Int64}}: - 3+2im 8+7im - 9+2im 4+6im - -julia> A -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 8+7im 4+6im -``` -""" -transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) - -""" - adjoint!(dest,src) - -Conjugate transpose array `src` and store the result in the preallocated array `dest`, which -should have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition -is supported and unexpected results will happen if `src` and `dest` have overlapping memory -regions. - -# Examples -```jldoctest -julia> A = [3+2im 9+2im; 8+7im 4+6im] -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 8+7im 4+6im - -julia> B = zeros(Complex{Int64}, 2, 2) -2×2 Matrix{Complex{Int64}}: - 0+0im 0+0im - 0+0im 0+0im - -julia> adjoint!(B, A); - -julia> B -2×2 Matrix{Complex{Int64}}: - 3-2im 8-7im - 9-2im 4-6im - -julia> A -2×2 Matrix{Complex{Int64}}: - 3+2im 9+2im - 8+7im 4+6im -``` -""" -adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) - -@noinline function check_transpose_axes(axesA, axesB) - axesB == reverse(axesA) || throw(DimensionMismatch("axes of the destination are incompatible with that of the source")) -end - -function transpose!(B::AbstractVector, A::AbstractMatrix) - check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) - copyto!(B, A) -end -function transpose!(B::AbstractMatrix, A::AbstractVector) - check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) - copyto!(B, A) -end -function adjoint!(B::AbstractVector, A::AbstractMatrix) - check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) - ccopy!(B, A) -end -function adjoint!(B::AbstractMatrix, A::AbstractVector) - check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) - ccopy!(B, A) -end - -const transposebaselength=64 -function transpose_f!(f, B::AbstractMatrix, A::AbstractMatrix) - inds = axes(A) - check_transpose_axes(axes(B), inds) - - m, n = length(inds[1]), length(inds[2]) - if m*n<=4*transposebaselength - @inbounds begin - for j = inds[2] - for i = inds[1] - B[j,i] = f(A[i,j]) - end - end - end - else - transposeblock!(f,B,A,m,n,first(inds[1])-1,first(inds[2])-1) - end - return B -end -function transposeblock!(f, B::AbstractMatrix, A::AbstractMatrix, m::Int, n::Int, offseti::Int, offsetj::Int) - if m*n<=transposebaselength - @inbounds begin - for j = offsetj .+ (1:n) - for i = offseti .+ (1:m) - B[j,i] = f(A[i,j]) - end - end - end - elseif m>n - newm=m>>1 - transposeblock!(f,B,A,newm,n,offseti,offsetj) - transposeblock!(f,B,A,m-newm,n,offseti+newm,offsetj) - else - newn=n>>1 - transposeblock!(f,B,A,m,newn,offseti,offsetj) - transposeblock!(f,B,A,m,n-newn,offseti,offsetj+newn) - end - return B -end - -function ccopy!(B, A) - RB, RA = eachindex(B), eachindex(A) - if RB == RA - for i = RB - B[i] = adjoint(A[i]) - end - else - for (i,j) = zip(RB, RA) - B[i] = adjoint(A[j]) - end - end - return B -end - -""" - copy(A::Transpose) - copy(A::Adjoint) - -Eagerly evaluate the lazy matrix transpose/adjoint. -Note that the transposition is applied recursively to elements. - -This operation is intended for linear algebra usage - for general data manipulation see -[`permutedims`](@ref Base.permutedims), which is non-recursive. - -# Examples -```jldoctest -julia> A = [1 2im; -3im 4] -2×2 Matrix{Complex{Int64}}: - 1+0im 0+2im - 0-3im 4+0im - -julia> T = transpose(A) -2×2 transpose(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: - 1+0im 0-3im - 0+2im 4+0im - -julia> copy(T) -2×2 Matrix{Complex{Int64}}: - 1+0im 0-3im - 0+2im 4+0im -``` -""" -copy(::Union{Transpose,Adjoint}) - -Base.copy(A::TransposeAbsMat) = transpose!(similar(A.parent, reverse(axes(A.parent))), A.parent) -Base.copy(A::AdjointAbsMat) = adjoint!(similar(A.parent, reverse(axes(A.parent))), A.parent) - -""" - copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, - A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) -> B - -Efficiently copy elements of matrix `A` to `B` with transposition as follows: - - B[ir_dest, jr_dest] = transpose(A)[jr_src, ir_src] - -The elements `B[ir_dest, jr_dest]` are overwritten. Furthermore, -the index range parameters must satisfy `length(ir_dest) == length(jr_src)` and -`length(jr_dest) == length(ir_src)`. -""" -copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, - A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) = - _copy_adjtrans!(B, ir_dest, jr_dest, A, ir_src, jr_src, transpose) - -""" - copy_adjoint!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, - A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) -> B - -Efficiently copy elements of matrix `A` to `B` with adjunction as follows: - - B[ir_dest, jr_dest] = adjoint(A)[jr_src, ir_src] - -The elements `B[ir_dest, jr_dest]` are overwritten. Furthermore, -the index range parameters must satisfy `length(ir_dest) == length(jr_src)` and -`length(jr_dest) == length(ir_src)`. -""" -copy_adjoint!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, - A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) = - _copy_adjtrans!(B, ir_dest, jr_dest, A, ir_src, jr_src, adjoint) - -function _copy_adjtrans!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, - A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}, - tfun::T) where {T} - if length(ir_dest) != length(jr_src) - throw(ArgumentError(LazyString("source and destination must have same size (got ", - length(jr_src)," and ",length(ir_dest),")"))) - end - if length(jr_dest) != length(ir_src) - throw(ArgumentError(LazyString("source and destination must have same size (got ", - length(ir_src)," and ",length(jr_dest),")"))) - end - @boundscheck checkbounds(B, ir_dest, jr_dest) - @boundscheck checkbounds(A, ir_src, jr_src) - idest = first(ir_dest) - for jsrc in jr_src - jdest = first(jr_dest) - for isrc in ir_src - B[idest,jdest] = tfun(A[isrc,jsrc]) - jdest += step(jr_dest) - end - idest += step(ir_dest) - end - return B -end - -function copy_similar(A::AdjOrTransAbsMat, ::Type{T}) where {T} - Ap = parent(A) - f! = inplace_adj_or_trans(A) - return f!(similar(Ap, T, reverse(axes(Ap))), Ap) -end - -function Base.copyto_unaliased!(deststyle::IndexStyle, dest::AbstractMatrix, srcstyle::IndexCartesian, src::AdjOrTransAbsMat) - if axes(dest) == axes(src) - f! = inplace_adj_or_trans(src) - f!(dest, parent(src)) - else - @invoke Base.copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) - end - return dest -end diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl deleted file mode 100644 index b602e08256afc..0000000000000 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ /dev/null @@ -1,2990 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -## Triangular - -# could be renamed to Triangular when that name has been fully deprecated -""" - AbstractTriangular - -Supertype of triangular matrix types such as [`LowerTriangular`](@ref), [`UpperTriangular`](@ref), -[`UnitLowerTriangular`](@ref) and [`UnitUpperTriangular`](@ref). -""" -abstract type AbstractTriangular{T} <: AbstractMatrix{T} end - -# First loop through all methods that don't need special care for upper/lower and unit diagonal -for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular) - @eval begin - struct $t{T,S<:AbstractMatrix{T}} <: AbstractTriangular{T} - data::S - - function $t{T,S}(data) where {T,S<:AbstractMatrix{T}} - require_one_based_indexing(data) - checksquare(data) - new{T,S}(data) - end - end - $t(A::$t) = A - $t{T}(A::$t{T}) where {T} = A - $t(A::AbstractMatrix) = $t{eltype(A), typeof(A)}(A) - $t{T}(A::AbstractMatrix) where {T} = $t(convert(AbstractMatrix{T}, A)) - $t{T}(A::$t) where {T} = $t(convert(AbstractMatrix{T}, A.data)) - - AbstractMatrix{T}(A::$t) where {T} = $t{T}(A) - AbstractMatrix{T}(A::$t{T}) where {T} = copy(A) - - size(A::$t) = size(A.data) - axes(A::$t) = axes(A.data) - - # For A<:AbstractTriangular, similar(A[, neweltype]) should yield a matrix with the same - # triangular type and underlying storage type as A. The following method covers these cases. - similar(A::$t, ::Type{T}) where {T} = $t(similar(parent(A), T)) - # On the other hand, similar(A, [neweltype,] shape...) should yield a matrix of the underlying - # storage type of A (not wrapped in a triangular type). The following method covers these cases. - similar(A::$t, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(A), T, dims) - - copy(A::$t) = $t(copy(A.data)) - Base.unaliascopy(A::$t) = $t(Base.unaliascopy(A.data)) - - real(A::$t{<:Complex}) = (B = real(A.data); $t(B)) - real(A::$t{<:Complex, <:StridedMaybeAdjOrTransMat}) = $t(real.(A)) - end -end - -""" - LowerTriangular(A::AbstractMatrix) - -Construct a `LowerTriangular` view of the matrix `A`. - -# Examples -```jldoctest -julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] -3×3 Matrix{Float64}: - 1.0 2.0 3.0 - 4.0 5.0 6.0 - 7.0 8.0 9.0 - -julia> LowerTriangular(A) -3×3 LowerTriangular{Float64, Matrix{Float64}}: - 1.0 ⋅ ⋅ - 4.0 5.0 ⋅ - 7.0 8.0 9.0 -``` -""" -LowerTriangular -""" - UpperTriangular(A::AbstractMatrix) - -Construct an `UpperTriangular` view of the matrix `A`. - -# Examples -```jldoctest -julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] -3×3 Matrix{Float64}: - 1.0 2.0 3.0 - 4.0 5.0 6.0 - 7.0 8.0 9.0 - -julia> UpperTriangular(A) -3×3 UpperTriangular{Float64, Matrix{Float64}}: - 1.0 2.0 3.0 - ⋅ 5.0 6.0 - ⋅ ⋅ 9.0 -``` -""" -UpperTriangular -""" - UnitLowerTriangular(A::AbstractMatrix) - -Construct a `UnitLowerTriangular` view of the matrix `A`. -Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref) -of `A` on its diagonal. - -# Examples -```jldoctest -julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] -3×3 Matrix{Float64}: - 1.0 2.0 3.0 - 4.0 5.0 6.0 - 7.0 8.0 9.0 - -julia> UnitLowerTriangular(A) -3×3 UnitLowerTriangular{Float64, Matrix{Float64}}: - 1.0 ⋅ ⋅ - 4.0 1.0 ⋅ - 7.0 8.0 1.0 -``` -""" -UnitLowerTriangular -""" - UnitUpperTriangular(A::AbstractMatrix) - -Construct an `UnitUpperTriangular` view of the matrix `A`. -Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref) -of `A` on its diagonal. - -# Examples -```jldoctest -julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] -3×3 Matrix{Float64}: - 1.0 2.0 3.0 - 4.0 5.0 6.0 - 7.0 8.0 9.0 - -julia> UnitUpperTriangular(A) -3×3 UnitUpperTriangular{Float64, Matrix{Float64}}: - 1.0 2.0 3.0 - ⋅ 1.0 6.0 - ⋅ ⋅ 1.0 -``` -""" -UnitUpperTriangular - -const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}} -const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}} -const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}} -const UnitUpperOrUnitLowerTriangular{T,S} = Union{UnitUpperTriangular{T,S}, UnitLowerTriangular{T,S}} - -uppertriangular(M) = UpperTriangular(M) -lowertriangular(M) = LowerTriangular(M) - -uppertriangular(U::UpperOrUnitUpperTriangular) = U -lowertriangular(U::LowerOrUnitLowerTriangular) = U - -Base.dataids(A::UpperOrLowerTriangular) = Base.dataids(A.data) - -imag(A::UpperTriangular) = UpperTriangular(imag(A.data)) -imag(A::LowerTriangular) = LowerTriangular(imag(A.data)) -imag(A::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) = imag.(A) -imag(A::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) = imag.(A) -function imag(A::UnitLowerTriangular) - L = LowerTriangular(A.data) - Lim = similar(L) # must be mutable to set diagonals to zero - Lim .= imag.(L) - for i in axes(Lim,1) - Lim[i,i] = zero(Lim[i,i]) - end - return Lim -end -function imag(A::UnitUpperTriangular) - U = UpperTriangular(A.data) - Uim = similar(U) # must be mutable to set diagonals to zero - Uim .= imag.(U) - for i in axes(Uim,1) - Uim[i,i] = zero(Uim[i,i]) - end - return Uim -end - -parent(A::UpperOrLowerTriangular) = A.data - -# For strided matrices, we may only loop over the filled triangle -copy(A::UpperOrLowerTriangular{<:Any, <:StridedMaybeAdjOrTransMat}) = copyto!(similar(A), A) - -# then handle all methods that requires specific handling of upper/lower and unit diagonal - -function full(A::Union{UpperTriangular,LowerTriangular}) - return _triangularize(A)(parent(A)) -end -function full(A::UnitUpperOrUnitLowerTriangular) - isupper = A isa UnitUpperTriangular - Ap = _triangularize(A)(parent(A), isupper ? 1 : -1) - diagview(Ap) .= diagview(A) - return Ap -end - -function full!(A::LowerTriangular) - B = A.data - tril!(B) - B -end -function full!(A::UnitLowerTriangular) - B = A.data - tril!(B) - for i in axes(A,1) - B[i,i] = oneunit(eltype(B)) - end - B -end -function full!(A::UpperTriangular) - B = A.data - triu!(B) - B -end -function full!(A::UnitUpperTriangular) - B = A.data - triu!(B) - for i in axes(A,1) - B[i,i] = oneunit(eltype(B)) - end - B -end - -_shouldforwardindex(U::UpperTriangular, row::Integer, col::Integer) = row <= col -_shouldforwardindex(U::LowerTriangular, row::Integer, col::Integer) = row >= col -_shouldforwardindex(U::UnitUpperTriangular, row::Integer, col::Integer) = row < col -_shouldforwardindex(U::UnitLowerTriangular, row::Integer, col::Integer) = row > col - -Base.isassigned(A::UpperOrLowerTriangular, i::Int, j::Int) = - _shouldforwardindex(A, i, j) ? isassigned(A.data, i, j) : true - -Base.isstored(A::UpperOrLowerTriangular, i::Int, j::Int) = - _shouldforwardindex(A, i, j) ? Base.isstored(A.data, i, j) : false - -@propagate_inbounds getindex(A::Union{UnitLowerTriangular{T}, UnitUpperTriangular{T}}, i::Int, j::Int) where {T} = - _shouldforwardindex(A, i, j) ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) -@propagate_inbounds getindex(A::Union{LowerTriangular, UpperTriangular}, i::Int, j::Int) = - _shouldforwardindex(A, i, j) ? A.data[i,j] : diagzero(A,i,j) - -_shouldforwardindex(U::UpperTriangular, b::BandIndex) = b.band >= 0 -_shouldforwardindex(U::LowerTriangular, b::BandIndex) = b.band <= 0 -_shouldforwardindex(U::UnitUpperTriangular, b::BandIndex) = b.band > 0 -_shouldforwardindex(U::UnitLowerTriangular, b::BandIndex) = b.band < 0 - -# these specialized getindex methods enable constant-propagation of the band -Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{UnitLowerTriangular{T}, UnitUpperTriangular{T}}, b::BandIndex) where {T} - _shouldforwardindex(A, b) ? A.data[b] : ifelse(b.band == 0, oneunit(T), zero(T)) -end -Base.@constprop :aggressive @propagate_inbounds function getindex(A::Union{LowerTriangular, UpperTriangular}, b::BandIndex) - _shouldforwardindex(A, b) ? A.data[b] : diagzero(A.data, b) -end - -_zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" -_zero_triangular_half_str(::Type{<:LowerOrUnitLowerTriangular}) = "upper" - -@noinline function throw_nonzeroerror(T, @nospecialize(x), i, j) - Ts = _zero_triangular_half_str(T) - Tn = nameof(T) - throw(ArgumentError( - lazy"cannot set index in the $Ts triangular part ($i, $j) of an $Tn matrix to a nonzero value ($x)")) -end -@noinline function throw_nononeerror(T, @nospecialize(x), i, j) - Tn = nameof(T) - throw(ArgumentError( - lazy"cannot set index on the diagonal ($i, $j) of an $Tn matrix to a non-unit value ($x)")) -end - -@propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer) - if i > j - iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) - else - A.data[i,j] = x - end - return A -end - -@propagate_inbounds function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer) - if i > j - iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) - elseif i == j - x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) - else - A.data[i,j] = x - end - return A -end - -@propagate_inbounds function setindex!(A::LowerTriangular, x, i::Integer, j::Integer) - if i < j - iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) - else - A.data[i,j] = x - end - return A -end - -@propagate_inbounds function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer) - if i < j - iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) - elseif i == j - x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) - else - A.data[i,j] = x - end - return A -end - -@noinline function throw_setindex_structuralzero_error(T, @nospecialize(x)) - Ts = _zero_triangular_half_str(T) - Tn = nameof(T) - throw(ArgumentError( - lazy"cannot set indices in the $Ts triangular part of an $Tn matrix to a nonzero value ($x)")) -end - -@inline function fill!(A::UpperTriangular, x) - iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) - for col in axes(A,2), row in firstindex(A,1):col - @inbounds A.data[row, col] = x - end - A -end -@inline function fill!(A::LowerTriangular, x) - iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) - for col in axes(A,2), row in col:lastindex(A,1) - @inbounds A.data[row, col] = x - end - A -end - -Base._reverse(A::UpperOrUnitUpperTriangular, dims::Integer) = reverse!(Matrix(A); dims) -Base._reverse(A::UpperTriangular, ::Colon) = LowerTriangular(reverse(A.data)) -Base._reverse(A::UnitUpperTriangular, ::Colon) = UnitLowerTriangular(reverse(A.data)) -Base._reverse(A::LowerOrUnitLowerTriangular, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::LowerTriangular, ::Colon) = UpperTriangular(reverse(A.data)) -Base._reverse(A::UnitLowerTriangular, ::Colon) = UnitUpperTriangular(reverse(A.data)) - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::Union{UpperTriangular,UnitUpperTriangular}, - i::Integer, j::Integer, s::AbstractString) - return i <= j ? s : Base.replace_with_centered_mark(s) -end -function Base.replace_in_print_matrix(A::Union{LowerTriangular,UnitLowerTriangular}, - i::Integer, j::Integer, s::AbstractString) - return i >= j ? s : Base.replace_with_centered_mark(s) -end - -istril(A::UnitLowerTriangular, k::Integer=0) = k >= 0 -istriu(A::UnitUpperTriangular, k::Integer=0) = k <= 0 -Base.@constprop :aggressive function istril(A::LowerTriangular, k::Integer=0) - k >= 0 && return true - return _istril(A, k) -end -# additional indirection to dispatch to optimized method for banded parents (defined in special.jl) -@inline function _istril(A::LowerTriangular, k) - P = parent(A) - for j in max(firstindex(P,2), k + 2):lastindex(P,2) - _iszero(@view P[max(j, begin):min(j - k - 1, end), j]) || return false - end - return true -end - -Base.@constprop :aggressive function istriu(A::UpperTriangular, k::Integer=0) - k <= 0 && return true - return _istriu(A, k) -end -# additional indirection to dispatch to optimized method for banded parents (defined in special.jl) -@inline function _istriu(A::UpperTriangular, k) - P = parent(A) - m = size(A, 1) - for j in firstindex(P,2):min(m + k - 1, lastindex(P,2)) - _iszero(@view P[max(begin, j - k + 1):min(j, end), j]) || return false - end - return true -end - -istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k) -istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k) -istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k) -istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k) - -function tril!(A::UpperTriangular{T}, k::Integer=0) where {T} - if k < 0 - fill!(A.data, zero(T)) - return A - elseif k == 0 - for j in axes(A.data,2), i in intersect(axes(A.data,1), 1:j-1) - A.data[i,j] = zero(T) - end - return A - else - return UpperTriangular(tril!(A.data,k)) - end -end -function triu!(A::UpperTriangular, k::Integer=0) - if k > 0 - for j in axes(A.data,2), i in intersect(axes(A.data,1), range(stop=j, length=k)) - A.data[i,j] = zero(eltype(A)) - end - end - return A -end - -function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T} - if k < 0 - fill!(A.data, zero(T)) - return UpperTriangular(A.data) - elseif k == 0 - fill!(A.data, zero(T)) - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(T) - end - return UpperTriangular(A.data) - else - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(T) - end - return UpperTriangular(tril!(A.data,k)) - end -end - -function triu!(A::UnitUpperTriangular, k::Integer=0) - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(eltype(A)) - end - return triu!(UpperTriangular(A.data), k) -end - -function triu!(A::LowerTriangular{T}, k::Integer=0) where {T} - if k > 0 - fill!(A.data, zero(T)) - return A - elseif k == 0 - for j in axes(A.data,2), i in j+1:lastindex(A.data,1) - A.data[i,j] = zero(T) - end - return A - else - return LowerTriangular(triu!(A.data, k)) - end -end - -function tril!(A::LowerTriangular, k::Integer=0) - if k < 0 - for j in axes(A.data,2), i in intersect(range(j, length=-k), axes(A.data,1)) - A.data[i, j] = zero(eltype(A)) - end - end - A -end - -function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T - if k > 0 - fill!(A.data, zero(T)) - return LowerTriangular(A.data) - elseif k == 0 - fill!(A.data, zero(T)) - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(T) - end - return LowerTriangular(A.data) - else - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(T) - end - return LowerTriangular(triu!(A.data, k)) - end -end - -function tril!(A::UnitLowerTriangular, k::Integer=0) - for i in diagind(A.data, IndexStyle(A.data)) - A.data[i] = oneunit(eltype(A)) - end - return tril!(LowerTriangular(A.data), k) -end - -adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data)) -adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data)) -adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data)) -adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data)) -transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data)) -transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data)) -transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data)) -transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data)) - -transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L', false, true)) -transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L', false, false)) -transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U', false, true)) -transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U', false, false)) -adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true, true)) -adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true, false)) -adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true)) -adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false)) - -diag(A::UpperOrLowerTriangular) = diag(A.data) -diag(A::Union{UnitLowerTriangular, UnitUpperTriangular}) = fill(oneunit(eltype(A)), size(A,1)) - -# Unary operations --(A::LowerTriangular) = LowerTriangular(-A.data) --(A::UpperTriangular) = UpperTriangular(-A.data) -function -(A::UnitLowerTriangular) - Adata = A.data - Anew = similar(Adata) # must be mutable, even if Adata is not - @. Anew = -Adata - for i in axes(A, 1) - Anew[i, i] = -A[i, i] - end - LowerTriangular(Anew) -end -function -(A::UnitUpperTriangular) - Adata = A.data - Anew = similar(Adata) # must be mutable, even if Adata is not - @. Anew = -Adata - for i in axes(A, 1) - Anew[i, i] = -A[i, i] - end - UpperTriangular(Anew) -end - -# use broadcasting if the parents are strided, where we loop only over the triangular part -for TM in (:LowerTriangular, :UpperTriangular) - @eval -(A::$TM{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast(-, A) -end - -tr(A::UpperOrLowerTriangular) = tr(A.data) -tr(A::Union{UnitLowerTriangular, UnitUpperTriangular}) = size(A, 1) * oneunit(eltype(A)) - -for T in (:UpperOrUnitUpperTriangular, :LowerOrUnitLowerTriangular) - @eval @propagate_inbounds function copyto!(dest::$T, U::$T) - if axes(dest) != axes(U) - @invoke copyto!(dest::AbstractArray, U::AbstractArray) - else - _copyto!(dest, U) - end - return dest - end -end - -# copy and scale -for (T, UT) in ((:UpperTriangular, :UnitUpperTriangular), (:LowerTriangular, :UnitLowerTriangular)) - @eval @inline function _copyto!(A::$T, B::$T) - @boundscheck checkbounds(A, axes(B)...) - copytrito!(parent(A), parent(B), uplo_char(A)) - return A - end - @eval @inline function _copyto!(A::$UT, B::$T) - for dind in diagind(A, IndexStyle(A)) - if A[dind] != B[dind] - throw_nononeerror(typeof(A), B[dind], Tuple(dind)...) - end - end - _copyto!($T(parent(A)), B) - return A - end -end -@inline function _copyto!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular) - @boundscheck checkbounds(A, axes(B)...) - B2 = Base.unalias(A, B) - Ap = parent(A) - B2p = parent(B2) - for j in axes(B2,2) - for i in firstindex(Ap,1):j-1 - @inbounds Ap[i,j] = B2p[i,j] - end - if A isa UpperTriangular # copy diagonal - @inbounds Ap[j,j] = B2[j,j] - end - end - return A -end -@inline function _copyto!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular) - @boundscheck checkbounds(A, axes(B)...) - B2 = Base.unalias(A, B) - Ap = parent(A) - B2p = parent(B2) - for j in axes(B2,2) - if A isa LowerTriangular # copy diagonal - @inbounds Ap[j,j] = B2[j,j] - end - for i in j+1:lastindex(Ap,1) - @inbounds Ap[i,j] = B2p[i,j] - end - end - return A -end - -_triangularize(::UpperOrUnitUpperTriangular) = triu -_triangularize(::LowerOrUnitLowerTriangular) = tril -_triangularize!(::UpperOrUnitUpperTriangular) = triu! -_triangularize!(::LowerOrUnitLowerTriangular) = tril! - -@propagate_inbounds function copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular) - if axes(dest) != axes(U) - @invoke copyto!(dest::StridedMatrix, U::AbstractArray) - else - _copyto!(dest, U) - end - return dest -end -@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular) - copytrito!(dest, parent(U), U isa UpperOrUnitUpperTriangular ? 'U' : 'L') - copytrito!(dest, U, U isa UpperOrUnitUpperTriangular ? 'L' : 'U') - return dest -end -@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular{<:Any, <:StridedMatrix}) - U2 = Base.unalias(dest, U) - copyto_unaliased!(dest, U2) - return dest -end -# for strided matrices, we explicitly loop over the arrays to improve cache locality -# This fuses the copytrito! for the two halves -@inline function copyto_unaliased!(dest::StridedMatrix, U::UpperOrUnitUpperTriangular{<:Any, <:StridedMatrix}) - @boundscheck checkbounds(dest, axes(U)...) - isunit = U isa UnitUpperTriangular - for col in axes(dest,2) - for row in firstindex(dest,1):col-isunit - @inbounds dest[row,col] = U.data[row,col] - end - for row in col+!isunit:lastindex(dest,1) - @inbounds dest[row,col] = U[row,col] - end - end - return dest -end -@inline function copyto_unaliased!(dest::StridedMatrix, L::LowerOrUnitLowerTriangular{<:Any, <:StridedMatrix}) - @boundscheck checkbounds(dest, axes(L)...) - isunit = L isa UnitLowerTriangular - for col in axes(dest,2) - for row in firstindex(dest,1):col-!isunit - @inbounds dest[row,col] = L[row,col] - end - for row in col+isunit:lastindex(dest,1) - @inbounds dest[row,col] = L.data[row,col] - end - end - return dest -end - -Base.@constprop :aggressive function copytrito_triangular!(Bdata, Adata, uplo, uplomatch, sz) - if uplomatch - copytrito!(Bdata, Adata, uplo) - else - BLAS.chkuplo(uplo) - LAPACK.lacpy_size_check(size(Bdata), sz) - # only the diagonal is copied in this case - copyto!(diagview(Bdata), diagview(Adata)) - end - return Bdata -end - -function copytrito!(B::UpperTriangular, A::UpperTriangular, uplo::AbstractChar) - m,n = size(A) - copytrito_triangular!(B.data, A.data, uplo, uplo == 'U', (m, m < n ? m : n)) - return B -end -function copytrito!(B::LowerTriangular, A::LowerTriangular, uplo::AbstractChar) - m,n = size(A) - copytrito_triangular!(B.data, A.data, uplo, uplo == 'L', (n < m ? n : m, n)) - return B -end - -uppertridata(A) = A -lowertridata(A) = A -# we restrict these specializations only to strided matrices to avoid cases where an UpperTriangular type -# doesn't share its indexing with the parent -uppertridata(A::UpperTriangular{<:Any, <:StridedMatrix}) = parent(A) -lowertridata(A::LowerTriangular{<:Any, <:StridedMatrix}) = parent(A) - -@inline _rscale_add!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) = - @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta)) -@inline _lscale_add!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) = - @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta)) - -function checksize1(A, B) - szA, szB = size(A), size(B) - szA == szB || throw(DimensionMismatch(lazy"size of A, $szA, does not match size of B, $szB")) - checksquare(B) -end - -function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - for i in firstindex(B.data,1):j - @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) - end - end - return A -end -function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - for i in firstindex(B.data,1):j - @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) - end - end - return A -end -function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - @inbounds _modify!(_add, c, A, (j,j)) - for i in firstindex(B.data,1):(j - 1) - @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) - end - end - return A -end -function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - @inbounds _modify!(_add, c, A, (j,j)) - for i in firstindex(B.data,1):(j - 1) - @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) - end - end - return A -end -function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - for i in j:lastindex(B.data,1) - @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) - end - end - return A -end -function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - for i in j:lastindex(B.data,1) - @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) - end - end - return A -end -function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - @inbounds _modify!(_add, c, A, (j,j)) - for i in (j + 1):lastindex(B.data,1) - @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) - end - end - return A -end -function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add) - checksize1(A, B) - iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j in axes(B.data,2) - @inbounds _modify!(_add, c, A, (j,j)) - for i in (j + 1):lastindex(B.data,1) - @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) - end - end - return A -end - -function _trirdiv!(A::UpperTriangular, B::UpperOrUnitUpperTriangular, c::Number) - checksize1(A, B) - for j in axes(B,2) - for i in firstindex(B,1):j - @inbounds A[i, j] = B[i, j] / c - end - end - return A -end -function _trirdiv!(A::LowerTriangular, B::LowerOrUnitLowerTriangular, c::Number) - checksize1(A, B) - for j in axes(B,2) - for i in j:lastindex(B,1) - @inbounds A[i, j] = B[i, j] / c - end - end - return A -end -function _trildiv!(A::UpperTriangular, c::Number, B::UpperOrUnitUpperTriangular) - checksize1(A, B) - for j in axes(B,2) - for i in firstindex(B,1):j - @inbounds A[i, j] = c \ B[i, j] - end - end - return A -end -function _trildiv!(A::LowerTriangular, c::Number, B::LowerOrUnitLowerTriangular) - checksize1(A, B) - for j in axes(B,2) - for i in j:lastindex(B,1) - @inbounds A[i, j] = c \ B[i, j] - end - end - return A -end - -rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul()) -lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul()) - -function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector) - require_one_based_indexing(x, y) - m = size(A, 1) - (length(x) == m == length(y)) || throw(DimensionMismatch()) - if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - end - x₁ = x[1] - r = dot(x₁, A[1,1], y[1]) - @inbounds for j in axes(A, 2)[2:end] - yj = y[j] - if !iszero(yj) - temp = adjoint(A[1,j]) * x₁ - @simd for i in 2:j - temp += adjoint(A[i,j]) * x[i] - end - r += dot(temp, yj) - end - end - return r -end -function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector) - require_one_based_indexing(x, y) - m = size(A, 1) - (length(x) == m == length(y)) || throw(DimensionMismatch()) - if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - end - x₁ = first(x) - r = dot(x₁, y[1]) - @inbounds for j in axes(A, 2)[2:end] - yj = y[j] - if !iszero(yj) - temp = adjoint(A[1,j]) * x₁ - @simd for i in 2:j-1 - temp += adjoint(A[i,j]) * x[i] - end - r += dot(temp, yj) - r += dot(x[j], yj) - end - end - return r -end -function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector) - require_one_based_indexing(x, y) - m = size(A, 1) - (length(x) == m == length(y)) || throw(DimensionMismatch()) - if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - end - r = zero(typeof(dot(first(x), first(A), first(y)))) - @inbounds for j in axes(A, 2) - yj = y[j] - if !iszero(yj) - temp = adjoint(A[j,j]) * x[j] - @simd for i in j+1:lastindex(A,1) - temp += adjoint(A[i,j]) * x[i] - end - r += dot(temp, yj) - end - end - return r -end -function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector) - require_one_based_indexing(x, y) - m = size(A, 1) - (length(x) == m == length(y)) || throw(DimensionMismatch()) - if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - end - r = zero(typeof(dot(first(x), first(y)))) - @inbounds for j in axes(A, 2) - yj = y[j] - if !iszero(yj) - temp = x[j] - @simd for i in j+1:lastindex(A,1) - temp += adjoint(A[i,j]) * x[i] - end - r += dot(temp, yj) - end - end - return r -end - -fillstored!(A::LowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), 0); A) -fillstored!(A::UnitLowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), -1); A) -fillstored!(A::UpperTriangular, x) = (fillband!(A.data, x, 0, size(A,2)-1); A) -fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A) - -# Binary operations -# use broadcasting if the parents are strided, where we loop only over the triangular part -function +(A::UpperTriangular, B::UpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - UpperTriangular(A.data + B.data) -end -function +(A::LowerTriangular, B::LowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - LowerTriangular(A.data + B.data) -end -function +(A::UpperTriangular, B::UnitUpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - UpperTriangular(A.data + triu(B.data, 1) + I) -end -function +(A::LowerTriangular, B::UnitLowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - LowerTriangular(A.data + tril(B.data, -1) + I) -end -function +(A::UnitUpperTriangular, B::UpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - UpperTriangular(triu(A.data, 1) + B.data + I) -end -function +(A::UnitLowerTriangular, B::LowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - LowerTriangular(tril(A.data, -1) + B.data + I) -end -function +(A::UnitUpperTriangular, B::UnitUpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I) -end -function +(A::UnitLowerTriangular, B::UnitLowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .+ B - LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) -end -+(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) + full(B) -+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) + copyto!(similar(parent(B), size(B)), B) - -function -(A::UpperTriangular, B::UpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - UpperTriangular(A.data - B.data) -end -function -(A::LowerTriangular, B::LowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - LowerTriangular(A.data - B.data) -end -function -(A::UpperTriangular, B::UnitUpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - UpperTriangular(A.data - triu(B.data, 1) - I) -end -function -(A::LowerTriangular, B::UnitLowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - LowerTriangular(A.data - tril(B.data, -1) - I) -end -function -(A::UnitUpperTriangular, B::UpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - UpperTriangular(triu(A.data, 1) - B.data + I) -end -function -(A::UnitLowerTriangular, B::LowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - LowerTriangular(tril(A.data, -1) - B.data + I) -end -function -(A::UnitUpperTriangular, B::UnitUpperTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - UpperTriangular(triu(A.data, 1) - triu(B.data, 1)) -end -function -(A::UnitLowerTriangular, B::UnitLowerTriangular) - (parent(A) isa StridedMatrix || parent(B) isa StridedMatrix) && return A .- B - LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) -end --(A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = full(A) - full(B) --(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A), size(A)), A) - copyto!(similar(parent(B), size(B)), B) - -function kron(A::UpperTriangular{T,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{S,<:StridedMaybeAdjOrTransMat}) where {T,S} - C = UpperTriangular(Matrix{promote_op(*, T, S)}(undef, _kronsize(A, B))) - return kron!(C, A, B) -end -function kron(A::LowerTriangular{T,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{S,<:StridedMaybeAdjOrTransMat}) where {T,S} - C = LowerTriangular(Matrix{promote_op(*, T, S)}(undef, _kronsize(A, B))) - return kron!(C, A, B) -end - -function kron!(C::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, A::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) - size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) - _triukron!(C.data, A.data, B.data) - return C -end -function kron!(C::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, A::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) - size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) - _trilkron!(C.data, A.data, B.data) - return C -end - -function _triukron!(C, A, B) - n_B = size(B, 1) - @inbounds for j in axes(A,2) - jnB = (j - 1) * n_B - for i in firstindex(A,1):(j-1) - Aij = A[i, j] - inB = (i - 1) * n_B - for l in axes(B,2) - for k in firstindex(B,1):l - C[inB+k, jnB+l] = Aij * B[k, l] - end - for k in firstindex(B,1):(l-1) - C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) - end - end - end - Ajj = A[j, j] - for l in axes(B,2) - for k in firstindex(B,1):l - C[jnB+k, jnB+l] = Ajj * B[k, l] - end - end - end -end - -function _trilkron!(C, A, B) - n_A = size(A, 1) - n_B = size(B, 1) - @inbounds for j in axes(A,2) - jnB = (j - 1) * n_B - Ajj = A[j, j] - for l in axes(B,2) - for k in l:lastindex(B,1) - C[jnB+k, jnB+l] = Ajj * B[k, l] - end - end - for i in (j+1):n_A - Aij = A[i, j] - inB = (i - 1) * n_B - for l in axes(B,2) - for k in l:lastindex(B,1) - C[inB+k, jnB+l] = Aij * B[k, l] - end - for k in (l+1):lastindex(B,1) - C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) - end - end - end - end -end - -###################### -# BlasFloat routines # -###################### - -# which triangle to use of the underlying data -uplo_char(::UpperOrUnitUpperTriangular) = 'U' -uplo_char(::LowerOrUnitLowerTriangular) = 'L' -uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:AdjOrTrans}) = 'L' -uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:AdjOrTrans}) = 'U' -uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'U' -uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'L' -uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'U' -uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'L' - -isunit_char(::UpperTriangular) = 'N' -isunit_char(::UnitUpperTriangular) = 'U' -isunit_char(::LowerTriangular) = 'N' -isunit_char(::UnitLowerTriangular) = 'U' - -# generic fallback for AbstractTriangular matrices outside of the four subtypes provided here -_trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) = - lmul!(A, copyto!(C, B)) -_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) = - lmul!(A, copyto!(C, B)) -_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = - rmul!(copyto!(C, A), B) -_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) = - lmul!(A, copyto!(C, B)) -# redirect for UpperOrLowerTriangular -_trimul!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVector) = - generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) -_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractMatrix) = - generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) -_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = - generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) -_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = - generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) -# disambiguation with AbstractTriangular -_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) = - generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) -_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = - generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) - -function lmul!(A::AbstractTriangular, B::AbstractVecOrMat) - if istriu(A) - _trimul!(B, uppertriangular(A), B) - else - _trimul!(B, lowertriangular(A), B) - end -end -function rmul!(A::AbstractMatrix, B::AbstractTriangular) - if istriu(B) - _trimul!(A, A, uppertriangular(B)) - else - _trimul!(A, A, lowertriangular(B)) - end -end - -for TC in (:AbstractVector, :AbstractMatrix) - @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - if isone(alpha) && iszero(beta) - return _trimul!(C, A, B) - else - return _generic_matvecmul!(C, 'N', A, B, alpha, beta) - end - end -end -for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix), - (:AbstractMatrix, :AbstractTriangular), - (:AbstractTriangular, :AbstractTriangular) - ) - @eval @inline function _mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - if isone(alpha) && iszero(beta) - return _trimul!(C, A, B) - else - return generic_matmatmul!(C, 'N', 'N', A, B, alpha, beta) - end - end -end - -ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = _ldiv!(C, A, B) -# generic fallback for AbstractTriangular, directs to 2-arg [l/r]div! -_ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = - ldiv!(A, copyto!(C, B)) -_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = - rdiv!(copyto!(C, A), B) -# redirect for UpperOrLowerTriangular to generic_*div! -_ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) = - generic_trimatdiv!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) -_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = - generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) - -function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) - if istriu(A) - _ldiv!(B, uppertriangular(A), B) - else - _ldiv!(B, lowertriangular(A), B) - end -end -function rdiv!(A::AbstractMatrix, B::AbstractTriangular) - if istriu(B) - _rdiv!(A, A, uppertriangular(B)) - else - _rdiv!(A, A, lowertriangular(B)) - end -end - -# preserve triangular structure in in-place multiplication/division -for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), - (:UpperTriangular, :UpperTriangular, :UnitUpperTriangular), - (:UpperTriangular, :UnitUpperTriangular, :UpperTriangular), - (:UnitUpperTriangular, :UnitUpperTriangular, :UnitUpperTriangular), - (:LowerTriangular, :LowerTriangular, :LowerTriangular), - (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular), - (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular), - (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular)) - @eval begin - function _trimul!(C::$cty, A::$aty, B::$bty) - _trimul!(parent(C), A, B) - return C - end - function _ldiv!(C::$cty, A::$aty, B::$bty) - _ldiv!(parent(C), A, B) - return C - end - function _rdiv!(C::$cty, A::$aty, B::$bty) - _rdiv!(parent(C), A, B) - return C - end - end -end - -for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'), - (:UnitLowerTriangular, 'L', 'U'), - (:UpperTriangular, 'U', 'N'), - (:UnitUpperTriangular, 'U', 'U')) - @eval begin - # Matrix inverse - inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = - $t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data)) - - function inv(A::$t{T}) where {T} - S = typeof(inv(oneunit(T))) - if S <: BlasFloat || S === T # i.e. A is unitless - $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A)))) - else - J = (one(T)*I)(size(A, 1)) - $t(ldiv!(similar(A, S, size(A)), A, J)) - end - end - - # Error bounds for triangular solve - errorbounds(A::$t{T,<:StridedMatrix}, X::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.trrfs!($uploc, 'N', $isunitc, A.data, B, X) - - # Condition numbers - function cond(A::$t{<:BlasFloat,<:StridedMatrix}, p::Real=2) - checksquare(A) - if p == 1 - return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data)) - elseif p == Inf - return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data)) - else # use fallback - return cond(copyto!(similar(parent(A)), A), p) - end - end - end -end - -# multiplication -generic_trimatmul!(c::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, b::AbstractVector{T}) where {T<:BlasFloat} = - BLAS.trmv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, c === b ? c : copyto!(c, b)) -generic_trimatmul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} = - BLAS.trmm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B)) -generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = - BLAS.trmm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A)) -# division -generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = - LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B)) -generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = - BLAS.trsm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A)) - -errorbounds(A::AbstractTriangular{T}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} = - error("not implemented yet! Please submit a pull request.") -function errorbounds(A::AbstractTriangular{TA}, X::AbstractVecOrMat{TX}, B::AbstractVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number} - TAXB = promote_type(TA, TB, TX, Float32) - errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B)) -end - -# Eigensystems -## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine -function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix}) - LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) -end -function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix}) - for i in axes(A, 1) - A.data[i,i] = 1 - end - LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) -end -function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix}) - LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) -end -function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix}) - for i in axes(A, 1) - A.data[i,i] = 1 - end - LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) -end - -#################### -# Generic routines # -#################### - -for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), - (LowerTriangular, UnitLowerTriangular)) - tstrided = t{<:Any, <:StridedMaybeAdjOrTransMat} - @eval begin - (*)(A::$t, x::Number) = $t(A.data*x) - function (*)(A::$tstrided, x::Number) - eltype_dest = promote_op(*, eltype(A), typeof(x)) - dest = $t(similar(parent(A), eltype_dest)) - _triscale!(dest, x, A, MulAddMul()) - end - - function (*)(A::$unitt, x::Number) - B = $t(A.data)*x - for i in axes(A, 1) - B.data[i,i] = x - end - return B - end - - (*)(x::Number, A::$t) = $t(x*A.data) - function (*)(x::Number, A::$tstrided) - eltype_dest = promote_op(*, typeof(x), eltype(A)) - dest = $t(similar(parent(A), eltype_dest)) - _triscale!(dest, x, A, MulAddMul()) - end - - function (*)(x::Number, A::$unitt) - B = x*$t(A.data) - for i in axes(A, 1) - B.data[i,i] = x - end - return B - end - - (/)(A::$t, x::Number) = $t(A.data/x) - function (/)(A::$tstrided, x::Number) - eltype_dest = promote_op(/, eltype(A), typeof(x)) - dest = $t(similar(parent(A), eltype_dest)) - _trirdiv!(dest, A, x) - end - - function (/)(A::$unitt, x::Number) - B = $t(A.data)/x - invx = inv(x) - for i in axes(A, 1) - B.data[i,i] = invx - end - return B - end - - (\)(x::Number, A::$t) = $t(x\A.data) - function (\)(x::Number, A::$tstrided) - eltype_dest = promote_op(\, typeof(x), eltype(A)) - dest = $t(similar(parent(A), eltype_dest)) - _trildiv!(dest, x, A) - end - - function (\)(x::Number, A::$unitt) - B = x\$t(A.data) - invx = inv(x) - for i in axes(A, 1) - B.data[i,i] = invx - end - return B - end - end -end - -## Generic triangular multiplication -function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) - require_one_based_indexing(C, A, B) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - oA = oneunit(eltype(A)) - unit = isunitc == 'U' - @inbounds if uploc == 'U' - if tfun === identity - for j in axes(B,2) - for i in axes(B,1) - Cij = (unit ? oA : A[i,i]) * B[i,j] - for k in i + 1:lastindex(B,1) - Cij += A[i,k] * B[k,j] - end - C[i,j] = Cij - end - end - else # tfun in (transpose, adjoint) - for j in axes(B,2) - for i in reverse(axes(B,1)) - Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] - for k in firstindex(B,1):i - 1 - Cij += tfun(A[k,i]) * B[k,j] - end - C[i,j] = Cij - end - end - end - else # uploc == 'L' - if tfun === identity - for j in axes(B,2) - for i in reverse(axes(B,1)) - Cij = (unit ? oA : A[i,i]) * B[i,j] - for k in firstindex(B,1):i - 1 - Cij += A[i,k] * B[k,j] - end - C[i,j] = Cij - end - end - else # tfun in (transpose, adjoint) - for j in axes(B,2) - for i in axes(B,1) - Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] - for k in i + 1:lastindex(B,1) - Cij += tfun(A[k,i]) * B[k,j] - end - C[i,j] = Cij - end - end - end - end - return C -end -# conjugate cases -function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat) - require_one_based_indexing(C, xA, B) - check_A_mul_B!_sizes(size(C), size(xA), size(B)) - A = parent(xA) - oA = oneunit(eltype(A)) - unit = isunitc == 'U' - @inbounds if uploc == 'U' - for j in axes(B,2) - for i in axes(B,1) - Cij = (unit ? oA : conj(A[i,i])) * B[i,j] - for k in i + 1:lastindex(B,1) - Cij += conj(A[i,k]) * B[k,j] - end - C[i,j] = Cij - end - end - else # uploc == 'L' - for j in axes(B,2) - for i in reverse(axes(B,1)) - Cij = (unit ? oA : conj(A[i,i])) * B[i,j] - for k in firstindex(B,1):i - 1 - Cij += conj(A[i,k]) * B[k,j] - end - C[i,j] = Cij - end - end - end - return C -end - -function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) - require_one_based_indexing(C, A, B) - check_A_mul_B!_sizes(size(C), size(A), size(B)) - oB = oneunit(eltype(B)) - unit = isunitc == 'U' - @inbounds if uploc == 'U' - if tfun === identity - for i in axes(A,1) - for j in reverse(axes(A,2)) - Cij = A[i,j] * (unit ? oB : B[j,j]) - for k in firstindex(A,2):j - 1 - Cij += A[i,k] * B[k,j] - end - C[i,j] = Cij - end - end - else # tfun in (transpose, adjoint) - for i in axes(A,1) - for j in axes(A,2) - Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) - for k in j + 1:lastindex(A,2) - Cij += A[i,k] * tfun(B[j,k]) - end - C[i,j] = Cij - end - end - end - else # uploc == 'L' - if tfun === identity - for i in axes(A,1) - for j in axes(A,2) - Cij = A[i,j] * (unit ? oB : B[j,j]) - for k in j + 1:lastindex(A,2) - Cij += A[i,k] * B[k,j] - end - C[i,j] = Cij - end - end - else # tfun in (transpose, adjoint) - for i in axes(A,1) - for j in reverse(axes(A,2)) - Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) - for k in firstindex(A,2):j - 1 - Cij += A[i,k] * tfun(B[j,k]) - end - C[i,j] = Cij - end - end - end - end - return C -end -# conjugate cases -function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) - require_one_based_indexing(C, A, xB) - check_A_mul_B!_sizes(size(C), size(A), size(xB)) - B = parent(xB) - oB = oneunit(eltype(B)) - unit = isunitc == 'U' - @inbounds if uploc == 'U' - for i in axes(A,1) - for j in reverse(axes(A,2)) - Cij = A[i,j] * (unit ? oB : conj(B[j,j])) - for k in firstindex(A,2):j - 1 - Cij += A[i,k] * conj(B[k,j]) - end - C[i,j] = Cij - end - end - else # uploc == 'L' - for i in axes(A,1) - for j in axes(A,2) - Cij = A[i,j] * (unit ? oB : conj(B[j,j])) - for k in j + 1:lastindex(A,2) - Cij += A[i,k] * conj(B[k,j]) - end - C[i,j] = Cij - end - end - end - return C -end - -#Generic solver using naive substitution - -@inline _ustrip(a) = oneunit(a) \ a -@inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a - -# manually hoisting b[j] significantly improves performance as of Dec 2015 -# manually eliding bounds checking significantly improves performance as of Dec 2015 -# replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj] -# does not significantly impact performance as of Dec 2015 -# in the transpose and conjugate transpose naive substitution variants, -# accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015 -function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) - require_one_based_indexing(C, A, B) - mA, nA = size(A) - m = size(B, 1) - if nA != m - throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) - end - if size(C) != size(B) - throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))")) - end - iszero(mA) && return C - oA = oneunit(eltype(A)) - @inbounds if uploc == 'U' - if isunitc == 'N' - if tfun === identity - for k in axes(B,2) - amm = A[m,m] - iszero(amm) && throw(SingularException(m)) - Cm = C[m,k] = amm \ B[m,k] - # fill C-column - for i in reverse(axes(B,1))[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm - end - for j in reverse(axes(B,1))[2:end] - ajj = A[j,j] - iszero(ajj) && throw(SingularException(j)) - Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j-1:-1:firstindex(B,1) - C[i,k] -= _ustrip(A[i,j]) * Cj - end - end - end - else # tfun in (adjoint, transpose) - for k in axes(B,2) - for j in axes(B,1) - ajj = A[j,j] - iszero(ajj) && throw(SingularException(j)) - Bj = B[j,k] - for i in firstindex(A,1):j-1 - Bj -= tfun(A[i,j]) * C[i,k] - end - C[j,k] = tfun(ajj) \ Bj - end - end - end - else # isunitc == 'U' - if tfun === identity - for k in axes(B,2) - Cm = C[m,k] = oA \ B[m,k] - # fill C-column - for i in reverse(axes(B,1))[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm - end - for j in reverse(axes(B,1))[2:end] - Cj = C[j,k] - for i in firstindex(A,1):j-1 - C[i,k] -= _ustrip(A[i,j]) * Cj - end - end - end - else # tfun in (adjoint, transpose) - for k in axes(B,2) - for j in axes(B,1) - Bj = B[j,k] - for i in firstindex(A,1):j-1 - Bj -= tfun(A[i,j]) * C[i,k] - end - C[j,k] = oA \ Bj - end - end - end - end - else # uploc == 'L' - if isunitc == 'N' - if tfun === identity - for k in axes(B,2) - a11 = A[1,1] - iszero(a11) && throw(SingularException(1)) - C1 = C[1,k] = a11 \ B[1,k] - # fill C-column - for i in axes(B,1)[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 - end - for j in axes(B,1)[2:end] - ajj = A[j,j] - iszero(ajj) && throw(SingularException(j)) - Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j+1:lastindex(A,1) - C[i,k] -= _ustrip(A[i,j]) * Cj - end - end - end - else # tfun in (adjoint, transpose) - for k in axes(B,2) - for j in reverse(axes(B,1)) - ajj = A[j,j] - iszero(ajj) && throw(SingularException(j)) - Bj = B[j,k] - for i in j+1:lastindex(A,1) - Bj -= tfun(A[i,j]) * C[i,k] - end - C[j,k] = tfun(ajj) \ Bj - end - end - end - else # isunitc == 'U' - if tfun === identity - for k in axes(B,2) - C1 = C[1,k] = oA \ B[1,k] - # fill C-column - for i in axes(B,1)[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 - end - for j in axes(B,1)[2:end] - Cj = C[j,k] - for i in j+1:lastindex(A,1) - C[i,k] -= _ustrip(A[i,j]) * Cj - end - end - end - else # tfun in (adjoint, transpose) - for k in axes(B,2) - for j in reverse(axes(B,1)) - Bj = B[j,k] - for i in j+1:lastindex(A,1) - Bj -= tfun(A[i,j]) * C[i,k] - end - C[j,k] = oA \ Bj - end - end - end - end - end - return C -end -# conjugate cases -function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat) - A = parent(xA) - require_one_based_indexing(C, A, B) - mA, nA = size(A) - m = size(B, 1) - if nA != m - throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) - end - if size(C) != size(B) - throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))")) - end - iszero(mA) && return C - oA = oneunit(eltype(A)) - @inbounds if uploc == 'U' - if isunitc == 'N' - for k in axes(B,2) - amm = conj(A[m,m]) - iszero(amm) && throw(SingularException(m)) - Cm = C[m,k] = amm \ B[m,k] - # fill C-column - for i in reverse(axes(B,1))[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm - end - for j in reverse(axes(B,1))[2:end] - ajj = conj(A[j,j]) - iszero(ajj) && throw(SingularException(j)) - Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j-1:-1:firstindex(A,1) - C[i,k] -= _ustrip(conj(A[i,j])) * Cj - end - end - end - else # isunitc == 'U' - for k in axes(B,2) - Cm = C[m,k] = oA \ B[m,k] - # fill C-column - for i in reverse(axes(B,1))[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm - end - for j in reverse(axes(B,1))[2:end] - Cj = C[j,k] - for i in firstindex(A,1):j-1 - C[i,k] -= _ustrip(conj(A[i,j])) * Cj - end - end - end - end - else # uploc == 'L' - if isunitc == 'N' - for k in axes(B,2) - a11 = conj(A[1,1]) - iszero(a11) && throw(SingularException(1)) - C1 = C[1,k] = a11 \ B[1,k] - # fill C-column - for i in axes(B,1)[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 - end - for j in axes(A,2)[2:end] - ajj = conj(A[j,j]) - iszero(ajj) && throw(SingularException(j)) - Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j+1:lastindex(A,1) - C[i,k] -= _ustrip(conj(A[i,j])) * Cj - end - end - end - else # isunitc == 'U' - for k in axes(B,2) - C1 = C[1,k] = oA \ B[1,k] - # fill C-column - for i in axes(B,1)[2:end] - C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 - end - for j in axes(A,2) - Cj = C[j,k] - for i in j+1:lastindex(A,1) - C[i,k] -= _ustrip(conj(A[i,j])) * Cj - end - end - end - end - end - return C -end - -function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) - require_one_based_indexing(C, A, B) - n = size(A,2) - if size(B, 1) != n - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - if size(C) != size(A) - throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))")) - end - oB = oneunit(eltype(B)) - unit = isunitc == 'U' - @inbounds if uploc == 'U' - if tfun === identity - for i in axes(A,1) - for j in axes(A,2) - Aij = A[i,j] - for k in firstindex(B,1):j - 1 - Aij -= C[i,k]*B[k,j] - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : B[j,j]) - end - end - else # tfun in (adjoint, transpose) - for i in axes(A,1) - for j in reverse(axes(A,2)) - Aij = A[i,j] - for k in j + 1:lastindex(B,2) - Aij -= C[i,k]*tfun(B[j,k]) - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : tfun(B[j,j])) - end - end - end - else # uploc == 'L' - if tfun === identity - for i in axes(A,1) - for j in reverse(axes(A,2)) - Aij = A[i,j] - for k in j + 1:lastindex(B,1) - Aij -= C[i,k]*B[k,j] - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : B[j,j]) - end - end - else # tfun in (adjoint, transpose) - for i in axes(A,1) - for j in axes(A,2) - Aij = A[i,j] - for k in firstindex(B,2):j - 1 - Aij -= C[i,k]*tfun(B[j,k]) - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : tfun(B[j,j])) - end - end - end - end - return C -end -function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) - B = parent(xB) - require_one_based_indexing(C, A, B) - n = size(A,2) - if size(B, 1) != n - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))")) - end - if size(C) != size(A) - throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))")) - end - oB = oneunit(eltype(B)) - unit = isunitc == 'U' - if uploc == 'U' - @inbounds for i in axes(A,1) - for j in axes(A,2) - Aij = A[i,j] - for k in firstindex(B,1):j - 1 - Aij -= C[i,k]*conj(B[k,j]) - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : conj(B[j,j])) - end - end - else # uploc == 'L' - @inbounds for i in axes(A,1) - for j in reverse(axes(A,2)) - Aij = A[i,j] - for k in j + 1:lastindex(B,1) - Aij -= C[i,k]*conj(B[k,j]) - end - unit || (iszero(B[j,j]) && throw(SingularException(j))) - C[i,j] = Aij / (unit ? oB : conj(B[j,j])) - end - end - end - return C -end - -# these are needed because we don't keep track of left- and right-multiplication in tritrimul! -rmul!(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B)) -rmul!(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B)) -rmul!(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B)) -rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B)) - -# Promotion -## Promotion methods in matmul don't apply to triangular multiplication since -## it is inplace. Hence we have to make very similar definitions, but without -## allocation of a result array. For multiplication and unit diagonal division -## the element type doesn't have to be stable under division whereas that is -## necessary in the general triangular solve problem. - -_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} = - promote_op(matprod, TA, TB) -_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = - promote_op(op, TA, TB) -## The general promotion methods -for mat in (:AbstractVector, :AbstractMatrix) - ### Left division with triangle to the left hence rhs cannot be transposed. No quotients. - @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat) - require_one_based_indexing(B) - TAB = _inner_type_promotion(\, eltype(A), eltype(B)) - ldiv!(similar(B, TAB, size(B)), A, B) - end - ### Left division with triangle to the left hence rhs cannot be transposed. Quotients. - @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat) - require_one_based_indexing(B) - TAB = promote_op(\, eltype(A), eltype(B)) - ldiv!(similar(B, TAB, size(B)), A, B) - end - ### Right division with triangle to the right hence lhs cannot be transposed. No quotients. - @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular}) - require_one_based_indexing(A) - TAB = _inner_type_promotion(/, eltype(A), eltype(B)) - _rdiv!(similar(A, TAB, size(A)), A, B) - end - ### Right division with triangle to the right hence lhs cannot be transposed. Quotients. - @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular}) - require_one_based_indexing(A) - TAB = promote_op(/, eltype(A), eltype(B)) - _rdiv!(similar(A, TAB, size(A)), A, B) - end -end - -## Some Triangular-Triangular cases. We might want to write tailored methods -## for these cases, but I'm not sure it is worth it. -for f in (:*, :\) - @eval begin - ($f)(A::LowerTriangular, B::LowerTriangular) = - LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) - ($f)(A::LowerTriangular, B::UnitLowerTriangular) = - LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) - ($f)(A::UnitLowerTriangular, B::LowerTriangular) = - LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) - ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) = - UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) - ($f)(A::UpperTriangular, B::UpperTriangular) = - UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) - ($f)(A::UpperTriangular, B::UnitUpperTriangular) = - UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) - ($f)(A::UnitUpperTriangular, B::UpperTriangular) = - UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) - ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) = - UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) - end -end -(/)(A::LowerTriangular, B::LowerTriangular) = - LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) -(/)(A::LowerTriangular, B::UnitLowerTriangular) = - LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) -(/)(A::UnitLowerTriangular, B::LowerTriangular) = - LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) -(/)(A::UnitLowerTriangular, B::UnitLowerTriangular) = - UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) -(/)(A::UpperTriangular, B::UpperTriangular) = - UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) -(/)(A::UpperTriangular, B::UnitUpperTriangular) = - UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) -(/)(A::UnitUpperTriangular, B::UpperTriangular) = - UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) -(/)(A::UnitUpperTriangular, B::UnitUpperTriangular) = - UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) - -# Complex matrix power for upper triangular factor, see: -# Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix", -# SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056–1078. -# Higham and Lin, "An improved Schur-Padé algorithm for fractional powers of -# a matrix and their Fréchet derivatives", SIAM. J. Matrix Anal. & Appl., -# 34(3), (2013) 1341–1360. -function powm!(A0::UpperTriangular, p::Real) - if abs(p) >= 1 - throw(ArgumentError(lazy"p must be a real number in (-1,1), got $p")) - end - - normA0 = opnorm(A0, 1) - rmul!(A0, 1/normA0) - - theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1] - checksquare(A0) - - A, m, s = invsquaring(A0, theta) - A = I - A - - # Compute accurate diagonal of I - T - sqrt_diag!(A0, A, s) - for i in axes(A,1) - A[i, i] = -A[i, i] - end - # Compute the Padé approximant - c = 0.5 * (p - m) / (2 * m - 1) - triu!(A) - S = c * A - Stmp = similar(S) - for j in m-1:-1:1 - j4 = 4 * j - c = (-p - j) / (j4 + 2) - for i in axes(S,1) - @inbounds S[i, i] = S[i, i] + 1 - end - copyto!(Stmp, S) - mul!(S, A, c) - ldiv!(Stmp, S) - - c = (p - j) / (j4 - 2) - for i in axes(S,1) - @inbounds S[i, i] = S[i, i] + 1 - end - copyto!(Stmp, S) - mul!(S, A, c) - ldiv!(Stmp, S) - end - for i in axes(S,1) - S[i, i] = S[i, i] + 1 - end - copyto!(Stmp, S) - mul!(S, A, -p) - ldiv!(Stmp, S) - for i in axes(S,1) - @inbounds S[i, i] = S[i, i] + 1 - end - - blockpower!(A0, S, p/(2^s)) - for m = 1:s - mul!(Stmp.data, S, S) - copyto!(S, Stmp) - blockpower!(A0, S, p/(2^(s-m))) - end - rmul!(S, normA0^p) - return S -end -powm(A::LowerTriangular, p::Real) = copy(transpose(powm!(copy(transpose(A)), p::Real))) - -# Complex matrix logarithm for the upper triangular factor, see: -# Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for -# the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169. -# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix -# logarithm and estimating the condition number", SIAM J. Sci. Comput., -# 35(4), (2013), C394–C410. -# -# Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip, -# Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham -# Julia version relicensed with permission from original authors -log(A::UpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A) -log(A::UnitUpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A) -log(A::LowerTriangular) = copy(transpose(log(copy(transpose(A))))) -log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A))))) - -function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat - # allocate real A if log(A) will be real and complex A otherwise - checksquare(A0) - if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0))) - A = T <: Complex ? real(A0) : copy(A0) - else - A = T <: Complex ? copy(A0) : complex(A0) - end - if A0 isa UnitUpperTriangular - A = UpperTriangular(parent(A)) - @inbounds for i in axes(A,1) - A[i,i] = 1 - end - end - Y0 = _log_quasitriu!(A0, A) - # return complex result for complex input - Y = T <: Complex ? complex(Y0) : Y0 - - if A0 isa UpperTriangular || A0 isa UnitUpperTriangular - return UpperTriangular(Y) - else - return Y - end -end -# type-stable implementation of log_quasitriu -# A is a copy of A0 that is overwritten while computing the result. It has the same eltype -# as the result. -function _log_quasitriu!(A0, A) - # Find Padé degree m and s while replacing A with A^(1/2^s) - m, s = _find_params_log_quasitriu!(A) - - # Compute accurate superdiagonal of A - _pow_superdiag_quasitriu!(A, A0, 0.5^s) - - # Compute accurate block diagonal of A - _sqrt_pow_diag_quasitriu!(A, A0, s) - - # Get the Gauss-Legendre quadrature points and weights - R = zeros(Float64, m, m) - for i in 1:m - 1 - R[i,i+1] = i / sqrt((2 * i)^2 - 1) - R[i+1,i] = R[i,i+1] - end - x,V = eigen(R) - w = Vector{Float64}(undef, m) - for i in 1:m - x[i] = (x[i] + 1) / 2 - w[i] = V[1,i]^2 - end - - # Compute the Padé approximation - t = eltype(A) - n = size(A, 1) - Y = zeros(t, n, n) - B = similar(A) - for k in 1:m - B .= t(x[k]) .* A - @inbounds for i in axes(B,1) - B[i,i] += 1 - end - Y .+= t(w[k]) .* rdiv_quasitriu!(A, B) - end - - # Scale back - lmul!(2.0^s, Y) - - # Compute accurate diagonal and superdiagonal of log(A) - _log_diag_quasitriu!(Y, A0) - - return Y -end - -# Auxiliary functions for matrix logarithm and matrix power - -# Find Padé degree m and s while replacing A with A^(1/2^s) -# Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for -# the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169. -# from Algorithm 4.1 -function _find_params_log_quasitriu!(A) - maxsqrt = 100 - theta = [1.586970738772063e-005, - 2.313807884242979e-003, - 1.938179313533253e-002, - 6.209171588994762e-002, - 1.276404810806775e-001, - 2.060962623452836e-001, - 2.879093714241194e-001] - tmax = size(theta, 1) - p = 0 - m = 0 - - # Find s0, the smallest s such that the ρ(triu(A)^(1/2^s) - I) ≤ theta[tmax], where ρ(X) - # is the spectral radius of X - d = complex.(diagview(A)) - dm1 = d .- 1 - s = 0 - while norm(dm1, Inf) > theta[tmax] && s < maxsqrt - d .= sqrt.(d) - dm1 .= d .- 1 - s = s + 1 - end - s0 = s - - # Compute repeated roots - for k in 1:min(s, maxsqrt) - _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) - end - - # these three never needed at the same time, so reuse the same temporary - AmI = AmI4 = AmI5 = A - I - AmI2 = AmI * AmI - AmI3 = AmI2 * AmI - d2 = sqrt(opnorm(AmI2, 1)) - d3 = cbrt(opnorm(AmI3, 1)) - alpha2 = max(d2, d3) - foundm = false - if alpha2 <= theta[2] - m = alpha2 <= theta[1] ? 1 : 2 - foundm = true - end - - while !foundm - more_sqrt = false - mul!(AmI4, AmI2, AmI2) - d4 = opnorm(AmI4, 1)^(1/4) - alpha3 = max(d3, d4) - if alpha3 <= theta[tmax] - local j - for outer j = 3:tmax - if alpha3 <= theta[j] - break - end - end - if j <= 6 - m = j - break - elseif alpha3 / 2 <= theta[5] && p < 2 - more_sqrt = true - p = p + 1 - end - end - - if !more_sqrt - mul!(AmI5, AmI3, AmI2) - d5 = opnorm(AmI5, 1)^(1/5) - alpha4 = max(d4, d5) - eta = min(alpha3, alpha4) - if eta <= theta[tmax] - j = 0 - for outer j = 6:tmax - if eta <= theta[j] - m = j - break - end - end - break - end - end - - if s == maxsqrt - m = tmax - break - end - _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) - copyto!(AmI, A) - for i in axes(AmI,1) - @inbounds AmI[i,i] -= 1 - end - mul!(AmI2, AmI, AmI) - mul!(AmI3, AmI2, AmI) - d3 = cbrt(opnorm(AmI3, 1)) - s = s + 1 - end - return m, s -end - -# Compute accurate diagonal of A = A0^s - I -function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s) - checksquare(A0) - @inbounds for i in axes(A0,1) - a = complex(A0[i,i]) - A[i,i] = _sqrt_pow(a, s) - end -end -# Compute accurate block diagonal of A = A0^s - I for upper quasi-triangular A0 produced -# by the Schur decomposition. Diagonal is made of 1x1 and 2x2 blocks. -# 2x2 blocks are real with non-negative conjugate pair eigenvalues -function _sqrt_pow_diag_quasitriu!(A, A0, s) - n = checksquare(A0) - t = typeof(sqrt(zero(eltype(A)))) - i = 1 - @inbounds while i < n - if iszero(A0[i+1,i]) # 1x1 block - A[i,i] = _sqrt_pow(t(A0[i,i]), s) - i += 1 - else # real 2x2 block - @views _sqrt_pow_diag_block_2x2!(A[i:i+1,i:i+1], A0[i:i+1,i:i+1], s) - i += 2 - end - end - if i == n # last block is 1x1 - @inbounds A[n,n] = _sqrt_pow(t(A0[n,n]), s) - end - return A -end -# compute a^(1/2^s)-1 -# Al-Mohy, "A more accurate Briggs method for the logarithm", -# Numer. Algorithms, 59, (2012), 393–402. -# Algorithm 2 -function _sqrt_pow(a::Number, s) - T = typeof(sqrt(zero(a))) - s == 0 && return T(a) - 1 - s0 = s - if imag(a) >= 0 && real(a) <= 0 && !iszero(a) # angle(a) ≥ π / 2 - a = sqrt(a) - s0 = s - 1 - end - z0 = a - 1 - a = sqrt(a) - r = 1 + a - for j in 1:s0-1 - a = sqrt(a) - r = r * (1 + a) - end - return z0 / r -end -# compute A0 = A^(1/2^s)-I for 2x2 real matrices A and A0 -# A has non-negative conjugate pair eigenvalues -# "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm" -# SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553 -# Algorithm 5.1 -Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s) - if iszero(s) - A[1,1] -= 1 - A[2,2] -= 1 - return A - end - _sqrt_real_2x2!(A, A0) - if isone(s) - A[1,1] -= 1 - A[2,2] -= 1 - else - # Z = A - I - z11, z21, z12, z22 = A[1,1] - 1, A[2,1], A[1,2], A[2,2] - 1 - # A = sqrt(A) - _sqrt_real_2x2!(A, A) - # P = A + I - p11, p21, p12, p22 = A[1,1] + 1, A[2,1], A[1,2], A[2,2] + 1 - for i in 1:(s - 2) - # A = sqrt(A) - _sqrt_real_2x2!(A, A) - a11, a21, a12, a22 = A[1,1], A[2,1], A[1,2], A[2,2] - # P += P * A - r11 = p11*(1 + a11) + p12*a21 - r22 = p21*a12 + p22*(1 + a22) - p21 = p21*(1 + a11) + p22*a21 - p12 = p11*a12 + p12*(1 + a22) - p11 = r11 - p22 = r22 - end - # A = Z / P - c = inv(p11*p22 - p21*p12) - A[1,1] = (p22*z11 - p21*z12) * c - A[2,1] = (p22*z21 - p21*z22) * c - A[1,2] = (p11*z12 - p12*z11) * c - A[2,2] = (p11*z22 - p12*z21) * c - end - return A -end -# Compute accurate superdiagonal of A = A0^s - I for upper quasi-triangular A0 produced -# by a Schur decomposition. -# Higham and Lin, "A Schur–Padé Algorithm for Fractional Powers of a Matrix" -# SIAM J. Matrix Anal. Appl., 32(3), (2011), 1056–1078. -# Equation 5.6 -# see also blockpower for when A0 is upper triangular -function _pow_superdiag_quasitriu!(A, A0, p) - n = checksquare(A0) - t = eltype(A) - k = 1 - @inbounds while k < n - if !iszero(A[k+1,k]) - k += 2 - continue - end - if !(k == n - 1 || iszero(A[k+2,k+1])) - k += 3 - continue - end - Ak = t(A0[k,k]) - Akp1 = t(A0[k+1,k+1]) - - Akp = Ak^p - Akp1p = Akp1^p - - if Ak == Akp1 - A[k,k+1] = p * A0[k,k+1] * Ak^(p-1) - elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) - A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) - else - logAk = log(Ak) - logAkp1 = log(Akp1) - z = (Akp1 - Ak)/(Akp1 + Ak) - if abs(z) > 1 - A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) - else - w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) - dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak); - A[k,k+1] = A0[k,k+1] * dd - end - end - k += 1 - end -end - -# Compute accurate block diagonal and superdiagonal of A = log(A0) for upper -# quasi-triangular A0 produced by the Schur decomposition. -function _log_diag_quasitriu!(A, A0) - n = checksquare(A0) - t = eltype(A) - k = 1 - @inbounds while k < n - if iszero(A0[k+1,k]) # 1x1 block - Ak = t(A0[k,k]) - logAk = log(Ak) - A[k,k] = logAk - if k < n - 2 && iszero(A0[k+2,k+1]) - Akp1 = t(A0[k+1,k+1]) - logAkp1 = log(Akp1) - A[k+1,k+1] = logAkp1 - if Ak == Akp1 - A[k,k+1] = A0[k,k+1] / Ak - elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) - A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak) - else - z = (Akp1 - Ak)/(Akp1 + Ak) - if abs(z) > 1 - A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak) - else - w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) - A[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak) - end - end - k += 2 - else - k += 1 - end - else # real 2x2 block - @views _log_diag_block_2x2!(A[k:k+1,k:k+1], A0[k:k+1,k:k+1]) - k += 2 - end - end - if k == n # last 1x1 block - @inbounds A[n,n] = log(t(A0[n,n])) - end - return A -end -# compute A0 = log(A) for 2x2 real matrices A and A0, where A0 is a diagonal 2x2 block -# produced by real Schur decomposition. -# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix -# logarithm and estimating the condition number", SIAM J. Sci. Comput., -# 35(4), (2013), C394–C410. -# Eq. 6.1 -Base.@propagate_inbounds function _log_diag_block_2x2!(A, A0) - a, b, c = A0[1,1], A0[1,2], A0[2,1] - # avoid underflow/overflow for large/small b and c - s = sqrt(abs(b)) * sqrt(abs(c)) - θ = atan(s, a) - t = θ / s - au = abs(a) - if au > s - a1 = log1p((s / au)^2) / 2 + log(au) - else - a1 = log1p((au / s)^2) / 2 + log(s) - end - A[1,1] = a1 - A[2,1] = c*t - A[1,2] = b*t - A[2,2] = a1 - return A -end - -# Used only by powm at the moment -# Repeatedly compute the square roots of A so that in the end its -# eigenvalues are close enough to the positive real line -function invsquaring(A0::UpperTriangular, theta) - require_one_based_indexing(theta) - # assumes theta is in ascending order - maxsqrt = 100 - tmax = size(theta, 1) - checksquare(A0) - A = complex(copy(A0)) - p = 0 - m = 0 - - # Compute repeated roots - d = complex(diag(A)) - dm1 = d .- 1 - s = 0 - while norm(dm1, Inf) > theta[tmax] && s < maxsqrt - d .= sqrt.(d) - dm1 .= d .- 1 - s = s + 1 - end - s0 = s - for k in 1:min(s, maxsqrt) - A = sqrt(A) - end - - AmI = A - I - d2 = sqrt(opnorm(AmI^2, 1)) - d3 = cbrt(opnorm(AmI^3, 1)) - alpha2 = max(d2, d3) - foundm = false - if alpha2 <= theta[2] - m = alpha2 <= theta[1] ? 1 : 2 - foundm = true - end - - while !foundm - more = false - if s > s0 - d3 = cbrt(opnorm(AmI^3, 1)) - end - d4 = opnorm(AmI^4, 1)^(1/4) - alpha3 = max(d3, d4) - if alpha3 <= theta[tmax] - local j - for outer j = 3:tmax - if alpha3 <= theta[j] - break - elseif alpha3 / 2 <= theta[5] && p < 2 - more = true - p = p + 1 - end - end - if j <= 6 - m = j - foundm = true - break - elseif alpha3 / 2 <= theta[5] && p < 2 - more = true - p = p + 1 - end - end - - if !more - d5 = opnorm(AmI^5, 1)^(1/5) - alpha4 = max(d4, d5) - eta = min(alpha3, alpha4) - if eta <= theta[tmax] - j = 0 - for outer j = 6:tmax - if eta <= theta[j] - m = j - break - end - break - end - end - if s == maxsqrt - m = tmax - break - end - A = sqrt(A) - AmI = A - I - s = s + 1 - end - end - - # Compute accurate superdiagonal of T - p = 1 / 2^s - A = complex(A) - blockpower!(A, A0, p) - return A,m,s -end - -# Compute accurate diagonal and superdiagonal of A = A0^p -function blockpower!(A::UpperTriangular, A0::UpperTriangular, p) - checksquare(A0) - @inbounds for k in axes(A0,1)[1:end-1] - Ak = complex(A0[k,k]) - Akp1 = complex(A0[k+1,k+1]) - - Akp = Ak^p - Akp1p = Akp1^p - - A[k,k] = Akp - A[k+1,k+1] = Akp1p - - if Ak == Akp1 - A[k,k+1] = p * A0[k,k+1] * Ak^(p-1) - elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) - A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) - else - logAk = log(Ak) - logAkp1 = log(Akp1) - z = (Akp1 - Ak)/(Akp1 + Ak) - if abs(z) > 1 - A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) - else - w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) - dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak); - A[k,k+1] = A0[k,k+1] * dd - end - end - end -end - -# Unwinding number -unw(x::Real) = 0 -unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) - -# compute A / B for upper quasi-triangular B, possibly overwriting B -function rdiv_quasitriu!(A, B) - checksquare(A) - AG = copy(A) - # use Givens rotations to annihilate 2x2 blocks - @inbounds for k in axes(B,2)[1:end-1] - s = B[k+1,k] - iszero(s) && continue # 1x1 block - G = first(givens(B[k+1,k+1], s, k, k+1)) - rmul!(B, G) - rmul!(AG, G) - end - return rdiv!(AG, UpperTriangular(B)) -end - -# End of auxiliary functions for matrix logarithm and matrix power - -sqrt(A::UpperTriangular) = sqrt_quasitriu(A) -function sqrt(A::UnitUpperTriangular{T}) where T - B = A.data - t = typeof(sqrt(zero(T))) - R = Matrix{t}(I, size(A)) - tt = typeof(oneunit(t)*oneunit(t)) - half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214 - @inbounds for j in axes(B,2) - for i in j-1:-1:firstindex(B) - r::tt = B[i,j] - @simd for k in i+1:j-1 - r -= R[i,k]*R[k,j] - end - iszero(r) || (R[i,j] = half*r) - end - end - return UnitUpperTriangular(R) -end -sqrt(A::LowerTriangular) = copy(transpose(sqrt(copy(transpose(A))))) -sqrt(A::UnitLowerTriangular) = copy(transpose(sqrt(copy(transpose(A))))) - -# Auxiliary functions for matrix square root - -# square root of upper triangular or real upper quasitriangular matrix -function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256) - n = checksquare(A0) - T = eltype(A0) - Tr = typeof(sqrt(real(zero(T)))) - Tc = typeof(sqrt(complex(zero(T)))) - if isreal(A0) - is_sqrt_real = true - if istriu(A0) - for i in axes(A0,1) - Aii = real(A0[i,i]) - if Aii < zero(Aii) - is_sqrt_real = false - break - end - end - end - if is_sqrt_real - R = zeros(Tr, size(A0)) - A = real(A0) - else - R = zeros(Tc, size(A0)) - A = A0 - end - else - A = A0 - R = zeros(Tc, size(A0)) - end - _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n) - Rc = eltype(A0) <: Real ? R : complex(R) - if A0 isa UpperTriangular - return UpperTriangular(Rc) - elseif A0 isa UnitUpperTriangular - return UnitUpperTriangular(Rc) - else - return Rc - end -end - -# in-place recursive sqrt of upper quasi-triangular matrix A from -# Deadman E., Higham N.J., Ralha R. (2013) Blocked Schur Algorithms for Computing the Matrix -# Square Root. Applied Parallel and Scientific Computing. PARA 2012. Lecture Notes in -# Computer Science, vol 7782. https://doi.org/10.1007/978-3-642-36803-5_12 -function _sqrt_quasitriu!(R, A; blockwidth=64, n=checksquare(A)) - if n ≤ blockwidth || !(eltype(R) <: BlasFloat) # base case, perform "point" algorithm - _sqrt_quasitriu_block!(R, A) - else # compute blockwise recursion - split = div(n, 2) - iszero(A[split+1, split]) || (split += 1) # don't split 2x2 diagonal block - r1 = 1:split - r2 = (split + 1):n - n1, n2 = split, n - split - A11, A12, A22 = @views A[r1,r1], A[r1,r2], A[r2,r2] - R11, R12, R22 = @views R[r1,r1], R[r1,r2], R[r2,r2] - # solve diagonal blocks recursively - _sqrt_quasitriu!(R11, A11; blockwidth=blockwidth, n=n1) - _sqrt_quasitriu!(R22, A22; blockwidth=blockwidth, n=n2) - # solve off-diagonal block - R12 .= .- A12 - _sylvester_quasitriu!(R11, R22, R12; blockwidth=blockwidth, nA=n1, nB=n2, raise=false) - end - return R -end - -function _sqrt_quasitriu_block!(R, A) - _sqrt_quasitriu_diag_block!(R, A) - _sqrt_quasitriu_offdiag_block!(R, A) - return R -end - -function _sqrt_quasitriu_diag_block!(R, A) - n = size(R, 1) - ta = eltype(R) <: Complex ? complex(eltype(A)) : eltype(A) - i = 1 - @inbounds while i < n - if iszero(A[i + 1, i]) - R[i, i] = sqrt(ta(A[i, i])) - i += 1 - else - # This branch is never reached when A is complex triangular - @assert eltype(A) <: Real - @views _sqrt_real_2x2!(R[i:(i + 1), i:(i + 1)], A[i:(i + 1), i:(i + 1)]) - i += 2 - end - end - if i == n - R[n, n] = sqrt(ta(A[n, n])) - end - return R -end - -function _sqrt_quasitriu_offdiag_block!(R, A) - n = size(R, 1) - j = 1 - @inbounds while j ≤ n - jsize_is_2 = j < n && !iszero(A[j + 1, j]) - i = j - 1 - while i > 0 - isize_is_2 = i > 1 && !iszero(A[i, i - 1]) - if isize_is_2 - if jsize_is_2 - _sqrt_quasitriu_offdiag_block_2x2!(R, A, i - 1, j) - else - _sqrt_quasitriu_offdiag_block_2x1!(R, A, i - 1, j) - end - i -= 2 - else - if jsize_is_2 - _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j) - else - _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j) - end - i -= 1 - end - end - j += 2 - !jsize_is_2 - end - return R -end - -# real square root of 2x2 diagonal block of quasi-triangular matrix from real Schur -# decomposition. Eqs 6.8-6.9 and Algorithm 6.5 of -# Higham, 2008, "Functions of Matrices: Theory and Computation", SIAM. -Base.@propagate_inbounds function _sqrt_real_2x2!(R, A) - # in the real Schur form, A[1, 1] == A[2, 2], and A[2, 1] * A[1, 2] < 0 - θ, a21, a12 = A[1, 1], A[2, 1], A[1, 2] - # avoid overflow/underflow of μ - # for real sqrt, |d| ≤ 2 max(|a12|,|a21|) - μ = sqrt(abs(a12)) * sqrt(abs(a21)) - α = _real_sqrt(θ, μ) - c = 2α - R[1, 1] = α - R[2, 1] = a21 / c - R[1, 2] = a12 / c - R[2, 2] = α - return R -end - -# real part of square root of θ+im*μ -@inline function _real_sqrt(θ, μ) - t = sqrt((abs(θ) + hypot(θ, μ)) / 2) - return θ ≥ 0 ? t : μ / 2t -end - -Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j) - Rii = R[i, i] - Rjj = R[j, j] - iszero(Rii) && iszero(Rjj) && return R - t = eltype(R) - tt = typeof(zero(t)*zero(t)) - r = tt(-A[i, j]) - @simd for k in (i + 1):(j - 1) - r += R[i, k] * R[k, j] - end - iszero(r) && return R - R[i, j] = sylvester(Rii, Rjj, r) - return R -end - -Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j) - jrange = j:(j + 1) - t = eltype(R) - tt = typeof(zero(t)*zero(t)) - r1 = tt(-A[i, j]) - r2 = tt(-A[i, j + 1]) - @simd for k in (i + 1):(j - 1) - rik = R[i, k] - r1 += rik * R[k, j] - r2 += rik * R[k, j + 1] - end - Rjj = @view R[jrange, jrange] - Rij = @view R[i, jrange] - Rij[1] = r1 - Rij[2] = r2 - _sylvester_1x2!(R[i, i], Rjj, Rij) - return R -end - -Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x1!(R, A, i, j) - irange = i:(i + 1) - t = eltype(R) - tt = typeof(zero(t)*zero(t)) - r1 = tt(-A[i, j]) - r2 = tt(-A[i + 1, j]) - @simd for k in (i + 2):(j - 1) - rkj = R[k, j] - r1 += R[i, k] * rkj - r2 += R[i + 1, k] * rkj - end - Rii = @view R[irange, irange] - Rij = @view R[irange, j] - Rij[1] = r1 - Rij[2] = r2 - @views _sylvester_2x1!(Rii, R[j, j], Rij) - return R -end - -Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x2!(R, A, i, j) - irange = i:(i + 1) - jrange = j:(j + 1) - t = eltype(R) - tt = typeof(zero(t)*zero(t)) - for i′ in irange, j′ in jrange - Cij = tt(-A[i′, j′]) - @simd for k in (i + 2):(j - 1) - Cij += R[i′, k] * R[k, j′] - end - R[i′, j′] = Cij - end - Rii = @view R[irange, irange] - Rjj = @view R[jrange, jrange] - Rij = @view R[irange, jrange] - if !iszero(Rij) && !all(isnan, Rij) - _sylvester_2x2!(Rii, Rjj, Rij) - end - return R -end - -# solve Sylvester's equation AX + XB = -C using blockwise recursion until the dimension of -# A and B are no greater than blockwidth, based on Algorithm 1 from -# Jonsson I, Kågström B. Recursive blocked algorithms for solving triangular systems— -# Part I: one-sided and coupled Sylvester-type matrix equations. (2002) ACM Trans Math Softw. -# 28(4), https://doi.org/10.1145/592843.592845. -# specify raise=false to avoid breaking the recursion if a LAPACKException is thrown when -# computing one of the blocks. -function _sylvester_quasitriu!(A, B, C; blockwidth=64, nA=checksquare(A), nB=checksquare(B), raise=true) - if 1 ≤ nA ≤ blockwidth && 1 ≤ nB ≤ blockwidth - _sylvester_quasitriu_base!(A, B, C; raise=raise) - elseif nA ≥ 2nB ≥ 2 - _sylvester_quasitriu_split1!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) - elseif nB ≥ 2nA ≥ 2 - _sylvester_quasitriu_split2!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) - else - _sylvester_quasitriu_splitall!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) - end - return C -end -function _sylvester_quasitriu_base!(A, B, C; raise=true) - try - _, scale = LAPACK.trsyl!('N', 'N', A, B, C) - rmul!(C, -inv(scale)) - catch e - if !(e isa LAPACKException) || raise - throw(e) - end - end - return C -end -function _sylvester_quasitriu_split1!(A, B, C; nA=checksquare(A), kwargs...) - iA = div(nA, 2) - iszero(A[iA + 1, iA]) || (iA += 1) # don't split 2x2 diagonal block - rA1, rA2 = 1:iA, (iA + 1):nA - nA1, nA2 = iA, nA-iA - A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2] - C1, C2 = @views C[rA1,:], C[rA2,:] - _sylvester_quasitriu!(A22, B, C2; nA=nA2, kwargs...) - mul!(C1, A12, C2, true, true) - _sylvester_quasitriu!(A11, B, C1; nA=nA1, kwargs...) - return C -end -function _sylvester_quasitriu_split2!(A, B, C; nB=checksquare(B), kwargs...) - iB = div(nB, 2) - iszero(B[iB + 1, iB]) || (iB += 1) # don't split 2x2 diagonal block - rB1, rB2 = 1:iB, (iB + 1):nB - nB1, nB2 = iB, nB-iB - B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2] - C1, C2 = @views C[:,rB1], C[:,rB2] - _sylvester_quasitriu!(A, B11, C1; nB=nB1, kwargs...) - mul!(C2, C1, B12, true, true) - _sylvester_quasitriu!(A, B22, C2; nB=nB2, kwargs...) - return C -end -function _sylvester_quasitriu_splitall!(A, B, C; nA=checksquare(A), nB=checksquare(B), kwargs...) - iA = div(nA, 2) - iszero(A[iA + 1, iA]) || (iA += 1) # don't split 2x2 diagonal block - iB = div(nB, 2) - iszero(B[iB + 1, iB]) || (iB += 1) # don't split 2x2 diagonal block - rA1, rA2 = 1:iA, (iA + 1):nA - nA1, nA2 = iA, nA-iA - rB1, rB2 = 1:iB, (iB + 1):nB - nB1, nB2 = iB, nB-iB - A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2] - B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2] - C11, C21, C12, C22 = @views C[rA1,rB1], C[rA2,rB1], C[rA1,rB2], C[rA2,rB2] - _sylvester_quasitriu!(A22, B11, C21; nA=nA2, nB=nB1, kwargs...) - mul!(C11, A12, C21, true, true) - _sylvester_quasitriu!(A11, B11, C11; nA=nA1, nB=nB1, kwargs...) - mul!(C22, C21, B12, true, true) - _sylvester_quasitriu!(A22, B22, C22; nA=nA2, nB=nB2, kwargs...) - mul!(C12, A12, C22, true, true) - mul!(C12, C11, B12, true, true) - _sylvester_quasitriu!(A11, B22, C12; nA=nA1, nB=nB2, kwargs...) - return C -end - -# End of auxiliary functions for matrix square root - -# Generic eigensystems -eigvals(A::AbstractTriangular) = diag(A) -# fallback for unknown types -function eigvecs(A::AbstractTriangular{<:BlasFloat}) - if istriu(A) - eigvecs(UpperTriangular(Matrix(A))) - else # istril(A) - eigvecs(LowerTriangular(Matrix(A))) - end -end -function eigvecs(A::AbstractTriangular{T}) where T - TT = promote_type(T, Float32) - if TT <: BlasFloat - return eigvecs(convert(AbstractMatrix{TT}, A)) - else - throw(ArgumentError(lazy"eigvecs type $(typeof(A)) not supported. Please submit a pull request.")) - end -end -det(A::UnitUpperTriangular{T}) where {T} = one(T) -det(A::UnitLowerTriangular{T}) where {T} = one(T) -logdet(A::UnitUpperTriangular{T}) where {T} = zero(T) -logdet(A::UnitLowerTriangular{T}) where {T} = zero(T) -logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T) -logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T) -det(A::UpperTriangular) = prod(diag(A.data)) -det(A::LowerTriangular) = prod(diag(A.data)) -function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T - sgn = one(T) - abs_det = zero(real(T)) - @inbounds for i in axes(A.data,1) - diag_i = A.data[i,i] - sgn *= sign(diag_i) - abs_det += log(abs(diag_i)) - end - return abs_det, sgn -end - -eigen(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A)) - -# Generic singular systems -for func in (:svd, :svd!, :svdvals) - @eval begin - ($func)(A::AbstractTriangular; kwargs...) = ($func)(copyto!(similar(parent(A)), A); kwargs...) - end -end - -factorize(A::AbstractTriangular) = A - -# disambiguation methods: /(Adjoint of AbsVec, <:AbstractTriangular) -/(u::AdjointAbsVec, A::Union{LowerTriangular,UpperTriangular}) = adjoint(adjoint(A) \ u.parent) -/(u::AdjointAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = adjoint(adjoint(A) \ u.parent) -# disambiguation methods: /(Transpose of AbsVec, <:AbstractTriangular) -/(u::TransposeAbsVec, A::Union{LowerTriangular,UpperTriangular}) = transpose(transpose(A) \ u.parent) -/(u::TransposeAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = transpose(transpose(A) \ u.parent) -# disambiguation methods: /(Transpose of AbsVec, Adj/Trans of <:AbstractTriangular) -for (tritype, comptritype) in ((:LowerTriangular, :UpperTriangular), - (:UnitLowerTriangular, :UnitUpperTriangular), - (:UpperTriangular, :LowerTriangular), - (:UnitUpperTriangular, :UnitLowerTriangular)) - @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Adjoint}) = transpose($comptritype(conj(parent(parent(A)))) \ u.parent) - @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Transpose}) = transpose(transpose(A) \ u.parent) -end - -# Cube root of a 2x2 real-valued matrix with complex conjugate eigenvalues and equal diagonal values. -# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots. -# SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989). -# https://doi.org/10.1137/s0895479801392697 -function _cbrt_2x2!(A::AbstractMatrix{T}) where {T<:Real} - @assert checksquare(A) == 2 - @inbounds begin - (A[1,1] == A[2,2]) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have equal diagonal values.")) - (A[1,2]*A[2,1] < 0) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have complex conjugate eigenvalues.")) - μ = sqrt(-A[1,2]*A[2,1]) - r = cbrt(hypot(A[1,1], μ)) - θ = atan(μ, A[1,1]) - s, c = sincos(θ/3) - α, β′ = r*c, r*s/µ - A[1,1] = α - A[2,2] = α - A[1,2] = β′*A[1,2] - A[2,1] = β′*A[2,1] - end - return A -end - -# Cube root of a quasi upper triangular matrix (output of Schur decomposition) -# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots. -# SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989). -# https://doi.org/10.1137/s0895479801392697 -@views function _cbrt_quasi_triu!(A::AbstractMatrix{T}) where {T<:Real} - m, n = size(A) - (m == n) || throw(ArgumentError("_cbrt_quasi_triu!: Matrix A must be square.")) - # Cube roots of 1x1 and 2x2 diagonal blocks - i = 1 - sizes = ones(Int,n) - S = zeros(T,2,n) - while i < n - if !iszero(A[i+1,i]) - _cbrt_2x2!(A[i:i+1,i:i+1]) - mul!(S[1:2,i:i+1], A[i:i+1,i:i+1], A[i:i+1,i:i+1]) - sizes[i] = 2 - sizes[i+1] = 0 - i += 2 - else - A[i,i] = cbrt(A[i,i]) - S[1,i] = A[i,i]*A[i,i] - i += 1 - end - end - if i == n - A[n,n] = cbrt(A[n,n]) - S[1,n] = A[n,n]*A[n,n] - end - # Algorithm 4.3 in Reference [1] - Δ = I(4) - M_L₀ = zeros(T,4,4) - M_L₁ = zeros(T,4,4) - M_Bᵢⱼ⁽⁰⁾ = zeros(T,2,2) - M_Bᵢⱼ⁽¹⁾ = zeros(T,2,2) - for k in axes(A,2)[1:end-1] - for i in axes(A,2)[1:end-k] - if sizes[i] == 0 || sizes[i+k] == 0 continue end - k₁, k₂ = i+1+(sizes[i+1]==0), i+k-1 - i₁, i₂, j₁, j₂, s₁, s₂ = i, i+sizes[i]-1, i+k, i+k+sizes[i+k]-1, sizes[i], sizes[i+k] - L₀ = M_L₀[1:s₁*s₂,1:s₁*s₂] - L₁ = M_L₁[1:s₁*s₂,1:s₁*s₂] - Bᵢⱼ⁽⁰⁾ = M_Bᵢⱼ⁽⁰⁾[1:s₁, 1:s₂] - Bᵢⱼ⁽¹⁾ = M_Bᵢⱼ⁽¹⁾[1:s₁, 1:s₂] - # Compute Bᵢⱼ⁽⁰⁾ and Bᵢⱼ⁽¹⁾ - mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,k₁:k₂], A[k₁:k₂,j₁:j₂]) - # Retrieve Rᵢ,ᵢ₊ₖ as A[i+k,i]' - mul!(Bᵢⱼ⁽¹⁾, A[i₁:i₂,k₁:k₂], A[j₁:j₂,k₁:k₂]') - # Solve Uᵢ,ᵢ₊ₖ using Reference [1, (4.10)] - kron!(L₀, Δ[1:s₂,1:s₂], S[1:s₁,i₁:i₂]) - L₀ .+= kron!(L₁, A[j₁:j₂,j₁:j₂]', A[i₁:i₂,i₁:i₂]) - L₀ .+= kron!(L₁, S[1:s₂,j₁:j₂]', Δ[1:s₁,1:s₁]) - mul!(A[i₁:i₂,j₁:j₂], A[i₁:i₂,i₁:i₂], Bᵢⱼ⁽⁰⁾, -1.0, 1.0) - A[i₁:i₂,j₁:j₂] .-= Bᵢⱼ⁽¹⁾ - ldiv!(lu!(L₀), A[i₁:i₂,j₁:j₂][:]) - # Compute and store Rᵢ,ᵢ₊ₖ' in A[i+k,i] - mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,i₁:i₂], A[i₁:i₂,j₁:j₂], 1.0, 1.0) - mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,j₁:j₂], A[j₁:j₂,j₁:j₂], 1.0, 1.0) - A[j₁:j₂,i₁:i₂] .= Bᵢⱼ⁽⁰⁾' - end - end - # Make quasi triangular - for j in axes(A,2) - for i=j+1+(sizes[j]==2):lastindex(A,1) - A[i,j] = 0 - end - end - return A -end - -# Cube roots of real-valued triangular matrices -cbrt(A::UpperTriangular{T}) where {T<:Real} = UpperTriangular(_cbrt_quasi_triu!(Matrix{T}(A))) -cbrt(A::LowerTriangular{T}) where {T<:Real} = LowerTriangular(_cbrt_quasi_triu!(Matrix{T}(A'))') diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl deleted file mode 100644 index 0d73e6dd46fdb..0000000000000 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ /dev/null @@ -1,1099 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -#### Specialized matrix types #### - -## (complex) symmetric tridiagonal matrices -struct SymTridiagonal{T, V<:AbstractVector{T}} <: AbstractMatrix{T} - dv::V # diagonal - ev::V # superdiagonal - function SymTridiagonal{T, V}(dv, ev) where {T, V<:AbstractVector{T}} - require_one_based_indexing(dv, ev) - if !(length(dv) - 1 <= length(ev) <= length(dv)) - throw(DimensionMismatch(lazy"subdiagonal has wrong length. Has length $(length(ev)), but should be either $(length(dv) - 1) or $(length(dv)).")) - end - new{T, V}(dv, ev) - end -end - -""" - SymTridiagonal(dv::V, ev::V) where V <: AbstractVector - -Construct a symmetric tridiagonal matrix from the diagonal (`dv`) and first -sub/super-diagonal (`ev`), respectively. The result is of type `SymTridiagonal` -and provides efficient specialized eigensolvers, but may be converted into a -regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). - -For `SymTridiagonal` block matrices, the elements of `dv` are symmetrized. -The argument `ev` is interpreted as the superdiagonal. Blocks from the -subdiagonal are (materialized) transpose of the corresponding superdiagonal blocks. - -# Examples -```jldoctest -julia> dv = [1, 2, 3, 4] -4-element Vector{Int64}: - 1 - 2 - 3 - 4 - -julia> ev = [7, 8, 9] -3-element Vector{Int64}: - 7 - 8 - 9 - -julia> SymTridiagonal(dv, ev) -4×4 SymTridiagonal{Int64, Vector{Int64}}: - 1 7 ⋅ ⋅ - 7 2 8 ⋅ - ⋅ 8 3 9 - ⋅ ⋅ 9 4 - -julia> A = SymTridiagonal(fill([1 2; 3 4], 3), fill([1 2; 3 4], 2)); - -julia> A[1,1] -2×2 Symmetric{Int64, Matrix{Int64}}: - 1 2 - 2 4 - -julia> A[1,2] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> A[2,1] -2×2 Matrix{Int64}: - 1 3 - 2 4 -``` -""" -SymTridiagonal(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T}(dv, ev) -SymTridiagonal{T}(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T,V}(dv, ev) -function SymTridiagonal{T}(dv::AbstractVector, ev::AbstractVector) where {T} - d = convert(AbstractVector{T}, dv)::AbstractVector{T} - e = convert(AbstractVector{T}, ev)::AbstractVector{T} - typeof(d) == typeof(e) ? - SymTridiagonal{T}(d, e) : - throw(ArgumentError("diagonal vectors needed to be convertible to same type")) -end -SymTridiagonal(d::AbstractVector{T}, e::AbstractVector{S}) where {T,S} = - SymTridiagonal{promote_type(T, S)}(d, e) - -""" - SymTridiagonal(A::AbstractMatrix) - -Construct a symmetric tridiagonal matrix from the diagonal and first superdiagonal -of the symmetric matrix `A`. - -# Examples -```jldoctest -julia> A = [1 2 3; 2 4 5; 3 5 6] -3×3 Matrix{Int64}: - 1 2 3 - 2 4 5 - 3 5 6 - -julia> SymTridiagonal(A) -3×3 SymTridiagonal{Int64, Vector{Int64}}: - 1 2 ⋅ - 2 4 5 - ⋅ 5 6 - -julia> B = reshape([[1 2; 2 3], [1 2; 3 4], [1 3; 2 4], [1 2; 2 3]], 2, 2); - -julia> SymTridiagonal(B) -2×2 SymTridiagonal{Matrix{Int64}, Vector{Matrix{Int64}}}: - [1 2; 2 3] [1 3; 2 4] - [1 2; 3 4] [1 2; 2 3] -``` -""" -function SymTridiagonal(A::AbstractMatrix) - checksquare(A) - du = diag(A, 1) - d = diag(A) - dl = diag(A, -1) - if all(((x, y),) -> x == transpose(y), zip(du, dl)) && all(issymmetric, d) - SymTridiagonal(d, du) - else - throw(ArgumentError("matrix is not symmetric; cannot convert to SymTridiagonal")) - end -end - -SymTridiagonal{T,V}(S::SymTridiagonal{T,V}) where {T,V<:AbstractVector{T}} = S -SymTridiagonal{T,V}(S::SymTridiagonal) where {T,V<:AbstractVector{T}} = - SymTridiagonal(convert(V, S.dv)::V, convert(V, S.ev)::V) -SymTridiagonal{T}(S::SymTridiagonal{T}) where {T} = S -SymTridiagonal{T}(S::SymTridiagonal) where {T} = - SymTridiagonal(convert(AbstractVector{T}, S.dv)::AbstractVector{T}, - convert(AbstractVector{T}, S.ev)::AbstractVector{T}) -SymTridiagonal(S::SymTridiagonal) = S - -AbstractMatrix{T}(S::SymTridiagonal) where {T} = SymTridiagonal{T}(S) -AbstractMatrix{T}(S::SymTridiagonal{T}) where {T} = copy(S) - -function Matrix{T}(M::SymTridiagonal) where T - n = size(M, 1) - Mf = Matrix{T}(undef, n, n) - n == 0 && return Mf - if haszero(T) # optimized path for types with zero(T) defined - n > 2 && fill!(Mf, zero(T)) - @inbounds for i = 1:n-1 - Mf[i,i] = symmetric(M.dv[i], :U) - Mf[i+1,i] = transpose(M.ev[i]) - Mf[i,i+1] = M.ev[i] - end - Mf[n,n] = symmetric(M.dv[n], :U) - else - copyto!(Mf, M) - end - return Mf -end -Matrix(M::SymTridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M) -Array(M::SymTridiagonal) = Matrix(M) - -size(A::SymTridiagonal) = (n = length(A.dv); (n, n)) -axes(M::SymTridiagonal) = (ax = axes(M.dv, 1); (ax, ax)) - -similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal(similar(S.dv, T), similar(S.ev, T)) -similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(S.dv, T, dims) - -# copyto! for matching axes -_copyto_banded!(dest::SymTridiagonal, src::SymTridiagonal) = - (copyto!(dest.dv, src.dv); copyto!(dest.ev, _evview(src)); dest) - -#Elementary operations -for func in (:conj, :copy, :real, :imag) - @eval ($func)(M::SymTridiagonal) = SymTridiagonal(($func)(M.dv), ($func)(M.ev)) -end - -transpose(S::SymTridiagonal) = S -adjoint(S::SymTridiagonal{<:Number}) = SymTridiagonal(vec(adjoint(S.dv)), vec(adjoint(S.ev))) -adjoint(S::SymTridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = - SymTridiagonal(adjoint(parent(S.dv)), adjoint(parent(S.ev))) - -permutedims(S::SymTridiagonal) = S -function permutedims(S::SymTridiagonal, perm) - Base.checkdims_perm(axes(S), axes(S), perm) - NTuple{2}(perm) == (2, 1) ? permutedims(S) : S -end -Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(adjoint.(x)), (S.parent.dv, S.parent.ev))...) - -ishermitian(S::SymTridiagonal) = isreal(S.dv) && isreal(_evview(S)) -issymmetric(S::SymTridiagonal) = true - -tr(S::SymTridiagonal) = sum(symmetric, S.dv) - -_diagiter(M::SymTridiagonal{<:Number}) = M.dv -_diagiter(M::SymTridiagonal) = (symmetric(x, :U) for x in M.dv) -_eviter_transposed(M::SymTridiagonal{<:Number}) = _evview(M) -_eviter_transposed(M::SymTridiagonal) = (transpose(x) for x in _evview(M)) - -function diag(M::SymTridiagonal, n::Integer=0) - # every branch call similar(..., ::Int) to make sure the - # same vector type is returned independent of n - v = similar(M.dv, max(0, length(M.dv)-abs(n))) - if n == 0 - return copyto!(v, _diagiter(M)) - elseif n == 1 - return copyto!(v, _evview(M)) - elseif n == -1 - return copyto!(v, _eviter_transposed(M)) - else - for i in eachindex(v) - v[i] = M[BandIndex(n,i)] - end - end - return v -end - -+(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv+B.dv, _evview(A)+_evview(B)) --(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv-B.dv, _evview(A)-_evview(B)) --(A::SymTridiagonal) = SymTridiagonal(-A.dv, -A.ev) -*(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv*B, A.ev*B) -*(B::Number, A::SymTridiagonal) = SymTridiagonal(B*A.dv, B*A.ev) -function rmul!(A::SymTridiagonal, x::Number) - if size(A,1) > 2 - # ensure that zeros are preserved on scaling - y = A[3,1] * x - iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - A.dv .*= x - _evview(A) .*= x - return A -end -function lmul!(x::Number, B::SymTridiagonal) - if size(B,1) > 2 - # ensure that zeros are preserved on scaling - y = x * B[3,1] - iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. B.dv = x * B.dv - ev = _evview(B) - @. ev = x * ev - return B -end -/(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv/B, A.ev/B) -\(B::Number, A::SymTridiagonal) = SymTridiagonal(B\A.dv, B\A.ev) -==(A::SymTridiagonal{<:Number}, B::SymTridiagonal{<:Number}) = - (A.dv == B.dv) && (_evview(A) == _evview(B)) -==(A::SymTridiagonal, B::SymTridiagonal) = - size(A) == size(B) && all(i -> A[i,i] == B[i,i], axes(A, 1)) && (_evview(A) == _evview(B)) - -function dot(x::AbstractVector, S::SymTridiagonal, y::AbstractVector) - require_one_based_indexing(x, y) - nx, ny = length(x), length(y) - (nx == size(S, 1) == ny) || throw(DimensionMismatch("dot")) - if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(S)), zero(eltype(y))) - return dot(x[1], S.dv[1], y[1]) - end - dv, ev = S.dv, S.ev - @inbounds begin - x₀ = x[1] - x₊ = x[2] - sub = transpose(ev[1]) - r = dot(adjoint(dv[1])*x₀ + adjoint(sub)*x₊, y[1]) - for j in 2:nx-1 - x₋, x₀, x₊ = x₀, x₊, x[j+1] - sup, sub = transpose(sub), transpose(ev[j]) - r += dot(adjoint(sup)*x₋ + adjoint(dv[j])*x₀ + adjoint(sub)*x₊, y[j]) - end - r += dot(adjoint(transpose(sub))*x₀ + adjoint(dv[nx])*x₊, y[nx]) - end - return r -end - -(\)(T::SymTridiagonal, B::AbstractVecOrMat) = ldlt(T)\B - -# division with optional shift for use in shifted-Hessenberg solvers (hessenberg.jl): -ldiv!(A::SymTridiagonal, B::AbstractVecOrMat; shift::Number=false) = ldiv!(ldlt(A, shift=shift), B) -rdiv!(B::AbstractVecOrMat, A::SymTridiagonal; shift::Number=false) = rdiv!(B, ldlt(A, shift=shift)) - -eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = Eigen(LAPACK.stegr!('V', A.dv, A.ev)...) -eigen(A::SymTridiagonal{T}) where T = eigen!(copymutable_oftype(A, eigtype(T))) - -eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) = - Eigen(LAPACK.stegr!('V', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)...) -eigen(A::SymTridiagonal{T}, irange::UnitRange) where T = - eigen!(copymutable_oftype(A, eigtype(T)), irange) - -eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) = - Eigen(LAPACK.stegr!('V', 'V', A.dv, A.ev, vl, vu, 0, 0)...) -eigen(A::SymTridiagonal{T}, vl::Real, vu::Real) where T = - eigen!(copymutable_oftype(A, eigtype(T)), vl, vu) - -eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = LAPACK.stev!('N', A.dv, A.ev)[1] -eigvals(A::SymTridiagonal{T}) where T = eigvals!(copymutable_oftype(A, eigtype(T))) - -eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) = - LAPACK.stegr!('N', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)[1] -eigvals(A::SymTridiagonal{T}, irange::UnitRange) where T = - eigvals!(copymutable_oftype(A, eigtype(T)), irange) - -eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) = - LAPACK.stegr!('N', 'V', A.dv, A.ev, vl, vu, 0, 0)[1] -eigvals(A::SymTridiagonal{T}, vl::Real, vu::Real) where T = - eigvals!(copymutable_oftype(A, eigtype(T)), vl, vu) - -#Computes largest and smallest eigenvalue -eigmax(A::SymTridiagonal) = eigvals(A, size(A, 1):size(A, 1))[1] -eigmin(A::SymTridiagonal) = eigvals(A, 1:1)[1] - -#Compute selected eigenvectors only corresponding to particular eigenvalues -""" - eigvecs(A::SymTridiagonal[, eigvals]) -> Matrix - -Return a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can -be obtained from the slice `M[:, k]`.) - -If the optional vector of eigenvalues `eigvals` is specified, `eigvecs` -returns the specific corresponding eigenvectors. - -# Examples -```jldoctest -julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) -3×3 SymTridiagonal{Float64, Vector{Float64}}: - 1.0 2.0 ⋅ - 2.0 2.0 3.0 - ⋅ 3.0 1.0 - -julia> eigvals(A) -3-element Vector{Float64}: - -2.1400549446402604 - 1.0000000000000002 - 5.140054944640259 - -julia> eigvecs(A) -3×3 Matrix{Float64}: - 0.418304 -0.83205 0.364299 - -0.656749 -7.39009e-16 0.754109 - 0.627457 0.5547 0.546448 - -julia> eigvecs(A, [1.]) -3×1 Matrix{Float64}: - 0.8320502943378438 - 4.263514128092366e-17 - -0.5547001962252291 -``` -""" -eigvecs(A::SymTridiagonal{<:BlasFloat,<:StridedVector}, eigvals::Vector{<:Real}) = LAPACK.stein!(A.dv, A.ev, eigvals) - -function svdvals!(A::SymTridiagonal) - vals = eigvals!(A) - return sort!(map!(abs, vals, vals); rev=true) -end - -# tril and triu - -Base.@constprop :aggressive function istriu(M::SymTridiagonal, k::Integer=0) - if k <= -1 - return true - elseif k == 0 - return iszero(_evview(M)) - else # k >= 1 - return iszero(_evview(M)) && iszero(M.dv) - end -end -Base.@constprop :aggressive istril(M::SymTridiagonal, k::Integer) = istriu(M, -k) -iszero(M::SymTridiagonal) = iszero(_evview(M)) && iszero(M.dv) -isone(M::SymTridiagonal) = iszero(_evview(M)) && all(isone, M.dv) -isdiag(M::SymTridiagonal) = iszero(_evview(M)) - - -function tril!(M::SymTridiagonal{T}, k::Integer=0) where T - n = length(M.dv) - if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) - elseif k < -1 - fill!(M.ev, zero(T)) - fill!(M.dv, zero(T)) - return Tridiagonal(M.ev,M.dv,copy(M.ev)) - elseif k == -1 - fill!(M.dv, zero(T)) - return Tridiagonal(M.ev,M.dv,zero(M.ev)) - elseif k == 0 - return Tridiagonal(M.ev,M.dv,zero(M.ev)) - else # if k >= 1 - return Tridiagonal(M.ev,M.dv,copy(M.ev)) - end -end - -function triu!(M::SymTridiagonal{T}, k::Integer=0) where T - n = length(M.dv) - if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) - elseif k > 1 - fill!(M.ev, zero(T)) - fill!(M.dv, zero(T)) - return Tridiagonal(M.ev,M.dv,copy(M.ev)) - elseif k == 1 - fill!(M.dv, zero(T)) - return Tridiagonal(zero(M.ev),M.dv,M.ev) - elseif k == 0 - return Tridiagonal(zero(M.ev),M.dv,M.ev) - else # if k <= -1 - return Tridiagonal(M.ev,M.dv,copy(M.ev)) - end -end - -################### -# Generic methods # -################### - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::SymTridiagonal, i::Integer, j::Integer, s::AbstractString) - i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s) -end - -# Implements the determinant using principal minors -# a, b, c are assumed to be the subdiagonal, diagonal, and superdiagonal of -# a tridiagonal matrix. -#Reference: -# R. Usmani, "Inversion of a tridiagonal Jacobi matrix", -# Linear Algebra and its Applications 212-213 (1994), pp.413-414 -# doi:10.1016/0024-3795(94)90414-6 -function det_usmani(a::V, b::V, c::V, shift::Number=0) where {T,V<:AbstractVector{T}} - require_one_based_indexing(a, b, c) - n = length(b) - θa = oneunit(T)+zero(shift) - if n == 0 - return θa - end - θb = b[1]+shift - for i in 2:n - θb, θa = (b[i]+shift)*θb - a[i-1]*c[i-1]*θa, θb - end - return θb -end - -# det with optional diagonal shift for use with shifted Hessenberg factorizations -det(A::SymTridiagonal; shift::Number=false) = det_usmani(A.ev, A.dv, A.ev, shift) -logabsdet(A::SymTridiagonal; shift::Number=false) = logabsdet(ldlt(A; shift=shift)) - -@inline function Base.isassigned(A::SymTridiagonal, i::Int, j::Int) - @boundscheck checkbounds(Bool, A, i, j) || return false - if i == j - return @inbounds isassigned(A.dv, i) - elseif i == j + 1 - return @inbounds isassigned(A.ev, j) - elseif i + 1 == j - return @inbounds isassigned(A.ev, i) - else - return true - end -end - -@inline function Base.isstored(A::SymTridiagonal, i::Int, j::Int) - @boundscheck checkbounds(A, i, j) - if i == j - return @inbounds Base.isstored(A.dv, i) - elseif i == j + 1 - return @inbounds Base.isstored(A.ev, j) - elseif i + 1 == j - return @inbounds Base.isstored(A.ev, i) - else - return false - end -end - -@inline function getindex(A::SymTridiagonal{T}, i::Int, j::Int) where T - @boundscheck checkbounds(A, i, j) - if i == j - return symmetric((@inbounds A.dv[i]), :U)::symmetric_type(eltype(A.dv)) - elseif i == j + 1 - return copy(transpose(@inbounds A.ev[j])) # materialized for type stability - elseif i + 1 == j - return @inbounds A.ev[i] - else - return zero(T) - end -end - -Base._reverse(A::SymTridiagonal, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::SymTridiagonal, dims::Colon) = SymTridiagonal(reverse(A.dv), reverse(A.ev)) -Base._reverse!(A::SymTridiagonal, dims::Colon) = (reverse!(A.dv); reverse!(A.ev); A) - -@inline function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) - @boundscheck checkbounds(A, i, j) - if i == j - issymmetric(x) || throw(ArgumentError("cannot set a diagonal entry of a SymTridiagonal to an asymmetric value")) - @inbounds A.dv[i] = x - else - throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j)")) - end - return A -end - -## Tridiagonal matrices ## -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,V}(dl, d, du) where {T,V<:AbstractVector{T}} - require_one_based_indexing(dl, d, du) - n = length(d) - if (length(dl) != n-1 || length(du) != n-1) && !(length(d) == 0 && length(dl) == 0 && length(du) == 0) - throw(ArgumentError(LazyString("cannot construct Tridiagonal from incompatible ", - "lengths of subdiagonal, diagonal and superdiagonal: ", - lazy"($(length(dl)), $(length(d)), $(length(du)))"))) - end - new{T,V}(dl, d, Base.unalias(dl, du)) - end - # constructor used in lu! - function Tridiagonal{T,V}(dl, d, du, du2) where {T,V<:AbstractVector{T}} - require_one_based_indexing(dl, d, du, du2) - # length checks? - new{T,V}(dl, d, Base.unalias(dl, du), du2) - end -end - -""" - 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 -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`. - -!!! note - The subdiagonal `dl` and the superdiagonal `du` must not be aliased to each other. - If aliasing is detected, the constructor will use a copy of `du` as its argument. - -# Examples -```jldoctest -julia> dl = [1, 2, 3]; - -julia> du = [4, 5, 6]; - -julia> d = [7, 8, 9, 0]; - -julia> Tridiagonal(dl, d, du) -4×4 Tridiagonal{Int64, Vector{Int64}}: - 7 4 ⋅ ⋅ - 1 8 5 ⋅ - ⋅ 2 9 6 - ⋅ ⋅ 3 0 -``` -""" -Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du) -Tridiagonal(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du, du2) -Tridiagonal(dl::AbstractVector{T}, d::AbstractVector{S}, du::AbstractVector{U}) where {T,S,U} = - Tridiagonal{promote_type(T, S, U)}(dl, d, du) -Tridiagonal(dl::AbstractVector{T}, d::AbstractVector{S}, du::AbstractVector{U}, du2::AbstractVector{V}) where {T,S,U,V} = - Tridiagonal{promote_type(T, S, U, V)}(dl, d, du, du2) -function Tridiagonal{T}(dl::AbstractVector, d::AbstractVector, du::AbstractVector) where {T} - l, d, u = map(x->convert(AbstractVector{T}, x), (dl, d, du)) - typeof(l) == typeof(d) == typeof(u) ? - Tridiagonal(l, d, u) : - throw(ArgumentError("diagonal vectors needed to be convertible to same type")) -end -function Tridiagonal{T}(dl::AbstractVector, d::AbstractVector, du::AbstractVector, du2::AbstractVector) where {T} - l, d, u, u2 = map(x->convert(AbstractVector{T}, x), (dl, d, du, du2)) - typeof(l) == typeof(d) == typeof(u) == typeof(u2) ? - Tridiagonal(l, d, u, u2) : - throw(ArgumentError("diagonal vectors needed to be convertible to same type")) -end - -""" - Tridiagonal(A) - -Construct a tridiagonal matrix from the first sub-diagonal, -diagonal and first super-diagonal of the matrix `A`. - -# Examples -```jldoctest -julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4] -4×4 Matrix{Int64}: - 1 2 3 4 - 1 2 3 4 - 1 2 3 4 - 1 2 3 4 - -julia> Tridiagonal(A) -4×4 Tridiagonal{Int64, Vector{Int64}}: - 1 2 ⋅ ⋅ - 1 2 3 ⋅ - ⋅ 2 3 4 - ⋅ ⋅ 3 4 -``` -""" -Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1)) - -Tridiagonal(A::Tridiagonal) = A -Tridiagonal{T}(A::Tridiagonal{T}) where {T} = A -function Tridiagonal{T}(A::Tridiagonal) where {T} - dl, d, du = map(x -> convert(AbstractVector{T}, x)::AbstractVector{T}, (A.dl, A.d, A.du)) - if isdefined(A, :du2) - Tridiagonal{T}(dl, d, du, convert(AbstractVector{T}, A.du2)::AbstractVector{T}) - else - Tridiagonal{T}(dl, d, du) - end -end -Tridiagonal{T,V}(A::Tridiagonal{T,V}) where {T,V<:AbstractVector{T}} = A -function Tridiagonal{T,V}(A::Tridiagonal) where {T,V<:AbstractVector{T}} - dl, d, du = map(x -> convert(V, x)::V, (A.dl, A.d, A.du)) - if isdefined(A, :du2) - Tridiagonal{T,V}(dl, d, du, convert(V, A.du2)::V) - else - Tridiagonal{T,V}(dl, d, du) - end -end - -size(M::Tridiagonal) = (n = length(M.d); (n, n)) -axes(M::Tridiagonal) = (ax = axes(M.d,1); (ax, ax)) - -function Matrix{T}(M::Tridiagonal) where {T} - A = Matrix{T}(undef, size(M)) - if haszero(T) # optimized path for types with zero(T) defined - size(A,1) > 2 && fill!(A, zero(T)) - copyto!(diagview(A), M.d) - copyto!(diagview(A,1), M.du) - copyto!(diagview(A,-1), M.dl) - else - copyto!(A, M) - end - A -end -Matrix(M::Tridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M) -Array(M::Tridiagonal) = Matrix(M) - -similar(M::Tridiagonal, ::Type{T}) where {T} = Tridiagonal(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) -similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(M.d, T, dims) - -# Operations on Tridiagonal matrices -# copyto! for matching axes -function _copyto_banded!(dest::Tridiagonal, src::Tridiagonal) - copyto!(dest.dl, src.dl) - copyto!(dest.d, src.d) - copyto!(dest.du, src.du) - dest -end - -#Elementary operations -for func in (:conj, :copy, :real, :imag) - @eval function ($func)(M::Tridiagonal) - Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du)) - end -end - -adjoint(S::Tridiagonal{<:Number}) = Tridiagonal(vec(adjoint(S.du)), vec(adjoint(S.d)), vec(adjoint(S.dl))) -adjoint(S::Tridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = - Tridiagonal(adjoint(parent(S.du)), adjoint(parent(S.d)), adjoint(parent(S.dl))) -transpose(S::Tridiagonal{<:Number}) = Tridiagonal(S.du, S.d, S.dl) -permutedims(T::Tridiagonal) = Tridiagonal(T.du, T.d, T.dl) -function permutedims(T::Tridiagonal, perm) - Base.checkdims_perm(axes(T), axes(T), perm) - NTuple{2}(perm) == (2, 1) ? permutedims(T) : T -end -Base.copy(aS::Adjoint{<:Any,<:Tridiagonal}) = (S = aS.parent; Tridiagonal(map(x -> copy.(adjoint.(x)), (S.du, S.d, S.dl))...)) -Base.copy(tS::Transpose{<:Any,<:Tridiagonal}) = (S = tS.parent; Tridiagonal(map(x -> copy.(transpose.(x)), (S.du, S.d, S.dl))...)) - -ishermitian(S::Tridiagonal) = all(ishermitian, S.d) && all(Iterators.map((x, y) -> x == y', S.du, S.dl)) -issymmetric(S::Tridiagonal) = all(issymmetric, S.d) && all(Iterators.map((x, y) -> x == transpose(y), S.du, S.dl)) - -\(A::Adjoint{<:Any,<:Tridiagonal}, B::Adjoint{<:Any,<:AbstractVecOrMat}) = copy(A) \ B - -function diag(M::Tridiagonal, n::Integer=0) - # every branch call similar(..., ::Int) to make sure the - # same vector type is returned independent of n - v = similar(M.d, max(0, length(M.d)-abs(n))) - if n == 0 - copyto!(v, M.d) - elseif n == -1 - copyto!(v, M.dl) - elseif n == 1 - copyto!(v, M.du) - elseif abs(n) <= size(M,1) - for i in eachindex(v) - v[i] = M[BandIndex(n,i)] - end - end - return v -end - -@inline function Base.isassigned(A::Tridiagonal, i::Int, j::Int) - @boundscheck checkbounds(Bool, A, i, j) || return false - if i == j - return @inbounds isassigned(A.d, i) - elseif i == j + 1 - return @inbounds isassigned(A.dl, j) - elseif i + 1 == j - return @inbounds isassigned(A.du, i) - else - return true - end -end - -@inline function Base.isstored(A::Tridiagonal, i::Int, j::Int) - @boundscheck checkbounds(A, i, j) - if i == j - return @inbounds Base.isstored(A.d, i) - elseif i == j + 1 - return @inbounds Base.isstored(A.dl, j) - elseif i + 1 == j - return @inbounds Base.isstored(A.du, i) - else - return false - end -end - -@inline function getindex(A::Tridiagonal{T}, i::Int, j::Int) where T - @boundscheck checkbounds(A, i, j) - if i == j - return @inbounds A.d[i] - elseif i == j + 1 - return @inbounds A.dl[j] - elseif i + 1 == j - return @inbounds A.du[i] - else - return zero(T) - end -end - -@inline function getindex(A::Tridiagonal{T}, b::BandIndex) where T - @boundscheck checkbounds(A, b) - if b.band == 0 - return @inbounds A.d[b.index] - elseif b.band == -1 - return @inbounds A.dl[b.index] - elseif b.band == 1 - return @inbounds A.du[b.index] - else - return zero(T) - end -end - -@inline function setindex!(A::Tridiagonal, x, i::Integer, j::Integer) - @boundscheck checkbounds(A, i, j) - if i == j - @inbounds A.d[i] = x - elseif i - j == 1 - @inbounds A.dl[j] = x - elseif j - i == 1 - @inbounds A.du[i] = x - elseif !iszero(x) - throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ", - lazy"the tridiagonal band to a nonzero value ($x)"))) - end - return A -end - -## structured matrix methods ## -function Base.replace_in_print_matrix(A::Tridiagonal,i::Integer,j::Integer,s::AbstractString) - i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s) -end - -# reverse - -Base._reverse(A::Tridiagonal, dims) = reverse!(Matrix(A); dims) -Base._reverse(A::Tridiagonal, dims::Colon) = Tridiagonal(reverse(A.du), reverse(A.d), reverse(A.dl)) -function Base._reverse!(A::Tridiagonal, dims::Colon) - n = length(A.du) # == length(A.dl), & always 1-based - # reverse and swap A.dl and A.du: - @inbounds for i in 1:n - A.dl[i], A.du[n+1-i] = A.du[n+1-i], A.dl[i] - end - reverse!(A.d) - return A -end - -#tril and triu - -iszero(M::Tridiagonal) = iszero(M.dl) && iszero(M.d) && iszero(M.du) -isone(M::Tridiagonal) = iszero(M.dl) && all(isone, M.d) && iszero(M.du) -Base.@constprop :aggressive function istriu(M::Tridiagonal, k::Integer=0) - if k <= -1 - return true - elseif k == 0 - return iszero(M.dl) - elseif k == 1 - return iszero(M.dl) && iszero(M.d) - else # k >= 2 - return iszero(M.dl) && iszero(M.d) && iszero(M.du) - end -end -Base.@constprop :aggressive function istril(M::Tridiagonal, k::Integer=0) - if k >= 1 - return true - elseif k == 0 - return iszero(M.du) - elseif k == -1 - return iszero(M.du) && iszero(M.d) - else # k <= -2 - return iszero(M.du) && iszero(M.d) && iszero(M.dl) - end -end -isdiag(M::Tridiagonal) = iszero(M.dl) && iszero(M.du) - -function tril!(M::Tridiagonal{T}, k::Integer=0) where T - n = length(M.d) - if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) - elseif k < -1 - fill!(M.dl, zero(T)) - fill!(M.d, zero(T)) - fill!(M.du, zero(T)) - elseif k == -1 - fill!(M.d, zero(T)) - fill!(M.du, zero(T)) - elseif k == 0 - fill!(M.du, zero(T)) - end - return M -end - -function triu!(M::Tridiagonal{T}, k::Integer=0) where T - n = length(M.d) - if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", - lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) - elseif k > 1 - fill!(M.dl, zero(T)) - fill!(M.d, zero(T)) - fill!(M.du, zero(T)) - elseif k == 1 - fill!(M.dl, zero(T)) - fill!(M.d, zero(T)) - elseif k == 0 - fill!(M.dl, zero(T)) - end - return M -end - -tr(M::Tridiagonal) = sum(M.d) - -################### -# Generic methods # -################### - -+(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl+B.dl, A.d+B.d, A.du+B.du) --(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl-B.dl, A.d-B.d, A.du-B.du) --(A::Tridiagonal) = Tridiagonal(-A.dl, -A.d, -A.du) -*(A::Tridiagonal, B::Number) = Tridiagonal(A.dl*B, A.d*B, A.du*B) -*(B::Number, A::Tridiagonal) = Tridiagonal(B*A.dl, B*A.d, B*A.du) -function rmul!(T::Tridiagonal, x::Number) - if size(T,1) > 2 - # ensure that zeros are preserved on scaling - y = T[3,1] * x - iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - T.dl .*= x - T.d .*= x - T.du .*= x - return T -end -function lmul!(x::Number, T::Tridiagonal) - if size(T,1) > 2 - # ensure that zeros are preserved on scaling - y = x * T[3,1] - iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ", - lazy"the tridiagonal band to a nonzero value ($y)"))) - end - @. T.dl = x * T.dl - @. T.d = x * T.d - @. T.du = x * T.du - return T -end -/(A::Tridiagonal, B::Number) = Tridiagonal(A.dl/B, A.d/B, A.du/B) -\(B::Number, A::Tridiagonal) = Tridiagonal(B\A.dl, B\A.d, B\A.du) - -==(A::Tridiagonal, B::Tridiagonal) = (A.dl==B.dl) && (A.d==B.d) && (A.du==B.du) -function ==(A::Tridiagonal, B::SymTridiagonal) - iseq = all(Iterators.map((x, y) -> x == transpose(y), A.du, A.dl)) - iseq = iseq && A.du == _evview(B) - iseq && all(Iterators.map((x, y) -> x == symmetric(y, :U), A.d, B.dv)) -end -==(A::SymTridiagonal, B::Tridiagonal) = B == A - -det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du) - -AbstractMatrix{T}(M::Tridiagonal) where {T} = Tridiagonal{T}(M) -AbstractMatrix{T}(M::Tridiagonal{T}) where {T} = copy(M) -Tridiagonal{T}(M::SymTridiagonal{T}) where {T} = Tridiagonal(M) -function SymTridiagonal{T}(M::Tridiagonal) where T - if issymmetric(M) - return SymTridiagonal{T}(convert(AbstractVector{T},M.d), convert(AbstractVector{T},M.dl)) - else - throw(ArgumentError("Tridiagonal is not symmetric, cannot convert to SymTridiagonal")) - end -end - -Base._sum(A::Tridiagonal, ::Colon) = sum(A.d) + sum(A.dl) + sum(A.du) -function Base._sum(A::SymTridiagonal, ::Colon) - se = sum(_evview(A)) - symmetric(sum(A.dv), :U) + se + transpose(se) -end - -function Base._sum(A::Tridiagonal, dims::Integer) - res = Base.reducedim_initarray(A, dims, zero(eltype(A))) - n = length(A.d) - if n == 0 - return res - elseif n == 1 - res[1] = A.d[1] - return res - end - @inbounds begin - if dims == 1 - res[1] = A.dl[1] + A.d[1] - for i = 2:n-1 - res[i] = A.dl[i] + A.d[i] + A.du[i-1] - end - res[n] = A.d[n] + A.du[n-1] - elseif dims == 2 - res[1] = A.d[1] + A.du[1] - for i = 2:n-1 - res[i] = A.dl[i-1] + A.d[i] + A.du[i] - end - res[n] = A.dl[n-1] + A.d[n] - elseif dims >= 3 - for i = 1:n-1 - res[i,i+1] = A.du[i] - res[i,i] = A.d[i] - res[i+1,i] = A.dl[i] - end - res[n,n] = A.d[n] - end - end - res -end - -function Base._sum(A::SymTridiagonal, dims::Integer) - res = Base.reducedim_initarray(A, dims, zero(eltype(A))) - n = length(A.dv) - if n == 0 - return res - elseif n == 1 - res[1] = A.dv[1] - return res - end - @inbounds begin - if dims == 1 - res[1] = transpose(A.ev[1]) + symmetric(A.dv[1], :U) - for i = 2:n-1 - res[i] = transpose(A.ev[i]) + symmetric(A.dv[i], :U) + A.ev[i-1] - end - res[n] = symmetric(A.dv[n], :U) + A.ev[n-1] - elseif dims == 2 - res[1] = symmetric(A.dv[1], :U) + A.ev[1] - for i = 2:n-1 - res[i] = transpose(A.ev[i-1]) + symmetric(A.dv[i], :U) + A.ev[i] - end - res[n] = transpose(A.ev[n-1]) + symmetric(A.dv[n], :U) - elseif dims >= 3 - for i = 1:n-1 - res[i,i+1] = A.ev[i] - res[i,i] = symmetric(A.dv[i], :U) - res[i+1,i] = transpose(A.ev[i]) - end - res[n,n] = symmetric(A.dv[n], :U) - end - end - res -end - -function dot(x::AbstractVector, A::Tridiagonal, y::AbstractVector) - require_one_based_indexing(x, y) - nx, ny = length(x), length(y) - (nx == size(A, 1) == ny) || throw(DimensionMismatch()) - if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) - return dot(x[1], A.d[1], y[1]) - end - @inbounds begin - x₀ = x[1] - x₊ = x[2] - dl, d, du = A.dl, A.d, A.du - r = dot(adjoint(d[1])*x₀ + adjoint(dl[1])*x₊, y[1]) - for j in 2:nx-1 - x₋, x₀, x₊ = x₀, x₊, x[j+1] - r += dot(adjoint(du[j-1])*x₋ + adjoint(d[j])*x₀ + adjoint(dl[j])*x₊, y[j]) - end - r += dot(adjoint(du[nx-1])*x₀ + adjoint(d[nx])*x₊, y[nx]) - end - return r -end - -function cholesky(S::SymTridiagonal, ::NoPivot = NoPivot(); check::Bool = true) - if !ishermitian(S) - check && checkpositivedefinite(-1) - return Cholesky(S, 'U', convert(BlasInt, -1)) - end - T = choltype(S) - cholesky!(Hermitian(Bidiagonal{T}(diag(S, 0), diag(S, 1), :U)), NoPivot(); check = check) -end - -# See dgtsv.f -""" - ldiv!(A::Tridiagonal, B::AbstractVecOrMat) -> B - -Compute `A \\ B` in-place by Gaussian elimination with partial pivoting and store the result -in `B`, returning the result. In the process, the diagonals of `A` are overwritten as well. - -!!! compat "Julia 1.11" - `ldiv!` for `Tridiagonal` left-hand sides requires at least Julia 1.11. -""" -function ldiv!(A::Tridiagonal, B::AbstractVecOrMat) - LinearAlgebra.require_one_based_indexing(B) - n = size(A, 1) - if n != size(B,1) - throw(DimensionMismatch(lazy"matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) - end - nrhs = size(B, 2) - - # Initialize variables - dl = A.dl - d = A.d - du = A.du - - @inbounds begin - for i in 1:n-1 - # pivot or not? - if abs(d[i]) >= abs(dl[i]) - # No interchange - if d[i] != 0 - fact = dl[i]/d[i] - d[i+1] -= fact*du[i] - for j in 1:nrhs - B[i+1,j] -= fact*B[i,j] - end - else - checknonsingular(i) - end - i < n-1 && (dl[i] = 0) - else - # Interchange - fact = d[i]/dl[i] - d[i] = dl[i] - tmp = d[i+1] - d[i+1] = du[i] - fact*tmp - du[i] = tmp - if i < n-1 - dl[i] = du[i+1] - du[i+1] = -fact*dl[i] - end - for j in 1:nrhs - temp = B[i,j] - B[i,j] = B[i+1,j] - B[i+1,j] = temp - fact*B[i+1,j] - end - end - end - iszero(d[n]) && checknonsingular(n) - # backward substitution - for j in 1:nrhs - B[n,j] /= d[n] - if n > 1 - B[n-1,j] = (B[n-1,j] - du[n-1]*B[n,j])/d[n-1] - end - for i in n-2:-1:1 - B[i,j] = (B[i,j] - du[i]*B[i+1,j] - dl[i]*B[i+2,j]) / d[i] - end - end - end - return B -end - -# combinations of Tridiagonal and Symtridiagonal -# copyto! for matching axes -function _copyto_banded!(A::Tridiagonal, B::SymTridiagonal) - Bev = _evview(B) - A.du .= Bev - # Broadcast identity for numbers to access the faster copyto! path - # This uses the fact that transpose(x::Number) = x and symmetric(x::Number) = x - A.dl .= (eltype(B) <: Number ? identity : transpose).(Bev) - A.d .= (eltype(B) <: Number ? identity : symmetric).(B.dv) - return A -end -function _copyto_banded!(A::SymTridiagonal, B::Tridiagonal) - issymmetric(B) || throw(ArgumentError("cannot copy an asymmetric Tridiagonal matrix to a SymTridiagonal")) - A.dv .= B.d - _evview(A) .= B.du - return A -end - -# display -function show(io::IO, T::Tridiagonal) - print(io, "Tridiagonal(") - show(io, T.dl) - print(io, ", ") - show(io, T.d) - print(io, ", ") - show(io, T.du) - print(io, ")") -end -function show(io::IO, S::SymTridiagonal) - print(io, "SymTridiagonal(") - show(io, _diagview(S)) - print(io, ", ") - show(io, S.ev) - print(io, ")") -end diff --git a/stdlib/LinearAlgebra/src/uniformscaling.jl b/stdlib/LinearAlgebra/src/uniformscaling.jl deleted file mode 100644 index 4422799fada85..0000000000000 --- a/stdlib/LinearAlgebra/src/uniformscaling.jl +++ /dev/null @@ -1,448 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -import Base: copy, adjoint, getindex, show, transpose, one, zero, inv, float, - hcat, vcat, hvcat, ^ - -""" - UniformScaling{T<:Number} - -Generically sized uniform scaling operator defined as a scalar times -the identity operator, `λ*I`. Although without an explicit `size`, it -acts similarly to a matrix in many cases and includes support for some -indexing. See also [`I`](@ref). - -!!! compat "Julia 1.6" - Indexing using ranges is available as of Julia 1.6. - -# Examples -```jldoctest -julia> J = UniformScaling(2.) -UniformScaling{Float64} -2.0*I - -julia> A = [1. 2.; 3. 4.] -2×2 Matrix{Float64}: - 1.0 2.0 - 3.0 4.0 - -julia> J*A -2×2 Matrix{Float64}: - 2.0 4.0 - 6.0 8.0 - -julia> J[1:2, 1:2] -2×2 Matrix{Float64}: - 2.0 0.0 - 0.0 2.0 -``` -""" -struct UniformScaling{T<:Number} - λ::T -end - -""" - I - -An object of type [`UniformScaling`](@ref), representing an identity matrix of any size. - -# Examples -```jldoctest -julia> fill(1, (5,6)) * I == fill(1, (5,6)) -true - -julia> [1 2im 3; 1im 2 3] * I -2×3 Matrix{Complex{Int64}}: - 1+0im 0+2im 3+0im - 0+1im 2+0im 3+0im -``` -""" -const I = UniformScaling(true) - -""" - (I::UniformScaling)(n::Integer) - -Construct a `Diagonal` matrix from a `UniformScaling`. - -!!! compat "Julia 1.2" - This method is available as of Julia 1.2. - -# Examples -```jldoctest -julia> I(3) -3×3 Diagonal{Bool, Vector{Bool}}: - 1 ⋅ ⋅ - ⋅ 1 ⋅ - ⋅ ⋅ 1 - -julia> (0.7*I)(3) -3×3 Diagonal{Float64, Vector{Float64}}: - 0.7 ⋅ ⋅ - ⋅ 0.7 ⋅ - ⋅ ⋅ 0.7 -``` -""" -(I::UniformScaling)(n::Integer) = Diagonal(fill(I.λ, n)) - -eltype(::Type{UniformScaling{T}}) where {T} = T -ndims(J::UniformScaling) = 2 -Base.has_offset_axes(::UniformScaling) = false -getindex(J::UniformScaling, ind::CartesianIndex{2}) = J[Tuple(ind)...] -getindex(J::UniformScaling, i::Integer,j::Integer) = ifelse(i==j,J.λ,zero(J.λ)) - -getindex(J::UniformScaling, n::Integer, m::AbstractVector{<:Integer}) = getindex(J, m, n) -function getindex(J::UniformScaling{T}, n::AbstractVector{<:Integer}, m::Integer) where T - v = zeros(T, axes(n)) - @inbounds for (i,ii) in pairs(n) - if ii == m - v[i] = J.λ - end - end - return v -end - -function getindex(J::UniformScaling{T}, n::AbstractVector{<:Integer}, m::AbstractVector{<:Integer}) where T - A = zeros(T, axes(n)..., axes(m)...) - @inbounds for (j,jj) in pairs(m), (i,ii) in pairs(n) - if ii == jj - A[i,j] = J.λ - end - end - return A -end - -function show(io::IO, ::MIME"text/plain", J::UniformScaling) - s = "$(J.λ)" - if occursin(r"\w+\s*[\+\-]\s*\w+", s) - s = "($s)" - end - print(io, typeof(J), "\n$s*I") -end -copy(J::UniformScaling) = UniformScaling(J.λ) - -Base.convert(::Type{UniformScaling{T}}, J::UniformScaling) where {T} = UniformScaling(convert(T, J.λ))::UniformScaling{T} - -conj(J::UniformScaling) = UniformScaling(conj(J.λ)) -real(J::UniformScaling) = UniformScaling(real(J.λ)) -imag(J::UniformScaling) = UniformScaling(imag(J.λ)) - -float(J::UniformScaling) = UniformScaling(float(J.λ)) - -transpose(J::UniformScaling) = J -adjoint(J::UniformScaling) = UniformScaling(conj(J.λ)) - -one(::Type{UniformScaling{T}}) where {T} = UniformScaling(one(T)) -one(J::UniformScaling{T}) where {T} = one(UniformScaling{T}) -oneunit(::Type{UniformScaling{T}}) where {T} = UniformScaling(oneunit(T)) -oneunit(J::UniformScaling{T}) where {T} = oneunit(UniformScaling{T}) -zero(::Type{UniformScaling{T}}) where {T} = UniformScaling(zero(T)) -zero(J::UniformScaling{T}) where {T} = zero(UniformScaling{T}) - -isdiag(::UniformScaling) = true -istriu(::UniformScaling) = true -istril(::UniformScaling) = true -issymmetric(::UniformScaling) = true -ishermitian(J::UniformScaling) = isreal(J.λ) -isposdef(J::UniformScaling) = isposdef(J.λ) - -(+)(J::UniformScaling, x::Number) = J.λ + x -(+)(x::Number, J::UniformScaling) = x + J.λ -(-)(J::UniformScaling, x::Number) = J.λ - x -(-)(x::Number, J::UniformScaling) = x - J.λ - -(+)(J::UniformScaling) = UniformScaling(+J.λ) -(+)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ+J2.λ) -(+)(B::BitArray{2}, J::UniformScaling) = Array(B) + J -(+)(J::UniformScaling, B::BitArray{2}) = J + Array(B) -(+)(J::UniformScaling, A::AbstractMatrix) = A + J - -(-)(J::UniformScaling) = UniformScaling(-J.λ) -(-)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ-J2.λ) -(-)(B::BitArray{2}, J::UniformScaling) = Array(B) - J -(-)(J::UniformScaling, B::BitArray{2}) = J - Array(B) -(-)(A::AbstractMatrix, J::UniformScaling) = A + (-J) - -# matrix functions -for f in ( :exp, :log, :cis, - :expm1, :log1p, - :sqrt, :cbrt, - :sin, :cos, :tan, - :asin, :acos, :atan, - :csc, :sec, :cot, - :acsc, :asec, :acot, - :sinh, :cosh, :tanh, - :asinh, :acosh, :atanh, - :csch, :sech, :coth, - :acsch, :asech, :acoth ) - @eval Base.$f(J::UniformScaling) = UniformScaling($f(J.λ)) -end -for f in (:sincos, :sincosd) - @eval Base.$f(J::UniformScaling) = map(UniformScaling, $f(J.λ)) -end - -# Unit{Lower/Upper}Triangular matrices become {Lower/Upper}Triangular under -# addition with a UniformScaling -for (t1, t2) in ((:UnitUpperTriangular, :UpperTriangular), - (:UnitLowerTriangular, :LowerTriangular)) - @eval begin - function (+)(UL::$t1, J::UniformScaling) - ULnew = copymutable_oftype(UL.data, Base.promote_op(+, eltype(UL), typeof(J))) - for i in axes(ULnew, 1) - ULnew[i,i] = one(ULnew[i,i]) + J - end - return ($t2)(ULnew) - end - end -end - -# Adding a complex UniformScaling to the diagonal of a Hermitian -# matrix breaks the hermiticity, if the UniformScaling is non-real. -# However, to preserve type stability, we do not special-case a -# UniformScaling{<:Complex} that happens to be real. -function (+)(A::Hermitian, J::UniformScaling{<:Complex}) - TS = Base.promote_op(+, eltype(A), typeof(J)) - B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) - for i in diagind(B, IndexStyle(B)) - B[i] = A[i] + J - end - return B -end - -function (-)(J::UniformScaling{<:Complex}, A::Hermitian) - TS = Base.promote_op(+, eltype(A), typeof(J)) - B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) - B .= .-B - for i in diagind(B, IndexStyle(B)) - B[i] = J - A[i] - end - return B -end - -function (+)(A::AbstractMatrix, J::UniformScaling) - checksquare(A) - B = copymutable_oftype(A, Base.promote_op(+, eltype(A), typeof(J))) - for i in intersect(axes(A,1), axes(A,2)) - @inbounds B[i,i] += J - end - return B -end - -function (-)(J::UniformScaling, A::AbstractMatrix) - checksquare(A) - B = convert(AbstractMatrix{Base.promote_op(+, eltype(A), typeof(J))}, -A) - for i in intersect(axes(A,1), axes(A,2)) - @inbounds B[i,i] += J - end - return B -end - -inv(J::UniformScaling) = UniformScaling(inv(J.λ)) -opnorm(J::UniformScaling, p::Real=2) = opnorm(J.λ, p) - -pinv(J::UniformScaling) = ifelse(iszero(J.λ), - UniformScaling(zero(inv(J.λ))), # type stability - UniformScaling(inv(J.λ))) - -function det(J::UniformScaling{T}) where T - if isone(J.λ) - one(T) - elseif iszero(J.λ) - zero(T) - else - throw(ArgumentError("Determinant of UniformScaling is only well-defined when λ = 0 or 1.")) - end -end - -function tr(J::UniformScaling{T}) where T - if iszero(J.λ) - zero(T) - else - throw(ArgumentError("Trace of UniformScaling is only well-defined when λ = 0")) - end -end - -*(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ*J2.λ) -*(B::BitArray{2}, J::UniformScaling) = *(Array(B), J::UniformScaling) -*(J::UniformScaling, B::BitArray{2}) = *(J::UniformScaling, Array(B)) -*(A::AbstractMatrix, J::UniformScaling) = A*J.λ -*(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) * J -*(J::UniformScaling, A::AbstractVecOrMat) = J.λ*A -*(x::Number, J::UniformScaling) = UniformScaling(x*J.λ) -*(J::UniformScaling, x::Number) = UniformScaling(J.λ*x) - -/(J1::UniformScaling, J2::UniformScaling) = J2.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ/J2.λ) -/(J::UniformScaling, A::AbstractMatrix) = - (invA = inv(A); lmul!(J.λ, convert(AbstractMatrix{promote_type(eltype(J),eltype(invA))}, invA))) -/(A::AbstractMatrix, J::UniformScaling) = J.λ == 0 ? throw(SingularException(1)) : A/J.λ -/(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) / J - -/(J::UniformScaling, x::Number) = UniformScaling(J.λ/x) -//(J::UniformScaling, x::Number) = UniformScaling(J.λ//x) - -\(J1::UniformScaling, J2::UniformScaling) = J1.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ\J2.λ) -\(J::UniformScaling, A::AbstractVecOrMat) = J.λ == 0 ? throw(SingularException(1)) : J.λ\A -\(A::AbstractMatrix, J::UniformScaling) = - (invA = inv(A); rmul!(convert(AbstractMatrix{promote_type(eltype(invA),eltype(J))}, invA), J.λ)) -\(F::Factorization, J::UniformScaling) = F \ J(size(F,1)) - -\(x::Number, J::UniformScaling) = UniformScaling(x\J.λ) - -@inline mul!(C::AbstractMatrix, A::AbstractMatrix, J::UniformScaling, alpha::Number, beta::Number) = - mul!(C, A, J.λ, alpha, beta) -@inline mul!(C::AbstractVecOrMat, J::UniformScaling, B::AbstractVecOrMat, alpha::Number, beta::Number) = - mul!(C, J.λ, B, alpha, beta) - -function mul!(out::AbstractMatrix{T}, a::Number, B::UniformScaling, α::Number, β::Number) where {T} - checksquare(out) - if iszero(β) # zero contribution of the out matrix - fill!(out, zero(T)) - elseif !isone(β) - rmul!(out, β) - end - s = convert(T, a*B.λ*α) - if !iszero(s) - @inbounds for i in diagind(out, IndexStyle(out)) - out[i] += s - end - end - return out -end -@inline mul!(out::AbstractMatrix, A::UniformScaling, b::Number, α::Number, β::Number)= - mul!(out, A.λ, UniformScaling(b), α, β) -rmul!(A::AbstractMatrix, J::UniformScaling) = rmul!(A, J.λ) -lmul!(J::UniformScaling, B::AbstractVecOrMat) = lmul!(J.λ, B) -rdiv!(A::AbstractMatrix, J::UniformScaling) = rdiv!(A, J.λ) -ldiv!(J::UniformScaling, B::AbstractVecOrMat) = ldiv!(J.λ, B) -ldiv!(Y::AbstractVecOrMat, J::UniformScaling, B::AbstractVecOrMat) = (Y .= J.λ .\ B) - -Broadcast.broadcasted(::typeof(*), x::Number,J::UniformScaling) = UniformScaling(x*J.λ) -Broadcast.broadcasted(::typeof(*), J::UniformScaling,x::Number) = UniformScaling(J.λ*x) - -Broadcast.broadcasted(::typeof(/), J::UniformScaling,x::Number) = UniformScaling(J.λ/x) - -Broadcast.broadcasted(::typeof(\), x::Number,J::UniformScaling) = UniformScaling(x\J.λ) - -(^)(J::UniformScaling, x::Number) = UniformScaling((J.λ)^x) -Base.literal_pow(::typeof(^), J::UniformScaling, x::Val) = UniformScaling(Base.literal_pow(^, J.λ, x)) - -Broadcast.broadcasted(::typeof(^), J::UniformScaling, x::Number) = UniformScaling(J.λ^x) -function Broadcast.broadcasted(::typeof(Base.literal_pow), ::typeof(^), J::UniformScaling, x::Val) - UniformScaling(Base.literal_pow(^, J.λ, x)) -end - -==(J1::UniformScaling,J2::UniformScaling) = (J1.λ == J2.λ) - -## equality comparison with UniformScaling -==(J::UniformScaling, A::AbstractMatrix) = A == J -function ==(A::AbstractMatrix, J::UniformScaling) - require_one_based_indexing(A) - size(A, 1) == size(A, 2) || return false - iszero(J.λ) && return iszero(A) - isone(J.λ) && return isone(A) - return A == J.λ*one(A) -end -function ==(A::StridedMatrix, J::UniformScaling) - size(A, 1) == size(A, 2) || return false - iszero(J.λ) && return iszero(A) - isone(J.λ) && return isone(A) - for j in axes(A, 2), i in axes(A, 1) - ifelse(i == j, A[i, j] == J.λ, iszero(A[i, j])) || return false - end - return true -end - -isequal(A::AbstractMatrix, J::UniformScaling) = false -isequal(J::UniformScaling, A::AbstractMatrix) = false - -function isapprox(J1::UniformScaling{T}, J2::UniformScaling{S}; - atol::Real=0, rtol::Real=Base.rtoldefault(T,S,atol), nans::Bool=false) where {T<:Number,S<:Number} - isapprox(J1.λ, J2.λ, rtol=rtol, atol=atol, nans=nans) -end -function isapprox(J::UniformScaling, A::AbstractMatrix; - atol::Real = 0, - rtol::Real = Base.rtoldefault(promote_leaf_eltypes(A), eltype(J), atol), - nans::Bool = false, norm::Function = norm) - n = checksquare(A) - normJ = norm === opnorm ? abs(J.λ) : - norm === LinearAlgebra.norm ? abs(J.λ) * sqrt(n) : - norm(Diagonal(fill(J.λ, n))) - return norm(A - J) <= max(atol, rtol * max(norm(A), normJ)) -end -isapprox(A::AbstractMatrix, J::UniformScaling; kwargs...) = isapprox(J, A; kwargs...) - -""" - copyto!(dest::AbstractMatrix, src::UniformScaling) - -Copies a [`UniformScaling`](@ref) onto a matrix. - -!!! compat "Julia 1.1" - In Julia 1.0 this method only supported a square destination matrix. Julia 1.1. added - support for a rectangular matrix. -""" -function copyto!(A::AbstractMatrix, J::UniformScaling) - require_one_based_indexing(A) - fill!(A, 0) - λ = J.λ - for i = 1:min(size(A,1),size(A,2)) - @inbounds A[i,i] = λ - end - return A -end - -function copyto!(A::Diagonal, J::UniformScaling) - A.diag .= J.λ - return A -end -function copyto!(A::Union{Bidiagonal, SymTridiagonal}, J::UniformScaling) - A.ev .= 0 - A.dv .= J.λ - return A -end -function copyto!(A::Tridiagonal, J::UniformScaling) - A.dl .= 0 - A.du .= 0 - A.d .= J.λ - return A -end - -""" - copy!(dest::AbstractMatrix, src::UniformScaling) - -Copies a [`UniformScaling`](@ref) onto a matrix. - -!!! compat "Julia 1.12" - This method is available as of Julia 1.12. -""" -Base.copy!(A::AbstractMatrix, J::UniformScaling) = copyto!(A, J) - -function cond(J::UniformScaling{T}) where T - onereal = inv(one(real(J.λ))) - return J.λ ≠ zero(T) ? onereal : oftype(onereal, Inf) -end - -## Matrix construction from UniformScaling -function Matrix{T}(s::UniformScaling, dims::Dims{2}) where {T} - A = zeros(T, dims) - v = T(s.λ) - for i in diagind(dims...) - @inbounds A[i] = v - end - return A -end -Matrix{T}(s::UniformScaling, m::Integer, n::Integer) where {T} = Matrix{T}(s, Dims((m, n))) -Matrix(s::UniformScaling, m::Integer, n::Integer) = Matrix(s, Dims((m, n))) -Matrix(s::UniformScaling, dims::Dims{2}) = Matrix{eltype(s)}(s, dims) -Array{T}(s::UniformScaling, dims::Dims{2}) where {T} = Matrix{T}(s, dims) -Array{T}(s::UniformScaling, m::Integer, n::Integer) where {T} = Matrix{T}(s, m, n) -Array(s::UniformScaling, m::Integer, n::Integer) = Matrix(s, m, n) -Array(s::UniformScaling, dims::Dims{2}) = Matrix(s, dims) - -dot(A::AbstractMatrix, J::UniformScaling) = dot(tr(A), J.λ) -dot(J::UniformScaling, A::AbstractMatrix) = dot(J.λ, tr(A)) - -dot(x::AbstractVector, J::UniformScaling, y::AbstractVector) = dot(x, J.λ, y) -dot(x::AbstractVector, a::Number, y::AbstractVector) = sum(t -> dot(t[1], a, t[2]), zip(x, y)) -dot(x::AbstractVector, a::Union{Real,Complex}, y::AbstractVector) = a*dot(x, y) - -# muladd -Base.muladd(A::UniformScaling, B::UniformScaling, z::UniformScaling) = - UniformScaling(A.λ * B.λ + z.λ) diff --git a/stdlib/LinearAlgebra/test/abstractq.jl b/stdlib/LinearAlgebra/test/abstractq.jl deleted file mode 100644 index 5bfd62b467718..0000000000000 --- a/stdlib/LinearAlgebra/test/abstractq.jl +++ /dev/null @@ -1,156 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestAbstractQ - -using Test -using LinearAlgebra -using LinearAlgebra: AbstractQ, AdjointQ -import LinearAlgebra: lmul!, rmul! -import Base: size, convert - -n = 5 - -@testset "custom AbstractQ type" begin - struct MyQ{T,S<:AbstractQ{T}} <: AbstractQ{T} - Q::S - end - MyQ{T}(Q::AbstractQ) where {T} = (P = convert(AbstractQ{T}, Q); MyQ{T,typeof(P)}(P)) - MyQ(Q::MyQ) = Q - - Base.size(Q::MyQ) = size(Q.Q) - LinearAlgebra.lmul!(Q::MyQ, B::AbstractVecOrMat) = lmul!(Q.Q, B) - LinearAlgebra.lmul!(adjQ::AdjointQ{<:Any,<:MyQ}, B::AbstractVecOrMat) = lmul!(parent(adjQ).Q', B) - LinearAlgebra.rmul!(A::AbstractVecOrMat, Q::MyQ) = rmul!(A, Q.Q) - LinearAlgebra.rmul!(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:MyQ}) = rmul!(A, parent(adjQ).Q') - Base.convert(::Type{AbstractQ{T}}, Q::MyQ) where {T} = MyQ{T}(Q.Q) - LinearAlgebra.det(Q::MyQ) = det(Q.Q) - - for T in (Float64, ComplexF64) - A = rand(T, n, n) - F = qr(A) - Q = MyQ(F.Q) - @test ndims(Q) == 2 - T <: Real && @test transpose(Q) == adjoint(Q) - T <: Complex && @test_throws ErrorException transpose(Q) - @test convert(AbstractQ{complex(T)}, Q) isa MyQ{complex(T)} - @test convert(AbstractQ{complex(T)}, Q') isa AdjointQ{<:complex(T),<:MyQ{complex(T)}} - @test *(Q) == Q - @test Q*I ≈ Q.Q*I rtol=2eps(real(T)) - @test Q'*I ≈ Q.Q'*I rtol=2eps(real(T)) - @test I*Q ≈ Q.Q*I rtol=2eps(real(T)) - @test I*Q' ≈ I*Q.Q' rtol=2eps(real(T)) - @test Q^3 ≈ Q*Q*Q - @test Q^2 ≈ Q*Q - @test Q^1 == Q - @test Q^(-1) == Q' - @test (Q')^(-1) == Q - @test (Q')^2 ≈ Q'*Q' - @test abs(det(Q)) ≈ 1 - @test logabsdet(Q)[1] ≈ 0 atol=2n*eps(real(T)) - y = rand(T, n) - @test Q * y ≈ Q.Q * y ≈ Q' \ y ≈ ldiv!(Q', copy(y)) ≈ ldiv!(zero(y), Q', y) - @test Q'y ≈ Q.Q' * y ≈ Q \ y ≈ ldiv!(Q, copy(y)) ≈ ldiv!(zero(y), Q, y) - @test y'Q ≈ y'Q.Q ≈ y' / Q' - @test y'Q' ≈ y'Q.Q' ≈ y' / Q - y = Matrix(y') - @test y*Q ≈ y*Q.Q ≈ y / Q' ≈ rdiv!(copy(y), Q') - @test y*Q' ≈ y*Q.Q' ≈ y / Q ≈ rdiv!(copy(y), Q) - Y = rand(T, n, n); X = similar(Y) - for transQ in (identity, adjoint), transY in (identity, adjoint), Y in (Y, Y') - @test mul!(X, transQ(Q), transY(Y)) ≈ transQ(Q) * transY(Y) ≈ transQ(Q.Q) * transY(Y) - @test mul!(X, transY(Y), transQ(Q)) ≈ transY(Y) * transQ(Q) ≈ transY(Y) * transQ(Q.Q) - end - @test convert(Matrix, Q) ≈ Matrix(Q) ≈ Q[:,:] ≈ copyto!(zeros(T, size(Q)), Q) ≈ Q.Q*I - @test convert(Matrix, Q') ≈ Matrix(Q') ≈ (Q')[:,:] ≈ copyto!(zeros(T, size(Q)), Q') ≈ Q.Q'*I - @test Q[1,:] == Q.Q[1,:] == view(Q, 1, :) - @test Q[:,1] == Q.Q[:,1] == view(Q, :, 1) - @test Q[1,1] == Q.Q[1,1] - @test Q[:] == Q.Q[:] - @test Q[:,1:3] == Q.Q[:,1:3] == view(Q, :, 1:3) - @test Q[:,1:3] ≈ Matrix(Q)[:,1:3] - @test Q[2:3,2:3] == view(Q, 2:3, 2:3) ≈ Matrix(Q)[2:3,2:3] - @test_throws BoundsError Q[0,1] - @test_throws BoundsError Q[n+1,1] - @test_throws BoundsError Q[1,0] - @test_throws BoundsError Q[1,n+1] - @test_throws BoundsError Q[:,1:n+1] - @test_throws BoundsError Q[:,0:n] - for perm in ((1, 2), (2, 1)) - P = PermutedDimsArray(zeros(T, size(Q)), perm) - @test copyto!(P, Q) ≈ Matrix(Q) - end - x = randn(T) - @test x * Q ≈ (x*I)*Q ≈ x * Q.Q - @test Q * x ≈ Q*(x*I) ≈ Q.Q * x - @test x * Q' ≈ (x*I)* Q' ≈ x * Q.Q' - @test Q' * x ≈ Q'*(x*I) ≈ Q.Q' * x - x = rand(T, 1) - Q = MyQ(qr(rand(T, 1, 1)).Q) - @test x * Q ≈ x * Q.Q - @test x * Q' ≈ x * Q.Q' - @test Q * x ≈ Q.Q * x - @test Q' * x ≈ Q.Q' * x - end - A = randn(Float64, 5, 3) - F = qr(A) - Q = MyQ(F.Q) - Prect = Matrix(F.Q) - Psquare = collect(F.Q) - @test Q == Prect - @test Q == Psquare - @test Q == F.Q*I - @test Q ≈ Prect - @test Q ≈ Psquare - @test Q ≈ F.Q*I - - @testset "similar" begin - QS = similar(Q) - @test QS isa Matrix{eltype(Q)} - @test size(QS) == size(Q) - - QS = similar(Q, Int8) - @test QS isa Matrix{Int8} - @test size(QS) == size(Q) - - QS = similar(Q, 1) - @test QS isa Vector{eltype(Q)} - @test size(QS) == (1,) - - QS = similar(Q, Int8, 2) - @test QS isa Vector{Int8} - @test size(QS) == (2,) - - QS = similar(Q, Int8, ()) - @test QS isa Array{Int8,0} - - QS = similar(Q, ()) - @test QS isa Array{eltype(Q),0} - end - - # matrix division - q, r = F - R = randn(Float64, 5, 5) - @test q / r ≈ Matrix(q) / r - @test_throws DimensionMismatch MyQ(q) / r # doesn't have size flexibility - @test q / R ≈ collect(q) / R - @test copy(r') \ q' ≈ (q / r)' - @test_throws DimensionMismatch copy(r') \ MyQ(q') - @test r \ q' ≈ r \ Matrix(q)' - @test R \ q' ≈ R \ MyQ(q') ≈ R \ collect(q') - @test R \ q ≈ R \ MyQ(q) ≈ R \ collect(q) - B = copy(A') - G = lq(B) - l, q = G - L = R - @test l \ q ≈ l \ Matrix(q) - @test_throws DimensionMismatch l \ MyQ(q) - @test L \ q ≈ L \ collect(q) - @test q' / copy(l') ≈ (l \ q)' - @test_throws DimensionMismatch MyQ(q') / copy(l') - @test q' / l ≈ Matrix(q)' / l - @test q' / L ≈ MyQ(q') / L ≈ collect(q)' / L - @test q / L ≈ Matrix(q) / L - @test MyQ(q) / L ≈ collect(q) / L -end - -end # module diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl deleted file mode 100644 index 903e3b17f0ef1..0000000000000 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ /dev/null @@ -1,273 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestAddmul - -using Base: rtoldefault -using Test -using LinearAlgebra -using LinearAlgebra: AbstractTriangular -using Random - -_rand(::Type{T}) where {T <: AbstractFloat} = T(randn()) -_rand(::Type{T}) where {F, T <: Complex{F}} = T(_rand(F), _rand(F)) -_rand(::Type{T}) where {T <: Integer} = - T(rand(max(typemin(T), -10):min(typemax(T), 10))) -_rand(::Type{BigInt}) = BigInt(_rand(Int)) - -function _rand(A::Type{<:Array}, shape) - T = eltype(A) - data = T[_rand(T) for _ in 1:prod(shape)] - return copy(reshape(data, shape)) -end - -constructor_of(::Type{T}) where T = getfield(parentmodule(T), nameof(T)) - -function _rand(A::Type{<: AbstractArray}, shape) - data = _rand(Array{eltype(A)}, shape) - T = constructor_of(A) - if A <: Union{Bidiagonal, Hermitian, Symmetric} - return T(data, rand([:U, :L])) - # Maybe test with both :U and :L? - end - return T(data) -end - -_rand(A::Type{<: SymTridiagonal{T}}, shape) where {T} = - SymTridiagonal(_rand(Symmetric{T}, shape)) - -const FloatOrC = Union{AbstractFloat, Complex{<: AbstractFloat}} -const IntegerOrC = Union{Integer, Complex{<: Integer}} -const LTri = Union{LowerTriangular, UnitLowerTriangular, Diagonal} -const UTri = Union{UpperTriangular, UnitUpperTriangular, Diagonal} - -needsquare(::Type{<:Matrix}) = false -needsquare(::Type) = true - -testdata = [] - -sizecandidates = 1:4 -floattypes = [ - Float64, Float32, ComplexF64, ComplexF32, # BlasFloat - BigFloat, -] -inttypes = [ - Int, - BigInt, -] -# `Bool` can be added to `inttypes` but it's hard to handle -# `InexactError` bug that is mentioned in: -# https://github.com/JuliaLang/julia/issues/30094#issuecomment-440175887 -alleltypes = [floattypes; inttypes] -celtypes = [Float64, ComplexF64, BigFloat, Int] - -mattypes = [ - Matrix, - Bidiagonal, - Diagonal, - Hermitian, - LowerTriangular, - SymTridiagonal, - Symmetric, - Tridiagonal, - UnitLowerTriangular, - UnitUpperTriangular, - UpperTriangular, -] - -isnanfillable(::AbstractArray) = false -isnanfillable(::Array{<:AbstractFloat}) = true -isnanfillable(A::AbstractArray{<:AbstractFloat}) = parent(A) isa Array - -""" -Sample `n` elements from `S` on average but make sure at least one -element is sampled. -""" -function sample(S, n::Real) - length(S) <= n && return S - xs = randsubseq(S, n / length(S)) - return length(xs) > 0 ? xs : rand(S, 1) # sample at least one -end - -function inputeltypes(celt, alleltypes = alleltypes) - # Skip if destination type is "too small" - celt <: Bool && return [] - filter(alleltypes) do aelt - celt <: Real && aelt <: Complex && return false - !(celt <: BigFloat) && aelt <: BigFloat && return false - !(celt <: BigInt) && aelt <: BigInt && return false - celt <: IntegerOrC && aelt <: FloatOrC && return false - if celt <: IntegerOrC && !(celt <: BigInt) - typemin(celt) > typemin(aelt) && return false - typemax(celt) < typemax(aelt) && return false - end - return true - end -end -# Note: using `randsubseq` instead of `rand` to avoid repetition. - -function inputmattypes(cmat, mattypes = mattypes) - # Skip if destination type is "too small" - cmat <: Union{Bidiagonal, Tridiagonal, SymTridiagonal, - UnitLowerTriangular, UnitUpperTriangular, - Hermitian, Symmetric} && return [] - filter(mattypes) do amat - cmat <: Diagonal && (amat <: Diagonal || return false) - cmat <: LowerTriangular && (amat <: LTri || return false) - cmat <: UpperTriangular && (amat <: UTri || return false) - return true - end -end - -n_samples = 1.5 -# n_samples = Inf # to try all combinations -for cmat in mattypes, - amat in sample(inputmattypes(cmat), n_samples), - bmat in sample(inputmattypes(cmat), n_samples), - celt in celtypes, - aelt in sample(inputeltypes(celt), n_samples), - belt in sample(inputeltypes(celt), n_samples) - - push!(testdata, (cmat{celt}, amat{aelt}, bmat{belt})) -end - -strongzero(α) = iszero(α) ? false : α -function compare_matmul(C, A, B, α, β, - rtol = max(rtoldefault.(real.(eltype.((C, A, B))))..., - rtoldefault.(real.(typeof.((α, β))))...); - Ac = collect(A), Bc = collect(B), Cc = collect(C)) - @testset let A=A, B=B, C=C, α=α, β=β - Ccopy = copy(C) - returned_mat = mul!(Ccopy, A, B, α, β) - @test returned_mat === Ccopy - atol = max(maximum(eps∘real∘float∘eltype, (C,A,B)), - maximum(eps∘real∘float∘typeof, (α,β))) - exp_val = Ac * Bc * strongzero(α) + Cc * strongzero(β) - @test collect(returned_mat) ≈ exp_val rtol=rtol atol=atol - rtol_match = isapprox(collect(returned_mat), exp_val, rtol=rtol) - if !(rtol_match || β isa Bool || isapprox(β, 0, atol=eps(typeof(β)))) - negβ = -β - returned_mat = mul!(copy(C), A, B, α, negβ) - exp_val = Ac * Bc * strongzero(α) + Cc * negβ - @test collect(returned_mat) ≈ exp_val rtol=rtol atol=atol - end - end -end - -@testset "mul!(::$TC, ::$TA, ::$TB, α, β)" for (TC, TA, TB) in testdata - if needsquare(TA) - na1 = na2 = rand(sizecandidates) - else - na1, na2 = rand(sizecandidates, 2) - end - if needsquare(TB) - nb2 = na2 - elseif needsquare(TC) - nb2 = na1 - else - nb2 = rand(sizecandidates) - end - asize = (na1, na2) - bsize = (na2, nb2) - csize = (na1, nb2) - - C = _rand(TC, csize) - A = _rand(TA, asize) - B = _rand(TB, bsize) - Cc = Matrix(C) - Ac = Matrix(A) - Bc = Matrix(B) - - @testset for α in Any[true, eltype(TC)(1), _rand(eltype(TC))], - β in Any[false, eltype(TC)(0), _rand(eltype(TC))] - - - # This is similar to how `isapprox` choose `rtol` (when - # `atol=0`) but consider all number types involved: - rtol = max(rtoldefault.(real.(eltype.((C, A, B))))..., - rtoldefault.(real.(typeof.((α, β))))...) - - compare_matmul(C, A, B, α, β, rtol; Ac, Bc, Cc) - - y = C[:, 1] - x = B[:, 1] - yc = Vector(y) - xc = Vector(x) - compare_matmul(y, A, x, α, β, rtol; Ac, Bc=xc, Cc=yc) - - if TC <: Matrix - @testset "adjoint and transpose" begin - @testset for fa in [identity, adjoint, transpose], - fb in [identity, adjoint, transpose] - fa === fb === identity && continue - - Af = fa === identity ? A : fa(_rand(TA, reverse(asize))) - Bf = fb === identity ? B : fb(_rand(TB, reverse(bsize))) - - compare_matmul(C, Af, Bf, α, β, rtol) - end - end - end - - if isnanfillable(C) - @testset "β = 0 ignores C .= NaN" begin - Ccopy = copy(C) - parent(Ccopy) .= NaN - compare_matmul(Ccopy, A, B, α, zero(eltype(C)), rtol; Ac, Bc, Cc) - end - end - - if isnanfillable(A) - @testset "α = 0 ignores A .= NaN" begin - Acopy = copy(A) - parent(Acopy) .= NaN - compare_matmul(C, Acopy, B, zero(eltype(A)), β, rtol; Ac, Bc, Cc) - end - end - end -end - -@testset "issue #55727" begin - C = zeros(1,1) - @testset "$(nameof(typeof(A)))" for A in Any[Diagonal([NaN]), - Bidiagonal([NaN], Float64[], :U), - Bidiagonal([NaN], Float64[], :L), - SymTridiagonal([NaN], Float64[]), - Tridiagonal(Float64[], [NaN], Float64[]), - ] - @testset "$(nameof(typeof(B)))" for B in Any[ - Diagonal([1.0]), - Bidiagonal([1.0], Float64[], :U), - Bidiagonal([1.0], Float64[], :L), - SymTridiagonal([1.0], Float64[]), - Tridiagonal(Float64[], [1.0], Float64[]), - ] - C .= 0 - @test mul!(C, A, B, 0.0, false)[] === 0.0 - @test mul!(C, B, A, 0.0, false)[] === 0.0 - end - end -end - -@testset "Diagonal scaling of a triangular matrix with a non-triangular destination" begin - for MT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) - U = MT(reshape([1:9;],3,3)) - M = Array(U) - D = Diagonal(1:3) - A = reshape([1:9;],3,3) - @test mul!(copy(A), U, D, 2, 3) == M * D * 2 + A * 3 - @test mul!(copy(A), D, U, 2, 3) == D * M * 2 + A * 3 - - # nan values with iszero(alpha) - D = Diagonal(fill(NaN,3)) - @test mul!(copy(A), U, D, 0, 3) == A * 3 - @test mul!(copy(A), D, U, 0, 3) == A * 3 - - # nan values with iszero(beta) - A = fill(NaN,3,3) - D = Diagonal(1:3) - @test mul!(copy(A), U, D, 2, 0) == M * D * 2 - @test mul!(copy(A), D, U, 2, 0) == D * M * 2 - end -end - -end # module diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl deleted file mode 100644 index 6cf2ff9ada09c..0000000000000 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ /dev/null @@ -1,721 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestAdjointTranspose - -using Test, LinearAlgebra - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") - -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -@testset "Adjoint and Transpose inner constructor basics" begin - intvec, intmat = [1, 2], [1 2; 3 4] - # Adjoint/Transpose eltype must match the type of the Adjoint/Transpose of the input eltype - @test_throws TypeError Adjoint{Float64,Vector{Int}}(intvec)[1,1] - @test_throws TypeError Adjoint{Float64,Matrix{Int}}(intmat)[1,1] - @test_throws TypeError Transpose{Float64,Vector{Int}}(intvec)[1,1] - @test_throws TypeError Transpose{Float64,Matrix{Int}}(intmat)[1,1] - # Adjoint/Transpose wrapped array type must match the input array type - @test_throws TypeError Adjoint{Int,Vector{Float64}}(intvec)[1,1] - @test_throws TypeError Adjoint{Int,Matrix{Float64}}(intmat)[1,1] - @test_throws TypeError Transpose{Int,Vector{Float64}}(intvec)[1,1] - @test_throws TypeError Transpose{Int,Matrix{Float64}}(intmat)[1,1] - # Adjoint/Transpose inner constructor basic functionality, concrete scalar eltype - @test (Adjoint{Int,Vector{Int}}(intvec)::Adjoint{Int,Vector{Int}}).parent === intvec - @test (Adjoint{Int,Matrix{Int}}(intmat)::Adjoint{Int,Matrix{Int}}).parent === intmat - @test (Transpose{Int,Vector{Int}}(intvec)::Transpose{Int,Vector{Int}}).parent === intvec - @test (Transpose{Int,Matrix{Int}}(intmat)::Transpose{Int,Matrix{Int}}).parent === intmat - # Adjoint/Transpose inner constructor basic functionality, abstract scalar eltype - anyvec, anymat = Any[1, 2], Any[1 2; 3 4] - @test (Adjoint{Any,Vector{Any}}(anyvec)::Adjoint{Any,Vector{Any}}).parent === anyvec - @test (Adjoint{Any,Matrix{Any}}(anymat)::Adjoint{Any,Matrix{Any}}).parent === anymat - @test (Transpose{Any,Vector{Any}}(anyvec)::Transpose{Any,Vector{Any}}).parent === anyvec - @test (Transpose{Any,Matrix{Any}}(anymat)::Transpose{Any,Matrix{Any}}).parent === anymat - # Adjoint/Transpose inner constructor basic functionality, concrete array eltype - intvecvec = [[1, 2], [3, 4]] - intmatmat = [[[1 2]] [[3 4]] [[5 6]]; [[7 8]] [[9 10]] [[11 12]]] - @test (X = Adjoint{Adjoint{Int,Vector{Int}},Vector{Vector{Int}}}(intvecvec); - isa(X, Adjoint{Adjoint{Int,Vector{Int}},Vector{Vector{Int}}}) && X.parent === intvecvec) - @test (X = Adjoint{Adjoint{Int,Matrix{Int}},Matrix{Matrix{Int}}}(intmatmat); - isa(X, Adjoint{Adjoint{Int,Matrix{Int}},Matrix{Matrix{Int}}}) && X.parent === intmatmat) - @test (X = Transpose{Transpose{Int,Vector{Int}},Vector{Vector{Int}}}(intvecvec); - isa(X, Transpose{Transpose{Int,Vector{Int}},Vector{Vector{Int}}}) && X.parent === intvecvec) - @test (X = Transpose{Transpose{Int,Matrix{Int}},Matrix{Matrix{Int}}}(intmatmat); - isa(X, Transpose{Transpose{Int,Matrix{Int}},Matrix{Matrix{Int}}}) && X.parent === intmatmat) -end - -@testset "Adjoint and Transpose outer constructor basics" begin - intvec, intmat = [1, 2], [1 2; 3 4] - # the wrapped array's eltype strictly determines the Adjoint/Transpose eltype - # so Adjoint{T}/Transpose{T} constructors are somewhat unnecessary and error-prone - # so ascertain that such calls throw whether or not T and the input eltype are compatible - @test_throws MethodError Adjoint{Int}(intvec) - @test_throws MethodError Adjoint{Int}(intmat) - @test_throws MethodError Adjoint{Float64}(intvec) - @test_throws MethodError Adjoint{Float64}(intmat) - @test_throws MethodError Transpose{Int}(intvec) - @test_throws MethodError Transpose{Int}(intmat) - @test_throws MethodError Transpose{Float64}(intvec) - @test_throws MethodError Transpose{Float64}(intmat) - # Adjoint/Transpose outer constructor basic functionality, concrete scalar eltype - @test (Adjoint(intvec)::Adjoint{Int,Vector{Int}}).parent === intvec - @test (Adjoint(intmat)::Adjoint{Int,Matrix{Int}}).parent === intmat - @test (Transpose(intvec)::Transpose{Int,Vector{Int}}).parent === intvec - @test (Transpose(intmat)::Transpose{Int,Matrix{Int}}).parent === intmat - # the tests for the inner constructors exercise abstract scalar and concrete array eltype, forgoing here -end - -@testset "Adjoint and Transpose add additional layers to already-wrapped objects" begin - intvec, intmat = [1, 2], [1 2; 3 4] - @test (A = Adjoint(Adjoint(intvec))::Adjoint{Int,Adjoint{Int,Vector{Int}}}; A.parent.parent === intvec) - @test (A = Adjoint(Adjoint(intmat))::Adjoint{Int,Adjoint{Int,Matrix{Int}}}; A.parent.parent === intmat) - @test (A = Transpose(Transpose(intvec))::Transpose{Int,Transpose{Int,Vector{Int}}}; A.parent.parent === intvec) - @test (A = Transpose(Transpose(intmat))::Transpose{Int,Transpose{Int,Matrix{Int}}}; A.parent.parent === intmat) -end - -@testset "Adjoint and Transpose basic AbstractArray functionality" begin - # vectors and matrices with real scalar eltype, and their adjoints/transposes - intvec, intmat = [1, 2], [1 2 3; 4 5 6] - tintvec, tintmat = [1 2], [1 4; 2 5; 3 6] - @testset "length methods" begin - @test length(Adjoint(intvec)) == length(intvec) - @test length(Adjoint(intmat)) == length(intmat) - @test length(Transpose(intvec)) == length(intvec) - @test length(Transpose(intmat)) == length(intmat) - end - @testset "size methods" begin - @test size(Adjoint(intvec)) == (1, length(intvec)) - @test size(Adjoint(intmat)) == reverse(size(intmat)) - @test size(Transpose(intvec)) == (1, length(intvec)) - @test size(Transpose(intmat)) == reverse(size(intmat)) - end - @testset "axes methods" begin - @test axes(Adjoint(intvec)) == (Base.OneTo(1), Base.OneTo(length(intvec))) - @test axes(Adjoint(intmat)) == reverse(axes(intmat)) - @test axes(Transpose(intvec)) == (Base.OneTo(1), Base.OneTo(length(intvec))) - @test axes(Transpose(intmat)) == reverse(axes(intmat)) - - A = OffsetArray([1,2], 2) - @test (@inferred axes(A')[2]) === axes(A,1) - @test (@inferred axes(A')[1]) === axes(A,2) - end - @testset "IndexStyle methods" begin - @test IndexStyle(Adjoint(intvec)) == IndexLinear() - @test IndexStyle(Adjoint(intmat)) == IndexCartesian() - @test IndexStyle(Transpose(intvec)) == IndexLinear() - @test IndexStyle(Transpose(intmat)) == IndexCartesian() - end - # vectors and matrices with complex scalar eltype, and their adjoints/transposes - complexintvec, complexintmat = [1im, 2im], [1im 2im 3im; 4im 5im 6im] - tcomplexintvec, tcomplexintmat = [1im 2im], [1im 4im; 2im 5im; 3im 6im] - acomplexintvec, acomplexintmat = conj.(tcomplexintvec), conj.(tcomplexintmat) - # vectors and matrices with real-vector and real-matrix eltype, and their adjoints/transposes - intvecvec = [[1, 2], [3, 4]] - tintvecvec = [[[1 2]] [[3 4]]] - intmatmat = [[[1 2]] [[3 4]] [[ 5 6]]; - [[7 8]] [[9 10]] [[11 12]]] - tintmatmat = [[hcat([1, 2])] [hcat([7, 8])]; - [hcat([3, 4])] [hcat([9, 10])]; - [hcat([5, 6])] [hcat([11, 12])]] - # vectors and matrices with complex-vector and complex-matrix eltype, and their adjoints/transposes - complexintvecvec, complexintmatmat = im .* (intvecvec, intmatmat) - tcomplexintvecvec, tcomplexintmatmat = im .* (tintvecvec, tintmatmat) - acomplexintvecvec, acomplexintmatmat = conj.(tcomplexintvecvec), conj.(tcomplexintmatmat) - @testset "getindex methods, elementary" begin - # implicitly test elementary definitions, for arrays with concrete real scalar eltype - @test Adjoint(intvec) == tintvec - @test Adjoint(intmat) == tintmat - @test Transpose(intvec) == tintvec - @test Transpose(intmat) == tintmat - # implicitly test elementary definitions, for arrays with concrete complex scalar eltype - @test Adjoint(complexintvec) == acomplexintvec - @test Adjoint(complexintmat) == acomplexintmat - @test Transpose(complexintvec) == tcomplexintvec - @test Transpose(complexintmat) == tcomplexintmat - # implicitly test elementary definitions, for arrays with concrete real-array eltype - @test Adjoint(intvecvec) == tintvecvec - @test Adjoint(intmatmat) == tintmatmat - @test Transpose(intvecvec) == tintvecvec - @test Transpose(intmatmat) == tintmatmat - # implicitly test elementary definitions, for arrays with concrete complex-array type - @test Adjoint(complexintvecvec) == acomplexintvecvec - @test Adjoint(complexintmatmat) == acomplexintmatmat - @test Transpose(complexintvecvec) == tcomplexintvecvec - @test Transpose(complexintmatmat) == tcomplexintmatmat - end - @testset "getindex(::AdjOrTransVec, ::Colon, ::AbstractArray{Int}) methods that preserve wrapper type" begin - # for arrays with concrete scalar eltype - @test Adjoint(intvec)[:, [1, 2]] == Adjoint(intvec) - @test Transpose(intvec)[:, [1, 2]] == Transpose(intvec) - @test Adjoint(complexintvec)[:, [1, 2]] == Adjoint(complexintvec) - @test Transpose(complexintvec)[:, [1, 2]] == Transpose(complexintvec) - # for arrays with concrete array eltype - @test Adjoint(intvecvec)[:, [1, 2]] == Adjoint(intvecvec) - @test Transpose(intvecvec)[:, [1, 2]] == Transpose(intvecvec) - @test Adjoint(complexintvecvec)[:, [1, 2]] == Adjoint(complexintvecvec) - @test Transpose(complexintvecvec)[:, [1, 2]] == Transpose(complexintvecvec) - end - @testset "getindex(::AdjOrTransVec, ::Colon, ::Colon) methods that preserve wrapper type" begin - # for arrays with concrete scalar eltype - @test Adjoint(intvec)[:, :] == Adjoint(intvec) - @test Transpose(intvec)[:, :] == Transpose(intvec) - @test Adjoint(complexintvec)[:, :] == Adjoint(complexintvec) - @test Transpose(complexintvec)[:, :] == Transpose(complexintvec) - # for arrays with concrete array elype - @test Adjoint(intvecvec)[:, :] == Adjoint(intvecvec) - @test Transpose(intvecvec)[:, :] == Transpose(intvecvec) - @test Adjoint(complexintvecvec)[:, :] == Adjoint(complexintvecvec) - @test Transpose(complexintvecvec)[:, :] == Transpose(complexintvecvec) - end - @testset "getindex(::AdjOrTransVec, ::Colon, ::Int) should preserve wrapper type on result entries" begin - # for arrays with concrete scalar eltype - @test Adjoint(intvec)[:, 2] == intvec[2:2] - @test Transpose(intvec)[:, 2] == intvec[2:2] - @test Adjoint(complexintvec)[:, 2] == conj.(complexintvec[2:2]) - @test Transpose(complexintvec)[:, 2] == complexintvec[2:2] - # for arrays with concrete array eltype - @test Adjoint(intvecvec)[:, 2] == Adjoint.(intvecvec[2:2]) - @test Transpose(intvecvec)[:, 2] == Transpose.(intvecvec[2:2]) - @test Adjoint(complexintvecvec)[:, 2] == Adjoint.(complexintvecvec[2:2]) - @test Transpose(complexintvecvec)[:, 2] == Transpose.(complexintvecvec[2:2]) - end - @testset "setindex! methods" begin - # for vectors with real scalar eltype - @test (wv = Adjoint(copy(intvec)); - wv === setindex!(wv, 3, 2) && - wv == setindex!(copy(tintvec), 3, 1, 2) ) - @test (wv = Transpose(copy(intvec)); - wv === setindex!(wv, 4, 2) && - wv == setindex!(copy(tintvec), 4, 1, 2) ) - # for matrices with real scalar eltype - @test (wA = Adjoint(copy(intmat)); - wA === setindex!(wA, 7, 3, 1) && - wA == setindex!(copy(tintmat), 7, 3, 1) ) - @test (wA = Transpose(copy(intmat)); - wA === setindex!(wA, 7, 3, 1) && - wA == setindex!(copy(tintmat), 7, 3, 1) ) - # for vectors with complex scalar eltype - @test (wz = Adjoint(copy(complexintvec)); - wz === setindex!(wz, 3im, 2) && - wz == setindex!(copy(acomplexintvec), 3im, 1, 2) ) - @test (wz = Transpose(copy(complexintvec)); - wz === setindex!(wz, 4im, 2) && - wz == setindex!(copy(tcomplexintvec), 4im, 1, 2) ) - # for matrices with complex scalar eltype - @test (wZ = Adjoint(copy(complexintmat)); - wZ === setindex!(wZ, 7im, 3, 1) && - wZ == setindex!(copy(acomplexintmat), 7im, 3, 1) ) - @test (wZ = Transpose(copy(complexintmat)); - wZ === setindex!(wZ, 7im, 3, 1) && - wZ == setindex!(copy(tcomplexintmat), 7im, 3, 1) ) - # for vectors with concrete real-vector eltype - @test (wv = Adjoint(copy(intvecvec)); - wv === setindex!(wv, Adjoint([5, 6]), 2) && - wv == setindex!(copy(tintvecvec), [5 6], 2)) - @test (wv = Transpose(copy(intvecvec)); - wv === setindex!(wv, Transpose([5, 6]), 2) && - wv == setindex!(copy(tintvecvec), [5 6], 2)) - # for matrices with concrete real-matrix eltype - @test (wA = Adjoint(copy(intmatmat)); - wA === setindex!(wA, Adjoint([13 14]), 3, 1) && - wA == setindex!(copy(tintmatmat), hcat([13, 14]), 3, 1)) - @test (wA = Transpose(copy(intmatmat)); - wA === setindex!(wA, Transpose([13 14]), 3, 1) && - wA == setindex!(copy(tintmatmat), hcat([13, 14]), 3, 1)) - # for vectors with concrete complex-vector eltype - @test (wz = Adjoint(copy(complexintvecvec)); - wz === setindex!(wz, Adjoint([5im, 6im]), 2) && - wz == setindex!(copy(acomplexintvecvec), [-5im -6im], 2)) - @test (wz = Transpose(copy(complexintvecvec)); - wz === setindex!(wz, Transpose([5im, 6im]), 2) && - wz == setindex!(copy(tcomplexintvecvec), [5im 6im], 2)) - # for matrices with concrete complex-matrix eltype - @test (wZ = Adjoint(copy(complexintmatmat)); - wZ === setindex!(wZ, Adjoint([13im 14im]), 3, 1) && - wZ == setindex!(copy(acomplexintmatmat), hcat([-13im, -14im]), 3, 1)) - @test (wZ = Transpose(copy(complexintmatmat)); - wZ === setindex!(wZ, Transpose([13im 14im]), 3, 1) && - wZ == setindex!(copy(tcomplexintmatmat), hcat([13im, 14im]), 3, 1)) - end -end - -@testset "Adjoint and Transpose convert methods that convert underlying storage" begin - intvec, intmat = [1, 2], [1 2 3; 4 5 6] - @test convert(Adjoint{Float64,Vector{Float64}}, Adjoint(intvec))::Adjoint{Float64,Vector{Float64}} == Adjoint(intvec) - @test convert(Adjoint{Float64,Matrix{Float64}}, Adjoint(intmat))::Adjoint{Float64,Matrix{Float64}} == Adjoint(intmat) - @test convert(Transpose{Float64,Vector{Float64}}, Transpose(intvec))::Transpose{Float64,Vector{Float64}} == Transpose(intvec) - @test convert(Transpose{Float64,Matrix{Float64}}, Transpose(intmat))::Transpose{Float64,Matrix{Float64}} == Transpose(intmat) -end - -isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) -using .Main.ImmutableArrays - -@testset "Adjoint and Transpose convert methods to AbstractArray" begin - # tests corresponding to #34995 - intvec, intmat = [1, 2], [1 2 3; 4 5 6] - statvec = ImmutableArray(intvec) - statmat = ImmutableArray(intmat) - - @test convert(AbstractArray{Float64}, Adjoint(statvec))::Adjoint{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Adjoint(statvec) - @test convert(AbstractArray{Float64}, Adjoint(statmat))::Array{Float64,2} == Adjoint(statmat) - @test convert(AbstractArray{Float64}, Transpose(statvec))::Transpose{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Transpose(statvec) - @test convert(AbstractArray{Float64}, Transpose(statmat))::Array{Float64,2} == Transpose(statmat) - @test convert(AbstractMatrix{Float64}, Adjoint(statvec))::Adjoint{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Adjoint(statvec) - @test convert(AbstractMatrix{Float64}, Adjoint(statmat))::Array{Float64,2} == Adjoint(statmat) - @test convert(AbstractMatrix{Float64}, Transpose(statvec))::Transpose{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Transpose(statvec) - @test convert(AbstractMatrix{Float64}, Transpose(statmat))::Array{Float64,2} == Transpose(statmat) -end - -@testset "Adjoint and Transpose similar methods" begin - intvec, intmat = [1, 2], [1 2 3; 4 5 6] - # similar with no additional specifications, vector (rewrapping) semantics - @test size(similar(Adjoint(intvec))::Adjoint{Int,Vector{Int}}) == size(Adjoint(intvec)) - @test size(similar(Transpose(intvec))::Transpose{Int,Vector{Int}}) == size(Transpose(intvec)) - # similar with no additional specifications, matrix (no-rewrapping) semantics - @test size(similar(Adjoint(intmat))::Matrix{Int}) == size(Adjoint(intmat)) - @test size(similar(Transpose(intmat))::Matrix{Int}) == size(Transpose(intmat)) - # similar with element type specification, vector (rewrapping) semantics - @test size(similar(Adjoint(intvec), Float64)::Adjoint{Float64,Vector{Float64}}) == size(Adjoint(intvec)) - @test size(similar(Transpose(intvec), Float64)::Transpose{Float64,Vector{Float64}}) == size(Transpose(intvec)) - # similar with element type specification, matrix (no-rewrapping) semantics - @test size(similar(Adjoint(intmat), Float64)::Matrix{Float64}) == size(Adjoint(intmat)) - @test size(similar(Transpose(intmat), Float64)::Matrix{Float64}) == size(Transpose(intmat)) - # similar with element type and arbitrary dims specifications - shape = (2, 2, 2) - @test size(similar(Adjoint(intvec), Float64, shape)::Array{Float64,3}) == shape - @test size(similar(Adjoint(intmat), Float64, shape)::Array{Float64,3}) == shape - @test size(similar(Transpose(intvec), Float64, shape)::Array{Float64,3}) == shape - @test size(similar(Transpose(intmat), Float64, shape)::Array{Float64,3}) == shape -end - -@testset "Adjoint and Transpose parent methods" begin - intvec, intmat = [1, 2], [1 2 3; 4 5 6] - @test parent(Adjoint(intvec)) === intvec - @test parent(Adjoint(intmat)) === intmat - @test parent(Transpose(intvec)) === intvec - @test parent(Transpose(intmat)) === intmat -end - -@testset "Adjoint and Transpose vector vec methods" begin - intvec = [1, 2] - @test vec(Adjoint(intvec)) === intvec - @test vec(Transpose(intvec)) === intvec - cvec = [1 + 1im] - @test vec(cvec')[1] == cvec[1]' - mvec = [[1 2; 3 4+5im]]; - @test vec(transpose(mvec))[1] == transpose(mvec[1]) - @test vec(adjoint(mvec))[1] == adjoint(mvec[1]) -end - -@testset "horizontal concatenation of Adjoint/Transpose-wrapped vectors and Numbers" begin - # horizontal concatenation of Adjoint/Transpose-wrapped vectors and Numbers - # should preserve the Adjoint/Transpose-wrapper to preserve semantics downstream - vec, tvec, avec = [1im, 2im], [1im 2im], [-1im -2im] - vecvec = [[1im, 2im], [3im, 4im]] - tvecvec = [[[1im 2im]] [[3im 4im]]] - avecvec = [[[-1im -2im]] [[-3im -4im]]] - # for arrays with concrete scalar eltype - @test hcat(Adjoint(vec), Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == hcat(avec, avec) - @test hcat(Adjoint(vec), 1, Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == hcat(avec, 1, avec) - @test hcat(Transpose(vec), Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == hcat(tvec, tvec) - @test hcat(Transpose(vec), 1, Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == hcat(tvec, 1, tvec) - # for arrays with concrete array eltype - @test hcat(Adjoint(vecvec), Adjoint(vecvec))::Adjoint{Adjoint{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == hcat(avecvec, avecvec) - @test hcat(Transpose(vecvec), Transpose(vecvec))::Transpose{Transpose{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == hcat(tvecvec, tvecvec) -end - -@testset "map/broadcast over Adjoint/Transpose-wrapped vectors and Numbers" begin - # map and broadcast over Adjoint/Transpose-wrapped vectors and Numbers - # should preserve the Adjoint/Transpose-wrapper to preserve semantics downstream - vec, tvec, avec = [1im, 2im], [1im 2im], [-1im -2im] - vecvec = [[1im, 2im], [3im, 4im]] - tvecvec = [[[1im 2im]] [[3im 4im]]] - avecvec = [[[-1im -2im]] [[-3im -4im]]] - # unary map over wrapped vectors with concrete scalar eltype - @test map(-, Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == -avec - @test map(-, Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == -tvec - # unary map over wrapped vectors with concrete array eltype - @test map(-, Adjoint(vecvec))::Adjoint{Adjoint{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == -avecvec - @test map(-, Transpose(vecvec))::Transpose{Transpose{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == -tvecvec - # binary map over wrapped vectors with concrete scalar eltype - @test map(+, Adjoint(vec), Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == avec + avec - @test map(+, Transpose(vec), Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == tvec + tvec - # binary map over wrapped vectors with concrete array eltype - @test map(+, Adjoint(vecvec), Adjoint(vecvec))::Adjoint{Adjoint{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == avecvec + avecvec - @test map(+, Transpose(vecvec), Transpose(vecvec))::Transpose{Transpose{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == tvecvec + tvecvec - # unary broadcast over wrapped vectors with concrete scalar eltype - @test broadcast(-, Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == -avec - @test broadcast(-, Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == -tvec - # unary broadcast over wrapped vectors with concrete array eltype - @test broadcast(-, Adjoint(vecvec))::Adjoint{Adjoint{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == -avecvec - @test broadcast(-, Transpose(vecvec))::Transpose{Transpose{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == -tvecvec - # binary broadcast over wrapped vectors with concrete scalar eltype - @test broadcast(+, Adjoint(vec), Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == avec + avec - @test broadcast(+, Transpose(vec), Transpose(vec))::Transpose{Complex{Int},Vector{Complex{Int}}} == tvec + tvec - # binary broadcast over wrapped vectors with concrete array eltype - @test broadcast(+, Adjoint(vecvec), Adjoint(vecvec))::Adjoint{Adjoint{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == avecvec + avecvec - @test broadcast(+, Transpose(vecvec), Transpose(vecvec))::Transpose{Transpose{Complex{Int},Vector{Complex{Int}}},Vector{Vector{Complex{Int}}}} == tvecvec + tvecvec - # trinary broadcast over wrapped vectors with concrete scalar eltype and numbers - @test broadcast(+, Adjoint(vec), 1, Adjoint(vec))::Adjoint{Complex{Int},Vector{Complex{Int}}} == avec + avec .+ 1 - @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 -end - -@testset "Adjoint/Transpose-wrapped vector multiplication" begin - realvec, realmat = [1, 2, 3], [1 2 3; 4 5 6; 7 8 9] - complexvec, complexmat = [1im, 2, -3im], [1im 2 3; 4 5 -6im; 7im 8 9] - # Adjoint/Transpose-vector * vector - @test Adjoint(realvec) * realvec == dot(realvec, realvec) - @test Transpose(realvec) * realvec == dot(realvec, realvec) - @test Adjoint(complexvec) * complexvec == dot(complexvec, complexvec) - @test Transpose(complexvec) * complexvec == dot(conj(complexvec), complexvec) - # vector * Adjoint/Transpose-vector - @test realvec * Adjoint(realvec) == broadcast(*, realvec, reshape(realvec, (1, 3))) - @test realvec * Transpose(realvec) == broadcast(*, realvec, reshape(realvec, (1, 3))) - @test complexvec * Adjoint(complexvec) == broadcast(*, complexvec, reshape(conj(complexvec), (1, 3))) - @test complexvec * Transpose(complexvec) == broadcast(*, complexvec, reshape(complexvec, (1, 3))) - # Adjoint/Transpose-vector * matrix - @test (Adjoint(realvec) * realmat)::Adjoint{Int,Vector{Int}} == - reshape(copy(Adjoint(realmat)) * realvec, (1, 3)) - @test (Transpose(realvec) * realmat)::Transpose{Int,Vector{Int}} == - reshape(copy(Transpose(realmat)) * realvec, (1, 3)) - @test (Adjoint(complexvec) * complexmat)::Adjoint{Complex{Int},Vector{Complex{Int}}} == - reshape(conj(copy(Adjoint(complexmat)) * complexvec), (1, 3)) - @test (Transpose(complexvec) * complexmat)::Transpose{Complex{Int},Vector{Complex{Int}}} == - reshape(copy(Transpose(complexmat)) * complexvec, (1, 3)) - # Adjoint/Transpose-vector * Adjoint/Transpose-matrix - @test (Adjoint(realvec) * Adjoint(realmat))::Adjoint{Int,Vector{Int}} == - reshape(realmat * realvec, (1, 3)) - @test (Transpose(realvec) * Transpose(realmat))::Transpose{Int,Vector{Int}} == - reshape(realmat * realvec, (1, 3)) - @test (Adjoint(complexvec) * Adjoint(complexmat))::Adjoint{Complex{Int},Vector{Complex{Int}}} == - reshape(conj(complexmat * complexvec), (1, 3)) - @test (Transpose(complexvec) * Transpose(complexmat))::Transpose{Complex{Int},Vector{Complex{Int}}} == - reshape(complexmat * complexvec, (1, 3)) -end - -@testset "Adjoint/Transpose-wrapped vector pseudoinversion" begin - realvec, complexvec = [1, 2, 3, 4], [1im, 2, 3im, 4] - rowrealvec, rowcomplexvec = reshape(realvec, (1, 4)), reshape(complexvec, (1, 4)) - # pinv(Adjoint/Transpose-vector) should match matrix equivalents - # TODO tighten type asserts once pinv yields Transpose/Adjoint - @test pinv(Adjoint(realvec))::Vector{Float64} ≈ pinv(rowrealvec) - @test pinv(Transpose(realvec))::Vector{Float64} ≈ pinv(rowrealvec) - @test pinv(Adjoint(complexvec))::Vector{ComplexF64} ≈ pinv(conj(rowcomplexvec)) - @test pinv(Transpose(complexvec))::Vector{ComplexF64} ≈ pinv(rowcomplexvec) -end - -@testset "Adjoint/Transpose-wrapped vector left-division" begin - realvec, complexvec = [1., 2., 3., 4.,], [1.0im, 2., 3.0im, 4.] - rowrealvec, rowcomplexvec = reshape(realvec, (1, 4)), reshape(complexvec, (1, 4)) - # \(Adjoint/Transpose-vector, Adjoint/Transpose-vector) should mat matrix equivalents - @test Adjoint(realvec)\Adjoint(realvec) ≈ rowrealvec\rowrealvec - @test Transpose(realvec)\Transpose(realvec) ≈ rowrealvec\rowrealvec - @test Adjoint(complexvec)\Adjoint(complexvec) ≈ conj(rowcomplexvec)\conj(rowcomplexvec) - @test Transpose(complexvec)\Transpose(complexvec) ≈ rowcomplexvec\rowcomplexvec -end - -@testset "Adjoint/Transpose-wrapped vector right-division" begin - realvec, realmat = [1, 2, 3], [1 0 0; 0 2 0; 0 0 3] - complexvec, complexmat = [1im, 2, -3im], [2im 0 0; 0 3 0; 0 0 -5im] - rowrealvec, rowcomplexvec = reshape(realvec, (1, 3)), reshape(complexvec, (1, 3)) - # /(Adjoint/Transpose-vector, matrix) - @test (Adjoint(realvec) / realmat)::Adjoint ≈ rowrealvec / realmat - @test (Adjoint(complexvec) / complexmat)::Adjoint ≈ conj(rowcomplexvec) / complexmat - @test (Transpose(realvec) / realmat)::Transpose ≈ rowrealvec / realmat - @test (Transpose(complexvec) / complexmat)::Transpose ≈ rowcomplexvec / complexmat - # /(Adjoint/Transpose-vector, Adjoint matrix) - @test (Adjoint(realvec) / Adjoint(realmat))::Adjoint ≈ rowrealvec / copy(Adjoint(realmat)) - @test (Adjoint(complexvec) / Adjoint(complexmat))::Adjoint ≈ conj(rowcomplexvec) / copy(Adjoint(complexmat)) - @test (Transpose(realvec) / Adjoint(realmat))::Transpose ≈ rowrealvec / copy(Adjoint(realmat)) - @test (Transpose(complexvec) / Adjoint(complexmat))::Transpose ≈ rowcomplexvec / copy(Adjoint(complexmat)) - # /(Adjoint/Transpose-vector, Transpose matrix) - @test (Adjoint(realvec) / Transpose(realmat))::Adjoint ≈ rowrealvec / copy(Transpose(realmat)) - @test (Adjoint(complexvec) / Transpose(complexmat))::Adjoint ≈ conj(rowcomplexvec) / copy(Transpose(complexmat)) - @test (Transpose(realvec) / Transpose(realmat))::Transpose ≈ rowrealvec / copy(Transpose(realmat)) - @test (Transpose(complexvec) / Transpose(complexmat))::Transpose ≈ rowcomplexvec / copy(Transpose(complexmat)) -end - -@testset "norm and opnorm of Adjoint/Transpose-wrapped vectors" begin - # definitions are in base/linalg/generic.jl - realvec, complexvec = [3, -4], [3im, -4im] - # one norm result should be sum(abs.(realvec)) == 7 - # two norm result should be sqrt(sum(abs.(realvec))) == 5 - # inf norm result should be maximum(abs.(realvec)) == 4 - for v in (realvec, complexvec) - @test norm(Adjoint(v)) ≈ 5 - @test norm(Adjoint(v), 1) ≈ 7 - @test norm(Adjoint(v), Inf) ≈ 4 - @test norm(Transpose(v)) ≈ 5 - @test norm(Transpose(v), 1) ≈ 7 - @test norm(Transpose(v), Inf) ≈ 4 - end - # one opnorm result should be maximum(abs.(realvec)) == 4 - # two opnorm result should be sqrt(sum(abs.(realvec))) == 5 - # inf opnorm result should be sum(abs.(realvec)) == 7 - for v in (realvec, complexvec) - @test opnorm(Adjoint(v)) ≈ 5 - @test opnorm(Adjoint(v), 1) ≈ 4 - @test opnorm(Adjoint(v), Inf) ≈ 7 - @test opnorm(Transpose(v)) ≈ 5 - @test opnorm(Transpose(v), 1) ≈ 4 - @test opnorm(Transpose(v), Inf) ≈ 7 - end -end - -@testset "adjoint and transpose of Numbers" begin - @test adjoint(1) == 1 - @test adjoint(1.0) == 1.0 - @test adjoint(1im) == -1im - @test adjoint(1.0im) == -1.0im - @test transpose(1) == 1 - @test transpose(1.0) == 1.0 - @test transpose(1im) == 1im - @test transpose(1.0im) == 1.0im -end - -@testset "adjoint!(a, b) return a" begin - a = fill(1.0+im, 5) - b = fill(1.0+im, 1, 5) - @test adjoint!(a, b) === a - @test adjoint!(b, a) === b -end - -@testset "copyto! uses adjoint!/transpose!" begin - for T in (Float64, ComplexF64), f in (transpose, adjoint), sz in ((5,4), (5,)) - S = rand(T, sz) - adjS = f(S) - A = similar(S') - copyto!(A, adjS) - @test A == adjS - end -end - -@testset "aliasing with adjoint and transpose" begin - A = collect(reshape(1:25, 5, 5)) .+ rand.().*im - B = copy(A) - B .= B' - @test B == A' - B = copy(A) - B .= transpose(B) - @test B == transpose(A) - B = copy(A) - B .= B .* B' - @test B == A .* A' -end - -@testset "test show methods for $t of Factorizations" for t in (adjoint, transpose) - A = randn(ComplexF64, 4, 4) - F = lu(A) - Fop = t(F) - @test sprint(show, Fop) == - "$t of "*sprint(show, parent(Fop)) - @test sprint((io, t) -> show(io, MIME"text/plain"(), t), Fop) == - "$t of "*sprint((io, t) -> show(io, MIME"text/plain"(), t), parent(Fop)) -end - -@testset "showarg" begin - io = IOBuffer() - - A = ones(Float64, 3,3) - - B = Adjoint(A) - @test summary(B) == "3×3 adjoint(::Matrix{Float64}) with eltype Float64" - @test Base.showarg(io, B, false) === nothing - @test String(take!(io)) == "adjoint(::Matrix{Float64})" - - B = Transpose(A) - @test summary(B) == "3×3 transpose(::Matrix{Float64}) with eltype Float64" - @test Base.showarg(io, B, false) === nothing - @test String(take!(io)) == "transpose(::Matrix{Float64})" -end - -@testset "show" begin - @test repr(adjoint([1,2,3])) == "adjoint([1, 2, 3])" - @test repr(transpose([1f0,2f0])) == "transpose(Float32[1.0, 2.0])" -end - -@testset "strided transposes" begin - for t in (Adjoint, Transpose) - @test strides(t(rand(3))) == (3, 1) - @test strides(t(rand(3,2))) == (3, 1) - @test strides(t(view(rand(3, 2), :))) == (6, 1) - @test strides(t(view(rand(3, 2), :, 1:2))) == (3, 1) - - A = rand(3) - @test pointer(t(A)) === pointer(A) - B = rand(3,1) - @test pointer(t(B)) === pointer(B) - end - @test_throws MethodError strides(Adjoint(rand(3) .+ rand(3).*im)) - @test_throws MethodError strides(Adjoint(rand(3, 2) .+ rand(3, 2).*im)) - @test strides(Transpose(rand(3) .+ rand(3).*im)) == (3, 1) - @test strides(Transpose(rand(3, 2) .+ rand(3, 2).*im)) == (3, 1) - - C = rand(3) .+ rand(3).*im - @test_throws ErrorException pointer(Adjoint(C)) - @test pointer(Transpose(C)) === pointer(C) - D = rand(3,2) .+ rand(3,2).*im - @test_throws ErrorException pointer(Adjoint(D)) - @test pointer(Transpose(D)) === pointer(D) -end - -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -@testset "offset axes" begin - s = Base.Slice(-3:3)' - @test axes(s) === (Base.OneTo(1), Base.IdentityUnitRange(-3:3)) - @test collect(LinearIndices(s)) == reshape(1:7, 1, 7) - @test collect(CartesianIndices(s)) == reshape([CartesianIndex(1,i) for i = -3:3], 1, 7) - @test s[1] == -3 - @test s[7] == 3 - @test s[4] == 0 - @test_throws BoundsError s[0] - @test_throws BoundsError s[8] - @test s[1,-3] == -3 - @test s[1, 3] == 3 - @test s[1, 0] == 0 - @test_throws BoundsError s[1,-4] - @test_throws BoundsError s[1, 4] -end - -@testset "specialized conj of Adjoint/Transpose" begin - realmat = [1 2; 3 4] - complexmat = ComplexF64[1+im 2; 3 4-im] - nested = [[complexmat] [-complexmat]; [0complexmat] [3complexmat]] - @testset "AdjOrTrans{...,$(typeof(i))}" for i in ( - realmat, vec(realmat), - complexmat, vec(complexmat), - nested, vec(nested), - ) - for (t,type) in ((transpose, Adjoint), (adjoint, Transpose)) - M = t(i) - @test conj(M) isa type - @test conj(M) == conj(collect(M)) - @test conj(conj(M)) === M - end - end - # test if `conj(transpose(::Hermitian))` is a no-op - hermitian = Hermitian([1 2+im; 2-im 3]) - @test conj(transpose(hermitian)) === hermitian -end - -@testset "empty and mismatched lengths" begin - # issue 36678 - @test_throws DimensionMismatch [1, 2]' * [1,2,3] - @test Int[]' * Int[] == 0 - @test transpose(Int[]) * Int[] == 0 -end - -@testset "reductions: $adjtrans" for adjtrans in (transpose, adjoint) - for (reduction, reduction!, op) in ((sum, sum!, +), (prod, prod!, *), (minimum, minimum!, min), (maximum, maximum!, max)) - T = op in (max, min) ? Float64 : ComplexF64 - mat = rand(T, 3,5) - rd1 = zeros(T, 1, 3) - rd2 = zeros(T, 5, 1) - rd3 = zeros(T, 1, 1) - @test reduction(adjtrans(mat)) ≈ reduction(copy(adjtrans(mat))) - @test reduction(adjtrans(mat), dims=1) ≈ reduction(copy(adjtrans(mat)), dims=1) - @test reduction(adjtrans(mat), dims=2) ≈ reduction(copy(adjtrans(mat)), dims=2) - @test reduction(adjtrans(mat), dims=(1,2)) ≈ reduction(copy(adjtrans(mat)), dims=(1,2)) - - @test reduction!(rd1, adjtrans(mat)) ≈ reduction!(rd1, copy(adjtrans(mat))) - @test reduction!(rd2, adjtrans(mat)) ≈ reduction!(rd2, copy(adjtrans(mat))) - @test reduction!(rd3, adjtrans(mat)) ≈ reduction!(rd3, copy(adjtrans(mat))) - - @test reduction(imag, adjtrans(mat)) ≈ reduction(imag, copy(adjtrans(mat))) - @test reduction(imag, adjtrans(mat), dims=1) ≈ reduction(imag, copy(adjtrans(mat)), dims=1) - @test reduction(imag, adjtrans(mat), dims=2) ≈ reduction(imag, copy(adjtrans(mat)), dims=2) - @test reduction(imag, adjtrans(mat), dims=(1,2)) ≈ reduction(imag, copy(adjtrans(mat)), dims=(1,2)) - - @test Base.mapreducedim!(imag, op, rd1, adjtrans(mat)) ≈ Base.mapreducedim!(imag, op, rd1, copy(adjtrans(mat))) - @test Base.mapreducedim!(imag, op, rd2, adjtrans(mat)) ≈ Base.mapreducedim!(imag, op, rd2, copy(adjtrans(mat))) - @test Base.mapreducedim!(imag, op, rd3, adjtrans(mat)) ≈ Base.mapreducedim!(imag, op, rd3, copy(adjtrans(mat))) - - op in (max, min) && continue - mat = [rand(T,2,2) for _ in 1:3, _ in 1:5] - rd1 = fill(zeros(T, 2, 2), 1, 3) - rd2 = fill(zeros(T, 2, 2), 5, 1) - rd3 = fill(zeros(T, 2, 2), 1, 1) - @test reduction(adjtrans(mat)) ≈ reduction(copy(adjtrans(mat))) - @test reduction(adjtrans(mat), dims=1) ≈ reduction(copy(adjtrans(mat)), dims=1) - @test reduction(adjtrans(mat), dims=2) ≈ reduction(copy(adjtrans(mat)), dims=2) - @test reduction(adjtrans(mat), dims=(1,2)) ≈ reduction(copy(adjtrans(mat)), dims=(1,2)) - - @test reduction(imag, adjtrans(mat)) ≈ reduction(imag, copy(adjtrans(mat))) - @test reduction(x -> x[1,2], adjtrans(mat)) ≈ reduction(x -> x[1,2], copy(adjtrans(mat))) - @test reduction(imag, adjtrans(mat), dims=1) ≈ reduction(imag, copy(adjtrans(mat)), dims=1) - @test reduction(x -> x[1,2], adjtrans(mat), dims=1) ≈ reduction(x -> x[1,2], copy(adjtrans(mat)), dims=1) - end - # see #46605 - Ac = [1 2; 3 4]' - @test mapreduce(identity, (x, y) -> 10x+y, copy(Ac)) == mapreduce(identity, (x, y) -> 10x+y, Ac) == 1234 - @test extrema([3,7,4]') == (3, 7) - @test mapreduce(x -> [x;;;], +, [1, 2, 3]') == sum(x -> [x;;;], [1, 2, 3]') == [6;;;] - @test mapreduce(string, *, [1 2; 3 4]') == mapreduce(string, *, copy([1 2; 3 4]')) == "1234" -end - -@testset "trace" begin - for T in (Float64, ComplexF64), t in (adjoint, transpose) - A = randn(T, 10, 10) - @test tr(t(A)) == tr(copy(t(A))) == t(tr(A)) - end -end - -@testset "structured printing" begin - D = Diagonal(1:3) - @test sprint(Base.print_matrix, Adjoint(D)) == sprint(Base.print_matrix, D) - @test sprint(Base.print_matrix, Transpose(D)) == sprint(Base.print_matrix, D) - D = Diagonal((1:3)*im) - D2 = Diagonal((1:3)*(-im)) - @test sprint(Base.print_matrix, Transpose(D)) == sprint(Base.print_matrix, D) - @test sprint(Base.print_matrix, Adjoint(D)) == sprint(Base.print_matrix, D2) - - struct OneHotVecOrMat{N} <: AbstractArray{Bool,N} - inds::NTuple{N,Int} - sz::NTuple{N,Int} - end - Base.size(x::OneHotVecOrMat) = x.sz - function Base.getindex(x::OneHotVecOrMat{N}, inds::Vararg{Int,N}) where {N} - checkbounds(x, inds...) - inds == x.inds - end - Base.replace_in_print_matrix(o::OneHotVecOrMat{1}, i::Integer, j::Integer, s::AbstractString) = - o.inds == (i,) ? s : Base.replace_with_centered_mark(s) - Base.replace_in_print_matrix(o::OneHotVecOrMat{2}, i::Integer, j::Integer, s::AbstractString) = - o.inds == (i,j) ? s : Base.replace_with_centered_mark(s) - - o = OneHotVecOrMat((2,), (4,)) - @test sprint(Base.print_matrix, Transpose(o)) == sprint(Base.print_matrix, OneHotVecOrMat((1,2), (1,4))) - @test sprint(Base.print_matrix, Adjoint(o)) == sprint(Base.print_matrix, OneHotVecOrMat((1,2), (1,4))) -end - -@testset "copy_transpose!" begin - # scalar case - A = [randn() for _ in 1:2, _ in 1:3] - At = copy(transpose(A)) - B = zero.(At) - LinearAlgebra.copy_transpose!(B, axes(B, 1), axes(B, 2), A, axes(A, 1), axes(A, 2)) - @test B == At - # matrix of matrices - A = [randn(2,3) for _ in 1:2, _ in 1:3] - At = copy(transpose(A)) - B = zero.(At) - LinearAlgebra.copy_transpose!(B, axes(B, 1), axes(B, 2), A, axes(A, 1), axes(A, 2)) - @test B == At -end - -@testset "error message in transpose" begin - v = zeros(2) - A = zeros(1,1) - B = zeros(2,3) - for (t1, t2) in Any[(A, v), (v, A), (A, B)] - @test_throws "axes of the destination are incompatible with that of the source" transpose!(t1, t2) - @test_throws "axes of the destination are incompatible with that of the source" adjoint!(t1, t2) - end -end - -end # module TestAdjointTranspose diff --git a/stdlib/LinearAlgebra/test/ambiguous_exec.jl b/stdlib/LinearAlgebra/test/ambiguous_exec.jl deleted file mode 100644 index 7b89c0a457afb..0000000000000 --- a/stdlib/LinearAlgebra/test/ambiguous_exec.jl +++ /dev/null @@ -1,21 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -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)) - expect = [] - good = true - while !isempty(ambig) - sigs = pop!(ambig) - i = findfirst(==(sigs), expect) - if i === nothing - println(stderr, "push!(expect, (", sigs[1], ", ", sigs[2], "))") - good = false - continue - end - deleteat!(expect, i) - end - @test isempty(expect) - @test good -end diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl deleted file mode 100644 index df30748e042b5..0000000000000 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ /dev/null @@ -1,1141 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestBidiagonal - -using Test, LinearAlgebra, Random -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 - -isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) -using .Main.InfiniteArrays - -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -using .Main.FillArrays - -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -include("testutils.jl") # test_approx_eq_modphase - -n = 10 #Size of test matrix -Random.seed!(1) - -@testset for relty in (Int, Float32, 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 - dv0 = zeros(elty, 0) - ev0 = zeros(elty, 0) - - @testset "Constructors" begin - for (x, y) in ((dv0, ev0), (dv, ev), (GenericArray(dv), GenericArray(ev))) - # from vectors - ubd = Bidiagonal(x, y, :U) - lbd = Bidiagonal(x, y, :L) - @test ubd != lbd || x === dv0 - @test ubd.dv === x - @test lbd.ev === y - @test_throws ArgumentError Bidiagonal(x, y, :R) - @test_throws ArgumentError Bidiagonal(x, y, 'R') - x == dv0 || @test_throws DimensionMismatch Bidiagonal(x, x, :U) - @test_throws MethodError Bidiagonal(x, y) - # from matrix - @test Bidiagonal(ubd, :U) == Bidiagonal(Matrix(ubd), :U) == ubd - @test Bidiagonal(lbd, :L) == Bidiagonal(Matrix(lbd), :L) == lbd - # from its own type - @test typeof(ubd)(ubd) === ubd - @test typeof(lbd)(lbd) === lbd - end - @test eltype(Bidiagonal{elty}([1,2,3,4], [1.0f0,2.0f0,3.0f0], :U)) == elty - @test eltype(Bidiagonal([1,2,3,4], [1.0f0,2.0f0,3.0f0], :U)) == Float32 # promotion test - @test isa(Bidiagonal{elty,Vector{elty}}(GenericArray(dv), ev, :U), Bidiagonal{elty,Vector{elty}}) - @test_throws MethodError Bidiagonal(dv, GenericArray(ev), :U) - @test_throws MethodError Bidiagonal(GenericArray(dv), ev, :U) - BI = Bidiagonal([1,2,3,4], [1,2,3], :U) - @test Bidiagonal(BI) === BI - @test isa(Bidiagonal{elty}(BI), Bidiagonal{elty}) - end - - @testset "getindex, setindex!, size, and similar" begin - ubd = Bidiagonal(dv, ev, :U) - lbd = Bidiagonal(dv, ev, :L) - # bidiagonal getindex / upper & lower - @test_throws BoundsError ubd[n + 1, 1] - @test_throws BoundsError ubd[1, n + 1] - @test ubd[2, 2] == dv[2] - # bidiagonal getindex / upper - @test ubd[2, 3] == ev[2] - @test iszero(ubd[3, 2]) - # bidiagonal getindex / lower - @test lbd[3, 2] == ev[2] - @test iszero(lbd[2, 3]) - # bidiagonal setindex! / upper - cubd = copy(ubd) - @test_throws ArgumentError ubd[2, 1] = 1 - @test_throws ArgumentError ubd[3, 1] = 1 - @test (cubd[2, 1] = 0; cubd == ubd) - @test ((cubd[1, 2] = 10) == 10; cubd[1, 2] == 10) - # bidiagonal setindex! / lower - clbd = copy(lbd) - @test_throws ArgumentError lbd[1, 2] = 1 - @test_throws ArgumentError lbd[1, 3] = 1 - @test (clbd[1, 2] = 0; clbd == lbd) - @test ((clbd[2, 1] = 10) == 10; clbd[2, 1] == 10) - # bidiagonal setindex! / upper & lower - @test_throws BoundsError ubd[n + 1, 1] = 1 - @test_throws BoundsError ubd[1, n + 1] = 1 - @test ((cubd[2, 2] = 10) == 10; cubd[2, 2] == 10) - # bidiagonal size - @test_throws BoundsError size(ubd, 0) - @test size(ubd, 1) == size(ubd, 2) == n - @test size(ubd, 3) == 1 - # bidiagonal similar - @test isa(similar(ubd), Bidiagonal{elty}) - @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)), 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') - Bl = Bidiagonal(rand(elty, 10), zeros(elty, 9), 'L') - @test_throws ArgumentError Bu[5, 4] = 1 - @test_throws ArgumentError Bl[4, 5] = 1 - - # setindex should return the destination - @test setindex!(ubd, 1, 1, 1) === ubd - end - - @testset "isstored" begin - ubd = Bidiagonal(dv, ev, :U) - lbd = Bidiagonal(dv, ev, :L) - # bidiagonal isstored / upper & lower - @test_throws BoundsError Base.isstored(ubd, n + 1, 1) - @test_throws BoundsError Base.isstored(ubd, 1, n + 1) - @test Base.isstored(ubd, 2, 2) - # bidiagonal isstored / upper - @test Base.isstored(ubd, 2, 3) - @test !Base.isstored(ubd, 3, 2) - # bidiagonal isstored / lower - @test Base.isstored(lbd, 3, 2) - @test !Base.isstored(lbd, 2, 3) - end - - @testset "show" begin - BD = Bidiagonal(dv, ev, :U) - @test sprint(show,BD) == "Bidiagonal($(repr(dv)), $(repr(ev)), :U)" - BD = Bidiagonal(dv,ev,:L) - @test sprint(show,BD) == "Bidiagonal($(repr(dv)), $(repr(ev)), :L)" - end - - @testset for uplo in (:U, :L) - T = Bidiagonal(dv, ev, uplo) - - @testset "Constructor and basic properties" begin - @test size(T, 1) == size(T, 2) == n - @test size(T) == (n, n) - @test Array(T) == diagm(0 => dv, (uplo === :U ? 1 : -1) => ev) - @test Bidiagonal(Array(T), uplo) == T - @test big.(T) == T - @test Array(abs.(T)) == abs.(diagm(0 => dv, (uplo === :U ? 1 : -1) => ev)) - @test Array(real(T)) == real(diagm(0 => dv, (uplo === :U ? 1 : -1) => ev)) - @test Array(imag(T)) == imag(diagm(0 => dv, (uplo === :U ? 1 : -1) => ev)) - end - - @testset for func in (conj, transpose, adjoint) - @test func(func(T)) == T - if func ∈ (transpose, adjoint) - @test func(func(T)) === T - end - end - - @testset "permutedims(::Bidiagonal)" begin - @test permutedims(permutedims(T)) === T - @test permutedims(T) == transpose.(transpose(T)) - @test permutedims(T, [1, 2]) === T - @test permutedims(T, (2, 1)) == permutedims(T) - end - - @testset "triu and tril" begin - zerosdv = zeros(elty, length(dv)) - zerosev = zeros(elty, length(ev)) - bidiagcopy(dv, ev, uplo) = Bidiagonal(copy(dv), copy(ev), uplo) - - @test istril(Bidiagonal(dv,ev,:L)) - @test istril(Bidiagonal(dv,ev,:L), 1) - @test !istril(Bidiagonal(dv,ev,:L), -1) - @test istril(Bidiagonal(zerosdv,ev,:L), -1) - @test !istril(Bidiagonal(zerosdv,ev,:L), -2) - @test istril(Bidiagonal(zerosdv,zerosev,:L), -2) - @test !istril(Bidiagonal(dv,ev,:U)) - @test istril(Bidiagonal(dv,ev,:U), 1) - @test !istril(Bidiagonal(dv,ev,:U), -1) - @test !istril(Bidiagonal(zerosdv,ev,:U), -1) - @test istril(Bidiagonal(zerosdv,zerosev,:U), -1) - @test tril!(bidiagcopy(dv,ev,:U),-1) == Bidiagonal(zerosdv,zerosev,:U) - @test tril!(bidiagcopy(dv,ev,:L),-1) == Bidiagonal(zerosdv,ev,:L) - @test tril!(bidiagcopy(dv,ev,:U),-2) == Bidiagonal(zerosdv,zerosev,:U) - @test tril!(bidiagcopy(dv,ev,:L),-2) == Bidiagonal(zerosdv,zerosev,:L) - @test tril!(bidiagcopy(dv,ev,:U),1) == Bidiagonal(dv,ev,:U) - @test tril!(bidiagcopy(dv,ev,:L),1) == Bidiagonal(dv,ev,:L) - @test tril!(bidiagcopy(dv,ev,:U)) == Bidiagonal(dv,zerosev,:U) - @test tril!(bidiagcopy(dv,ev,:L)) == Bidiagonal(dv,ev,:L) - @test_throws ArgumentError tril!(bidiagcopy(dv, ev, :U), -n - 2) - @test_throws ArgumentError tril!(bidiagcopy(dv, ev, :U), n) - - @test istriu(Bidiagonal(dv,ev,:U)) - @test istriu(Bidiagonal(dv,ev,:U), -1) - @test !istriu(Bidiagonal(dv,ev,:U), 1) - @test istriu(Bidiagonal(zerosdv,ev,:U), 1) - @test !istriu(Bidiagonal(zerosdv,ev,:U), 2) - @test istriu(Bidiagonal(zerosdv,zerosev,:U), 2) - @test !istriu(Bidiagonal(dv,ev,:L)) - @test istriu(Bidiagonal(dv,ev,:L), -1) - @test !istriu(Bidiagonal(dv,ev,:L), 1) - @test !istriu(Bidiagonal(zerosdv,ev,:L), 1) - @test istriu(Bidiagonal(zerosdv,zerosev,:L), 1) - @test triu!(bidiagcopy(dv,ev,:L),1) == Bidiagonal(zerosdv,zerosev,:L) - @test triu!(bidiagcopy(dv,ev,:U),1) == Bidiagonal(zerosdv,ev,:U) - @test triu!(bidiagcopy(dv,ev,:U),2) == Bidiagonal(zerosdv,zerosev,:U) - @test triu!(bidiagcopy(dv,ev,:L),2) == Bidiagonal(zerosdv,zerosev,:L) - @test triu!(bidiagcopy(dv,ev,:U),-1) == Bidiagonal(dv,ev,:U) - @test triu!(bidiagcopy(dv,ev,:L),-1) == Bidiagonal(dv,ev,:L) - @test triu!(bidiagcopy(dv,ev,:L)) == Bidiagonal(dv,zerosev,:L) - @test triu!(bidiagcopy(dv,ev,:U)) == Bidiagonal(dv,ev,:U) - @test_throws ArgumentError triu!(bidiagcopy(dv, ev, :U), -n) - @test_throws ArgumentError triu!(bidiagcopy(dv, ev, :U), n + 2) - @test !isdiag(Bidiagonal(dv,ev,:U)) - @test !isdiag(Bidiagonal(dv,ev,:L)) - @test isdiag(Bidiagonal(dv,zerosev,:U)) - @test isdiag(Bidiagonal(dv,zerosev,:L)) - end - - @testset "iszero and isone" begin - for uplo in (:U, :L) - BDzero = Bidiagonal(zeros(elty, 10), zeros(elty, 9), uplo) - BDone = Bidiagonal(ones(elty, 10), zeros(elty, 9), uplo) - BDmix = Bidiagonal(zeros(elty, 10), zeros(elty, 9), uplo) - BDmix[end,end] = one(elty) - - @test iszero(BDzero) - @test !isone(BDzero) - @test !iszero(BDone) - @test isone(BDone) - @test !iszero(BDmix) - @test !isone(BDmix) - end - end - - @testset "trace" begin - for uplo in (:U, :L) - B = Bidiagonal(dv, ev, uplo) - if relty <: Integer - @test tr(B) == tr(Matrix(B)) - else - @test tr(B) ≈ tr(Matrix(B)) rtol=2eps(relty) - end - end - end - - Tfull = Array(T) - @testset "Linear solves" begin - if relty <: AbstractFloat - c = convert(Matrix{elty}, randn(n,n)) - b = convert(Matrix{elty}, randn(n, 2)) - if (elty <: Complex) - b += im*convert(Matrix{elty}, randn(n, 2)) - end - elseif relty <: Integer - c = convert(Matrix{elty}, rand(1:10, n, n)) - b = convert(Matrix{elty}, rand(1:10, n, 2)) - if (elty <: Complex) - b += im*convert(Matrix{elty}, rand(1:10, n, 2)) - end - end - condT = cond(map(ComplexF64,Tfull)) - promty = typeof((zero(relty)*zero(relty) + zero(relty)*zero(relty))/one(relty)) - if relty != BigFloat - x = transpose(T)\transpose(c) - tx = transpose(Tfull) \ transpose(c) - elty <: AbstractFloat && @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - @test_throws DimensionMismatch transpose(T)\transpose(b) - x = T'\copy(transpose(c)) - tx = Tfull'\copy(transpose(c)) - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - @test_throws DimensionMismatch T'\copy(transpose(b)) - x = T\transpose(c) - tx = Tfull\transpose(c) - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - @test_throws DimensionMismatch T\transpose(b) - end - offsizemat = Matrix{elty}(undef, n+1, 2) - @test_throws DimensionMismatch T \ offsizemat - @test_throws DimensionMismatch transpose(T) \ offsizemat - @test_throws DimensionMismatch T' \ offsizemat - - if elty <: BigFloat - @test_throws SingularException ldiv!(Bidiagonal(zeros(elty, n), ones(elty, n-1), :U), rand(elty, n)) - @test_throws SingularException ldiv!(Bidiagonal(zeros(elty, n), ones(elty, n-1), :L), rand(elty, n)) - end - let bb = b, cc = c - for atype in ("Array", "SubArray") - if atype == "Array" - b = bb - c = cc - else - b = view(bb, 1:n) - c = view(cc, 1:n, 1:2) - end - end - x = T \ b - tx = Tfull \ b - @test_throws DimensionMismatch ldiv!(T, Vector{elty}(undef, n+1)) - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = transpose(T) \ b - tx = transpose(Tfull) \ b - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = copy(transpose(b)) / T - tx = copy(transpose(b)) / Tfull - @test_throws DimensionMismatch rdiv!(Matrix{elty}(undef, 1, n+1), T) - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = copy(transpose(b)) / transpose(T) - tx = copy(transpose(b)) / transpose(Tfull) - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - @testset "Generic Mat-vec ops" begin - @test T*b ≈ Tfull*b - @test T'*b ≈ Tfull'*b - if relty != BigFloat # not supported by pivoted QR - @test T/b' ≈ Tfull/b' - end - end - end - zdv = Vector{elty}(undef, 0) - zev = Vector{elty}(undef, 0) - zA = Bidiagonal(zdv, zev, :U) - zb = Vector{elty}(undef, 0) - @test ldiv!(zA, zb) === zb - @testset "linear solves with abstract matrices" begin - diag = b[:,1] - D = Diagonal(diag) - x = T \ D - tx = Tfull \ D - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = D / T - tx = D / Tfull - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = transpose(T) \ D - tx = transpose(Tfull) \ D - @test norm(x-tx,Inf) <= 4*condT*max(eps()*norm(tx,Inf), eps(promty)*norm(x,Inf)) - x = D / transpose(T) - tx = D / transpose(Tfull) - @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) - 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 - 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 - end - end - - if elty <: BlasReal - @testset "$f" for f in (floor, trunc, round, ceil) - @test (f.(Int, T))::Bidiagonal == Bidiagonal(f.(Int, T.dv), f.(Int, T.ev), T.uplo) - @test (f.(T))::Bidiagonal == Bidiagonal(f.(T.dv), f.(T.ev), T.uplo) - end - end - - @testset "diag" begin - @test (@inferred diag(T))::typeof(dv) == dv - @test (@inferred diag(T, uplo === :U ? 1 : -1))::typeof(dv) == ev - @test (@inferred diag(T,2))::typeof(dv) == zeros(elty, n-2) - @test isempty(@inferred diag(T, -n - 1)) - @test isempty(@inferred diag(T, n + 1)) - # test diag with another wrapped vector type - gdv, gev = GenericArray(dv), GenericArray(ev) - G = Bidiagonal(gdv, gev, uplo) - @test (@inferred diag(G))::typeof(gdv) == gdv - @test (@inferred diag(G, uplo === :U ? 1 : -1))::typeof(gdv) == gev - @test (@inferred diag(G,2))::typeof(gdv) == GenericArray(zeros(elty, n-2)) - end - - @testset "Eigensystems" begin - if relty <: AbstractFloat - d1, v1 = eigen(T) - d2, v2 = eigen(map(elty<:Complex ? ComplexF64 : Float64,Tfull), sortby=nothing) - @test (uplo === :U ? d1 : reverse(d1)) ≈ d2 - if elty <: Real - test_approx_eq_modphase(v1, uplo === :U ? v2 : v2[:,n:-1:1]) - end - end - end - - @testset "Singular systems" begin - if (elty <: BlasReal) - @test AbstractArray(svd(T)) ≈ AbstractArray(svd!(copy(Tfull))) - @test svdvals(Tfull) ≈ svdvals(T) - u1, d1, v1 = svd(Tfull) - u2, d2, v2 = svd(T) - @test d1 ≈ d2 - if elty <: Real - test_approx_eq_modphase(u1, u2) - test_approx_eq_modphase(copy(v1), copy(v2)) - end - @test 0 ≈ norm(u2*Diagonal(d2)*v2'-Tfull) atol=n*max(n^2*eps(relty),norm(u1*Diagonal(d1)*v1'-Tfull)) - @inferred svdvals(T) - @inferred svd(T) - end - end - - @testset "Binary operations" begin - @test -T == Bidiagonal(-T.dv,-T.ev,T.uplo) - @test convert(elty,-1.0) * T == Bidiagonal(-T.dv,-T.ev,T.uplo) - @test T / convert(elty,-1.0) == Bidiagonal(-T.dv,-T.ev,T.uplo) - @test T * convert(elty,-1.0) == Bidiagonal(-T.dv,-T.ev,T.uplo) - @testset for uplo2 in (:U, :L) - dv = convert(Vector{elty}, relty <: AbstractFloat ? randn(n) : rand(1:10, n)) - ev = convert(Vector{elty}, relty <: AbstractFloat ? randn(n-1) : rand(1:10, n-1)) - T2 = Bidiagonal(dv, ev, uplo2) - Tfull2 = Array(T2) - for op in (+, -, *) - @test Array(op(T, T2)) ≈ op(Tfull, Tfull2) - end - A = kron(T.dv, T.dv') - @test T * A ≈ lmul!(T, copy(A)) - @test A * T ≈ rmul!(copy(A), T) - end - # test pass-through of mul! for SymTridiagonal*Bidiagonal - TriSym = SymTridiagonal(T.dv, T.ev) - @test Array(TriSym*T) ≈ Array(TriSym)*Array(T) - # test pass-through of mul! for AbstractTriangular*Bidiagonal - Tri = UpperTriangular(diagm(1 => T.ev)) - Dia = Diagonal(T.dv) - @test Array(Tri*T) ≈ Array(Tri)*Array(T) ≈ rmul!(copy(Tri), T) - @test Array(T*Tri) ≈ Array(T)*Array(Tri) ≈ lmul!(T, copy(Tri)) - # test mul! itself for these types - for AA in (Tri, Dia) - for f in (identity, transpose, adjoint) - C = rand(elty, n, n) - D = copy(C) + 2.0 * Array(f(AA) * T) - mul!(C, f(AA), T, 2.0, 1.0) ≈ D - end - end - # test mul! for BiTrySym * adjoint/transpose AbstractMat - for f in (identity, transpose, adjoint) - C = relty == Int ? rand(float(elty), n, n) : rand(elty, n, n) - B = rand(elty, n, n) - D = C + 2.0 * Array(T*f(B)) - @test mul!(C, T, f(B), 2.0, 1.0) ≈ D - @test lmul!(T, copy(f(B))) ≈ T * f(B) - @test rmul!(copy(f(B)), T) ≈ f(B) * T - end - - # Issue #31870 - # Bi/Tri/Sym times Diagonal - Diag = Diagonal(rand(elty, 10)) - BidiagU = Bidiagonal(rand(elty, 10), rand(elty, 9), 'U') - BidiagL = Bidiagonal(rand(elty, 10), rand(elty, 9), 'L') - Tridiag = Tridiagonal(rand(elty, 9), rand(elty, 10), rand(elty, 9)) - SymTri = SymTridiagonal(rand(elty, 10), rand(elty, 9)) - - mats = Any[Diag, BidiagU, BidiagL, Tridiag, SymTri] - for a in mats - for b in mats - @test a*b ≈ Matrix(a)*Matrix(b) - end - end - - @test typeof(BidiagU*Diag) <: Bidiagonal - @test typeof(BidiagL*Diag) <: Bidiagonal - @test typeof(Tridiag*Diag) <: Tridiagonal - @test typeof(SymTri*Diag) <: Tridiagonal - - @test typeof(BidiagU*Diag) <: Bidiagonal - @test typeof(Diag*BidiagL) <: Bidiagonal - @test typeof(Diag*Tridiag) <: Tridiagonal - @test typeof(Diag*SymTri) <: Tridiagonal - end - - @test inv(T)*Tfull ≈ Matrix(I, n, n) - @test factorize(T) === T - end - BD = Bidiagonal(dv, ev, :U) - @test Matrix{ComplexF64}(BD) == BD -end - -# Issue 10742 and similar -let A = Bidiagonal([1,2,3], [0,0], :U) - @test istril(A) - @test isdiag(A) -end - -# test construct from range -@test Bidiagonal(1:3, 1:2, :U) == [1 1 0; 0 2 2; 0 0 3] - -@testset "promote_rule" begin - A = Bidiagonal(fill(1f0,10),fill(1f0,9),:U) - B = rand(Float64,10,10) - C = Tridiagonal(rand(Float64,9),rand(Float64,10),rand(Float64,9)) - @test promote_rule(Matrix{Float64}, Bidiagonal{Float64}) == Matrix{Float64} - @test promote(B,A) == (B, convert(Matrix{Float64}, A)) - @test promote(B,A) isa Tuple{Matrix{Float64}, Matrix{Float64}} - @test promote(C,A) == (C,Tridiagonal(zeros(Float64,9),convert(Vector{Float64},A.dv),convert(Vector{Float64},A.ev))) - @test promote(C,A) isa Tuple{Tridiagonal, Tridiagonal} -end - -using LinearAlgebra: fillstored!, UnitLowerTriangular -@testset "fill! and fillstored!" begin - let # fillstored! - A = Tridiagonal(randn(2), randn(3), randn(2)) - @test fillstored!(A, 3) == Tridiagonal([3, 3], [3, 3, 3], [3, 3]) - B = Bidiagonal(randn(3), randn(2), :U) - @test fillstored!(B, 2) == Bidiagonal([2,2,2], [2,2], :U) - S = SymTridiagonal(randn(3), randn(2)) - @test fillstored!(S, 1) == SymTridiagonal([1,1,1], [1,1]) - Ult = UnitLowerTriangular(randn(3,3)) - @test fillstored!(Ult, 3) == UnitLowerTriangular([1 0 0; 3 1 0; 3 3 1]) - end - let # fill!(exotic, 0) - exotic_arrays = Any[Tridiagonal(randn(3), randn(4), randn(3)), - Bidiagonal(randn(3), randn(2), rand([:U,:L])), - SymTridiagonal(randn(3), randn(2)), - Diagonal(randn(5)), - # LowerTriangular(randn(3,3)), # AbstractTriangular fill! deprecated, see below - # UpperTriangular(randn(3,3)) # AbstractTriangular fill! deprecated, see below - ] - for A in exotic_arrays - @test iszero(fill!(A, 0)) - end - - # Diagonal fill! is no longer deprecated. See #29780 - # AbstractTriangular fill! was defined as fillstored!, - # not matching the general behavior of fill!, and so it has been deprecated. - # In a future dev cycle, this fill! methods should probably be reintroduced - # with behavior matching that of fill! for other structured matrix types. - # In the interim, equivalently test fillstored! below - @test iszero(fillstored!(Diagonal(fill(1, 3)), 0)) - @test iszero(fillstored!(LowerTriangular(fill(1, 3, 3)), 0)) - @test iszero(fillstored!(UpperTriangular(fill(1, 3, 3)), 0)) - end - let # fill!(small, x) - val = randn() - b = Bidiagonal(randn(1,1), :U) - st = SymTridiagonal(randn(1,1)) - d = Diagonal(rand(1)) - for x in (b, st, d) - @test Array(fill!(x, val)) == fill!(Array(x), val) - end - b = Bidiagonal(randn(2,2), :U) - st = SymTridiagonal(randn(3), randn(2)) - t = Tridiagonal(randn(3,3)) - d = Diagonal(rand(3)) - for x in (b, t, st, d) - @test_throws ArgumentError fill!(x, val) - @test Array(fill!(x, 0)) == fill!(Array(x), 0) - end - end -end - -@testset "pathological promotion (#24707)" begin - @test promote_type(Matrix{Int}, Bidiagonal{Tuple{S}} where S<:Integer) <: Matrix - @test promote_type(Matrix{Tuple{T}} where T<:Integer, Bidiagonal{Tuple{S}} where S<:Integer) <: Matrix - @test promote_type(Matrix{Tuple{T}} where T<:Integer, Bidiagonal{Int}) <: Matrix - @test promote_type(Tridiagonal{Int}, Bidiagonal{Tuple{S}} where S<:Integer) <: Tridiagonal - @test promote_type(Tridiagonal{Tuple{T}} where T<:Integer, Bidiagonal{Tuple{S}} where S<:Integer) <: Tridiagonal - @test promote_type(Tridiagonal{Tuple{T}} where T<:Integer, Bidiagonal{Int}) <: Tridiagonal -end - -@testset "solve with matrix elements" begin - A = triu(tril(randn(9, 9), 3), -3) - b = randn(9) - Alb = Bidiagonal(Any[tril(A[1:3,1:3]), tril(A[4:6,4:6]), tril(A[7:9,7:9])], - Any[triu(A[4:6,1:3]), triu(A[7:9,4:6])], 'L') - Aub = Bidiagonal(Any[triu(A[1:3,1:3]), triu(A[4:6,4:6]), triu(A[7:9,7:9])], - Any[tril(A[1:3,4:6]), tril(A[4:6,7:9])], 'U') - bb = Any[b[1:3], b[4:6], b[7:9]] - @test vcat((Alb\bb)...) ≈ LowerTriangular(A)\b - @test vcat((Aub\bb)...) ≈ UpperTriangular(A)\b - Alb = Bidiagonal([tril(A[1:3,1:3]), tril(A[4:6,4:6]), tril(A[7:9,7:9])], - [triu(A[4:6,1:3]), triu(A[7:9,4:6])], 'L') - Aub = Bidiagonal([triu(A[1:3,1:3]), triu(A[4:6,4:6]), triu(A[7:9,7:9])], - [tril(A[1:3,4:6]), tril(A[4:6,7:9])], 'U') - d = [randn(3,3) for _ in 1:3] - dl = [randn(3,3) for _ in 1:2] - B = [randn(3,3) for _ in 1:3, _ in 1:3] - for W in (UpperTriangular, LowerTriangular), t in (identity, adjoint, transpose) - @test Matrix(t(Alb) \ W(B)) ≈ t(Alb) \ Matrix(W(B)) - @test Matrix(t(Aub) \ W(B)) ≈ t(Aub) \ Matrix(W(B)) - @test Matrix(W(B) / t(Alb)) ≈ Matrix(W(B)) / t(Alb) - @test Matrix(W(B) / t(Aub)) ≈ Matrix(W(B)) / t(Aub) - end -end - -@testset "sum, mapreduce" begin - Bu = Bidiagonal([1,2,3], [1,2], :U) - Budense = Matrix(Bu) - Bl = Bidiagonal([1,2,3], [1,2], :L) - Bldense = Matrix(Bl) - @test sum(Bu) == 9 - @test sum(Bl) == 9 - @test_throws ArgumentError sum(Bu, dims=0) - @test sum(Bu, dims=1) == sum(Budense, dims=1) - @test sum(Bu, dims=2) == sum(Budense, dims=2) - @test sum(Bu, dims=3) == sum(Budense, dims=3) - @test typeof(sum(Bu, dims=1)) == typeof(sum(Budense, dims=1)) - @test mapreduce(one, min, Bu, dims=1) == mapreduce(one, min, Budense, dims=1) - @test mapreduce(one, min, Bu, dims=2) == mapreduce(one, min, Budense, dims=2) - @test mapreduce(one, min, Bu, dims=3) == mapreduce(one, min, Budense, dims=3) - @test typeof(mapreduce(one, min, Bu, dims=1)) == typeof(mapreduce(one, min, Budense, dims=1)) - @test mapreduce(zero, max, Bu, dims=1) == mapreduce(zero, max, Budense, dims=1) - @test mapreduce(zero, max, Bu, dims=2) == mapreduce(zero, max, Budense, dims=2) - @test mapreduce(zero, max, Bu, dims=3) == mapreduce(zero, max, Budense, dims=3) - @test typeof(mapreduce(zero, max, Bu, dims=1)) == typeof(mapreduce(zero, max, Budense, dims=1)) - @test_throws ArgumentError sum(Bl, dims=0) - @test sum(Bl, dims=1) == sum(Bldense, dims=1) - @test sum(Bl, dims=2) == sum(Bldense, dims=2) - @test sum(Bl, dims=3) == sum(Bldense, dims=3) - @test typeof(sum(Bl, dims=1)) == typeof(sum(Bldense, dims=1)) - @test mapreduce(one, min, Bl, dims=1) == mapreduce(one, min, Bldense, dims=1) - @test mapreduce(one, min, Bl, dims=2) == mapreduce(one, min, Bldense, dims=2) - @test mapreduce(one, min, Bl, dims=3) == mapreduce(one, min, Bldense, dims=3) - @test typeof(mapreduce(one, min, Bl, dims=1)) == typeof(mapreduce(one, min, Bldense, dims=1)) - @test mapreduce(zero, max, Bl, dims=1) == mapreduce(zero, max, Bldense, dims=1) - @test mapreduce(zero, max, Bl, dims=2) == mapreduce(zero, max, Bldense, dims=2) - @test mapreduce(zero, max, Bl, dims=3) == mapreduce(zero, max, Bldense, dims=3) - @test typeof(mapreduce(zero, max, Bl, dims=1)) == typeof(mapreduce(zero, max, Bldense, dims=1)) - - Bu = Bidiagonal([2], Int[], :U) - Budense = Matrix(Bu) - Bl = Bidiagonal([2], Int[], :L) - Bldense = Matrix(Bl) - @test sum(Bu) == 2 - @test sum(Bl) == 2 - @test_throws ArgumentError sum(Bu, dims=0) - @test sum(Bu, dims=1) == sum(Budense, dims=1) - @test sum(Bu, dims=2) == sum(Budense, dims=2) - @test sum(Bu, dims=3) == sum(Budense, dims=3) - @test typeof(sum(Bu, dims=1)) == typeof(sum(Budense, dims=1)) -end - -@testset "empty sub-diagonal" begin - # `mul!` must use non-specialized method when sub-diagonal is empty - A = [1 2 3 4]' - @test A * Tridiagonal(ones(1, 1)) == A -end - -@testset "generalized dot" begin - for elty in (Float64, ComplexF64), n in (5, 1) - dv = randn(elty, n) - ev = randn(elty, n-1) - x = randn(elty, n) - y = randn(elty, n) - for uplo in (:U, :L) - B = Bidiagonal(dv, ev, uplo) - @test dot(x, B, y) ≈ dot(B'x, y) ≈ dot(x, B*y) ≈ dot(x, Matrix(B), y) - end - dv = Vector{elty}(undef, 0) - ev = Vector{elty}(undef, 0) - x = Vector{elty}(undef, 0) - y = Vector{elty}(undef, 0) - for uplo in (:U, :L) - B = Bidiagonal(dv, ev, uplo) - @test dot(x, B, y) === zero(elty) - end - end -end - -@testset "multiplication of bidiagonal and triangular matrix" begin - n = 5 - for eltyB in (Int, ComplexF64) - if eltyB == Int - BU = Bidiagonal(rand(1:7, n), rand(1:7, n - 1), :U) - BL = Bidiagonal(rand(1:7, n), rand(1:7, n - 1), :L) - else - BU = Bidiagonal(randn(eltyB, n), randn(eltyB, n - 1), :U) - BL = Bidiagonal(randn(eltyB, n), randn(eltyB, n - 1), :L) - end - for eltyT in (Int, ComplexF64) - for TriT in (LowerTriangular, UnitLowerTriangular, UpperTriangular, UnitUpperTriangular) - if eltyT == Int - T = TriT(rand(1:7, n, n)) - else - T = TriT(randn(eltyT, n, n)) - end - for B in (BU, BL) - MB = Matrix(B) - MT = Matrix(T) - for transB in (identity, adjoint, transpose), transT in (identity, adjoint, transpose) - @test transB(B) * transT(T) ≈ transB(MB) * transT(MT) - @test transT(T) * transB(B) ≈ transT(MT) * transB(MB) - end - end - end - end - end -end - -struct MyNotANumberType - n::Float64 -end -Base.zero(n::MyNotANumberType) = MyNotANumberType(zero(Float64)) -Base.zero(T::Type{MyNotANumberType}) = MyNotANumberType(zero(Float64)) -Base.copy(n::MyNotANumberType) = MyNotANumberType(copy(n.n)) -Base.transpose(n::MyNotANumberType) = n - -@testset "transpose for a non-numeric eltype" begin - @test !(MyNotANumberType(1.0) isa Number) - a = [MyNotANumberType(1.0), MyNotANumberType(2.0), MyNotANumberType(3.0)] - b = [MyNotANumberType(5.0), MyNotANumberType(6.0)] - B = Bidiagonal(a, b, :U) - tB = transpose(B) - @test tB == Bidiagonal(a, b, :L) - @test transpose(copy(tB)) == B -end - -@testset "empty bidiagonal matrices" begin - dv0 = zeros(0) - ev0 = zeros(0) - zm = zeros(0, 0) - ubd = Bidiagonal(dv0, ev0, :U) - lbd = Bidiagonal(dv0, ev0, :L) - @test size(ubd) == (0, 0) - @test_throws BoundsError getindex(ubd, 1, 1) - @test_throws BoundsError setindex!(ubd, 0.0, 1, 1) - @test similar(ubd) == ubd - @test similar(lbd, Int) == zeros(Int, 0, 0) - @test ubd == zm - @test lbd == zm - @test ubd == lbd - @test ubd * ubd == ubd - @test lbd + lbd == lbd - @test lbd' == ubd - @test ubd' == lbd - @test triu(ubd, 1) == ubd - @test triu(lbd, 1) == ubd - @test tril(ubd, -1) == ubd - @test tril(lbd, -1) == ubd - @test_throws ArgumentError triu(ubd) - @test_throws ArgumentError tril(ubd) - @test sum(ubd) == 0.0 - @test reduce(+, ubd) == 0.0 - @test reduce(+, ubd, dims=1) == zeros(1, 0) - @test reduce(+, ubd, dims=2) == zeros(0, 1) - @test hcat(ubd, ubd) == zm - @test vcat(ubd, lbd) == zm - @test hcat(lbd, ones(0, 3)) == ones(0, 3) - @test fill!(copy(ubd), 1.0) == ubd - @test map(abs, ubd) == zm - @test lbd .+ 1 == zm - @test lbd + ubd isa Bidiagonal - @test lbd .+ ubd isa Bidiagonal - @test ubd * 5 == ubd - @test ubd .* 3 == ubd -end - -@testset "non-commutative algebra (#39701)" begin - A = Bidiagonal(Quaternion.(randn(5), randn(5), randn(5), randn(5)), Quaternion.(randn(4), randn(4), randn(4), randn(4)), :U) - c = Quaternion(1,2,3,4) - @test A * c ≈ Matrix(A) * c - @test A / c ≈ Matrix(A) / c - @test c * A ≈ c * Matrix(A) - @test c \ A ≈ c \ Matrix(A) -end - -isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) -using .Main.ImmutableArrays - -@testset "Conversion to AbstractArray" begin - # tests corresponding to #34995 - dv = ImmutableArray([1, 2, 3, 4]) - ev = ImmutableArray([7, 8, 9]) - Bu = Bidiagonal(dv, ev, :U) - Bl = Bidiagonal(dv, ev, :L) - - @test convert(AbstractArray{Float64}, Bu)::Bidiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Bu - @test convert(AbstractMatrix{Float64}, Bu)::Bidiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Bu - @test convert(AbstractArray{Float64}, Bl)::Bidiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Bl - @test convert(AbstractMatrix{Float64}, Bl)::Bidiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Bl -end - -@testset "block-bidiagonal matrix indexing" begin - dv = [ones(4,3), ones(2,2).*2, ones(2,3).*3, ones(4,4).*4] - evu = [ones(4,2), ones(2,3).*2, ones(2,4).*3] - evl = [ones(2,3), ones(2,2).*2, ones(4,3).*3] - BU = Bidiagonal(dv, evu, :U) - BL = Bidiagonal(dv, evl, :L) - # check that all the matrices along a column have the same number of columns, - # and the matrices along a row have the same number of rows - for j in axes(BU, 2), i in 2:size(BU, 1) - @test size(BU[i,j], 2) == size(BU[1,j], 2) - @test size(BU[i,j], 1) == size(BU[i,1], 1) - if j < i || j > i + 1 - @test iszero(BU[i,j]) - end - end - for j in axes(BL, 2), i in 2:size(BL, 1) - @test size(BL[i,j], 2) == size(BL[1,j], 2) - @test size(BL[i,j], 1) == size(BL[i,1], 1) - if j < i-1 || j > i - @test iszero(BL[i,j]) - end - end - - @test diag(BU, -1) == [zeros(size(dv[i+1], 1), size(dv[i],2)) for i in 1:length(dv)-1] - @test diag(BL, 1) == [zeros(size(dv[i], 1), size(dv[i+1],2)) for i in 1:length(dv)-1] - - M = ones(2,2) - for n in 0:1 - dv = fill(M, n) - ev = fill(M, 0) - B = Bidiagonal(dv, ev, :U) - @test B == Matrix{eltype(B)}(B) - end - - @testset "non-standard axes" begin - LinearAlgebra.diagzero(T::Type, ax::Tuple{SizedArrays.SOneTo, Vararg{SizedArrays.SOneTo}}) = - zeros(T, ax) - - s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - B = Bidiagonal(fill(s,4), fill(s,3), :U) - @test @inferred(B[2,1]) isa typeof(s) - @test all(iszero, B[2,1]) - end -end - -@testset "copyto!" begin - ev, dv = [1:4;], [1:5;] - B = Bidiagonal(dv, ev, :U) - B2 = copyto!(zero(B), B) - @test B2 == B - for (ul1, ul2) in ((:U, :L), (:L, :U)) - B3 = Bidiagonal(dv, zero(ev), ul1) - B2 = Bidiagonal(zero(dv), zero(ev), ul2) - @test copyto!(B2, B3) == B3 - end - - @testset "mismatched sizes" begin - dv2 = [4; @view dv[2:end]] - @test copyto!(B, Bidiagonal([4], Int[], :U)) == Bidiagonal(dv2, ev, :U) - @test copyto!(B, Bidiagonal([4], Int[], :L)) == Bidiagonal(dv2, ev, :U) - @test copyto!(B, Bidiagonal(Int[], Int[], :U)) == Bidiagonal(dv, ev, :U) - @test copyto!(B, Bidiagonal(Int[], Int[], :L)) == Bidiagonal(dv, ev, :U) - end -end - -@testset "copyto! with UniformScaling" begin - @testset "Fill" begin - for len in (4, InfiniteArrays.Infinity()) - d = FillArrays.Fill(1, len) - ud = FillArrays.Fill(0, len-1) - B = Bidiagonal(d, ud, :U) - @test copyto!(B, I) === B - end - end - B = Bidiagonal(fill(2, 4), fill(3, 3), :U) - copyto!(B, I) - @test all(isone, diag(B)) - @test all(iszero, diag(B, 1)) -end - -@testset "diagind" begin - B = Bidiagonal(1:4, 1:3, :U) - M = Matrix(B) - @testset for k in -4:4 - @test B[diagind(B,k)] == M[diagind(M,k)] - end -end - -@testset "custom axes" begin - dv, uv = OffsetArray(1:4), OffsetArray(1:3) - B = Bidiagonal(dv, uv, :U) - ax = axes(dv, 1) - @test axes(B) === (ax, ax) -end - -@testset "avoid matmul ambiguities with ::MyMatrix * ::AbstractMatrix" begin - A = [i+j for i in 1:2, j in 1:2] - S = SizedArrays.SizedArray{(2,2)}(A) - B = Bidiagonal([1:2;], [1;], :U) - @test S * B == A * B - @test B * S == B * A - C1, C2 = zeros(2,2), zeros(2,2) - @test mul!(C1, S, B) == mul!(C2, A, B) - @test mul!(C1, S, B, 1, 2) == mul!(C2, A, B, 1 ,2) - @test mul!(C1, B, S) == mul!(C2, B, A) - @test mul!(C1, B, S, 1, 2) == mul!(C2, B, A, 1 ,2) - - v = [i for i in 1:2] - sv = SizedArrays.SizedArray{(2,)}(v) - @test B * sv == B * v - C1, C2 = zeros(2), zeros(2) - @test mul!(C1, B, sv) == mul!(C2, B, v) - @test mul!(C1, B, sv, 1, 2) == mul!(C2, B, v, 1 ,2) -end - -@testset "Reverse operation on Bidiagonal" begin - n = 5 - d = randn(n) - e = randn(n - 1) - for uplo in (:U, :L) - B = Bidiagonal(d, e, uplo) - @test reverse(B, dims=1) == reverse(Matrix(B), dims=1) - @test reverse(B, dims=2) == reverse(Matrix(B), dims=2) - @test reverse(B)::Bidiagonal == reverse(Matrix(B)) - end -end - -@testset "Matrix conversion for non-numeric" begin - B = Bidiagonal(fill(Diagonal([1,3]), 3), fill(Diagonal([1,3]), 2), :U) - M = Matrix{eltype(B)}(B) - @test M isa Matrix{eltype(B)} - @test M == B -end - -@testset "getindex with Integers" begin - dv, ev = 1:4, 1:3 - B = Bidiagonal(dv, ev, :U) - @test_throws "invalid index" B[3, true] - @test B[1,2] == B[Int8(1),UInt16(2)] == B[big(1), Int16(2)] -end - -@testset "rmul!/lmul! with banded matrices" begin - dv, ev = rand(4), rand(3) - for A in (Bidiagonal(dv, ev, :U), Bidiagonal(dv, ev, :L)) - @testset "$(nameof(typeof(B)))" for B in ( - Bidiagonal(dv, ev, :U), - Bidiagonal(dv, ev, :L), - Diagonal(dv) - ) - @test_throws ArgumentError rmul!(B, A) - @test_throws ArgumentError lmul!(A, B) - end - end - @testset "non-commutative" begin - S32 = SizedArrays.SizedArray{(3,2)}(rand(3,2)) - S33 = SizedArrays.SizedArray{(3,3)}(rand(3,3)) - S22 = SizedArrays.SizedArray{(2,2)}(rand(2,2)) - for uplo in (:L, :U) - B = Bidiagonal(fill(S32, 4), fill(S32, 3), uplo) - D = Diagonal(fill(S22, size(B,2))) - @test rmul!(copy(B), D) ≈ B * D - D = Diagonal(fill(S33, size(B,1))) - @test lmul!(D, copy(B)) ≈ D * B - end - - B = Bidiagonal(fill(S33, 4), fill(S33, 3), :U) - D = Diagonal(fill(S32, 4)) - @test lmul!(B, Array(D)) ≈ B * D - B = Bidiagonal(fill(S22, 4), fill(S22, 3), :U) - @test rmul!(Array(D), B) ≈ D * B - end -end - -@testset "rmul!/lmul! with numbers" begin - for T in (Bidiagonal(rand(4), rand(3), :U), Bidiagonal(rand(4), rand(3), :L)) - @test rmul!(copy(T), 0.2) ≈ rmul!(Array(T), 0.2) - @test lmul!(0.2, copy(T)) ≈ lmul!(0.2, Array(T)) - @test_throws ArgumentError rmul!(T, NaN) - @test_throws ArgumentError lmul!(NaN, T) - end - for T in (Bidiagonal(rand(1), rand(0), :U), Bidiagonal(rand(1), rand(0), :L)) - @test all(isnan, rmul!(copy(T), NaN)) - @test all(isnan, lmul!(NaN, copy(T))) - end -end - -@testset "mul with Diagonal" begin - for n in 0:4 - dv, ev = rand(n), rand(max(n-1,0)) - d = rand(n) - for uplo in (:U, :L) - A = Bidiagonal(dv, ev, uplo) - D = Diagonal(d) - M = Matrix(A) - S = similar(A, size(A)) - @test A * D ≈ mul!(S, A, D) ≈ M * D - @test D * A ≈ mul!(S, D, A) ≈ D * M - @test mul!(copy(S), D, A, 2, 2) ≈ D * M * 2 + S * 2 - @test mul!(copy(S), A, D, 2, 2) ≈ M * D * 2 + S * 2 - - A2 = Bidiagonal(dv, zero(ev), uplo) - M2 = Array(A2) - S2 = Bidiagonal(copy(dv), copy(ev), uplo == (:U) ? (:L) : (:U)) - MS2 = Array(S2) - @test mul!(copy(S2), D, A2) ≈ D * M2 - @test mul!(copy(S2), A2, D) ≈ M2 * D - @test mul!(copy(S2), A2, D, 2, 2) ≈ M2 * D * 2 + MS2 * 2 - @test mul!(copy(S2), D, A2, 2, 2) ≈ D * M2 * 2 + MS2 * 2 - end - end - - t1 = SizedArrays.SizedArray{(2,3)}([1 2 3; 3 4 5]) - t2 = SizedArrays.SizedArray{(3,2)}([1 2; 3 4; 5 6]) - dv, ev, d = fill(t1, 4), fill(2t1, 3), fill(t2, 4) - for uplo in (:U, :L) - A = Bidiagonal(dv, ev, uplo) - D = Diagonal(d) - @test A * D ≈ Array(A) * Array(D) - @test D * A ≈ Array(D) * Array(A) - end -end - -@testset "conversion to Tridiagonal for immutable bands" begin - n = 4 - dv = FillArrays.Fill(3, n) - ev = FillArrays.Fill(2, n-1) - z = FillArrays.Fill(0, n-1) - dvf = FillArrays.Fill(Float64(3), n) - evf = FillArrays.Fill(Float64(2), n-1) - zf = FillArrays.Fill(Float64(0), n-1) - B = Bidiagonal(dv, ev, :U) - @test Tridiagonal{Int}(B) === Tridiagonal(B) === Tridiagonal(z, dv, ev) - @test Tridiagonal{Float64}(B) === Tridiagonal(zf, dvf, evf) - B = Bidiagonal(dv, ev, :L) - @test Tridiagonal{Int}(B) === Tridiagonal(B) === Tridiagonal(ev, dv, z) - @test Tridiagonal{Float64}(B) === Tridiagonal(evf, dvf, zf) -end - -@testset "off-band indexing error" begin - B = Bidiagonal(Vector{BigInt}(undef, 4), Vector{BigInt}(undef,3), :L) - @test_throws "cannot set entry" B[1,2] = 4 -end - -@testset "mul with empty arrays" begin - A = zeros(5,0) - B = Bidiagonal(zeros(0), zeros(0), :U) - BL = Bidiagonal(zeros(5), zeros(4), :U) - @test size(A * B) == size(A) - @test size(BL * A) == size(A) - @test size(B * B) == size(B) - C = similar(A) - @test mul!(C, A, B) == A * B - @test mul!(C, BL, A) == BL * A - @test mul!(similar(B), B, B) == B * B - @test mul!(similar(B, size(B)), B, B) == B * B - - v = zeros(size(B,2)) - @test size(B * v) == size(v) - @test mul!(similar(v), B, v) == B * v - - D = Diagonal(zeros(size(B,2))) - @test size(B * D) == size(D * B) == size(D) - @test mul!(similar(D), B, D) == mul!(similar(D), D, B) == B * D -end - -@testset "mul for small matrices" begin - @testset for n in 0:6 - D = Diagonal(rand(n)) - v = rand(n) - @testset for uplo in (:L, :U) - B = Bidiagonal(rand(n), rand(max(n-1,0)), uplo) - M = Matrix(B) - - @test B * v ≈ M * v - @test mul!(similar(v), B, v) ≈ M * v - @test mul!(ones(size(v)), B, v, 2, 3) ≈ M * v * 2 .+ 3 - - @test B * B ≈ M * M - @test mul!(similar(B, size(B)), B, B) ≈ M * M - @test mul!(ones(size(B)), B, B, 2, 4) ≈ M * M * 2 .+ 4 - - for m in 0:6 - AL = rand(m,n) - AR = rand(n,m) - @test AL * B ≈ AL * M - @test B * AR ≈ M * AR - @test mul!(similar(AL), AL, B) ≈ AL * M - @test mul!(similar(AR), B, AR) ≈ M * AR - @test mul!(ones(size(AL)), AL, B, 2, 4) ≈ AL * M * 2 .+ 4 - @test mul!(ones(size(AR)), B, AR, 2, 4) ≈ M * AR * 2 .+ 4 - end - - @test B * D ≈ M * D - @test D * B ≈ D * M - @test mul!(similar(B), B, D) ≈ M * D - @test mul!(similar(B), B, D) ≈ M * D - @test mul!(similar(B, size(B)), D, B) ≈ D * M - @test mul!(similar(B, size(B)), B, D) ≈ M * D - @test mul!(ones(size(B)), D, B, 2, 4) ≈ D * M * 2 .+ 4 - @test mul!(ones(size(B)), B, D, 2, 4) ≈ M * D * 2 .+ 4 - end - BL = Bidiagonal(rand(n), rand(max(0, n-1)), :L) - ML = Matrix(BL) - BU = Bidiagonal(rand(n), rand(max(0, n-1)), :U) - MU = Matrix(BU) - T = Tridiagonal(zeros(max(0, n-1)), zeros(n), zeros(max(0, n-1))) - @test mul!(T, BL, BU) ≈ ML * MU - @test mul!(T, BU, BL) ≈ MU * ML - T = Tridiagonal(ones(max(0, n-1)), ones(n), ones(max(0, n-1))) - @test mul!(copy(T), BL, BU, 2, 3) ≈ ML * MU * 2 + T * 3 - @test mul!(copy(T), BU, BL, 2, 3) ≈ MU * ML * 2 + T * 3 - end - - n = 4 - arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) - for B in ( - Bidiagonal(fill(arr,n), fill(arr,n-1), :L), - Bidiagonal(fill(arr,n), fill(arr,n-1), :U), - ) - @test B * B ≈ Matrix(B) * Matrix(B) - BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) - BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) - @test BL * B ≈ Matrix(BL) * Matrix(B) - @test BU * B ≈ Matrix(BU) * Matrix(B) - @test B * BL ≈ Matrix(B) * Matrix(BL) - @test B * BU ≈ Matrix(B) * Matrix(BU) - D = Diagonal(fill(arr,n)) - @test D * B ≈ Matrix(D) * Matrix(B) - @test B * D ≈ Matrix(B) * Matrix(D) - end -end - -end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/blas.jl b/stdlib/LinearAlgebra/test/blas.jl deleted file mode 100644 index 80494da7babbe..0000000000000 --- a/stdlib/LinearAlgebra/test/blas.jl +++ /dev/null @@ -1,783 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestBLAS - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasReal, BlasComplex -using Libdl: dlsym, dlopen -fabs(x::Real) = abs(x) -fabs(x::Complex) = abs(real(x)) + abs(imag(x)) - -# help function to build packed storage -function pack(A, uplo) - AP = eltype(A)[] - n = size(A, 1) - for j in 1:n, i in (uplo === :L ? (j:n) : (1:j)) - push!(AP, A[i,j]) - end - return AP -end - -@testset "vec_pointer_stride" begin - a = float(rand(1:20,4,4,4)) - @test BLAS.asum(a) == sum(a) # dense case - @test BLAS.asum(view(a,1:2:4,:,:)) == sum(view(a,1:2:4,:,:)) # vector like - @test BLAS.asum(view(a,1:3,2:2,3:3)) == sum(view(a,1:3,2:2,3:3)) - @test BLAS.asum(view(a,1:1,1:3,1:1)) == sum(view(a,1:1,1:3,1:1)) - @test BLAS.asum(view(a,1:1,1:1,1:3)) == sum(view(a,1:1,1:1,1:3)) - @test_throws ArgumentError BLAS.asum(view(a,1:3:4,:,:)) # non-vector like - @test_throws ArgumentError BLAS.asum(view(a,1:2,1:1,1:3)) -end -Random.seed!(100) -## BLAS tests - testing the interface code to BLAS routines -@testset for elty in [Float32, Float64, ComplexF32, ComplexF64] - - @testset "syr2k!" begin - U = randn(elty, 5, 2) - V = randn(elty, 5, 2) - @test tril(LinearAlgebra.BLAS.syr2k('L','N',U,V)) ≈ tril(U*transpose(V) + V*transpose(U)) - @test triu(LinearAlgebra.BLAS.syr2k('U','N',U,V)) ≈ triu(U*transpose(V) + V*transpose(U)) - @test tril(LinearAlgebra.BLAS.syr2k('L','T',U,V)) ≈ tril(transpose(U)*V + transpose(V)*U) - @test triu(LinearAlgebra.BLAS.syr2k('U','T',U,V)) ≈ triu(transpose(U)*V + transpose(V)*U) - end - - if elty in (ComplexF32, ComplexF64) - @testset "her2k!" begin - U = randn(elty, 5, 2) - V = randn(elty, 5, 2) - @test tril(LinearAlgebra.BLAS.her2k('L','N',U,V)) ≈ tril(U*V' + V*U') - @test triu(LinearAlgebra.BLAS.her2k('U','N',U,V)) ≈ triu(U*V' + V*U') - @test tril(LinearAlgebra.BLAS.her2k('L','C',U,V)) ≈ tril(U'*V + V'*U) - @test triu(LinearAlgebra.BLAS.her2k('U','C',U,V)) ≈ triu(U'*V + V'*U) - end - end - - o4 = fill(elty(1), 4) - z4 = zeros(elty, 4) - - I4 = Matrix{elty}(I, 4, 4) - I43 = Matrix{elty}(I, 4, 3) - L4 = tril(fill(elty(1), 4,4)) - U4 = triu(fill(elty(1), 4,4)) - Z4 = zeros(elty, (4,4)) - - elm1 = elty(-1) - el2 = elty(2) - v14 = elty[1:4;] - v41 = elty[4:-1:1;] - - let n = 10 - @testset "dot products" begin - if elty <: Real - x1 = randn(elty, n) - x2 = randn(elty, n) - @test BLAS.dot(x1,x2) ≈ sum(x1.*x2) - @test_throws DimensionMismatch BLAS.dot(x1,rand(elty, n + 1)) - else - z1 = randn(elty, n) - z2 = randn(elty, n) - @test BLAS.dotc(z1,z2) ≈ sum(conj(z1).*z2) - @test BLAS.dotu(z1,z2) ≈ sum(z1.*z2) - @test_throws DimensionMismatch BLAS.dotc(z1,rand(elty, n + 1)) - @test_throws DimensionMismatch BLAS.dotu(z1,rand(elty, n + 1)) - end - end - @testset "iamax" begin - x = randn(elty, n) - @test BLAS.iamax(x) == findmax(fabs, x)[2] - end - @testset "rot!" begin - x = randn(elty, n) - y = randn(elty, n) - c = rand(real(elty)) - for sty in unique!([real(elty), elty]) - s = rand(sty) - x2 = copy(x) - y2 = copy(y) - BLAS.rot!(n, x, 1, y, 1, c, s) - @test x ≈ c*x2 + s*y2 - @test y ≈ -conj(s)*x2 + c*y2 - end - end - @testset "axp(b)y" begin - x1 = randn(elty, n) - x2 = randn(elty, n) - α = rand(elty) - β = rand(elty) - for X1 in (x1, view(x1,n:-1:1)), X2 in (x2, view(x2, n:-1:1)) - @test BLAS.axpy!(α,deepcopy(X1),deepcopy(X2)) ≈ α*X1 + X2 - @test BLAS.axpby!(α,deepcopy(X1),β,deepcopy(X2)) ≈ α*X1 + β*X2 - end - for ind1 in (1:n, n:-1:1), ind2 in (1:n, n:-1:1) - @test BLAS.axpy!(α,copy(x1),ind1,copy(x2),ind2) ≈ x2 + α*(ind1 == ind2 ? x1 : reverse(x1)) - end - @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), rand(elty, n + 1)) - @test_throws DimensionMismatch BLAS.axpby!(α, copy(x1), β, rand(elty, n + 1)) - @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 1:n) - @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 0:div(n,2), copy(x2), 1:(div(n, 2) + 1)) - @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 0:(div(n, 2) - 1)) - end - @testset "nrm2, iamax, and asum for StridedVectors" begin - a = rand(elty,n) - for ind in (2:2:n, n:-2:2) - b = view(a, ind, 1) - @test BLAS.nrm2(b) ≈ sqrt(sum(abs2, b)) - @test BLAS.asum(b) ≈ sum(fabs, b) - @test BLAS.iamax(b) == findmax(fabs, b)[2] * (step(ind) >= 0) - end - end - @testset "nrm2 with non-finite elements" begin - # These tests would have caught - # when running on appropriate hardware. - a = zeros(elty,n) - a[begin] = elty(-Inf) - @test BLAS.nrm2(a) === abs2(elty(Inf)) - a[begin] = elty(NaN) - @test BLAS.nrm2(a) === abs2(elty(NaN)) - end - @testset "deterministic mul!" begin - # mul! should be deterministic, see #53054 - function tester_53054() - C = ComplexF32 - mat = zeros(C, 1, 1) - for _ in 1:100 - v = [C(1-0.2im) C(2+0.3im)] - mul!(mat, v, v', C(1+im), 1) - end - return mat - end - @test allequal(tester_53054() for _ in 1:10000) - end - @testset "scal" begin - α = rand(elty) - a = rand(elty,n) - @test BLAS.scal(n,α,a,1) ≈ α * a - for v in (a, view(a, n:-1:1)) - @test BLAS.scal!(α, deepcopy(v)) ≈ α * v - end - end - - @testset "ger, geru, her, syr" for x in (rand(elty, n), view(rand(elty,2n), 1:2:2n), view(rand(elty,n), n:-1:1)), - y in (rand(elty,n), view(rand(elty,3n), 1:3:3n), view(rand(elty,2n), 2n:-2:2)) - - A = rand(elty,n,n) - α = rand(elty) - - @test BLAS.ger!(α,x,y,copy(A)) ≈ A + α*x*y' - @test_throws DimensionMismatch BLAS.ger!(α,Vector{elty}(undef,n+1),y,copy(A)) - - @test BLAS.geru!(α,x,y,copy(A)) ≈ A + α*x*transpose(y) - @test_throws DimensionMismatch BLAS.geru!(α,Vector{elty}(undef,n+1),y,copy(A)) - - A = rand(elty,n,n) - A = A + transpose(A) - @test issymmetric(A) - @test triu(BLAS.syr!('U',α,x,copy(A))) ≈ triu(A + α*x*transpose(x)) - @test_throws DimensionMismatch BLAS.syr!('U',α,Vector{elty}(undef,n+1),copy(A)) - - if elty <: Complex - A = rand(elty,n,n) - A = A + A' - α = real(α) - @test triu(BLAS.her!('U',α,x,copy(A))) ≈ triu(A + α*x*x') - @test_throws DimensionMismatch BLAS.her!('U',α,Vector{elty}(undef,n+1),copy(A)) - end - end - @testset "copy" begin - x1 = randn(elty, n) - x2 = randn(elty, n) - for ind1 in (1:n, n:-1:1), ind2 in (1:n, n:-1:1) - @test x2 === BLAS.copyto!(x2, ind1, x1, ind2) == (ind1 == ind2 ? x1 : reverse(x1)) - end - @test_throws DimensionMismatch BLAS.copyto!(x2, 1:n, x1, 1:(n - 1)) - @test_throws ArgumentError BLAS.copyto!(x1, 0:div(n, 2), x2, 1:(div(n, 2) + 1)) - @test_throws ArgumentError BLAS.copyto!(x1, 1:(div(n, 2) + 1), x2, 0:div(n, 2)) - end - @testset "trmv and trsv" begin - A = rand(elty,n,n) - x = rand(elty,n) - xerr = Vector{elty}(undef,n+1) - for uplo in ('U', 'L'), diag in ('U','N'), trans in ('N', 'T', 'C') - Wrapper = if uplo == 'U' - diag == 'U' ? UnitUpperTriangular : UpperTriangular - else - diag == 'U' ? UnitLowerTriangular : LowerTriangular - end - fun = trans == 'N' ? identity : trans == 'T' ? transpose : adjoint - fullA = collect(fun(Wrapper(A))) - @testset "trmv" begin - @test BLAS.trmv(uplo,trans,diag,A,x) ≈ fullA * x - @test_throws DimensionMismatch BLAS.trmv(uplo,trans,diag,A,xerr) - for xx in (x, view(x, n:-1:1)) - @test BLAS.trmv!(uplo,trans,diag,A,deepcopy(xx)) ≈ fullA * xx - end - end - @testset "trsv" begin - @test BLAS.trsv(uplo,trans,diag,A,x) ≈ fullA \ x - @test_throws DimensionMismatch BLAS.trsv(uplo,trans,diag,A,xerr) - for xx in (x, view(x, n:-1:1)) - @test BLAS.trsv!(uplo,trans,diag,A,deepcopy(xx)) ≈ fullA \ xx - end - end - end - end - @testset "symmetric/Hermitian multiplication" begin - x = rand(elty,n) - A = rand(elty,n,n) - y = rand(elty, n) - α = randn(elty) - β = randn(elty) - Aherm = A + A' - Asymm = A + transpose(A) - offsizevec, offsizemat = Array{elty}.(undef,(n+1, (n,n+1))) - @testset "symv and hemv" for uplo in ('U', 'L') - @test BLAS.symv(uplo,Asymm,x) ≈ Asymm*x - for xx in (x, view(x, n:-1:1)), yy in (y, view(y, n:-1:1)) - @test BLAS.symv!(uplo,α,Asymm,xx,β,deepcopy(yy)) ≈ α * Asymm * xx + β * yy - end - @test_throws DimensionMismatch BLAS.symv!(uplo,α,Asymm,x,β,offsizevec) - @test_throws DimensionMismatch BLAS.symv(uplo,offsizemat,x) - if elty <: BlasComplex - @test BLAS.hemv(uplo,Aherm,x) ≈ Aherm*x - for xx in (x, view(x, n:-1:1)), yy in (y, view(y, n:-1:1)) - @test BLAS.hemv!(uplo,α,Aherm,xx,β,deepcopy(yy)) ≈ α * Aherm * xx + β * yy - end - @test_throws DimensionMismatch BLAS.hemv(uplo,offsizemat,x) - @test_throws DimensionMismatch BLAS.hemv!(uplo,one(elty),Aherm,x,one(elty),offsizevec) - end - end - - @testset "symm error throwing" begin - Cnn, Cnm, Cmn = Matrix{elty}.(undef,((n,n), (n,n-1), (n-1,n))) - @test_throws DimensionMismatch BLAS.symm('L','U',Cnm,Cnn) - @test_throws DimensionMismatch BLAS.symm('R','U',Cmn,Cnn) - @test_throws DimensionMismatch BLAS.symm!('L','U',one(elty),Asymm,Cnn,one(elty),Cmn) - @test_throws DimensionMismatch BLAS.symm!('L','U',one(elty),Asymm,Cnn,one(elty),Cnm) - @test_throws DimensionMismatch BLAS.symm!('L','U',one(elty),Asymm,Cmn,one(elty),Cnn) - @test_throws DimensionMismatch BLAS.symm!('R','U',one(elty),Asymm,Cnm,one(elty),Cmn) - @test_throws DimensionMismatch BLAS.symm!('R','U',one(elty),Asymm,Cnn,one(elty),Cnm) - @test_throws DimensionMismatch BLAS.symm!('R','U',one(elty),Asymm,Cmn,one(elty),Cnn) - if elty <: BlasComplex - @test_throws DimensionMismatch BLAS.hemm('L','U',Cnm,Cnn) - @test_throws DimensionMismatch BLAS.hemm('R','U',Cmn,Cnn) - @test_throws DimensionMismatch BLAS.hemm!('L','U',one(elty),Aherm,Cnn,one(elty),Cmn) - @test_throws DimensionMismatch BLAS.hemm!('L','U',one(elty),Aherm,Cnn,one(elty),Cnm) - @test_throws DimensionMismatch BLAS.hemm!('L','U',one(elty),Aherm,Cmn,one(elty),Cnn) - @test_throws DimensionMismatch BLAS.hemm!('R','U',one(elty),Aherm,Cnm,one(elty),Cmn) - @test_throws DimensionMismatch BLAS.hemm!('R','U',one(elty),Aherm,Cnn,one(elty),Cnm) - @test_throws DimensionMismatch BLAS.hemm!('R','U',one(elty),Aherm,Cmn,one(elty),Cnn) - end - end - end - @testset "trmm error throwing" begin - Cnn, Cmn, Cnm = Matrix{elty}.(undef,((n,n), (n+1,n), (n,n+1))) - @test_throws DimensionMismatch BLAS.trmm('L','U','N','N',one(elty),triu(Cnn),Cmn) - @test_throws DimensionMismatch BLAS.trmm('R','U','N','N',one(elty),triu(Cnn),Cnm) - end - - # hpmv! - if elty in (ComplexF32, ComplexF64) - @testset "hpmv!" begin - # Both matrix dimensions n coincide, as we have Hermitian matrices. - # Define the inputs and outputs of hpmv!, y = α*A*x+β*y - α = rand(elty) - A = rand(elty, n, n) - x = rand(elty, n) - β = rand(elty) - y = rand(elty, n) - for uplo in (:L, :U) - Cuplo = String(uplo)[1] - AH = Hermitian(A, uplo) - # Create lower/upper triangular packing of AL - AP = pack(AH, uplo) - for xx in (x, view(x,n:-1:1)), yy in (y, view(y,n:-1:1)) - @test BLAS.hpmv!(Cuplo, α, AP, xx, β, deepcopy(yy)) ≈ α*AH*xx + β*yy - end - AP′ = view(zeros(elty, n*(n+1)),1:2:n*(n+1)) - @test_throws ErrorException BLAS.hpmv!(Cuplo, α, AP′, x, β, y) - AP′ = view(AP, 1:length(AP′) - 1) - @test_throws DimensionMismatch BLAS.hpmv!(Cuplo, α, AP′, x, β, y) - @test_throws DimensionMismatch BLAS.hpmv!(Cuplo, α, AP′, x, β, view(y,1:n-1)) - end - end - end - - # spmv! - if elty in (Float32, Float64) - @testset "spmv!" begin - # Both matrix dimensions n coincide, as we have symmetric matrices. - # Define the inputs and outputs of spmv!, y = α*A*x+β*y - α = rand(elty) - A = rand(elty, n, n) - x = rand(elty, n) - β = rand(elty) - y = rand(elty, n) - for uplo in (:L, :U) - Cuplo = String(uplo)[1] - AS = Symmetric(A, uplo) - # Create lower/upper triangular packing of AL - AP = pack(AS, uplo) - for xx in (x, view(x,n:-1:1)), yy in (y, view(y,n:-1:1)) - @test BLAS.spmv!(Cuplo, α, AP, xx, β, deepcopy(yy)) ≈ α*AS*xx + β*yy - end - AP′ = view(zeros(elty, n*(n+1)),1:2:n*(n+1)) - @test_throws ErrorException BLAS.spmv!(Cuplo, α, AP′, x, β, y) - AP′ = view(AP, 1:length(AP′) - 1) - @test_throws DimensionMismatch BLAS.spmv!(Cuplo, α, AP′, x, β, y) - @test_throws DimensionMismatch BLAS.spmv!(Cuplo, α, AP′, x, β, view(y,1:n-1)) - end - end - end - - # spr! - if elty in (Float32, Float64) - @testset "spr! $elty" begin - α = rand(elty) - M = rand(elty, n, n) - AL = Symmetric(M, :L) - AU = Symmetric(M, :U) - for x in (rand(elty, n), view(rand(elty, n), n:-1:1)) - ALP_result_julia_lower = pack(α*x*x' + AL, :L) - ALP_result_blas_lower = pack(AL, :L) - BLAS.spr!('L', α, x, ALP_result_blas_lower) - @test ALP_result_julia_lower ≈ ALP_result_blas_lower - ALP_result_blas_lower = append!(pack(AL, :L), ones(elty, 10)) - BLAS.spr!('L', α, x, ALP_result_blas_lower) - @test ALP_result_julia_lower ≈ ALP_result_blas_lower[1:end-10] - ALP_result_blas_lower = reshape(pack(AL, :L), 1, length(ALP_result_julia_lower), 1) - BLAS.spr!('L', α, x, ALP_result_blas_lower) - @test ALP_result_julia_lower ≈ vec(ALP_result_blas_lower) - - AUP_result_julia_upper = pack(α*x*x' + AU, :U) - AUP_result_blas_upper = pack(AU, :U) - BLAS.spr!('U', α, x, AUP_result_blas_upper) - @test AUP_result_julia_upper ≈ AUP_result_blas_upper - AUP_result_blas_upper = append!(pack(AU, :U), ones(elty, 10)) - BLAS.spr!('U', α, x, AUP_result_blas_upper) - @test AUP_result_julia_upper ≈ AUP_result_blas_upper[1:end-10] - AUP_result_blas_upper = reshape(pack(AU, :U), 1, length(AUP_result_julia_upper), 1) - BLAS.spr!('U', α, x, AUP_result_blas_upper) - @test AUP_result_julia_upper ≈ vec(AUP_result_blas_upper) - end - end - end - - #trsm - A = triu(rand(elty,n,n)) - B = rand(elty,(n,n)) - @test BLAS.trsm('L','U','N','N',one(elty),A,B) ≈ A\B - - #will work for SymTridiagonal,Tridiagonal,Bidiagonal! - @testset "banded matrix mv" begin - @testset "gbmv" begin - TD = Tridiagonal(rand(elty,n-1),rand(elty,n),rand(elty,n-1)) - x = rand(elty, n) - #put TD into the BLAS format! - fTD = zeros(elty,3,n) - fTD[1,2:n] = TD.du - fTD[2,:] = TD.d - fTD[3,1:n-1] = TD.dl - @test BLAS.gbmv('N',n,1,1,fTD,x) ≈ TD*x - y = rand(elty, n) - α = randn(elty) - β = randn(elty) - for xx in (x, view(x, n:-1:1)), yy in (y, view(y, n:-1:1)) - @test BLAS.gbmv!('N',n,1,1,α,fTD,xx,β,deepcopy(yy)) ≈ α * TD * xx + β * yy - end - end - #will work for SymTridiagonal only! - @testset "sbmv and hbmv" begin - x = rand(elty,n) - if elty <: BlasReal - ST = SymTridiagonal(rand(elty,n),rand(elty,n-1)) - #put TD into the BLAS format! - fST = zeros(elty,2,n) - fST[1,2:n] = ST.ev - fST[2,:] = ST.dv - @test BLAS.sbmv('U',1,fST,x) ≈ ST*x - y = rand(elty, n) - α = randn(elty) - β = randn(elty) - for xx in (x, view(x, n:-1:1)), yy in (y, view(y, n:-1:1)) - @test BLAS.sbmv!('U',1,α,fST,xx,β,deepcopy(yy)) ≈ α * ST * xx + β * yy - end - else - dv = rand(real(elty),n) - ev = rand(elty,n-1) - bH = zeros(elty,2,n) - bH[1,2:n] = ev - bH[2,:] = dv - fullH = diagm(0 => dv, -1 => conj(ev), 1 => ev) - @test BLAS.hbmv('U',1,bH,x) ≈ fullH*x - y = rand(elty, n) - α = randn(elty) - β = randn(elty) - for xx in (x, view(x, n:-1:1)), yy in (y, view(y, n:-1:1)) - @test BLAS.hbmv!('U',1,α,bH,xx,β,deepcopy(yy)) ≈ α * fullH * xx + β * yy - end - end - end - end - end - - @testset "gemv" begin - @test all(BLAS.gemv('N', I4, o4) .== o4) - @test all(BLAS.gemv('T', I4, o4) .== o4) - @test all(BLAS.gemv('N', el2, I4, o4) .== el2 * o4) - @test all(BLAS.gemv('T', el2, I4, o4) .== el2 * o4) - @test_throws DimensionMismatch BLAS.gemv('N',I43,o4) - o4cp = copy(o4) - @test_throws DimensionMismatch BLAS.gemv!('T',one(elty),I43,o4,one(elty),o4cp) - @test_throws DimensionMismatch BLAS.gemv!('C',one(elty),I43,o4,one(elty),o4cp) - @test all(BLAS.gemv!('N', one(elty), I4, o4, elm1, o4cp) .== z4) - @test all(o4cp .== z4) - o4cp[:] = o4 - @test all(BLAS.gemv!('T', one(elty), I4, o4, elm1, o4cp) .== z4) - @test all(o4cp .== z4) - @test all(BLAS.gemv('N', U4, o4) .== v41) - @test all(BLAS.gemv('N', U4, o4) .== v41) - @testset "non-standard strides" begin - A = rand(elty, 3, 4) - x = rand(elty, 5) - for y = (view(ones(elty, 5), 1:2:5), view(ones(elty, 7), 6:-2:2)) - ycopy = copy(y) - @test BLAS.gemv!('N', elty(2), view(A, :, 2:2:4), view(x, 1:3:4), elty(3), y) ≈ 2*A[:,2:2:4]*x[1:3:4] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!('N', elty(2), view(A, :, 4:-2:2), view(x, 1:3:4), elty(3), y) ≈ 2*A[:,4:-2:2]*x[1:3:4] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!('N', elty(2), view(A, :, 2:2:4), view(x, 4:-3:1), elty(3), y) ≈ 2*A[:,2:2:4]*x[4:-3:1] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!('N', elty(2), view(A, :, 4:-2:2), view(x, 4:-3:1), elty(3), y) ≈ 2*A[:,4:-2:2]*x[4:-3:1] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!('N', elty(2), view(A, :, StepRangeLen(1,0,1)), view(x, 1:1), elty(3), y) ≈ 2*A[:,1:1]*x[1:1] + 3*ycopy # stride(A,2) == 0 - end - @test BLAS.gemv!('N', elty(1), zeros(elty, 0, 5), zeros(elty, 5), elty(1), zeros(elty, 0)) == elty[] # empty matrix, stride(A,2) == 0 - @test BLAS.gemv('N', elty(-1), view(A, 2:3, 1:2:3), view(x, 2:-1:1)) ≈ -1*A[2:3,1:2:3]*x[2:-1:1] - @test BLAS.gemv('N', view(A, 2:3, 3:-2:1), view(x, 1:2:3)) ≈ A[2:3,3:-2:1]*x[1:2:3] - for (trans, f) = (('T',transpose), ('C',adjoint)) - for y = (view(ones(elty, 3), 1:2:3), view(ones(elty, 5), 4:-2:2)) - ycopy = copy(y) - @test BLAS.gemv!(trans, elty(2), view(A, :, 2:2:4), view(x, 1:2:5), elty(3), y) ≈ 2*f(A[:,2:2:4])*x[1:2:5] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!(trans, elty(2), view(A, :, 4:-2:2), view(x, 1:2:5), elty(3), y) ≈ 2*f(A[:,4:-2:2])*x[1:2:5] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!(trans, elty(2), view(A, :, 2:2:4), view(x, 5:-2:1), elty(3), y) ≈ 2*f(A[:,2:2:4])*x[5:-2:1] + 3*ycopy - ycopy = copy(y) - @test BLAS.gemv!(trans, elty(2), view(A, :, 4:-2:2), view(x, 5:-2:1), elty(3), y) ≈ 2*f(A[:,4:-2:2])*x[5:-2:1] + 3*ycopy - end - @test BLAS.gemv!(trans, elty(2), view(A, :, StepRangeLen(1,0,1)), view(x, 1:2:5), elty(3), elty[1]) ≈ 2*f(A[:,1:1])*x[1:2:5] + elty[3] # stride(A,2) == 0 - end - for trans = ('N', 'T', 'C') - @test_throws ErrorException BLAS.gemv(trans, view(A, 1:2:3, 1:2), view(x, 1:2)) # stride(A,1) must be 1 - end - end - end - @testset "gemmt" begin - for (wrapper, uplo) in ((LowerTriangular, 'L'), (UpperTriangular, 'U')) - @test wrapper(BLAS.gemmt(uplo, 'N', 'N', I4, I4)) ≈ wrapper(I4) - @test wrapper(BLAS.gemmt(uplo, 'N', 'T', I4, I4)) ≈ wrapper(I4) - @test wrapper(BLAS.gemmt(uplo, 'T', 'N', I4, I4)) ≈ wrapper(I4) - @test wrapper(BLAS.gemmt(uplo, 'T', 'T', I4, I4)) ≈ wrapper(I4) - @test wrapper(BLAS.gemmt(uplo, 'N', 'N', el2, I4, I4)) ≈ wrapper(el2 * I4) - @test wrapper(BLAS.gemmt(uplo, 'N', 'T', el2, I4, I4)) ≈ wrapper(el2 * I4) - @test wrapper(BLAS.gemmt(uplo, 'T', 'N', el2, I4, I4)) ≈ wrapper(el2 * I4) - @test wrapper(BLAS.gemmt(uplo, 'T', 'T', el2, I4, I4)) ≈ wrapper(el2 * I4) - I4cp = copy(I4) - @test wrapper(BLAS.gemmt!(uplo, 'N', 'N', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) - @test I4cp ≈ Z4 - I4cp[:] = I4 - @test wrapper(BLAS.gemmt!(uplo, 'N', 'T', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) - @test I4cp ≈ Z4 - I4cp[:] = I4 - @test wrapper(BLAS.gemmt!(uplo, 'T', 'N', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) - @test I4cp ≈ Z4 - I4cp[:] = I4 - @test wrapper(BLAS.gemmt!(uplo, 'T', 'T', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) - @test I4cp ≈ Z4 - M1 = uplo == 'U' ? U4 : I4 - @test wrapper(BLAS.gemmt(uplo, 'N', 'N', I4, U4)) ≈ wrapper(M1) - M2 = uplo == 'U' ? I4 : U4' - @test wrapper(BLAS.gemmt(uplo, 'N', 'T', I4, U4)) ≈ wrapper(M2) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I43, I4, elm1, I43) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I4, I4, elm1, Matrix{elty}(I, 5, 5)) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I43, I4, elm1, I4) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'T', 'N', one(elty), I4, I43, elm1, I43) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'T', one(elty), I43, I43, elm1, I43) - @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'T', 'T', one(elty), I43, I43, elm1, Matrix{elty}(I, 3, 4)) - end - end - @testset "gemm" begin - @test all(BLAS.gemm('N', 'N', I4, I4) .== I4) - @test all(BLAS.gemm('N', 'T', I4, I4) .== I4) - @test all(BLAS.gemm('T', 'N', I4, I4) .== I4) - @test all(BLAS.gemm('T', 'T', I4, I4) .== I4) - @test all(BLAS.gemm('N', 'N', el2, I4, I4) .== el2 * I4) - @test all(BLAS.gemm('N', 'T', el2, I4, I4) .== el2 * I4) - @test all(BLAS.gemm('T', 'N', el2, I4, I4) .== el2 * I4) - @test all(BLAS.gemm('T', 'T', el2, I4, I4) .== el2 * I4) - I4cp = copy(I4) - @test all(BLAS.gemm!('N', 'N', one(elty), I4, I4, elm1, I4cp) .== Z4) - @test all(I4cp .== Z4) - I4cp[:] = I4 - @test all(BLAS.gemm!('N', 'T', one(elty), I4, I4, elm1, I4cp) .== Z4) - @test all(I4cp .== Z4) - I4cp[:] = I4 - @test all(BLAS.gemm!('T', 'N', one(elty), I4, I4, elm1, I4cp) .== Z4) - @test all(I4cp .== Z4) - I4cp[:] = I4 - @test all(BLAS.gemm!('T', 'T', one(elty), I4, I4, elm1, I4cp) .== Z4) - @test all(I4cp .== Z4) - @test all(BLAS.gemm('N', 'N', I4, U4) .== U4) - @test all(BLAS.gemm('N', 'T', I4, U4) .== L4) - @test_throws DimensionMismatch BLAS.gemm!('N','N', one(elty), I4, I4, elm1, Matrix{elty}(I, 5, 5)) - @test_throws DimensionMismatch BLAS.gemm!('N','N', one(elty), I43, I4, elm1, I4) - @test_throws DimensionMismatch BLAS.gemm!('T','N', one(elty), I43, I4, elm1, I43) - @test_throws DimensionMismatch BLAS.gemm!('N','T', one(elty), I43, I43, elm1, I43) - @test_throws DimensionMismatch BLAS.gemm!('T','T', one(elty), I43, I43, elm1, Matrix{elty}(I, 3, 4)) - end - @testset "gemm compared to (sy)(he)rk" begin - if eltype(elm1) <: Complex - @test all(triu(BLAS.herk('U', 'N', U4)) .== triu(BLAS.gemm('N', 'T', U4, U4))) - @test all(tril(BLAS.herk('L', 'N', U4)) .== tril(BLAS.gemm('N', 'T', U4, U4))) - @test all(triu(BLAS.herk('U', 'N', L4)) .== triu(BLAS.gemm('N', 'T', L4, L4))) - @test all(tril(BLAS.herk('L', 'N', L4)) .== tril(BLAS.gemm('N', 'T', L4, L4))) - @test all(triu(BLAS.herk('U', 'C', U4)) .== triu(BLAS.gemm('T', 'N', U4, U4))) - @test all(tril(BLAS.herk('L', 'C', U4)) .== tril(BLAS.gemm('T', 'N', U4, U4))) - @test all(triu(BLAS.herk('U', 'C', L4)) .== triu(BLAS.gemm('T', 'N', L4, L4))) - @test all(tril(BLAS.herk('L', 'C', L4)) .== tril(BLAS.gemm('T', 'N', L4, L4))) - ans = similar(L4) - @test all(tril(BLAS.herk('L','C', L4)) .== tril(BLAS.herk!('L', 'C', real(one(elty)), L4, real(zero(elty)), ans))) - @test all(LinearAlgebra.copytri!(ans, 'L') .== LinearAlgebra.BLAS.gemm('T', 'N', L4, L4)) - @test_throws DimensionMismatch BLAS.herk!('L','N',real(one(elty)),Matrix{elty}(I, 5, 5),real(one(elty)), Matrix{elty}(I, 6, 6)) - else - @test all(triu(BLAS.syrk('U', 'N', U4)) .== triu(BLAS.gemm('N', 'T', U4, U4))) - @test all(tril(BLAS.syrk('L', 'N', U4)) .== tril(BLAS.gemm('N', 'T', U4, U4))) - @test all(triu(BLAS.syrk('U', 'N', L4)) .== triu(BLAS.gemm('N', 'T', L4, L4))) - @test all(tril(BLAS.syrk('L', 'N', L4)) .== tril(BLAS.gemm('N', 'T', L4, L4))) - @test all(triu(BLAS.syrk('U', 'T', U4)) .== triu(BLAS.gemm('T', 'N', U4, U4))) - @test all(tril(BLAS.syrk('L', 'T', U4)) .== tril(BLAS.gemm('T', 'N', U4, U4))) - @test all(triu(BLAS.syrk('U', 'T', L4)) .== triu(BLAS.gemm('T', 'N', L4, L4))) - @test all(tril(BLAS.syrk('L', 'T', L4)) .== tril(BLAS.gemm('T', 'N', L4, L4))) - ans = similar(L4) - @test all(tril(BLAS.syrk('L','T', L4)) .== tril(BLAS.syrk!('L', 'T', one(elty), L4, zero(elty), ans))) - @test all(LinearAlgebra.copytri!(ans, 'L') .== BLAS.gemm('T', 'N', L4, L4)) - @test_throws DimensionMismatch BLAS.syrk!('L','N',one(elty), Matrix{elty}(I, 5, 5),one(elty), Matrix{elty}(I, 6, 6)) - end - end -end - -@testset "syr for eltype $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty, 5, 5) - @test triu(A[1,:] * transpose(A[1,:])) ≈ BLAS.syr!('U', one(elty), A[1,:], zeros(elty, 5, 5)) - @test tril(A[1,:] * transpose(A[1,:])) ≈ BLAS.syr!('L', one(elty), A[1,:], zeros(elty, 5, 5)) - @test triu(A[1,:] * transpose(A[1,:])) ≈ BLAS.syr!('U', one(elty), view(A, 1, :), zeros(elty, 5, 5)) - @test tril(A[1,:] * transpose(A[1,:])) ≈ BLAS.syr!('L', one(elty), view(A, 1, :), zeros(elty, 5, 5)) -end - -@testset "her for eltype $elty" for elty in (ComplexF32, ComplexF64) - A = rand(elty, 5, 5) - @test triu(A[1,:] * A[1,:]') ≈ BLAS.her!('U', one(real(elty)), A[1,:], zeros(elty, 5, 5)) - @test tril(A[1,:] * A[1,:]') ≈ BLAS.her!('L', one(real(elty)), A[1,:], zeros(elty, 5, 5)) - @test triu(A[1,:] * A[1,:]') ≈ BLAS.her!('U', one(real(elty)), view(A, 1, :), zeros(elty, 5, 5)) - @test tril(A[1,:] * A[1,:]') ≈ BLAS.her!('L', one(real(elty)), view(A, 1, :), zeros(elty, 5, 5)) -end - -struct WrappedArray{T,N} <: AbstractArray{T,N} - A::Array{T,N} -end - -Base.size(A::WrappedArray) = size(A.A) -Base.getindex(A::WrappedArray, i::Int) = A.A[i] -Base.getindex(A::WrappedArray{T, N}, I::Vararg{Int, N}) where {T, N} = A.A[I...] -Base.setindex!(A::WrappedArray, v, i::Int) = setindex!(A.A, v, i) -Base.setindex!(A::WrappedArray{T, N}, v, I::Vararg{Int, N}) where {T, N} = setindex!(A.A, v, I...) -Base.cconvert(::Type{Ptr{T}}, A::WrappedArray{T}) where T = Base.cconvert(Ptr{T}, A.A) - -Base.strides(A::WrappedArray) = strides(A.A) -Base.elsize(::Type{WrappedArray{T,N}}) where {T,N} = Base.elsize(Array{T,N}) - -@testset "strided interface adjtrans" begin - x = WrappedArray([1, 2, 3, 4]) - @test stride(x,1) == 1 - @test stride(x,2) == stride(x,3) == 4 - @test strides(x') == strides(transpose(x)) == (4,1) - @test pointer(x') == pointer(transpose(x)) == pointer(x) - @test_throws BoundsError stride(x,0) - - A = WrappedArray([1 2; 3 4; 5 6]) - @test stride(A,1) == 1 - @test stride(A,2) == 3 - @test stride(A,3) == stride(A,4) >= 6 - @test strides(A') == strides(transpose(A)) == (3,1) - @test pointer(A') == pointer(transpose(A)) == pointer(A) - @test_throws BoundsError stride(A,0) - - y = WrappedArray([1+im, 2, 3, 4]) - @test strides(transpose(y)) == (4,1) - @test pointer(transpose(y)) == pointer(y) - @test_throws MethodError strides(y') - @test_throws ErrorException pointer(y') - - B = WrappedArray([1+im 2; 3 4; 5 6]) - @test strides(transpose(B)) == (3,1) - @test pointer(transpose(B)) == pointer(B) - @test_throws MethodError strides(B') - @test_throws ErrorException pointer(B') - - @test_throws MethodError stride(1:5,0) - @test_throws MethodError stride(1:5,1) - @test_throws MethodError stride(1:5,2) - @test_throws MethodError strides(transpose(1:5)) - @test_throws MethodError strides((1:5)') - @test_throws ErrorException pointer(transpose(1:5)) - @test_throws ErrorException pointer((1:5)') -end - -@testset "strided interface blas" begin - for elty in (Float32, Float64, ComplexF32, ComplexF64) - # Level 1 - x = WrappedArray(elty[1, 2, 3, 4]) - y = WrappedArray(elty[5, 6, 7, 8]) - BLAS.blascopy!(2, x, 1, y, 2) - @test y == WrappedArray(elty[1, 6, 2, 8]) - BLAS.scal!(2, elty(2), x, 1) - @test x == WrappedArray(elty[2, 4, 3, 4]) - @test BLAS.nrm2(1, x, 2) == elty(2) - @test BLAS.nrm2(x) == BLAS.nrm2(x.A) - BLAS.asum(x) == elty(13) - BLAS.axpy!(4, elty(2), x, 1, y, 1) - @test y == WrappedArray(elty[5, 14, 8, 16]) - BLAS.axpby!(elty(2), x, elty(3), y) - @test y == WrappedArray(elty[19, 50, 30, 56]) - @test BLAS.iamax(x) == 2 - - M = fill(elty(1.0), 3, 3) - @test BLAS.scal!(elty(2), view(M,:,2)) === view(M,:,2) - @test BLAS.scal!(elty(3), view(M,3,:)) === view(M,3,:) - @test M == elty[1. 2. 1.; 1. 2. 1.; 3. 6. 3.] - # Level 2 - A = WrappedArray(elty[1 2; 3 4]) - x = WrappedArray(elty[1, 2]) - y = WrappedArray(elty[3, 4]) - @test BLAS.gemv!('N', elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[13, 26]) - @test BLAS.gbmv!('N', 2, 1, 0, elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[15, 40]) - @test BLAS.symv!('U', elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[25, 60]) - @test BLAS.trmv!('U', 'N', 'N', A, y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[145, 240]) - @test BLAS.trsv!('U', 'N', 'N', A, y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[25,60]) - @test BLAS.ger!(elty(2), x, y, A) isa WrappedArray{elty,2} - @test A == WrappedArray(elty[51 122; 103 244]) - @test BLAS.syr!('L', elty(2), x, A) isa WrappedArray{elty,2} - @test A == WrappedArray(elty[53 122; 107 252]) - # Level 3 - A = WrappedArray(elty[1 2; 3 4]) - B = WrappedArray(elty[5 6; 7 8]) - C = WrappedArray(elty[9 10; 11 12]) - BLAS.gemm!('N', 'N', elty(2), A, B, elty(1), C) isa WrappedArray{elty,2} - @test C == WrappedArray([47 54; 97 112]) - BLAS.symm!('L', 'U', elty(2), A, B, elty(1), C) isa WrappedArray{elty,2} - @test C == WrappedArray([85 98; 173 200]) - BLAS.syrk!('U', 'N', elty(2), A, elty(1), C) isa WrappedArray{elty,2} - @test C == WrappedArray([95 120; 173 250]) - BLAS.syr2k!('U', 'N', elty(2), A, B, elty(1), C) isa WrappedArray{elty,2} - @test C == WrappedArray([163 244; 173 462]) - BLAS.trmm!('L', 'U', 'N', 'N', elty(2), A, B) isa WrappedArray{elty,2} - @test B == WrappedArray([38 44; 56 64]) - BLAS.trsm!('L', 'U', 'N', 'N', elty(2), A, B) isa WrappedArray{elty,2} - @test B == WrappedArray([20 24; 28 32]) - end - for elty in (Float32, Float64) - # Level 1 - x = WrappedArray(elty[1, 2, 3, 4]) - y = WrappedArray(elty[5, 6, 7, 8]) - @test BLAS.dot(2, x, 1, y, 2) == elty(19) - # Level 2 - A = WrappedArray(elty[1 2; 3 4]) - x = WrappedArray(elty[1, 2]) - y = WrappedArray(elty[3, 4]) - BLAS.sbmv!('U', 1, elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[17,24]) - end - for elty in (ComplexF32, ComplexF64) - # Level 1 - x = WrappedArray(elty[1+im, 2+2im, 3+3im, 4+im]) - y = WrappedArray(elty[5-im, 6-2im, 7-3im, 8-im]) - @test BLAS.dotc(2, x, 1, y, 2) == elty(12-26im) - @test BLAS.dotu(2, x, 1, y, 2) == elty(26+12im) - # Level 2 - A = WrappedArray(elty[1+im 2+2im; 3+3im 4+4im]) - x = WrappedArray(elty[1+im, 2+2im]) - y = WrappedArray(elty[5-im, 6-2im]) - @test BLAS.hemv!('U', elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[7+17im, 30+14im]) - BLAS.hbmv!('U', 1, elty(2), A, x, elty(1), y) isa WrappedArray{elty,1} - @test y == WrappedArray(elty[13+39im, 54+30im]) - @test BLAS.her!('L', real(elty(2)), x, A) isa WrappedArray{elty,2} - @test A == WrappedArray(elty[5 2+2im; 11+3im 20]) - # Level 3 - A = WrappedArray(elty[1+im 2+2im; 3+3im 4+4im]) - B = WrappedArray(elty[1+im 2+2im; 3+3im 4+4im]) - C = WrappedArray(elty[1+im 2+2im; 3+3im 4+4im]) - @test BLAS.hemm!('L', 'U', elty(2), A, B, elty(1), C) isa WrappedArray{elty,2} - @test C == WrappedArray([3+27im 6+38im; 35+27im 52+36im]) - @test BLAS.herk!('U', 'N', real(elty(2)), A, real(elty(1)), C) isa WrappedArray{elty,2} - @test C == WrappedArray([23 50+38im; 35+27im 152]) - @test BLAS.her2k!('U', 'N', elty(2), A, B, real(elty(1)), C) isa WrappedArray{elty,2} - @test C == WrappedArray([63 138+38im; 35+27im 352]) - end -end - -@testset "get_set_num_threads" begin - default = BLAS.get_num_threads() - @test default isa Int - @test default > 0 - BLAS.set_num_threads(1) - @test BLAS.get_num_threads() === 1 - BLAS.set_num_threads(default) - @test BLAS.get_num_threads() === default -end - -@testset "test for 0-strides" for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = randn(elty, 10, 10); - a = view([randn(elty)], 1 .+ 0(1:10)) - b = view([randn(elty)], 1 .+ 0(1:10)) - α, β = randn(elty), randn(elty) - @testset "dot/dotc/dotu" begin - if elty <: Real - @test BLAS.dot(a,b) ≈ sum(a.*b) - else - @test BLAS.dotc(a,b) ≈ sum(conj(a).*b) - @test BLAS.dotu(a,b) ≈ sum(a.*b) - end - end - @testset "axp(b)y!" begin - @test BLAS.axpy!(α,a,copy(b)) ≈ α*a + b - @test BLAS.axpby!(α,a,β,copy(b)) ≈ α*a + β*b - @test_throws "dest" BLAS.axpy!(α,a,b) - @test_throws "dest" BLAS.axpby!(α,a,β,b) - end - @test BLAS.iamax(a) == 0 - @test_throws "dest" BLAS.scal!(b[1], a) - @testset "nrm2/asum" begin # OpenBLAS always return 0.0 - @test_throws "input" BLAS.nrm2(a) - @test_throws "input" BLAS.asum(a) - end - # All level2 reject 0-stride array. - @testset "gemv!" begin - @test_throws "input" BLAS.gemv!('N', true, A, a, false, copy(b)) - @test_throws "dest" BLAS.gemv!('N', true, A, copy(a), false, b) - end -end - -# Make sure we can use `Base.libblas_name`. Avoid causing -# https://github.com/JuliaLang/julia/issues/48427 again. -@testset "libblas_name" begin - dot_sym = dlsym(dlopen(Base.libblas_name), "cblas_ddot" * (Sys.WORD_SIZE == 64 ? "64_" : "")) - @test 23.0 === @ccall $(dot_sym)(2::Int, [2.0, 3.0]::Ref{Cdouble}, 1::Int, [4.0, 5.0]::Ref{Cdouble}, 1::Int)::Cdouble -end - -end # module TestBLAS diff --git a/stdlib/LinearAlgebra/test/bunchkaufman.jl b/stdlib/LinearAlgebra/test/bunchkaufman.jl deleted file mode 100644 index 68c519d1197ed..0000000000000 --- a/stdlib/LinearAlgebra/test/bunchkaufman.jl +++ /dev/null @@ -1,260 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestBunchKaufman - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted -using Base: getproperty - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(12343212) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 -a2real = randn(n,n)/2 -a2img = randn(n,n)/2 -breal = randn(n,2)/2 -bimg = randn(n,2)/2 - -areint = rand(1:7, n, n) -aimint = rand(1:7, n, n) -a2reint = rand(1:7, n, n) -a2imint = rand(1:7, n, n) -breint = rand(1:5, n, 2) -bimint = rand(1:5, n, 2) - -@testset "$eltya argument A" for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int, ### - Float16, Complex{Float16}, BigFloat, Complex{BigFloat}, Complex{Int}, BigInt, - Complex{BigInt}, Rational{BigInt}, Complex{Rational{BigInt}}) - # a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - # a2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - a = convert(Matrix{eltya}, eltya <: Complex ? (real(eltya) <: AbstractFloat ? - complex.(areal, aimg) : complex.(areint, aimint)) : (eltya <: AbstractFloat ? - areal : areint)) - a2 = convert(Matrix{eltya}, eltya <: Complex ? (real(eltya) <: AbstractFloat ? - complex.(a2real, a2img) : complex.(a2reint, a2imint)) : (eltya <: AbstractFloat ? - a2real : a2reint)) - asym = transpose(a) + a # symmetric indefinite - aher = a' + a # Hermitian indefinite - apd = a' * a # Positive-definite - for (a, a2, aher, apd) in ((a, a2, aher, apd), - (view(a, 1:n, 1:n), - view(a2, 1:n, 1:n), - view(aher, 1:n, 1:n), - view(apd , 1:n, 1:n))) - ε = εa = eps(abs(float(one(eltya)))) - - # Inertia tests - @testset "$uplo Bunch-Kaufman factor inertia" for uplo in (:L, :U) - @testset "rook pivoting: $rook" for rook in (false, true) - test_list = eltya <: Complex ? (Hermitian(aher, uplo), Hermitian(apd, uplo)) : - (Symmetric(transpose(a) + a, uplo), Hermitian(aher, uplo), - Hermitian(apd, uplo)) - ελ = n*max(eps(Float64), εa) # zero-eigenvalue threshold - ελ = typeof(Integer(one(real(eltya)))) <: Signed ? Rational{BigInt}(ελ) : - real(eltya(ελ)) - for M in test_list - bc = bunchkaufman(M, rook) - D = bc.D - λ = real(eltya <: Complex ? eigen(ComplexF64.(D)).values : - eigen(Float64.(D)).values) - σ₁ = norm(λ, Inf) - np = sum(λ .> ελ*σ₁) - nn = sum(λ .< -ελ*σ₁) - nz = n - np - nn - if real(eltya) <: AbstractFloat - @test inertia(bc) == (np, nn, nz) - else - @test inertia(bc; rtol=ελ) == (np, nn, nz) - end - end - end - end - - # check that factorize gives a Bunch-Kaufman - if eltya <: Union{Float32, Float64, ComplexF32, ComplexF64, Int} - # Default behaviour only uses Bunch-Kaufman for these types, for now. - @test isa(factorize(asym), LinearAlgebra.BunchKaufman) - @test isa(factorize(aher), LinearAlgebra.BunchKaufman) - end - @testset "$uplo Bunch-Kaufman factor of indefinite matrix" for uplo in (:L, :U) - bc1 = bunchkaufman(Hermitian(aher, uplo)) - @test LinearAlgebra.issuccess(bc1) - @test logabsdet(bc1)[1] ≈ log(abs(det(bc1))) - if eltya <: Real - @test logabsdet(bc1)[2] == sign(det(bc1)) - else - @test logabsdet(bc1)[2] ≈ sign(det(bc1)) - end - @test inv(bc1)*aher ≈ Matrix(I, n, n) - @testset for rook in (false, true) - @test inv(bunchkaufman(Symmetric(transpose(a) + a, uplo), rook))*(transpose(a) + a) ≈ Matrix(I, n, n) - if eltya <: BlasFloat - # test also bunchkaufman! without explicit type tag - # no bunchkaufman! method for Int ... yet - @test inv(bunchkaufman!(transpose(a) + a, rook))*(transpose(a) + a) ≈ Matrix(I, n, n) - end - @test size(bc1) == size(bc1.LD) - @test size(bc1, 1) == size(bc1.LD, 1) - @test size(bc1, 2) == size(bc1.LD, 2) - if eltya <: BlasReal - @test_throws ArgumentError bunchkaufman(a) - end - # Test extraction of factors - if eltya <: Real - @test getproperty(bc1, uplo)*bc1.D*getproperty(bc1, uplo)' ≈ aher[bc1.p, bc1.p] - @test getproperty(bc1, uplo)*bc1.D*getproperty(bc1, uplo)' ≈ bc1.P*aher*bc1.P' - end - - bc1 = bunchkaufman(Symmetric(asym, uplo)) - @test getproperty(bc1, uplo)*bc1.D*transpose(getproperty(bc1, uplo)) ≈ asym[bc1.p, bc1.p] - @test getproperty(bc1, uplo)*bc1.D*transpose(getproperty(bc1, uplo)) ≈ bc1.P*asym*transpose(bc1.P) - @test_throws FieldError bc1.Z - @test_throws ArgumentError uplo === :L ? bc1.U : bc1.L - end - # test Base.iterate - ref_objs = (bc1.D, uplo === :L ? bc1.L : bc1.U, bc1.p) - for (bki, bkobj) in enumerate(bc1) - @test bkobj == ref_objs[bki] - end - if eltya <: BlasFloat - @test convert(LinearAlgebra.BunchKaufman{eltya}, bc1) === bc1 - @test convert(LinearAlgebra.Factorization{eltya}, bc1) === bc1 - if eltya <: BlasReal - @test convert(LinearAlgebra.Factorization{Float16}, bc1) == convert(LinearAlgebra.BunchKaufman{Float16}, bc1) - elseif eltya <: BlasComplex - @test convert(LinearAlgebra.Factorization{ComplexF16}, bc1) == convert(LinearAlgebra.BunchKaufman{ComplexF16}, bc1) - end - end - @test Base.propertynames(bc1) == (:p, :P, :L, :U, :D) - end - - @testset "$eltyb argument B" for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int, ### - Float16, Complex{Float16}, BigFloat, Complex{BigFloat}, Complex{Int}, BigInt, - Complex{BigInt}, Rational{BigInt}, Complex{Rational{BigInt}}) - # b = eltyb == Int ? rand(1:5, n, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) - b = convert(Matrix{eltyb}, eltyb <: Complex ? (real(eltyb) <: AbstractFloat ? - complex.(breal, bimg) : complex.(breint, bimint)) : (eltyb <: AbstractFloat ? - breal : breint)) - for b in (b, view(b, 1:n, 1:2)) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - epsc = eltya <: Complex ? sqrt(2)*n : n # tolerance scale - - @testset "$uplo Bunch-Kaufman factor of indefinite matrix" for uplo in (:L, :U) - bc1 = bunchkaufman(Hermitian(aher, uplo)) - # @test aher*(bc1\b) ≈ b atol=1000ε - cda = eltya <: Complex ? cond(ComplexF64.(aher)) : cond(Float64.(aher)) - cda = real(eltya) <: AbstractFloat ? real(eltya(cda)) : cda - @test norm(aher*(bc1\b) - b) <= epsc*sqrt(eps(cda))*max( - norm(aher*(bc1\b)), norm(b)) - end - - @testset "$uplo Bunch-Kaufman factors of a pos-def matrix" for uplo in (:U, :L) - @testset "rook pivoting: $rook" for rook in (false, true) - bc2 = bunchkaufman(Hermitian(apd, uplo), rook) - @test LinearAlgebra.issuccess(bc2) - bks = split(sprint(show, "text/plain", bc2), "\n") - @test bks[1] == summary(bc2) - @test bks[2] == "D factor:" - @test bks[4+n] == "$uplo factor:" - @test bks[6+2n] == "permutation:" - @test logdet(bc2) ≈ log(det(bc2)) - @test logabsdet(bc2)[1] ≈ log(abs(det(bc2))) - @test logabsdet(bc2)[2] == sign(det(bc2)) - # @test inv(bc2)*apd ≈ Matrix(I, n, n) rtol=Base.rtoldefault(real(eltya)) - # @test apd*(bc2\b) ≈ b rtol=eps(cond(apd)) - @test norm(inv(bc2)*apd - Matrix(I, n, n)) <= epsc*Base.rtoldefault( - real(eltya))*max(norm(inv(bc2)*apd), norm(Matrix(I, n, n))) - cda = eltya <: Complex ? cond(ComplexF64.(apd)) : cond(Float64.(apd)) - cda = real(eltya) <: AbstractFloat ? real(eltya(cda)) : cda - @test norm(apd*(bc2\b) - b) <= epsc*sqrt(eps(cda))*max( - norm(apd*(bc2\b)), norm(b)) - @test ishermitian(bc2) - @test !issymmetric(bc2) || eltya <: Real - end - end - end - end - end -end - -@testset "Singular matrices" begin - R = Float64[1 0; 0 0] - C = ComplexF64[1 0; 0 0] - for A in (R, Symmetric(R), C, Hermitian(C)) - @test_throws SingularException bunchkaufman(A) - @test_throws SingularException bunchkaufman!(copy(A)) - @test_throws SingularException bunchkaufman(A; check = true) - @test_throws SingularException bunchkaufman!(copy(A); check = true) - @test !issuccess(bunchkaufman(A; check = false)) - @test !issuccess(bunchkaufman!(copy(A); check = false)) - end - F = bunchkaufman(R; check = false) - @test sprint(show, "text/plain", F) == "Failed factorization of type $(typeof(F))" -end - -@testset "test example due to @timholy in PR 15354" begin - A = rand(6,5); A = complex(A'*A) # to avoid calling the real-lhs-complex-rhs method - F = cholesky(A); - v6 = rand(ComplexF64, 6) - v5 = view(v6, 1:5) - @test F\v5 == F\v6[1:5] -end - -@testset "issue #32080" begin - A = Symmetric([-5 -9 9; -9 4 1; 9 1 2]) - B = bunchkaufman(A, true) - @test B.U * B.D * B.U' ≈ A[B.p, B.p] -end - -@test_throws DomainError logdet(bunchkaufman([-1 -1; -1 1])) -@test logabsdet(bunchkaufman([8 4; 4 2]; check = false))[1] == -Inf - -@testset "0x0 matrix" begin - for ul in (:U, :L) - B = bunchkaufman(Symmetric(ones(0, 0), ul)) - @test isa(B, BunchKaufman) - @test B.D == Tridiagonal([], [], []) - @test B.P == ones(0, 0) - @test B.p == [] - if ul === :U - @test B.U == UnitUpperTriangular(ones(0, 0)) - @test_throws ArgumentError B.L - else - @test B.L == UnitLowerTriangular(ones(0, 0)) - @test_throws ArgumentError B.U - end - end -end - -@testset "adjoint of BunchKaufman" begin - Ar = randn(5, 5) - Ar = Ar + Ar' - Actmp = complex.(randn(5, 5), randn(5, 5)) - Ac1 = Actmp + Actmp' - Ac2 = Actmp + transpose(Actmp) - b = ones(size(Ar, 1)) - - F = bunchkaufman(Ar) - @test F\b == F'\b - - F = bunchkaufman(Ac1) - @test F\b == F'\b - - F = bunchkaufman(Ac2) - @test_throws ArgumentError("adjoint not implemented for complex symmetric matrices") F' -end - -@testset "BunchKaufman for AbstractMatrix" begin - S = SymTridiagonal(fill(2.0, 4), ones(3)) - B = bunchkaufman(S) - @test B.U * B.D * B.U' ≈ S -end - -end # module TestBunchKaufman diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl deleted file mode 100644 index 6ba72432048a9..0000000000000 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ /dev/null @@ -1,661 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestCholesky - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted, - PosDefException, RankDeficientException, chkfullrank - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") - -isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) -using .Main.Quaternions - -function unary_ops_tests(a, ca, tol; n=size(a, 1)) - @test inv(ca)*a ≈ Matrix(I, n, n) - @test a*inv(ca) ≈ Matrix(I, n, n) - @test abs((det(ca) - det(a))/det(ca)) <= tol # Ad hoc, but statistically verified, revisit - @test logdet(ca) ≈ logdet(a) broken = eltype(a) <: Quaternion - @test logdet(ca) ≈ log(det(ca)) # logdet is less likely to overflow - logabsdet_ca = logabsdet(ca) - logabsdet_a = logabsdet(a) - @test logabsdet_ca[1] ≈ logabsdet_a[1] - @test logabsdet_ca[2] ≈ logabsdet_a[2] - @test isposdef(ca) - @test_throws FieldError ca.Z - @test size(ca) == size(a) - @test Array(copy(ca)) ≈ a - @test tr(ca) ≈ tr(a) skip=ca isa CholeskyPivoted -end - -function factor_recreation_tests(a_U, a_L) - c_U = cholesky(a_U) - c_L = cholesky(a_L) - cl = c_L.U - ls = c_L.L - @test Array(c_U) ≈ Array(c_L) ≈ a_U - @test ls*ls' ≈ a_U - @test triu(c_U.factors) ≈ c_U.U - @test tril(c_L.factors) ≈ c_L.L - @test istriu(cl) - @test cl'cl ≈ a_U - @test cl'cl ≈ a_L -end - -@testset "core functionality" begin - n = 10 - - # Split n into 2 parts for tests needing two matrices - n1 = div(n, 2) - n2 = 2*n1 - - Random.seed!(12344) - - areal = randn(n,n)/2 - aimg = randn(n,n)/2 - a2real = randn(n,n)/2 - a2img = randn(n,n)/2 - breal = randn(n,2)/2 - bimg = randn(n,2)/2 - - for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Complex{BigFloat}, Quaternion{Float64}, Int) - a = if eltya == Int - rand(1:7, n, n) - elseif eltya <: Real - convert(Matrix{eltya}, areal) - elseif eltya <: Complex - convert(Matrix{eltya}, complex.(areal, aimg)) - else - convert(Matrix{eltya}, Quaternion.(areal, aimg, a2real, a2img)) - end - - ε = εa = eps(abs(float(one(eltya)))) - - # Test of symmetric pos. def. strided matrix - apd = Matrix(Hermitian(a'*a)) - capd = @inferred cholesky(apd) - r = capd.U - κ = cond(apd, 1) #condition number - - unary_ops_tests(apd, capd, ε*κ*n) - if eltya != Int - @test Factorization{eltya}(capd) === capd - if eltya <: Real - @test Array(Factorization{complex(eltya)}(capd)) ≈ Array(cholesky(complex(apd))) - @test eltype(Factorization{complex(eltya)}(capd)) == complex(eltya) - end - end - @testset "throw for non-square input" begin - A = rand(eltya, 2, 3) - @test_throws DimensionMismatch cholesky(A) - @test_throws DimensionMismatch cholesky!(A) - end - - #Test error bound on reconstruction of matrix: LAWNS 14, Lemma 2.1 - - #these tests were failing on 64-bit linux when inside the inner loop - #for eltya = ComplexF32 and eltyb = Int. The E[i,j] had NaN32 elements - #but only with Random.seed!(1234321) set before the loops. - E = abs.(apd - r'*r) - for i=1:n, j=1:n - @test E[i,j] <= (n+1)ε/(1-(n+1)ε)*sqrt(real(apd[i,i]*apd[j,j])) - end - E = abs.(apd - Matrix(capd)) - for i=1:n, j=1:n - @test E[i,j] <= (n+1)ε/(1-(n+1)ε)*sqrt(real(apd[i,i]*apd[j,j])) - end - @test LinearAlgebra.issuccess(capd) - @inferred(logdet(capd)) - - apos = real(apd[1,1]) - @test all(x -> x ≈ √apos, cholesky(apos).factors) - - # Test cholesky with Symmetric/Hermitian upper/lower - apds = Symmetric(apd) - apdsL = Symmetric(apd, :L) - apdh = Hermitian(apd) - apdhL = Hermitian(apd, :L) - if eltya <: Real - capds = cholesky(apds) - unary_ops_tests(apds, capds, ε*κ*n) - if eltya <: BlasReal - capds = cholesky!(copy(apds)) - unary_ops_tests(apds, capds, ε*κ*n) - end - ulstring = sprint((t, s) -> show(t, "text/plain", s), capds.UL) - @test sprint((t, s) -> show(t, "text/plain", s), capds) == "$(typeof(capds))\nU factor:\n$ulstring" - else - capdh = cholesky(apdh) - unary_ops_tests(apdh, capdh, ε*κ*n) - capdh = cholesky!(copy(apdh)) - unary_ops_tests(apdh, capdh, ε*κ*n) - capdh = cholesky!(copy(apd)) - unary_ops_tests(apd, capdh, ε*κ*n) - ulstring = sprint((t, s) -> show(t, "text/plain", s), capdh.UL) - @test sprint((t, s) -> show(t, "text/plain", s), capdh) == "$(typeof(capdh))\nU factor:\n$ulstring" - end - - # test cholesky of 2x2 Strang matrix - S = SymTridiagonal{eltya}([2, 2], [-1]) - for uplo in (:U, :L) - @test Matrix(@inferred cholesky(Hermitian(S, uplo))) ≈ S - if eltya <: Real - @test Matrix(@inferred cholesky(Symmetric(S, uplo))) ≈ S - end - end - @test Matrix(cholesky(S).U) ≈ [2 -1; 0 float(eltya)(sqrt(real(eltya)(3)))] / float(eltya)(sqrt(real(eltya)(2))) - @test Matrix(cholesky(S)) ≈ S - - # test extraction of factor and re-creating original matrix - if eltya <: Real - factor_recreation_tests(apds, apdsL) - else - factor_recreation_tests(apdh, apdhL) - end - - #pivoted upper Cholesky - for tol in (0.0, -1.0), APD in (apdh, apdhL) - cpapd = cholesky(APD, RowMaximum(), tol=tol) - unary_ops_tests(APD, cpapd, ε*κ*n) - @test rank(cpapd) == n - @test all(diff(real(diag(cpapd.factors))).<=0.) # diagonal should be non-increasing - - @test cpapd.P*cpapd.L*cpapd.U*cpapd.P' ≈ apd - end - - for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int) - b = if eltya <: Quaternion - convert(Matrix{eltya}, Quaternion.(breal, bimg, bimg, bimg)) - elseif eltyb == Int - rand(1:5, n, 2) - elseif eltyb <: Complex - convert(Matrix{eltyb}, complex.(breal, bimg)) - elseif eltyb <: Real - convert(Matrix{eltyb}, breal) - end - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - - for b in (b, view(b, 1:n, 1)) # Array and SubArray - - # Test error bound on linear solver: LAWNS 14, Theorem 2.1 - # This is a surprisingly loose bound - x = capd\b - @test norm(x-apd\b,1)/norm(x,1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(apd*x-b,1)/norm(b,1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - - @test norm(a*(capd\(a'*b)) - b,1)/norm(b,1) <= ε*κ*n # Ad hoc, revisit - - lapd = cholesky(apdhL) - @test norm(apd * (lapd\b) - b)/norm(b) <= ε*κ*n - @test norm(apd * (lapd\b[1:n]) - b[1:n])/norm(b[1:n]) <= ε*κ*n - - cpapd = cholesky(apdh, RowMaximum()) - @test norm(apd * (cpapd\b) - b)/norm(b) <= ε*κ*n # Ad hoc, revisit - @test norm(apd * (cpapd\b[1:n]) - b[1:n])/norm(b[1:n]) <= ε*κ*n - - lpapd = cholesky(apdhL, RowMaximum()) - @test norm(apd * (lpapd\b) - b)/norm(b) <= ε*κ*n # Ad hoc, revisit - @test norm(apd * (lpapd\b[1:n]) - b[1:n])/norm(b[1:n]) <= ε*κ*n - end - end - - for eltyb in (Float64, ComplexF64) - Breal = convert(Matrix{BigFloat}, randn(n,n)/2) - Bimg = convert(Matrix{BigFloat}, randn(n,n)/2) - B = if eltya <: Quaternion - Quaternion.(Float64.(Breal), Float64.(Bimg), Float64.(Bimg), Float64.(Bimg)) - elseif eltya <: Complex || eltyb <: Complex - complex.(Breal, Bimg) - else - Breal - end - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - - for B in (B, view(B, 1:n, 1:n)) # Array and SubArray - - # Test error bound on linear solver: LAWNS 14, Theorem 2.1 - # This is a surprisingly loose bound - BB = copy(B) - ldiv!(capd, BB) - @test norm(apd \ B - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(apd * BB - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - cpapd = cholesky(apdh, RowMaximum()) - BB = copy(B) - ldiv!(cpapd, BB) - @test norm(apd \ B - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(apd * BB - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - end - end - - @testset "solve with generic Cholesky" begin - Breal = convert(Matrix{BigFloat}, randn(n,n)/2) - Bimg = convert(Matrix{BigFloat}, randn(n,n)/2) - B = if eltya <: Quaternion - eltya.(Breal, Bimg, Bimg, Bimg) - elseif eltya <: Complex - complex.(Breal, Bimg) - else - Breal - end - εb = eps(abs(float(one(eltype(B))))) - ε = max(εa,εb) - - for B in (B, view(B, 1:n, 1:n)) # Array and SubArray - - # Test error bound on linear solver: LAWNS 14, Theorem 2.1 - # This is a surprisingly loose bound - cpapd = cholesky(eltya <: Real ? apds : apdh) - BB = copy(B) - rdiv!(BB, cpapd) - @test norm(B / apd - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(BB * apd - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - cpapd = cholesky(eltya <: Real ? apdsL : apdhL) - BB = copy(B) - rdiv!(BB, cpapd) - @test norm(B / apd - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(BB * apd - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - cpapd = cholesky(eltya <: Real ? apds : apdh, RowMaximum()) - BB = copy(B) - rdiv!(BB, cpapd) - @test norm(B / apd - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(BB * apd - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - cpapd = cholesky(eltya <: Real ? apdsL : apdhL, RowMaximum()) - BB = copy(B) - rdiv!(BB, cpapd) - @test norm(B / apd - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - @test norm(BB * apd - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ - end - end - if eltya <: BlasFloat - @testset "generic cholesky!" begin - if eltya <: Complex - A = complex.(randn(5,5), randn(5,5)) - else - A = randn(5,5) - end - A = convert(Matrix{eltya}, A'A) - @test Matrix(cholesky(A).L) ≈ Matrix(invoke(LinearAlgebra._chol!, Tuple{AbstractMatrix, Type{LowerTriangular}}, copy(A), LowerTriangular)[1]) - @test Matrix(cholesky(A).U) ≈ Matrix(invoke(LinearAlgebra._chol!, Tuple{AbstractMatrix, Type{UpperTriangular}}, copy(A), UpperTriangular)[1]) - end - end - end - - @testset "eltype/matrixtype conversions" begin - apd = Matrix(Hermitian(areal'*areal)) - capd = cholesky(apd) - @test convert(Cholesky{Float64}, capd) === capd - @test convert(Cholesky{Float64,Matrix{Float64}}, capd) === convert(typeof(capd), capd) === capd - @test eltype(convert(Cholesky{Float32}, capd)) === Float32 - @test eltype(convert(Cholesky{Float32,Matrix{Float32}}, capd)) === Float32 - - capd = cholesky(apd, RowMaximum()) - @test convert(CholeskyPivoted{Float64}, capd) === capd - @test convert(CholeskyPivoted{Float64,Matrix{Float64}}, capd) === capd - @test convert(CholeskyPivoted{Float64,Matrix{Float64},Vector{Int}}, capd) === convert(typeof(capd), capd) === capd - @test eltype(convert(CholeskyPivoted{Float32}, capd)) === Float32 - @test eltype(convert(CholeskyPivoted{Float32,Matrix{Float32}}, capd)) === Float32 - @test eltype(convert(CholeskyPivoted{Float32,Matrix{Float32},Vector{Int}}, capd)) === Float32 - @test eltype(convert(CholeskyPivoted{Float32,Matrix{Float32},Vector{Int16}}, capd).piv) === Int16 - end -end - -@testset "behavior for non-positive definite matrices" for T in (Float64, ComplexF64, BigFloat) - A = T[1 2; 2 1] - B = T[1 2; 0 1] - C = T[2 0; 0 0] - # check = (true|false) - for M in (A, Hermitian(A), B, C) - @test_throws PosDefException cholesky(M) - @test_throws PosDefException cholesky!(copy(M)) - @test_throws PosDefException cholesky(M; check = true) - @test_throws PosDefException cholesky!(copy(M); check = true) - @test !issuccess(cholesky(M; check = false)) - @test !issuccess(cholesky!(copy(M); check = false)) - end - for M in (A, Hermitian(A)) # hermitian, but not semi-positive definite - @test_throws RankDeficientException cholesky(M, RowMaximum()) - @test_throws RankDeficientException cholesky!(copy(M), RowMaximum()) - @test_throws RankDeficientException cholesky(M, RowMaximum(); check = true) - @test_throws RankDeficientException cholesky!(copy(M), RowMaximum(); check = true) - @test !issuccess(cholesky(M, RowMaximum(); check = false)) - @test !issuccess(cholesky!(copy(M), RowMaximum(); check = false)) - C = cholesky(M, RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) - C = cholesky!(copy(M), RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) - end - for M in (B,) # not hermitian - @test_throws PosDefException(-1) cholesky(M, RowMaximum()) - @test_throws PosDefException(-1) cholesky!(copy(M), RowMaximum()) - @test_throws PosDefException(-1) cholesky(M, RowMaximum(); check = true) - @test_throws PosDefException(-1) cholesky!(copy(M), RowMaximum(); check = true) - @test !issuccess(cholesky(M, RowMaximum(); check = false)) - @test !issuccess(cholesky!(copy(M), RowMaximum(); check = false)) - C = cholesky(M, RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) - C = cholesky!(copy(M), RowMaximum(); check = false) - @test_throws RankDeficientException chkfullrank(C) - end - @test !isposdef(A) - str = sprint((io, x) -> show(io, "text/plain", x), cholesky(A; check = false)) -end - -@testset "Cholesky factor of Matrix with non-commutative elements, here 2x2-matrices" begin - X = Matrix{Float64}[0.1*rand(2,2) for i in 1:3, j = 1:3] - L = Matrix(LinearAlgebra._chol!(X*X', LowerTriangular)[1]) - U = Matrix(LinearAlgebra._chol!(X*X', UpperTriangular)[1]) - XX = Matrix(X*X') - - @test sum(sum(norm, L*L' - XX)) < eps() - @test sum(sum(norm, U'*U - XX)) < eps() -end - -@testset "Non-strided Cholesky solves" begin - B = randn(5, 5) - v = rand(5) - @test cholesky(Diagonal(v)) \ B ≈ Diagonal(v) \ B - @test B / cholesky(Diagonal(v)) ≈ B / Diagonal(v) - @test inv(cholesky(Diagonal(v)))::Diagonal ≈ Diagonal(1 ./ v) -end - -struct WrappedVector{T} <: AbstractVector{T} - data::Vector{T} -end -Base.copy(v::WrappedVector) = WrappedVector(copy(v.data)) -Base.size(v::WrappedVector) = size(v.data) -Base.getindex(v::WrappedVector, i::Integer) = getindex(v.data, i) -Base.setindex!(v::WrappedVector, val, i::Integer) = setindex!(v.data, val, i) - -@testset "cholesky up- and downdates" begin - A = complex.(randn(10,5), randn(10, 5)) - v = complex.(randn(5), randn(5)) - w = WrappedVector(v) - for uplo in (:U, :L) - AcA = A'*A - BcB = AcA + v*v' - BcB = (BcB + BcB')/2 - F = cholesky(Hermitian(AcA, uplo)) - G = cholesky(Hermitian(BcB, uplo)) - @test getproperty(lowrankupdate(F, v), uplo) ≈ getproperty(G, uplo) - @test getproperty(lowrankupdate(F, w), uplo) ≈ getproperty(G, uplo) - @test_throws DimensionMismatch lowrankupdate(F, Vector{eltype(v)}(undef,length(v)+1)) - @test getproperty(lowrankdowndate(G, v), uplo) ≈ getproperty(F, uplo) - @test getproperty(lowrankdowndate(G, w), uplo) ≈ getproperty(F, uplo) - @test_throws DimensionMismatch lowrankdowndate(G, Vector{eltype(v)}(undef,length(v)+1)) - end -end - -@testset "issue #13243, unexpected nans in complex cholesky" begin - apd = [5.8525753f0 + 0.0f0im -0.79540455f0 + 0.7066077f0im 0.98274714f0 + 1.3824869f0im 2.619998f0 + 1.8532984f0im -1.8306153f0 - 1.2336911f0im 0.32275113f0 + 0.015575029f0im 2.1968813f0 + 1.0640624f0im 0.27894387f0 + 0.97911835f0im 3.0476584f0 + 0.18548489f0im 0.3842994f0 + 0.7050991f0im - -0.79540455f0 - 0.7066077f0im 8.313246f0 + 0.0f0im -1.8076122f0 - 0.8882447f0im 0.47806996f0 + 0.48494184f0im 0.5096429f0 - 0.5395974f0im -0.7285097f0 - 0.10360408f0im -1.1760061f0 - 2.7146957f0im -0.4271084f0 + 0.042899966f0im -1.7228563f0 + 2.8335886f0im 1.8942566f0 + 0.6389735f0im - 0.98274714f0 - 1.3824869f0im -1.8076122f0 + 0.8882447f0im 9.367975f0 + 0.0f0im -0.1838578f0 + 0.6468568f0im -1.8338387f0 + 0.7064959f0im 0.041852742f0 - 0.6556877f0im 2.5673025f0 + 1.9732997f0im -1.1148382f0 - 0.15693812f0im 2.4704504f0 - 1.0389464f0im 1.0858271f0 - 1.298006f0im - 2.619998f0 - 1.8532984f0im 0.47806996f0 - 0.48494184f0im -0.1838578f0 - 0.6468568f0im 3.1117508f0 + 0.0f0im -1.956626f0 + 0.22825956f0im 0.07081801f0 - 0.31801307f0im 0.3698375f0 - 0.5400855f0im 0.80686307f0 + 1.5315914f0im 1.5649154f0 - 1.6229297f0im -0.112077385f0 + 1.2014246f0im - -1.8306153f0 + 1.2336911f0im 0.5096429f0 + 0.5395974f0im -1.8338387f0 - 0.7064959f0im -1.956626f0 - 0.22825956f0im 3.6439795f0 + 0.0f0im -0.2594722f0 + 0.48786148f0im -0.47636223f0 - 0.27821827f0im -0.61608654f0 - 2.01858f0im -2.7767487f0 + 1.7693765f0im 0.048102796f0 - 0.9741874f0im - 0.32275113f0 - 0.015575029f0im -0.7285097f0 + 0.10360408f0im 0.041852742f0 + 0.6556877f0im 0.07081801f0 + 0.31801307f0im -0.2594722f0 - 0.48786148f0im 3.624376f0 + 0.0f0im -1.6697118f0 + 0.4017511f0im -1.4397877f0 - 0.7550918f0im -0.31456697f0 - 1.0403451f0im -0.31978557f0 + 0.13701046f0im - 2.1968813f0 - 1.0640624f0im -1.1760061f0 + 2.7146957f0im 2.5673025f0 - 1.9732997f0im 0.3698375f0 + 0.5400855f0im -0.47636223f0 + 0.27821827f0im -1.6697118f0 - 0.4017511f0im 6.8273163f0 + 0.0f0im -0.10051322f0 + 0.24303961f0im 1.4415971f0 + 0.29750675f0im 1.221786f0 - 0.85654986f0im - 0.27894387f0 - 0.97911835f0im -0.4271084f0 - 0.042899966f0im -1.1148382f0 + 0.15693812f0im 0.80686307f0 - 1.5315914f0im -0.61608654f0 + 2.01858f0im -1.4397877f0 + 0.7550918f0im -0.10051322f0 - 0.24303961f0im 3.4057708f0 + 0.0f0im -0.5856801f0 - 1.0203559f0im 0.7103452f0 + 0.8422135f0im - 3.0476584f0 - 0.18548489f0im -1.7228563f0 - 2.8335886f0im 2.4704504f0 + 1.0389464f0im 1.5649154f0 + 1.6229297f0im -2.7767487f0 - 1.7693765f0im -0.31456697f0 + 1.0403451f0im 1.4415971f0 - 0.29750675f0im -0.5856801f0 + 1.0203559f0im 7.005772f0 + 0.0f0im -0.9617417f0 - 1.2486815f0im - 0.3842994f0 - 0.7050991f0im 1.8942566f0 - 0.6389735f0im 1.0858271f0 + 1.298006f0im -0.112077385f0 - 1.2014246f0im 0.048102796f0 + 0.9741874f0im -0.31978557f0 - 0.13701046f0im 1.221786f0 + 0.85654986f0im 0.7103452f0 - 0.8422135f0im -0.9617417f0 + 1.2486815f0im 3.4629636f0 + 0.0f0im] - b = [-0.905011814118756 + 0.2847570854574069im -0.7122162951294634 - 0.630289556702497im - -0.7620356655676837 + 0.15533508334193666im 0.39947219167701153 - 0.4576746001199889im - -0.21782716937787788 - 0.9222220085490986im -0.727775859267237 + 0.50638268521728im - -1.0509472322215125 + 0.5022165705328413im -0.7264975746431271 + 0.31670415674097235im - -0.6650468984506477 - 0.5000967284800251im -0.023682508769195098 + 0.18093440285319276im - -0.20604111555491242 + 0.10570814584017311im 0.562377322638969 - 0.2578030745663871im - -0.3451346708401685 + 1.076948486041297im 0.9870834574024372 - 0.2825689605519449im - 0.25336108035924787 + 0.975317836492159im 0.0628393808469436 - 0.1253397353973715im - 0.11192755545114 - 0.1603741874112385im 0.8439562576196216 + 1.0850814110398734im - -1.0568488936791578 - 0.06025820467086475im 0.12696236014017806 - 0.09853584666755086im] - cholesky(Hermitian(apd, :L), RowMaximum()) \ b - r = cholesky(apd).U - E = abs.(apd - r'*r) - ε = eps(abs(float(one(ComplexF32)))) - n = 10 - for i=1:n, j=1:n - @test E[i,j] <= (n+1)ε/(1-(n+1)ε)*real(sqrt(apd[i,i]*apd[j,j])) - end -end - -@testset "cholesky Diagonal" begin - # real - d = abs.(randn(3)) .+ 0.1 - D = Diagonal(d) - CD = cholesky(D) - CM = cholesky(Matrix(D)) - @test CD isa Cholesky{Float64} - @test CD.U ≈ Diagonal(.√d) ≈ CM.U - @test D ≈ CD.L * CD.U - @test CD.info == 0 - CD = cholesky(D, RowMaximum()) - CM = cholesky(Matrix(D), RowMaximum()) - @test CD isa CholeskyPivoted{Float64} - @test CD.U ≈ Diagonal(.√sort(d, rev=true)) ≈ CM.U - @test D ≈ Matrix(CD) - @test CD.info == 0 - - F = cholesky(Hermitian(I(3))) - @test F isa Cholesky{Float64,<:Diagonal} - @test Matrix(F) ≈ I(3) - F = cholesky(I(3), RowMaximum()) - @test F isa CholeskyPivoted{Float64,<:Diagonal} - @test Matrix(F) ≈ I(3) - - # real, failing - @test_throws PosDefException cholesky(Diagonal([1.0, -2.0])) - @test_throws RankDeficientException cholesky(Diagonal([1.0, -2.0]), RowMaximum()) - Dnpd = cholesky(Diagonal([1.0, -2.0]); check = false) - @test Dnpd.info == 2 - Dnpd = cholesky(Diagonal([1.0, -2.0]), RowMaximum(); check = false) - @test Dnpd.info == 1 - @test Dnpd.rank == 1 - - # complex - D = complex(D) - CD = cholesky(Hermitian(D)) - CM = cholesky(Matrix(Hermitian(D))) - @test CD isa Cholesky{ComplexF64,<:Diagonal} - @test CD.U ≈ Diagonal(.√d) ≈ CM.U - @test D ≈ CD.L * CD.U - @test CD.info == 0 - CD = cholesky(D, RowMaximum()) - CM = cholesky(Matrix(D), RowMaximum()) - @test CD isa CholeskyPivoted{ComplexF64,<:Diagonal} - @test CD.U ≈ Diagonal(.√sort(d, by=real, rev=true)) ≈ CM.U - @test D ≈ Matrix(CD) - @test CD.info == 0 - - # complex, failing - D[2, 2] = 0.0 + 0im - @test_throws PosDefException cholesky(D) - @test_throws RankDeficientException cholesky(D, RowMaximum()) - Dnpd = cholesky(D; check = false) - @test Dnpd.info == 2 - Dnpd = cholesky(D, RowMaximum(); check = false) - @test Dnpd.info == 1 - @test Dnpd.rank == 2 - - # InexactError for Int - @test_throws InexactError cholesky!(Diagonal([2, 1])) - - # tolerance - D = Diagonal([0.5, 1]) - @test_throws RankDeficientException cholesky(D, RowMaximum(), tol=nextfloat(0.5)) - CD = cholesky(D, RowMaximum(), tol=nextfloat(0.5), check=false) - @test rank(CD) == 1 - @test !issuccess(CD) - @test Matrix(cholesky(D, RowMaximum(), tol=prevfloat(0.5))) ≈ D -end - -@testset "Cholesky for AbstractMatrix" begin - S = SymTridiagonal(fill(2.0, 4), ones(3)) - C = cholesky(S) - @test C.L * C.U ≈ S -end - -@testset "constructor with non-BlasInt arguments" begin - - x = rand(5,5) - chol = cholesky(x'x) - - factors, uplo, info = chol.factors, chol.uplo, chol.info - - @test Cholesky(factors, uplo, Int32(info)) == chol - @test Cholesky(factors, uplo, Int64(info)) == chol - - cholp = cholesky(x'x, RowMaximum()) - - factors, uplo, piv, rank, tol, info = - cholp.factors, cholp.uplo, cholp.piv, cholp.rank, cholp.tol, cholp.info - - @test CholeskyPivoted(factors, uplo, piv, Int32(rank), tol, info) == cholp - @test CholeskyPivoted(factors, uplo, piv, Int64(rank), tol, info) == cholp - - @test CholeskyPivoted(factors, uplo, piv, rank, tol, Int32(info)) == cholp - @test CholeskyPivoted(factors, uplo, piv, rank, tol, Int64(info)) == cholp - -end - -@testset "issue #33704, casting low-rank CholeskyPivoted to Matrix" begin - A = randn(1,8) - B = A'A - C = cholesky(B, RowMaximum(), check=false) - @test B ≈ Matrix(C) -end - -@testset "CholeskyPivoted and Factorization" begin - A = randn(8,8) - B = A'A - C = cholesky(B, RowMaximum(), check=false) - @test CholeskyPivoted{eltype(C)}(C) === C - @test Factorization{eltype(C)}(C) === C - @test Array(CholeskyPivoted{complex(eltype(C))}(C)) ≈ Array(cholesky(complex(B), RowMaximum(), check=false)) - @test Array(Factorization{complex(eltype(C))}(C)) ≈ Array(cholesky(complex(B), RowMaximum(), check=false)) - @test eltype(Factorization{complex(eltype(C))}(C)) == complex(eltype(C)) -end - -@testset "REPL printing of CholeskyPivoted" begin - A = randn(8,8) - B = A'A - C = cholesky(B, RowMaximum(), check=false) - cholstring = sprint((t, s) -> show(t, "text/plain", s), C) - rankstring = "$(C.uplo) factor with rank $(rank(C)):" - factorstring = sprint((t, s) -> show(t, "text/plain", s), C.uplo == 'U' ? C.U : C.L) - permstring = sprint((t, s) -> show(t, "text/plain", s), C.p) - @test cholstring == "$(summary(C))\n$rankstring\n$factorstring\npermutation:\n$permstring" -end - -@testset "destructuring for Cholesky[Pivoted]" begin - for val in (NoPivot(), RowMaximum()) - A = rand(8, 8) - B = A'A - C = cholesky(B, val, check=false) - l, u = C - @test l == C.L - @test u == C.U - end -end - -@testset "issue #37356, diagonal elements of hermitian generic matrix" begin - B = Hermitian(hcat([one(BigFloat) + im])) - @test Matrix(cholesky(B)) ≈ B - C = Hermitian(hcat([one(BigFloat) + im]), :L) - @test Matrix(cholesky(C)) ≈ C -end - -@testset "constructing a Cholesky factor from a triangular matrix" begin - A = [1.0 2.0; 3.0 4.0] - let - U = UpperTriangular(A) - C = Cholesky(U) - @test C isa Cholesky{Float64} - @test C.U == U - @test C.L == U' - end - let - L = LowerTriangular(A) - C = Cholesky(L) - @test C isa Cholesky{Float64} - @test C.L == L - @test C.U == L' - end -end - -@testset "adjoint of Cholesky" begin - A = randn(5, 5) - A = A'A - F = cholesky(A) - b = ones(size(A, 1)) - @test F\b == F'\b -end - -@testset "Float16" begin - A = Float16[4. 12. -16.; 12. 37. -43.; -16. -43. 98.] - B = cholesky(A) - B32 = cholesky(Float32.(A)) - @test B isa Cholesky{Float16, Matrix{Float16}} - @test B.U isa UpperTriangular{Float16, Matrix{Float16}} - @test B.L isa LowerTriangular{Float16, Matrix{Float16}} - @test B.UL isa UpperTriangular{Float16, Matrix{Float16}} - @test B.U ≈ B32.U - @test B.L ≈ B32.L - @test B.UL ≈ B32.UL - @test Matrix(B) ≈ A - B = cholesky(A, RowMaximum()) - B32 = cholesky(Float32.(A), RowMaximum()) - @test B isa CholeskyPivoted{Float16,Matrix{Float16}} - @test B.U isa UpperTriangular{Float16, Matrix{Float16}} - @test B.L isa LowerTriangular{Float16, Matrix{Float16}} - @test B.U ≈ B32.U - @test B.L ≈ B32.L - @test Matrix(B) ≈ A -end - -@testset "det and logdet" begin - A = [4083 3825 5876 2048 4470 5490; - 3825 3575 5520 1920 4200 5140; - 5876 5520 8427 2940 6410 7903; - 2048 1920 2940 1008 2240 2740; - 4470 4200 6410 2240 4875 6015; - 5490 5140 7903 2740 6015 7370] - B = cholesky(A, RowMaximum(), check=false) - @test det(B) == 0.0 - @test det(B) ≈ det(A) atol=eps() - @test logdet(B) == -Inf - @test logabsdet(B)[1] == -Inf -end - -@testset "partly initialized factors" begin - @testset for uplo in ('U', 'L') - M = Matrix{BigFloat}(undef, 2, 2) - M[1,1] = M[2,2] = M[1+(uplo=='L'), 1+(uplo=='U')] = 3 - C = Cholesky(M, uplo, 0) - @test C == C - @test C.L == C.U' - # parameters are arbitrary - C = CholeskyPivoted(M, uplo, [1,2], 2, 0.0, 0) - @test C.L == C.U' - end -end - -@testset "diag" begin - for T in (Float64, ComplexF64), k in (0, 1, -3), uplo in (:U, :L) - A = randn(T, 100, 100) - P = Hermitian(A' * A, uplo) - C = cholesky(P) - @test diag(P, k) ≈ diag(C, k) - end -end - -@testset "cholesky_of_cholesky" begin - for T in (Float64, ComplexF64), uplo in (:U, :L) - A = randn(T, 100, 100) - P = Hermitian(A' * A, uplo) - C = cholesky(P) - CC = cholesky(C) - @test C == CC - end -end - -end # module TestCholesky diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl deleted file mode 100644 index a7616e2fc294a..0000000000000 --- a/stdlib/LinearAlgebra/test/dense.jl +++ /dev/null @@ -1,1331 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestDense - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -import Main.FillArrays - -@testset "Check that non-floats are correctly promoted" begin - @test [1 0 0; 0 1 0]\[1,1] ≈ [1;1;0] -end - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(1234323) - -@testset "Matrix condition number" begin - ainit = rand(n, n) - @testset "for $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - ainit = convert(Matrix{elty}, ainit) - for a in (copy(ainit), view(ainit, 1:n, 1:n)) - ainv = inv(a) - @test cond(a, 1) == opnorm(a, 1) *opnorm(ainv, 1) - @test cond(a, Inf) == opnorm(a, Inf)*opnorm(ainv, Inf) - @test cond(a[:, 1:5]) == (\)(extrema(svdvals(a[:, 1:5]))...) - @test_throws ArgumentError cond(a,3) - end - end - @testset "Singular matrices" for p in (1, 2, Inf) - @test cond(zeros(Int, 2, 2), p) == Inf - @test cond(zeros(2, 2), p) == Inf - @test cond([0 0; 1 1], p) == Inf - @test cond([0. 0.; 1. 1.], p) == Inf - end - @testset "Issue #33547, condition number of 2x2 matrix" begin - M = [1.0 -2.0 - -2.0 -1.5] - @test cond(M, 1) ≈ 2.227272727272727 - end - @testset "Condition numbers of a non-random matrix" begin - # To ensure that we detect any regressions in the underlying functions - Mars= [11 24 7 20 3 - 4 12 25 8 16 - 17 5 13 21 9 - 10 18 1 14 22 - 23 6 19 2 15] - @test cond(Mars, 1) ≈ 7.1 - @test cond(Mars, 2) ≈ 6.181867355918493 - @test cond(Mars, Inf) ≈ 7.1 - end -end - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 -a2real = randn(n,n)/2 -a2img = randn(n,n)/2 -breal = randn(n,2)/2 -bimg = randn(n,2)/2 - -@testset "For A containing $eltya" for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int) - ainit = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - ainit2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - ε = εa = eps(abs(float(one(eltya)))) - - apd = ainit'*ainit # symmetric positive-definite - @testset "Positive definiteness" begin - @test !isposdef(ainit) - @test isposdef(apd) - if eltya != Int # cannot perform cholesky! for Matrix{Int} - @test !isposdef!(copy(ainit)) - @test isposdef!(copy(apd)) - end - end - @testset "For b containing $eltyb" for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int) - binit = eltyb == Int ? rand(1:5, n, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - for (a, b) in ((copy(ainit), copy(binit)), (view(ainit, 1:n, 1:n), view(binit, 1:n, 1:2))) - @testset "Solve square general system of equations" begin - κ = cond(a,1) - x = a \ b - @test_throws DimensionMismatch b'\b - @test_throws DimensionMismatch b\b' - @test norm(a*x - b, 1)/norm(b) < ε*κ*n*2 # Ad hoc, revisit! - @test zeros(eltya,n)\fill(eltya(1),n) ≈ (zeros(eltya,n,1)\fill(eltya(1),n,1))[1,1] - end - - @testset "Test nullspace" begin - a15null = nullspace(a[:,1:n1]') - @test rank([a[:,1:n1] a15null]) == 10 - @test norm(a[:,1:n1]'a15null,Inf) ≈ zero(eltya) atol=300ε - @test norm(a15null'a[:,1:n1],Inf) ≈ zero(eltya) atol=400ε - @test size(nullspace(b), 2) == 0 - @test size(nullspace(b, rtol=0.001), 2) == 0 - @test size(nullspace(b, atol=100*εb), 2) == 0 - @test size(nullspace(b, 100*εb), 2) == 0 - @test nullspace(zeros(eltya,n)) == Matrix(I, 1, 1) - @test nullspace(zeros(eltya,n), 0.1) == Matrix(I, 1, 1) - # test empty cases - @test @inferred(nullspace(zeros(n, 0))) == Matrix(I, 0, 0) - @test @inferred(nullspace(zeros(0, n))) == Matrix(I, n, n) - # test vector cases - @test size(@inferred nullspace(a[:, 1])) == (1, 0) - @test size(@inferred nullspace(zero(a[:, 1]))) == (1, 1) - @test nullspace(zero(a[:, 1]))[1,1] == 1 - # test adjortrans vectors, including empty ones - @test size(@inferred nullspace(a[:, 1]')) == (n, n - 1) - @test @inferred(nullspace(a[1:0, 1]')) == Matrix(I, 0, 0) - @test size(@inferred nullspace(b[1, :]')) == (2, 1) - @test @inferred(nullspace(b[1, 1:0]')) == Matrix(I, 0, 0) - @test size(@inferred nullspace(transpose(a[:, 1]))) == (n, n - 1) - @test size(@inferred nullspace(transpose(b[1, :]))) == (2, 1) - end - end - end # for eltyb - - for (a, a2) in ((copy(ainit), copy(ainit2)), (view(ainit, 1:n, 1:n), view(ainit2, 1:n, 1:n))) - @testset "Test pinv" begin - pinva15 = pinv(a[:,1:n1]) - @test a[:,1:n1]*pinva15*a[:,1:n1] ≈ a[:,1:n1] - @test pinva15*a[:,1:n1]*pinva15 ≈ pinva15 - pinva15 = pinv(a[:,1:n1]') # the Adjoint case - @test a[:,1:n1]'*pinva15*a[:,1:n1]' ≈ a[:,1:n1]' - @test pinva15*a[:,1:n1]'*pinva15 ≈ pinva15 - - @test size(pinv(Matrix{eltya}(undef,0,0))) == (0,0) - end - - @testset "Lyapunov/Sylvester" begin - x = lyap(a, a2) - @test -a2 ≈ a*x + x*a' - y = lyap(a', a2') - @test y ≈ lyap(Array(a'), Array(a2')) - @test -a2' ≈ a'y + y*a - z = lyap(Tridiagonal(a)', Diagonal(a2)) - @test z ≈ lyap(Array(Tridiagonal(a)'), Array(Diagonal(a2))) - @test -Diagonal(a2) ≈ Tridiagonal(a)'*z + z*Tridiagonal(a) - x2 = sylvester(a[1:3, 1:3], a[4:n, 4:n], a2[1:3,4:n]) - @test -a2[1:3, 4:n] ≈ a[1:3, 1:3]*x2 + x2*a[4:n, 4:n] - y2 = sylvester(a[1:3, 1:3]', a[4:n, 4:n]', a2[4:n,1:3]') - @test y2 ≈ sylvester(Array(a[1:3, 1:3]'), Array(a[4:n, 4:n]'), Array(a2[4:n,1:3]')) - @test -a2[4:n, 1:3]' ≈ a[1:3, 1:3]'*y2 + y2*a[4:n, 4:n]' - z2 = sylvester(Tridiagonal(a[1:3, 1:3]), Diagonal(a[4:n, 4:n]), a2[1:3,4:n]) - @test z2 ≈ sylvester(Array(Tridiagonal(a[1:3, 1:3])), Array(Diagonal(a[4:n, 4:n])), Array(a2[1:3,4:n])) - @test -a2[1:3, 4:n] ≈ Tridiagonal(a[1:3, 1:3])*z2 + z2*Diagonal(a[4:n, 4:n]) - end - - @testset "Matrix square root" begin - asq = sqrt(a) - @test asq*asq ≈ a - @test sqrt(transpose(a))*sqrt(transpose(a)) ≈ transpose(a) - @test sqrt(adjoint(a))*sqrt(adjoint(a)) ≈ adjoint(a) - asym = a + a' # symmetric indefinite - asymsq = sqrt(asym) - @test asymsq*asymsq ≈ asym - @test sqrt(transpose(asym))*sqrt(transpose(asym)) ≈ transpose(asym) - @test sqrt(adjoint(asym))*sqrt(adjoint(asym)) ≈ adjoint(asym) - if eltype(a) <: Real # real square root - apos = a * a - @test sqrt(apos)^2 ≈ apos - @test eltype(sqrt(apos)) <: Real - # test that real but Complex input produces Complex output - @test sqrt(complex(apos)) ≈ sqrt(apos) - @test eltype(sqrt(complex(apos))) <: Complex - end - end - - @testset "Powers" begin - if eltya <: AbstractFloat - z = zero(eltya) - t = convert(eltya,2) - r = convert(eltya,2.5) - @test a^z ≈ Matrix(I, size(a)) - @test a^t ≈ a^2 - @test Matrix{eltya}(I, n, n)^r ≈ Matrix(I, size(a)) - end - end - end # end for loop over arraytype - - @testset "Factorize" begin - d = rand(eltya,n) - e = rand(eltya,n-1) - e2 = rand(eltya,n-1) - f = rand(eltya,n-2) - A = diagm(0 => d) - @test factorize(A) == Diagonal(d) - A += diagm(-1 => e) - @test factorize(A) == Bidiagonal(d,e,:L) - A += diagm(-2 => f) - @test factorize(A) == LowerTriangular(A) - A = diagm(0 => d, 1 => e) - @test factorize(A) == Bidiagonal(d,e,:U) - if eltya <: Real - A = diagm(0 => d, 1 => e, -1 => e) - @test Matrix(factorize(A)) ≈ Matrix(factorize(SymTridiagonal(d,e))) - A = diagm(0 => d, 1 => e, -1 => e, 2 => f, -2 => f) - @test inv(factorize(A)) ≈ inv(factorize(Symmetric(A))) - end - A = diagm(0 => d, 1 => e, -1 => e2) - @test Matrix(factorize(A)) ≈ Matrix(factorize(Tridiagonal(e2,d,e))) - A = diagm(0 => d, 1 => e, 2 => f) - @test factorize(A) == UpperTriangular(A) - - x = rand(eltya) - @test factorize(x) == x - end -end # for eltya - -@testset "Test diagm for vectors" begin - @test diagm(zeros(50)) == diagm(0 => zeros(50)) - @test diagm(ones(50)) == diagm(0 => ones(50)) - v = randn(500) - @test diagm(v) == diagm(0 => v) - @test diagm(500, 501, v) == diagm(500, 501, 0 => v) -end - -@testset "Non-square diagm" begin - x = [7, 8] - for m=1:4, n=2:4 - if m < 2 || n < 3 - @test_throws DimensionMismatch diagm(m,n, 0 => x, 1 => x) - @test_throws DimensionMismatch diagm(n,m, 0 => x, -1 => x) - else - M = zeros(m,n) - M[1:2,1:3] = [7 7 0; 0 8 8] - @test diagm(m,n, 0 => x, 1 => x) == M - @test diagm(n,m, 0 => x, -1 => x) == M' - end - end -end - -@testset "Test pinv (rtol, atol)" begin - M = [1 0 0; 0 1 0; 0 0 0] - @test pinv(M,atol=1)== zeros(3,3) - @test pinv(M,rtol=0.5)== M -end - -@testset "Test inv of matrix of NaNs" begin - for eltya in (NaN16, NaN32, NaN32) - r = fill(eltya, 2, 2) - @test_throws ArgumentError inv(r) - c = fill(complex(eltya, eltya), 2, 2) - @test_throws ArgumentError inv(c) - end -end - -@testset "test out of bounds triu/tril" begin - local m, n = 5, 7 - ainit = rand(m, n) - for a in (copy(ainit), view(ainit, 1:m, 1:n)) - @test triu(a, -m) == a - @test triu(a, n + 2) == zero(a) - @test tril(a, -m - 2) == zero(a) - @test tril(a, n) == a - end -end - -@testset "triu M > N case bug fix" begin - mat=[1 2; - 3 4; - 5 6; - 7 8] - res=[1 2; - 3 4; - 0 6; - 0 0] - @test triu(mat, -1) == res -end - -@testset "Tests norms" begin - nnorm = 10 - mmat = 10 - nmat = 8 - @testset "For $elty" for elty in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int32, Int64, BigInt) - x = fill(elty(1),10) - @testset "Vector" begin - xs = view(x,1:2:10) - @test norm(x, -Inf) ≈ 1 - @test norm(x, -1) ≈ 1/10 - @test norm(x, 0) ≈ 10 - @test norm(x, 1) ≈ 10 - @test norm(x, 2) ≈ sqrt(10) - @test norm(x, 3) ≈ cbrt(10) - @test norm(x, Inf) ≈ 1 - if elty <: LinearAlgebra.BlasFloat - @test norm(x, 1:4) ≈ 2 - @test_throws BoundsError norm(x,-1:4) - @test_throws BoundsError norm(x,1:11) - end - @test norm(xs, -Inf) ≈ 1 - @test norm(xs, -1) ≈ 1/5 - @test norm(xs, 0) ≈ 5 - @test norm(xs, 1) ≈ 5 - @test norm(xs, 2) ≈ sqrt(5) - @test norm(xs, 3) ≈ cbrt(5) - @test norm(xs, Inf) ≈ 1 - end - - @testset "Issue #12552:" begin - if real(elty) <: AbstractFloat - for p in [-Inf,-1,1,2,3,Inf] - @test isnan(norm(elty[0,NaN],p)) - @test isnan(norm(elty[NaN,0],p)) - end - end - end - - @testset "Number" begin - norm(x[1:1]) === norm(x[1], -Inf) - norm(x[1:1]) === norm(x[1], 0) - norm(x[1:1]) === norm(x[1], 1) - norm(x[1:1]) === norm(x[1], 2) - norm(x[1:1]) === norm(x[1], Inf) - end - - @testset "Absolute homogeneity, triangle inequality, & vectorized versions" begin - for i = 1:10 - xinit = elty <: Integer ? convert(Vector{elty}, rand(1:10, nnorm)) : - elty <: Complex ? convert(Vector{elty}, complex.(randn(nnorm), randn(nnorm))) : - convert(Vector{elty}, randn(nnorm)) - yinit = elty <: Integer ? convert(Vector{elty}, rand(1:10, nnorm)) : - elty <: Complex ? convert(Vector{elty}, complex.(randn(nnorm), randn(nnorm))) : - convert(Vector{elty}, randn(nnorm)) - α = elty <: Integer ? randn() : - elty <: Complex ? convert(elty, complex(randn(),randn())) : - convert(elty, randn()) - for (x, y) in ((copy(xinit), copy(yinit)), (view(xinit,1:2:nnorm), view(yinit,1:2:nnorm))) - # Absolute homogeneity - @test norm(α*x,-Inf) ≈ abs(α)*norm(x,-Inf) - @test norm(α*x,-1) ≈ abs(α)*norm(x,-1) - @test norm(α*x,1) ≈ abs(α)*norm(x,1) - @test norm(α*x) ≈ abs(α)*norm(x) # two is default - @test norm(α*x,3) ≈ abs(α)*norm(x,3) - @test norm(α*x,Inf) ≈ abs(α)*norm(x,Inf) - - # Triangle inequality - @test norm(x + y,1) <= norm(x,1) + norm(y,1) - @test norm(x + y) <= norm(x) + norm(y) # two is default - @test norm(x + y,3) <= norm(x,3) + norm(y,3) - @test norm(x + y,Inf) <= norm(x,Inf) + norm(y,Inf) - - # Against vectorized versions - @test norm(x,-Inf) ≈ minimum(abs.(x)) - @test norm(x,-1) ≈ inv(sum(1 ./ abs.(x))) - @test norm(x,0) ≈ sum(x .!= 0) - @test norm(x,1) ≈ sum(abs.(x)) - @test norm(x) ≈ sqrt(sum(abs2.(x))) - @test norm(x,3) ≈ cbrt(sum(abs.(x).^3.)) - @test norm(x,Inf) ≈ maximum(abs.(x)) - end - end - end - - @testset "Matrix (Operator) opnorm" begin - A = fill(elty(1),10,10) - As = view(A,1:5,1:5) - @test opnorm(A, 1) ≈ 10 - elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test opnorm(A, 2) ≈ 10 - @test opnorm(A, Inf) ≈ 10 - @test opnorm(As, 1) ≈ 5 - elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test opnorm(As, 2) ≈ 5 - @test opnorm(As, Inf) ≈ 5 - end - - @testset "Absolute homogeneity, triangle inequality, & norm" begin - for i = 1:10 - Ainit = elty <: Integer ? convert(Matrix{elty}, rand(1:10, mmat, nmat)) : - elty <: Complex ? convert(Matrix{elty}, complex.(randn(mmat, nmat), randn(mmat, nmat))) : - convert(Matrix{elty}, randn(mmat, nmat)) - Binit = elty <: Integer ? convert(Matrix{elty}, rand(1:10, mmat, nmat)) : - elty <: Complex ? convert(Matrix{elty}, complex.(randn(mmat, nmat), randn(mmat, nmat))) : - convert(Matrix{elty}, randn(mmat, nmat)) - α = elty <: Integer ? randn() : - elty <: Complex ? convert(elty, complex(randn(),randn())) : - convert(elty, randn()) - for (A, B) in ((copy(Ainit), copy(Binit)), (view(Ainit,1:nmat,1:nmat), view(Binit,1:nmat,1:nmat))) - # Absolute homogeneity - @test norm(α*A,1) ≈ abs(α)*norm(A,1) - elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test norm(α*A) ≈ abs(α)*norm(A) # two is default - @test norm(α*A,Inf) ≈ abs(α)*norm(A,Inf) - - # Triangle inequality - @test norm(A + B,1) <= norm(A,1) + norm(B,1) - elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test norm(A + B) <= norm(A) + norm(B) # two is default - @test norm(A + B,Inf) <= norm(A,Inf) + norm(B,Inf) - - # norm - for p in (-Inf, Inf, (-2:3)...) - @test norm(A, p) == norm(vec(A), p) - end - end - end - - @testset "issue #10234" begin - if elty <: AbstractFloat || elty <: Complex - z = zeros(elty, 100) - z[1] = -Inf - for p in [-2,-1.5,-1,-0.5,0.5,1,1.5,2,Inf] - @test norm(z, p) == (p < 0 ? 0 : Inf) - @test norm(elty[Inf],p) == Inf - end - end - end - end - end - - @testset "issue #10234" begin - @test norm(Any[Inf],-2) == norm(Any[Inf],-1) == norm(Any[Inf],1) == norm(Any[Inf],1.5) == norm(Any[Inf],2) == norm(Any[Inf],Inf) == Inf - end - - @testset "overflow/underflow in norms" begin - @test norm(Float64[1e-300, 1], -3)*1e300 ≈ 1 - @test norm(Float64[1e300, 1], 3)*1e-300 ≈ 1 - end -end - -## Issue related tests -@testset "issue #1447" begin - A = [1.0+0.0im 0; 0 1] - B = pinv(A) - for i = 1:4 - @test A[i] ≈ B[i] - end -end - -@testset "issue #2246" begin - A = [1 2 0 0; 0 1 0 0; 0 0 0 0; 0 0 0 0] - Asq = sqrt(A) - @test Asq*Asq ≈ A - A2 = view(A, 1:2, 1:2) - A2sq = sqrt(A2) - @test A2sq*A2sq ≈ A2 - - N = 3 - @test log(det(Matrix(1.0I, N, N))) ≈ logdet(Matrix(1.0I, N, N)) -end - -@testset "issue #2637" begin - a = [1, 2, 3] - b = [4, 5, 6] - @test kron(Matrix(I, 2, 2), Matrix(I, 2, 2)) == Matrix(I, 4, 4) - @test kron(a,b) == [4,5,6,8,10,12,12,15,18] - @test kron(a',b') == [4 5 6 8 10 12 12 15 18] - @test kron(a,b') == [4 5 6; 8 10 12; 12 15 18] - @test kron(a',b) == [4 8 12; 5 10 15; 6 12 18] - @test kron(a, Matrix(1I, 2, 2)) == [1 0; 0 1; 2 0; 0 2; 3 0; 0 3] - @test kron(Matrix(1I, 2, 2), a) == [ 1 0; 2 0; 3 0; 0 1; 0 2; 0 3] - @test kron(Matrix(1I, 2, 2), 2) == Matrix(2I, 2, 2) - @test kron(3, Matrix(1I, 3, 3)) == Matrix(3I, 3, 3) - @test kron(a,2) == [2, 4, 6] - @test kron(b',2) == [8 10 12] -end - -@testset "kron!" begin - a = [1.0, 0.0] - b = [0.0, 1.0] - @test kron!([1.0, 0.0], b, 0.5) == [0.0; 0.5] - @test kron!([1.0, 0.0], 0.5, b) == [0.0; 0.5] - c = Vector{Float64}(undef, 4) - kron!(c, a, b) - @test c == [0.0; 1.0; 0.0; 0.0] - c = Matrix{Float64}(undef, 2, 2) - kron!(c, a, b') - @test c == [0.0 1.0; 0.0 0.0] -end - -@testset "kron adjoint" begin - a = [1+im, 2, 3] - b = [4, 5, 6+7im] - @test kron(a', b') isa Adjoint - @test kron(a', b') == kron(a, b)' - @test kron(transpose(a), b') isa Transpose - @test kron(transpose(a), b') == kron(permutedims(a), collect(b')) - @test kron(transpose(a), transpose(b)) isa Transpose - @test kron(transpose(a), transpose(b)) == transpose(kron(a, b)) -end - -@testset "issue #4796" begin - dim=2 - S=zeros(Complex,dim,dim) - T=zeros(Complex,dim,dim) - fill!(T, 1) - z = 2.5 + 1.5im - S[1] = z - @test S*T == [z z; 0 0] - - # similar issue for Array{Real} - @test Real[1 2] * Real[1.5; 2.0] == Real[5.5] -end - -@testset "Matrix exponential" begin - @testset "Tests for $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - A1 = convert(Matrix{elty}, [4 2 0; 1 4 1; 1 1 4]) - eA1 = convert(Matrix{elty}, [147.866622446369 127.781085523181 127.781085523182; - 183.765138646367 183.765138646366 163.679601723179; - 71.797032399996 91.8825693231832 111.968106246371]') - @test exp(A1) ≈ eA1 - @test exp(adjoint(A1)) ≈ adjoint(eA1) - @test exp(transpose(A1)) ≈ transpose(eA1) - for f in (sin, cos, sinh, cosh, tanh, tan) - @test f(adjoint(A1)) ≈ f(copy(adjoint(A1))) - end - - A2 = convert(Matrix{elty}, - [29.87942128909879 0.7815750847907159 -2.289519314033932; - 0.7815750847907159 25.72656945571064 8.680737820540137; - -2.289519314033932 8.680737820540137 34.39400925519054]) - eA2 = convert(Matrix{elty}, - [ 5496313853692458.0 -18231880972009236.0 -30475770808580460.0; - -18231880972009252.0 60605228702221920.0 101291842930249760.0; - -30475770808580480.0 101291842930249728.0 169294411240851968.0]) - @test exp(A2) ≈ eA2 - @test exp(adjoint(A2)) ≈ adjoint(eA2) - @test exp(transpose(A2)) ≈ transpose(eA2) - - A3 = convert(Matrix{elty}, [-131 19 18;-390 56 54;-387 57 52]) - eA3 = convert(Matrix{elty}, [-1.50964415879218 -5.6325707998812 -4.934938326092; - 0.367879439109187 1.47151775849686 1.10363831732856; - 0.135335281175235 0.406005843524598 0.541341126763207]') - @test exp(A3) ≈ eA3 - @test exp(adjoint(A3)) ≈ adjoint(eA3) - @test exp(transpose(A3)) ≈ transpose(eA3) - - A4 = convert(Matrix{elty}, [0.25 0.25; 0 0]) - eA4 = convert(Matrix{elty}, [1.2840254166877416 0.2840254166877415; 0 1]) - @test exp(A4) ≈ eA4 - @test exp(adjoint(A4)) ≈ adjoint(eA4) - @test exp(transpose(A4)) ≈ transpose(eA4) - - A5 = convert(Matrix{elty}, [0 0.02; 0 0]) - eA5 = convert(Matrix{elty}, [1 0.02; 0 1]) - @test exp(A5) ≈ eA5 - @test exp(adjoint(A5)) ≈ adjoint(eA5) - @test exp(transpose(A5)) ≈ transpose(eA5) - - # Hessenberg - @test hessenberg(A1).H ≈ convert(Matrix{elty}, - [4.000000000000000 -1.414213562373094 -1.414213562373095 - -1.414213562373095 4.999999999999996 -0.000000000000000 - 0 -0.000000000000002 3.000000000000000]) - - # cis always returns a complex matrix - if elty <: Real - eltyim = Complex{elty} - else - eltyim = elty - end - - @test cis(A1) ≈ convert(Matrix{eltyim}, [-0.339938 + 0.000941506im 0.772659 - 0.8469im 0.52745 + 0.566543im; - 0.650054 - 0.140179im -0.0762135 + 0.284213im 0.38633 - 0.42345im ; - 0.650054 - 0.140179im 0.913779 + 0.143093im -0.603663 - 0.28233im ]) rtol=7e-7 - end - - @testset "Additional tests for $elty" for elty in (Float64, ComplexF64) - A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); - 1/3 1/4 1/5 1/6; - 1/4 1/5 1/6 1/7; - 1/5 1/6 1/7 1/8]) - @test exp(log(A4)) ≈ A4 - @test exp(log(transpose(A4))) ≈ transpose(A4) - @test exp(log(adjoint(A4))) ≈ adjoint(A4) - - A5 = convert(Matrix{elty}, [1 1 0 1; 0 1 1 0; 0 0 1 1; 1 0 0 1]) - @test exp(log(A5)) ≈ A5 - @test exp(log(transpose(A5))) ≈ transpose(A5) - @test exp(log(adjoint(A5))) ≈ adjoint(A5) - - A6 = convert(Matrix{elty}, [-5 2 0 0 ; 1/2 -7 3 0; 0 1/3 -9 4; 0 0 1/4 -11]) - @test exp(log(A6)) ≈ A6 - @test exp(log(transpose(A6))) ≈ transpose(A6) - @test exp(log(adjoint(A6))) ≈ adjoint(A6) - - A7 = convert(Matrix{elty}, [1 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1]) - @test exp(log(A7)) ≈ A7 - @test exp(log(transpose(A7))) ≈ transpose(A7) - @test exp(log(adjoint(A7))) ≈ adjoint(A7) - end - - @testset "Integer promotion tests" begin - for (elty1, elty2) in ((Int64, Float64), (Complex{Int64}, ComplexF64)) - A4int = convert(Matrix{elty1}, [1 2; 3 4]) - A4float = convert(Matrix{elty2}, A4int) - @test exp(A4int) == exp(A4float) - end - end - - @testset "^ tests" for elty in (Float32, Float64, ComplexF32, ComplexF64, Int32, Int64) - # should all be exact as the lhs functions are simple aliases - @test ℯ^(fill(elty(2), (4,4))) == exp(fill(elty(2), (4,4))) - @test 2^(fill(elty(2), (4,4))) == exp(log(2)*fill(elty(2), (4,4))) - @test 2.0^(fill(elty(2), (4,4))) == exp(log(2.0)*fill(elty(2), (4,4))) - end - - A8 = 100 * [-1+1im 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1] - @test exp(log(A8)) ≈ A8 -end - -@testset "Matrix trigonometry" begin - @testset "Tests for $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - A1 = convert(Matrix{elty}, [3 2 0; 1 3 1; 1 1 3]) - A2 = convert(Matrix{elty}, - [3.975884257819758 0.15631501695814318 -0.4579038628067864; - 0.15631501695814318 4.545313891142127 1.7361475641080275; - -0.4579038628067864 1.7361475641080275 6.478801851038108]) - A3 = convert(Matrix{elty}, [0.25 0.25; 0 0]) - A4 = convert(Matrix{elty}, [0 0.02; 0 0]) - A5 = convert(Matrix{elty}, [2.0 0; 0 3.0]) - - cosA1 = convert(Matrix{elty},[-0.18287716254368605 -0.29517205254584633 0.761711400552759; - 0.23326967400345625 0.19797853773269333 -0.14758602627292305; - 0.23326967400345636 0.6141253742798355 -0.5637328628200653]) - sinA1 = convert(Matrix{elty}, [0.2865568596627417 -1.107751980582015 -0.13772915374386513; - -0.6227405671629401 0.2176922827908092 -0.5538759902910078; - -0.6227405671629398 -0.6916051440348725 0.3554214365346742]) - @test @inferred(cos(A1)) ≈ cosA1 - @test @inferred(sin(A1)) ≈ sinA1 - - cosA2 = convert(Matrix{elty}, [-0.6331745163802187 0.12878366262380136 -0.17304181968301532; - 0.12878366262380136 -0.5596234510748788 0.5210483146041339; - -0.17304181968301532 0.5210483146041339 0.002263776356015268]) - sinA2 = convert(Matrix{elty},[-0.6677253518411841 -0.32599318928375437 0.020799609079003523; - -0.32599318928375437 -0.04568726058081066 0.5388748740270427; - 0.020799609079003523 0.5388748740270427 0.6385462428126032]) - @test cos(A2) ≈ cosA2 - @test sin(A2) ≈ sinA2 - - cosA3 = convert(Matrix{elty}, [0.9689124217106446 -0.031087578289355197; 0.0 1.0]) - sinA3 = convert(Matrix{elty}, [0.24740395925452285 0.24740395925452285; 0.0 0.0]) - @test cos(A3) ≈ cosA3 - @test sin(A3) ≈ sinA3 - - cosA4 = convert(Matrix{elty}, [1.0 0.0; 0.0 1.0]) - sinA4 = convert(Matrix{elty}, [0.0 0.02; 0.0 0.0]) - @test cos(A4) ≈ cosA4 - @test sin(A4) ≈ sinA4 - - # Identities - for (i, A) in enumerate((A1, A2, A3, A4, A5)) - @test @inferred(sincos(A)) == (sin(A), cos(A)) - @test cos(A)^2 + sin(A)^2 ≈ Matrix(I, size(A)) - @test cos(A) ≈ cos(-A) - @test sin(A) ≈ -sin(-A) - @test @inferred(tan(A)) ≈ sin(A) / cos(A) - - @test cos(A) ≈ real(exp(im*A)) - @test sin(A) ≈ imag(exp(im*A)) - @test cos(A) ≈ real(cis(A)) - @test sin(A) ≈ imag(cis(A)) - @test @inferred(cis(A)) ≈ cos(A) + im * sin(A) - - @test @inferred(cosh(A)) ≈ 0.5 * (exp(A) + exp(-A)) - @test @inferred(sinh(A)) ≈ 0.5 * (exp(A) - exp(-A)) - @test @inferred(cosh(A)) ≈ cosh(-A) - @test @inferred(sinh(A)) ≈ -sinh(-A) - - # Some of the following identities fail for A3, A4 because the matrices are singular - if i in (1, 2, 5) - @test @inferred(sec(A)) ≈ inv(cos(A)) - @test @inferred(csc(A)) ≈ inv(sin(A)) - @test @inferred(cot(A)) ≈ inv(tan(A)) - @test @inferred(sech(A)) ≈ inv(cosh(A)) - @test @inferred(csch(A)) ≈ inv(sinh(A)) - @test @inferred(coth(A)) ≈ inv(@inferred tanh(A)) - end - # The following identities fail for A1, A2 due to rounding errors; - # probably needs better algorithm for the general case - if i in (3, 4, 5) - @test cosh(A)^2 - sinh(A)^2 ≈ Matrix(I, size(A)) - @test tanh(A) ≈ sinh(A) / cosh(A) - end - end - end - - @testset "Additional tests for $elty" for elty in (ComplexF32, ComplexF64) - A5 = convert(Matrix{elty}, [1im 2; 0.02+0.5im 3]) - - @test sincos(A5) == (sin(A5), cos(A5)) - - @test cos(A5)^2 + sin(A5)^2 ≈ Matrix(I, size(A5)) - @test cosh(A5)^2 - sinh(A5)^2 ≈ Matrix(I, size(A5)) - @test cos(A5)^2 + sin(A5)^2 ≈ Matrix(I, size(A5)) - @test tan(A5) ≈ sin(A5) / cos(A5) - @test tanh(A5) ≈ sinh(A5) / cosh(A5) - - @test sec(A5) ≈ inv(cos(A5)) - @test csc(A5) ≈ inv(sin(A5)) - @test cot(A5) ≈ inv(tan(A5)) - @test sech(A5) ≈ inv(cosh(A5)) - @test csch(A5) ≈ inv(sinh(A5)) - @test coth(A5) ≈ inv(tanh(A5)) - - @test cos(A5) ≈ 0.5 * (exp(im*A5) + exp(-im*A5)) - @test sin(A5) ≈ -0.5im * (exp(im*A5) - exp(-im*A5)) - @test cos(A5) ≈ 0.5 * (cis(A5) + cis(-A5)) - @test sin(A5) ≈ -0.5im * (cis(A5) - cis(-A5)) - - @test cosh(A5) ≈ 0.5 * (exp(A5) + exp(-A5)) - @test sinh(A5) ≈ 0.5 * (exp(A5) - exp(-A5)) - end - - @testset "Additional tests for $elty" for elty in (Int32, Int64, Complex{Int32}, Complex{Int64}) - A1 = convert(Matrix{elty}, [1 2; 3 4]) - A2 = convert(Matrix{elty}, [1 2; 2 1]) - - cosA1 = convert(Matrix{float(elty)}, [0.855423165077998 -0.11087638101074865; - -0.16631457151612294 0.689108593561875]) - cosA2 = convert(Matrix{float(elty)}, [-0.22484509536615283 -0.7651474012342925; - -0.7651474012342925 -0.22484509536615283]) - - @test cos(A1) ≈ cosA1 - @test cos(A2) ≈ cosA2 - - sinA1 = convert(Matrix{float(elty)}, [-0.46558148631373036 -0.14842445991317652; - -0.22263668986976476 -0.6882181761834951]) - sinA2 = convert(Matrix{float(elty)}, [-0.3501754883740146 0.4912954964338818; - 0.4912954964338818 -0.3501754883740146]) - - @test sin(A1) ≈ sinA1 - @test sin(A2) ≈ sinA2 - end - - @testset "Inverse functions for $elty" for elty in (Float32, Float64) - A1 = convert(Matrix{elty}, [0.244637 -0.63578; - 0.22002 0.189026]) - A2 = convert(Matrix{elty}, [1.11656 -0.098672 0.158485; - -0.098672 0.100933 -0.107107; - 0.158485 -0.107107 0.612404]) - - for A in (A1, A2) - @test cos(acos(cos(A))) ≈ cos(A) - @test sin(asin(sin(A))) ≈ sin(A) - @test tan(atan(tan(A))) ≈ tan(A) - @test cosh(acosh(cosh(A))) ≈ cosh(A) - @test sinh(asinh(sinh(A))) ≈ sinh(A) - @test tanh(atanh(tanh(A))) ≈ tanh(A) - @test sec(asec(sec(A))) ≈ sec(A) - @test csc(acsc(csc(A))) ≈ csc(A) - @test cot(acot(cot(A))) ≈ cot(A) - @test sech(asech(sech(A))) ≈ sech(A) - @test csch(acsch(csch(A))) ≈ csch(A) - @test coth(acoth(coth(A))) ≈ coth(A) - end - end - - @testset "Inverse functions for $elty" for elty in (ComplexF32, ComplexF64) - A1 = convert(Matrix{elty}, [ 0.143721-0.0im -0.138386-0.106905im; - -0.138386+0.106905im 0.306224-0.0im]) - A2 = convert(Matrix{elty}, [1im 2; 0.02+0.5im 3]) - A3 = convert(Matrix{elty}, [0.138721-0.266836im 0.0971722-0.13715im 0.205046-0.137136im; - -0.0154974-0.00358254im 0.152163-0.445452im 0.0314575-0.536521im; - -0.387488+0.0294059im -0.0448773+0.114305im 0.230684-0.275894im]) - for A in (A1, A2, A3) - @test cos(acos(cos(A))) ≈ cos(A) - @test sin(asin(sin(A))) ≈ sin(A) - @test tan(atan(tan(A))) ≈ tan(A) - @test cosh(acosh(cosh(A))) ≈ cosh(A) - @test sinh(asinh(sinh(A))) ≈ sinh(A) - @test tanh(atanh(tanh(A))) ≈ tanh(A) - @test sec(asec(sec(A))) ≈ sec(A) - @test csc(acsc(csc(A))) ≈ csc(A) - @test cot(acot(cot(A))) ≈ cot(A) - @test sech(asech(sech(A))) ≈ sech(A) - @test csch(acsch(csch(A))) ≈ csch(A) - @test coth(acoth(coth(A))) ≈ coth(A) - - # Definition of principal values (Aprahamian & Higham, 2016, pp. 4-5) - abstol = sqrt(eps(real(elty))) * norm(acosh(A)) - @test all(z -> (0 < real(z) < π || - abs(real(z)) < abstol && imag(z) >= 0 || - abs(real(z) - π) < abstol && imag(z) <= 0), - eigen(acos(A)).values) - @test all(z -> (-π/2 < real(z) < π/2 || - abs(real(z) + π/2) < abstol && imag(z) >= 0 || - abs(real(z) - π/2) < abstol && imag(z) <= 0), - eigen(asin(A)).values) - @test all(z -> (-π < imag(z) < π && real(z) > 0 || - 0 <= imag(z) < π && abs(real(z)) < abstol || - abs(imag(z) - π) < abstol && real(z) >= 0), - eigen(acosh(A)).values) - @test all(z -> (-π/2 < imag(z) < π/2 || - abs(imag(z) + π/2) < abstol && real(z) <= 0 || - abs(imag(z) - π/2) < abstol && real(z) <= 0), - eigen(asinh(A)).values) - end - end -end - -@testset "issue 5116" begin - A9 = [0 10 0 0; -1 0 0 0; 0 0 0 0; -2 0 0 0] - eA9 = [-0.999786072879326 -0.065407069689389 0.0 0.0 - 0.006540706968939 -0.999786072879326 0.0 0.0 - 0.0 0.0 1.0 0.0 - 0.013081413937878 -3.999572145758650 0.0 1.0] - @test exp(A9) ≈ eA9 - - A10 = [ 0. 0. 0. 0. ; 0. 0. -im 0.; 0. im 0. 0.; 0. 0. 0. 0.] - eA10 = [ 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im - 0.0+0.0im 1.543080634815244+0.0im 0.0-1.175201193643801im 0.0+0.0im - 0.0+0.0im 0.0+1.175201193643801im 1.543080634815243+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im] - @test exp(A10) ≈ eA10 -end - -@testset "Additional matrix logarithm tests" for elty in (Float64, ComplexF64) - A11 = convert(Matrix{elty}, [3 2; -5 -3]) - @test exp(log(A11)) ≈ A11 - - A13 = convert(Matrix{elty}, [2 0; 0 2]) - @test typeof(log(A13)) == Array{elty, 2} - - T = elty == Float64 ? Symmetric : Hermitian - @test typeof(log(T(A13))) == T{elty, Array{elty, 2}} - - A1 = convert(Matrix{elty}, [4 2 0; 1 4 1; 1 1 4]) - logA1 = convert(Matrix{elty}, [1.329661349 0.5302876358 -0.06818951543; - 0.2310490602 1.295566591 0.2651438179; - 0.2310490602 0.1969543025 1.363756107]) - @test log(A1) ≈ logA1 - @test exp(log(A1)) ≈ A1 - @test typeof(log(A1)) == Matrix{elty} - - A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); - 1/3 1/4 1/5 1/6; - 1/4 1/5 1/6 1/7; - 1/5 1/6 1/7 1/8]) - logA4 = convert(Matrix{elty}, [-1.73297159 1.857349738 0.4462766564 0.2414170219; - 1.857349738 -5.335033737 2.994142974 0.5865285289; - 0.4462766564 2.994142974 -7.351095988 3.318413247; - 0.2414170219 0.5865285289 3.318413247 -5.444632124]) - @test log(A4) ≈ logA4 - @test exp(log(A4)) ≈ A4 - @test typeof(log(A4)) == Matrix{elty} - - # real triu matrix - A5 = convert(Matrix{elty}, [1 2 3; 0 4 5; 0 0 6]) # triu - logA5 = convert(Matrix{elty}, [0.0 0.9241962407465937 0.5563245488984037; - 0.0 1.3862943611198906 1.0136627702704109; - 0.0 0.0 1.791759469228055]) - @test log(A5) ≈ logA5 - @test exp(log(A5)) ≈ A5 - @test typeof(log(A5)) == Matrix{elty} - - # real quasitriangular schur form with 2 2x2 blocks, 2 1x1 blocks, and all positive eigenvalues - A6 = convert(Matrix{elty}, [2 3 2 2 3 1; - 1 3 3 2 3 1; - 3 3 3 1 1 2; - 2 1 2 2 2 2; - 1 1 2 2 3 1; - 2 2 2 2 1 3]) - @test exp(log(A6)) ≈ A6 - @test typeof(log(A6)) == Matrix{elty} - - # real quasitriangular schur form with a negative eigenvalue - A7 = convert(Matrix{elty}, [1 3 3 2 2 2; - 1 2 1 3 1 2; - 3 1 2 3 2 1; - 3 1 2 2 2 1; - 3 1 3 1 2 1; - 1 1 3 1 1 3]) - @test exp(log(A7)) ≈ A7 - @test typeof(log(A7)) == Matrix{complex(elty)} - - if elty <: Complex - A8 = convert(Matrix{elty}, [1 + 1im 1 + 1im 1 - 1im; - 1 + 1im -1 + 1im 1 + 1im; - 1 - 1im 1 + 1im -1 - 1im]) - logA8 = convert( - Matrix{elty}, - [0.9478628953131517 + 1.3725201223387407im -0.2547157147532057 + 0.06352318334299434im 0.8560050197863862 - 1.0471975511965979im; - -0.2547157147532066 + 0.06352318334299467im -0.16285783922644065 + 0.2617993877991496im 0.2547157147532063 + 2.1579182857361894im; - 0.8560050197863851 - 1.0471975511965974im 0.25471571475320665 + 2.1579182857361903im 0.9478628953131519 - 0.8489213467404436im], - ) - @test log(A8) ≈ logA8 - @test exp(log(A8)) ≈ A8 - @test typeof(log(A8)) == Matrix{elty} - end -end - -@testset "matrix logarithm is type-inferable" for elty in (Float32,Float64,ComplexF32,ComplexF64) - A1 = randn(elty, 4, 4) - @inferred Union{Matrix{elty},Matrix{complex(elty)}} log(A1) -end - -@testset "Additional matrix square root tests" for elty in (Float64, ComplexF64) - A11 = convert(Matrix{elty}, [3 2; -5 -3]) - @test sqrt(A11)^2 ≈ A11 - - A13 = convert(Matrix{elty}, [2 0; 0 2]) - @test typeof(sqrt(A13)) == Array{elty, 2} - - T = elty == Float64 ? Symmetric : Hermitian - @test typeof(sqrt(T(A13))) == T{elty, Array{elty, 2}} - - A1 = convert(Matrix{elty}, [4 2 0; 1 4 1; 1 1 4]) - sqrtA1 = convert(Matrix{elty}, [1.971197119306979 0.5113118387140085 -0.03301921523780871; - 0.23914631173809942 1.9546875116880718 0.2556559193570036; - 0.23914631173810008 0.22263670411919556 1.9877067269258815]) - @test sqrt(A1) ≈ sqrtA1 - @test sqrt(A1)^2 ≈ A1 - @test typeof(sqrt(A1)) == Matrix{elty} - - A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); - 1/3 1/4 1/5 1/6; - 1/4 1/5 1/6 1/7; - 1/5 1/6 1/7 1/8]) - sqrtA4 = convert( - Matrix{elty}, - [0.590697761556362 0.3055006800405779 0.19525404749300546 0.14007621469988107; - 0.30550068004057784 0.2825388389385975 0.21857572599211642 0.17048692323164674; - 0.19525404749300565 0.21857572599211622 0.21155429252242863 0.18976816626246887; - 0.14007621469988046 0.17048692323164724 0.1897681662624689 0.20075085592778794], - ) - @test sqrt(A4) ≈ sqrtA4 - @test sqrt(A4)^2 ≈ A4 - @test typeof(sqrt(A4)) == Matrix{elty} - - # real triu matrix - A5 = convert(Matrix{elty}, [1 2 3; 0 4 5; 0 0 6]) # triu - sqrtA5 = convert(Matrix{elty}, [1.0 0.6666666666666666 0.6525169217864183; - 0.0 2.0 1.1237243569579454; - 0.0 0.0 2.449489742783178]) - @test sqrt(A5) ≈ sqrtA5 - @test sqrt(A5)^2 ≈ A5 - @test typeof(sqrt(A5)) == Matrix{elty} - - # real quasitriangular schur form with 2 2x2 blocks, 2 1x1 blocks, and all positive eigenvalues - A6 = convert(Matrix{elty}, [2 3 2 2 3 1; - 1 3 3 2 3 1; - 3 3 3 1 1 2; - 2 1 2 2 2 2; - 1 1 2 2 3 1; - 2 2 2 2 1 3]) - @test sqrt(A6)^2 ≈ A6 - @test typeof(sqrt(A6)) == Matrix{elty} - - # real quasitriangular schur form with a negative eigenvalue - A7 = convert(Matrix{elty}, [1 3 3 2 2 2; - 1 2 1 3 1 2; - 3 1 2 3 2 1; - 3 1 2 2 2 1; - 3 1 3 1 2 1; - 1 1 3 1 1 3]) - @test sqrt(A7)^2 ≈ A7 - @test typeof(sqrt(A7)) == Matrix{complex(elty)} - - if elty <: Complex - A8 = convert(Matrix{elty}, [1 + 1im 1 + 1im 1 - 1im; - 1 + 1im -1 + 1im 1 + 1im; - 1 - 1im 1 + 1im -1 - 1im]) - sqrtA8 = convert( - Matrix{elty}, - [1.2559748527474284 + 0.6741878819930323im 0.20910077991005582 + 0.24969165051825476im 0.591784212275146 - 0.6741878819930327im; - 0.2091007799100553 + 0.24969165051825515im 0.3320953202361413 + 0.2915044496279425im 0.33209532023614136 + 1.0568713143581219im; - 0.5917842122751455 - 0.674187881993032im 0.33209532023614147 + 1.0568713143581223im 0.7147787526012315 - 0.6323750828833452im], - ) - @test sqrt(A8) ≈ sqrtA8 - @test sqrt(A8)^2 ≈ A8 - @test typeof(sqrt(A8)) == Matrix{elty} - end -end - -@testset "issue #40141" begin - x = [-1 -eps() 0 0; eps() -1 0 0; 0 0 -1 -eps(); 0 0 eps() -1] - @test sqrt(x)^2 ≈ x - - x2 = [-1 -eps() 0 0; 3eps() -1 0 0; 0 0 -1 -3eps(); 0 0 eps() -1] - @test sqrt(x2)^2 ≈ x2 - - x3 = [-1 -eps() 0 0; eps() -1 0 0; 0 0 -1 -eps(); 0 0 eps() Inf] - @test all(isnan, sqrt(x3)) - - # test overflow/underflow handled - x4 = [0 -1e200; 1e200 0] - @test sqrt(x4)^2 ≈ x4 - - x5 = [0 -1e-200; 1e-200 0] - @test sqrt(x5)^2 ≈ x5 - - x6 = [1.0 1e200; -1e-200 1.0] - @test sqrt(x6)^2 ≈ x6 -end - -@testset "matrix logarithm block diagonal underflow/overflow" begin - x1 = [0 -1e200; 1e200 0] - @test exp(log(x1)) ≈ x1 - - x2 = [0 -1e-200; 1e-200 0] - @test exp(log(x2)) ≈ x2 - - x3 = [1.0 1e200; -1e-200 1.0] - @test exp(log(x3)) ≈ x3 -end - -@testset "issue #7181" begin - A = [ 1 5 9 - 2 6 10 - 3 7 11 - 4 8 12 ] - @test diag(A,-5) == [] - @test diag(A,-4) == [] - @test diag(A,-3) == [4] - @test diag(A,-2) == [3,8] - @test diag(A,-1) == [2,7,12] - @test diag(A, 0) == [1,6,11] - @test diag(A, 1) == [5,10] - @test diag(A, 2) == [9] - @test diag(A, 3) == [] - @test diag(A, 4) == [] - - @test diag(zeros(0,0)) == [] - @test diag(zeros(0,0),1) == [] - @test diag(zeros(0,0),-1) == [] - - @test diag(zeros(1,0)) == [] - @test diag(zeros(1,0),-1) == [] - @test diag(zeros(1,0),1) == [] - @test diag(zeros(1,0),-2) == [] - - @test diag(zeros(0,1)) == [] - @test diag(zeros(0,1),1) == [] - @test diag(zeros(0,1),-1) == [] - @test diag(zeros(0,1),2) == [] -end - -@testset "diagview" begin - for sz in ((3,3), (3,5), (5,3)) - A = rand(sz...) - for k in -5:5 - @test diagview(A,k) == diag(A,k) - end - end -end - -@testset "issue #39857" begin - @test lyap(1.0+2.0im, 3.0+4.0im) == -1.5 - 2.0im -end - -@testset "$elty Matrix to real power" for elty in (Float64, ComplexF64) - # Tests proposed at Higham, Deadman: Testing Matrix Function Algorithms Using Identities, March 2014 - #Aa : only positive real eigenvalues - Aa = convert(Matrix{elty}, [5 4 2 1; 0 1 -1 -1; -1 -1 3 0; 1 1 -1 2]) - - #Ab : both positive and negative real eigenvalues - Ab = convert(Matrix{elty}, [1 2 3; 4 7 1; 2 1 4]) - - #Ac : complex eigenvalues - Ac = convert(Matrix{elty}, [5 4 2 1;0 1 -1 -1;-1 -1 3 6;1 1 -1 5]) - - #Ad : defective Matrix - Ad = convert(Matrix{elty}, [3 1; 0 3]) - - #Ah : Hermitian Matrix - Ah = convert(Matrix{elty}, [3 1; 1 3]) - if elty <: LinearAlgebra.BlasComplex - Ah += [0 im; -im 0] - end - - #ADi : Diagonal Matrix - ADi = convert(Matrix{elty}, [3 0; 0 3]) - if elty <: LinearAlgebra.BlasComplex - ADi += [im 0; 0 im] - end - - for A in (Aa, Ab, Ac, Ad, Ah, ADi) - @test A^(1/2) ≈ sqrt(A) - @test A^(-1/2) ≈ inv(sqrt(A)) - @test A^(3/4) ≈ sqrt(A) * sqrt(sqrt(A)) - @test A^(-3/4) ≈ inv(A) * sqrt(sqrt(A)) - @test A^(17/8) ≈ A^2 * sqrt(sqrt(sqrt(A))) - @test A^(-17/8) ≈ inv(A^2 * sqrt(sqrt(sqrt(A)))) - @test (A^0.2)^5 ≈ A - @test (A^(2/3))*(A^(1/3)) ≈ A - @test (A^im)^(-im) ≈ A - end - - Tschurpow = Union{Matrix{real(elty)}, Matrix{complex(elty)}} - @test (@inferred Tschurpow LinearAlgebra.schurpow(Aa, 2.0)) ≈ Aa^2 -end - -@testset "BigFloat triangular real power" begin - A = Float64[3 1; 0 3] - @test A^(3/4) ≈ big.(A)^(3/4) -end - -@testset "diagonal integer matrix to real power" begin - A = Matrix(Diagonal([1, 2, 3])) - @test A^2.3 ≈ float(A)^2.3 -end - -@testset "issue #23366 (Int Matrix to Int power)" begin - @testset "Tests for $elty" for elty in (Int128, Int16, Int32, Int64, Int8, - UInt128, UInt16, UInt32, UInt64, UInt8, - BigInt) - #@info "Testing $elty" - @test elty[1 1;1 0]^-1 == [0 1; 1 -1] - @test elty[1 1;1 0]^-2 == [1 -1; -1 2] - @test (@inferred elty[1 1;1 0]^2) == elty[2 1;1 1] - I_ = elty[1 0;0 1] - @test I_^-1 == I_ - if !(elty<:Unsigned) - @test (@inferred (-I_)^-1) == -I_ - @test (@inferred (-I_)^-2) == I_ - end - # make sure that type promotion for ^(::Matrix{<:Integer}, ::Integer) - # is analogous to type promotion for ^(::Integer, ::Integer) - # e.g. [1 1;1 0]^big(10000) should return Matrix{BigInt}, the same - # way as 2^big(10000) returns BigInt - for elty2 = (Int64, BigInt) - TT = Base.promote_op(^, elty, elty2) - @test (@inferred elty[1 1;1 0]^elty2(1))::Matrix{TT} == [1 1;1 0] - end - end -end - -@testset "Least squares solutions" begin - a = [fill(1, 20) 1:20 1:20] - b = reshape(Matrix(1.0I, 8, 5), 20, 2) - @testset "Tests for type $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - a = convert(Matrix{elty}, a) - b = convert(Matrix{elty}, b) - - # Vector rhs - x = a[:,1:2]\b[:,1] - @test ((a[:,1:2]*x-b[:,1])'*(a[:,1:2]*x-b[:,1]))[1] ≈ convert(elty, 2.546616541353384) - - # Matrix rhs - x = a[:,1:2]\b - @test det((a[:,1:2]*x-b)'*(a[:,1:2]*x-b)) ≈ convert(elty, 4.437969924812031) - - # Rank deficient - x = a\b - @test det((a*x-b)'*(a*x-b)) ≈ convert(elty, 4.437969924812031) - - # Underdetermined minimum norm - x = convert(Matrix{elty}, [1 0 0; 0 1 -1]) \ convert(Vector{elty}, [1,1]) - @test x ≈ convert(Vector{elty}, [1, 0.5, -0.5]) - - # symmetric, positive definite - @test inv(convert(Matrix{elty}, [6. 2; 2 1])) ≈ convert(Matrix{elty}, [0.5 -1; -1 3]) - - # symmetric, indefinite - @test inv(convert(Matrix{elty}, [1. 2; 2 1])) ≈ convert(Matrix{elty}, [-1. 2; 2 -1]/3) - end -end - -function test_rdiv_pinv_consistency(a, b) - @test (a*b)/b ≈ a*(b/b) ≈ (a*b)*pinv(b) ≈ a*(b*pinv(b)) - @test typeof((a*b)/b) == typeof(a*(b/b)) == typeof((a*b)*pinv(b)) == typeof(a*(b*pinv(b))) -end -function test_ldiv_pinv_consistency(a, b) - @test a\(a*b) ≈ (a\a)*b ≈ (pinv(a)*a)*b ≈ pinv(a)*(a*b) - @test typeof(a\(a*b)) == typeof((a\a)*b) == typeof((pinv(a)*a)*b) == typeof(pinv(a)*(a*b)) -end -function test_div_pinv_consistency(a, b) - test_rdiv_pinv_consistency(a, b) - test_ldiv_pinv_consistency(a, b) -end - -@testset "/ and \\ consistency with pinv for vectors" begin - @testset "Tests for type $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - c = rand(elty, 5) - r = (elty <: Complex ? adjoint : transpose)(rand(elty, 5)) - cm = rand(elty, 5, 1) - rm = rand(elty, 1, 5) - @testset "dot products" begin - test_div_pinv_consistency(r, c) - test_div_pinv_consistency(rm, c) - test_div_pinv_consistency(r, cm) - test_div_pinv_consistency(rm, cm) - end - @testset "outer products" begin - test_div_pinv_consistency(c, r) - test_div_pinv_consistency(cm, rm) - end - @testset "matrix/vector" begin - m = rand(5, 5) - test_ldiv_pinv_consistency(m, c) - test_rdiv_pinv_consistency(r, m) - end - end -end - -@testset "test ops on Numbers for $elty" for elty in [Float32,Float64,ComplexF32,ComplexF64] - a = rand(elty) - @test isposdef(one(elty)) - @test lyap(one(elty),a) == -a/2 -end - -@testset "strides" begin - a = rand(10) - b = view(a,2:2:10) - @test LinearAlgebra.stride1(a) == 1 - @test LinearAlgebra.stride1(b) == 2 -end - -@testset "inverse of Adjoint" begin - A = randn(n, n) - - @test @inferred(inv(A'))*A' ≈ I - @test @inferred(inv(transpose(A)))*transpose(A) ≈ I - - B = complex.(A, randn(n, n)) - - @test @inferred(inv(B'))*B' ≈ I - @test @inferred(inv(transpose(B)))*transpose(B) ≈ I -end - -@testset "Factorize fallback for Adjoint/Transpose" begin - a = rand(Complex{Int8}, n, n) - @test Array(transpose(factorize(Transpose(a)))) ≈ Array(factorize(a)) - @test transpose(factorize(transpose(a))) == factorize(a) - @test Array(adjoint(factorize(Adjoint(a)))) ≈ Array(factorize(a)) - @test adjoint(factorize(adjoint(a))) == factorize(a) -end - -@testset "Matrix log issue #32313" begin - for A in ([30 20; -50 -30], [10.0im 0; 0 -10.0im], randn(6,6)) - @test exp(log(A)) ≈ A - end -end - -@testset "Matrix log PR #33245" begin - # edge case for divided difference - A1 = triu(ones(3,3),1) + diagm([1.0, -2eps()-1im, -eps()+0.75im]) - @test exp(log(A1)) ≈ A1 - # case where no sqrt is needed (s=0) - A2 = [1.01 0.01 0.01; 0 1.01 0.01; 0 0 1.01] - @test exp(log(A2)) ≈ A2 -end - -@testset "sqrt of empty Matrix of type $T" for T in [Int,Float32,Float64,ComplexF32,ComplexF64] - @test sqrt(Matrix{T}(undef, 0, 0)) == Matrix{T}(undef, 0, 0) - @test_throws DimensionMismatch sqrt(Matrix{T}(undef, 0, 3)) -end - -struct TypeWithoutZero end -Base.zero(::Type{TypeWithoutZero}) = TypeWithZero() -struct TypeWithZero end -Base.promote_rule(::Type{TypeWithoutZero}, ::Type{TypeWithZero}) = TypeWithZero -Base.zero(::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} -end - -@testset "cbrt(A::AbstractMatrix{T})" begin - N = 10 - - # Non-square - A = randn(N,N+2) - @test_throws DimensionMismatch cbrt(A) - - # Real valued diagonal - D = Diagonal(randn(N)) - T = cbrt(D) - @test T*T*T ≈ D - @test eltype(D) == eltype(T) - # Real valued triangular - U = UpperTriangular(randn(N,N)) - T = cbrt(U) - @test T*T*T ≈ U - @test eltype(U) == eltype(T) - L = LowerTriangular(randn(N,N)) - T = cbrt(L) - @test T*T*T ≈ L - @test eltype(L) == eltype(T) - # Real valued symmetric - S = (A -> (A+A')/2)(randn(N,N)) - T = cbrt(Symmetric(S,:U)) - @test T*T*T ≈ S - @test eltype(S) == eltype(T) - # Real valued symmetric - S = (A -> (A+A')/2)(randn(N,N)) - T = cbrt(Symmetric(S,:L)) - @test T*T*T ≈ S - @test eltype(S) == eltype(T) - # Real valued Hermitian - S = (A -> (A+A')/2)(randn(N,N)) - T = cbrt(Hermitian(S,:U)) - @test T*T*T ≈ S - @test eltype(S) == eltype(T) - # Real valued Hermitian - S = (A -> (A+A')/2)(randn(N,N)) - T = cbrt(Hermitian(S,:L)) - @test T*T*T ≈ S - @test eltype(S) == eltype(T) - # Real valued arbitrary - A = randn(N,N) - T = cbrt(A) - @test T*T*T ≈ A - @test eltype(A) == eltype(T) -end - -@testset "tr" begin - @testset "block matrices" begin - S = [1 2; 3 4] - M = fill(S, 3, 3) - @test tr(M) == 3S - @test tr(view(M, :, :)) == 3S - @test tr(view(M, axes(M)...)) == 3S - end - @testset "avoid promotion" begin - A = Int8[1 3; 2 4] - @test tr(A) === Int8(5) - @test tr(view(A, :, :)) === Int8(5) - @test tr(view(A, axes(A)...)) === Int8(5) - end -end - -@testset "trig functions for non-strided" begin - @testset for T in (Float32,ComplexF32) - A = FillArrays.Fill(T(0.1), 4, 4) # all.(<(1), eigvals(A)) for atanh - M = Matrix(A) - @testset for f in (sin,cos,tan,sincos,sinh,cosh,tanh) - @test f(A) == f(M) - end - @testset for f in (asin,acos,atan,asinh,acosh,atanh) - @test f(A) == f(M) - end - end -end - -end # module TestDense diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl deleted file mode 100644 index 16f3d2287f317..0000000000000 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ /dev/null @@ -1,1455 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestDiagonal - -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 - -isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) -using .Main.InfiniteArrays - -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -using .Main.FillArrays - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -const n=12 # Size of matrix problem to test -Random.seed!(1) - -@testset for relty in (Float32, Float64, BigFloat), elty in (relty, Complex{relty}) - dd=convert(Vector{elty}, randn(n)) - vv=convert(Vector{elty}, randn(n)) - UU=convert(Matrix{elty}, randn(n,n)) - if elty <: Complex - dd+=im*convert(Vector{elty}, randn(n)) - vv+=im*convert(Vector{elty}, randn(n)) - UU+=im*convert(Matrix{elty}, randn(n,n)) - end - D = Diagonal(dd) - DM = Matrix(Diagonal(dd)) - - @testset "constructor" begin - for x in (dd, GenericArray(dd)) - @test Diagonal(x)::Diagonal{elty,typeof(x)} == DM - @test Diagonal(x).diag === x - @test Diagonal{elty}(x)::Diagonal{elty,typeof(x)} == DM - @test Diagonal{elty}(x).diag === x - @test Diagonal{elty}(D) === D - end - @test eltype(Diagonal{elty}([1,2,3,4])) == elty - @test isa(Diagonal{elty,Vector{elty}}(GenericArray([1,2,3,4])), Diagonal{elty,Vector{elty}}) - @test isa(Diagonal{elty}(rand(Int,n,n)), Diagonal{elty,Vector{elty}}) - DI = Diagonal([1,2,3,4]) - @test Diagonal(DI) === DI - @test isa(Diagonal{elty}(DI), Diagonal{elty}) - - # diagonal matrices may be converted to Diagonal - local A = [1 0; 0 2] - local DA = convert(Diagonal{Float32,Vector{Float32}}, A) - @test DA isa Diagonal{Float32,Vector{Float32}} - @test DA == A - - # issue #26178 - @test_throws MethodError convert(Diagonal, [1,2,3,4]) - @test_throws DimensionMismatch convert(Diagonal, [1 2 3 4]) - @test_throws InexactError convert(Diagonal, ones(2,2)) - - # Test reversing - # Test reversing along rows - @test reverse(D, dims=1) == reverse(Matrix(D), dims=1) - - # Test reversing along columns - @test reverse(D, dims=2) == reverse(Matrix(D), dims=2) - - # Test reversing the entire matrix - @test reverse(D)::Diagonal == reverse(Matrix(D)) == reverse!(copy(D)) - end - - @testset "Basic properties" begin - @test_throws BoundsError size(D,0) - @test size(D,1) == size(D,2) == length(dd) - @test size(D,3) == 1 - @test typeof(convert(Diagonal{ComplexF32},D)) <: Diagonal{ComplexF32} - @test typeof(convert(AbstractMatrix{ComplexF32},D)) <: Diagonal{ComplexF32} - - @test Array(real(D)) == real(DM) - @test Array(abs.(D)) == abs.(DM) - @test Array(imag(D)) == imag(DM) - - @test parent(D) == dd - @test D[1,1] == dd[1] - @test D[1,2] == 0 - - @test issymmetric(D) - @test isdiag(D) - @test isdiag(Diagonal([[1 0; 0 1], [1 0; 0 1]])) - @test !isdiag(Diagonal([[1 0; 0 1], [1 0; 1 1]])) - @test istriu(D) - @test istriu(D, -1) - @test !istriu(D, 1) - @test istriu(Diagonal(zero(diag(D))), 1) - @test istril(D) - @test !istril(D, -1) - @test istril(D, 1) - @test istril(Diagonal(zero(diag(D))), -1) - @test Base.isstored(D,1,1) - @test !Base.isstored(D,1,2) - @test_throws BoundsError Base.isstored(D, n + 1, 1) - if elty <: Real - @test ishermitian(D) - end - end - - @testset "diag" begin - @test isempty(@inferred diag(D, n+1)) - @test isempty(@inferred diag(D, -n-1)) - @test (@inferred diag(D))::typeof(dd) == dd - @test (@inferred diag(D, 0))::typeof(dd) == dd - @test (@inferred diag(D, 1))::typeof(dd) == zeros(elty, n-1) - DG = Diagonal(GenericArray(dd)) - @test (@inferred diag(DG))::typeof(GenericArray(dd)) == GenericArray(dd) - @test (@inferred diag(DG, 1))::typeof(GenericArray(dd)) == GenericArray(zeros(elty, n-1)) - end - - - @testset "Simple unary functions" begin - for op in (-,) - @test op(D)==op(DM) - end - - for func in (det, tr) - @test func(D) ≈ func(DM) atol=n^2*eps(relty)*(1+(elty<:Complex)) - end - - if eltype(D) <: Real - @test minimum(D) ≈ minimum(DM) - @test maximum(D) ≈ maximum(DM) - end - - if relty <: BlasFloat - for func in (exp, cis, sinh, cosh, tanh, sech, csch, coth) - @test func(D) ≈ func(DM) atol=n^3*eps(relty) - end - @test log(Diagonal(abs.(D.diag))) ≈ log(abs.(DM)) atol=n^3*eps(relty) - end - if elty <: BlasComplex - for func in (logdet, sqrt, sin, cos, tan, sec, csc, cot, - asin, acos, atan, asec, acsc, acot, - asinh, acosh, atanh, asech, acsch, acoth) - @test func(D) ≈ func(DM) atol=n^2*eps(relty)*2 - end - end - end - - @testset "Two-dimensional Euler formula for Diagonal" begin - @test cis(Diagonal([π, π])) ≈ -I - end - - @testset "Linear solve" begin - for (v, U) in ((vv, UU), (view(vv, 1:n), view(UU, 1:n, 1:2))) - @test D*v ≈ DM*v atol=n*eps(relty)*(1+(elty<:Complex)) - @test D*U ≈ DM*U atol=n^2*eps(relty)*(1+(elty<:Complex)) - - @test transpose(U)*D ≈ transpose(U)*Array(D) - @test U'*D ≈ U'*Array(D) - - if relty != BigFloat - atol_two = 2n^2 * eps(relty) * (1 + (elty <: Complex)) - atol_three = 2n^3 * eps(relty) * (1 + (elty <: Complex)) - @test D\v ≈ DM\v atol=atol_two - @test D\U ≈ DM\U atol=atol_three - @test ldiv!(D, copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(transpose(D), copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(adjoint(conj(D)), copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(D, copy(U)) ≈ DM\U atol=atol_three - @test ldiv!(transpose(D), copy(U)) ≈ DM\U atol=atol_three - @test ldiv!(adjoint(conj(D)), copy(U)) ≈ DM\U atol=atol_three - # this method tests AbstractMatrix/AbstractVec for second arg - Usym_bad = Symmetric(ones(elty, n+1, n+1)) - @test_throws DimensionMismatch ldiv!(D, copy(Usym_bad)) - - @test ldiv!(zero(v), D, copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(zero(v), transpose(D), copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(zero(v), adjoint(conj(D)), copy(v)) ≈ DM\v atol=atol_two - @test ldiv!(zero(U), D, copy(U)) ≈ DM\U atol=atol_three - @test ldiv!(zero(U), transpose(D), copy(U)) ≈ DM\U atol=atol_three - @test ldiv!(zero(U), adjoint(conj(D)), copy(U)) ≈ DM\U atol=atol_three - - Uc = copy(U') - target = rmul!(Uc, Diagonal(inv.(D.diag))) - @test rdiv!(Uc, D) ≈ target atol=atol_three - @test_throws DimensionMismatch rdiv!(Matrix{elty}(I, n-1, n-1), D) - @test_throws SingularException rdiv!(Uc, Diagonal(fill!(similar(D.diag), 0))) - @test rdiv!(Uc, transpose(D)) ≈ target atol=atol_three - @test rdiv!(Uc, adjoint(conj(D))) ≈ target atol=atol_three - @test ldiv!(D, Matrix{eltype(D)}(I, size(D))) ≈ D \ Matrix{eltype(D)}(I, size(D)) atol=atol_three - @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) - @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)) - b2 = copy(b) - c = ldiv!(D, b) - d = Array(D)\b2 - @test c ≈ d - @test_throws SingularException ldiv!(Diagonal(zeros(elty, n)), b) - b = rand(elty, n+1, n+1) - @test_throws DimensionMismatch ldiv!(D, copy(b)) - b = view(rand(elty, n+1), Vector(1:n+1)) - @test_throws DimensionMismatch ldiv!(D, b) - end - end - end - d = convert(Vector{elty}, randn(n)) - D2 = Diagonal(d) - DM2= Matrix(Diagonal(d)) - @testset "Binary operations" begin - for op in (+, -, *) - @test Array(op(D, D2)) ≈ op(DM, DM2) - end - @testset "with plain numbers" begin - a = rand() - @test Array(a*D) ≈ a*DM - @test Array(D*a) ≈ DM*a - @test Array(D/a) ≈ DM/a - if elty <: Real - @test Array(abs.(D)^a) ≈ abs.(DM)^a - else - @test Array(D^a) ≈ DM^a - end - @test Diagonal(1:100)^2 == Diagonal((1:100).^2) - p = 3 - @test Diagonal(1:100)^p == Diagonal((1:100).^p) - @test Diagonal(1:100)^(-1) == Diagonal(inv.(1:100)) - @test Diagonal(1:100)^2.0 == Diagonal((1:100).^2.0) - @test Diagonal(1:100)^(2.0+0im) == Diagonal((1:100).^(2.0+0im)) - end - - if relty <: BlasFloat - 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) - end - end - - #a few missing mults - bd = Bidiagonal(D2) - @test D*transpose(D2) ≈ Array(D)*transpose(Array(D2)) - @test D2*transpose(D) ≈ Array(D2)*transpose(Array(D)) - @test D2*D' ≈ Array(D2)*Array(D)' - - #division of two Diagonals - @test D/D2 ≈ Diagonal(D.diag./D2.diag) - @test D\D2 ≈ Diagonal(D2.diag./D.diag) - - # QR \ Diagonal - A = rand(elty, n, n) - qrA = qr(A) - @test qrA \ D ≈ A \ D - - # HermOrSym - A = rand(elty, n, n) - Asym = Symmetric(A + transpose(A), :U) - Aherm = Hermitian(A + adjoint(A), :U) - for op in (+, -) - @test op(Asym, D) isa Symmetric - @test Array(op(Asym, D)) ≈ Array(Symmetric(op(Array(Asym), Array(D)))) - @test op(D, Asym) isa Symmetric - @test Array(op(D, Asym)) ≈ Array(Symmetric(op(Array(D), Array(Asym)))) - if !(elty <: Real) - Dr = real(D) - @test op(Aherm, Dr) isa Hermitian - @test Array(op(Aherm, Dr)) ≈ Array(Hermitian(op(Array(Aherm), Array(Dr)))) - @test op(Dr, Aherm) isa Hermitian - @test Array(op(Dr, Aherm)) ≈ Array(Hermitian(op(Array(Dr), Array(Aherm)))) - end - end - @test Array(D*transpose(Asym)) ≈ Array(D) * Array(transpose(Asym)) - @test Array(D*adjoint(Asym)) ≈ Array(D) * Array(adjoint(Asym)) - @test Array(D*transpose(Aherm)) ≈ Array(D) * Array(transpose(Aherm)) - @test Array(D*adjoint(Aherm)) ≈ Array(D) * Array(adjoint(Aherm)) - @test Array(transpose(Asym)*transpose(D)) ≈ Array(transpose(Asym)) * Array(transpose(D)) - @test Array(transpose(D)*transpose(Asym)) ≈ Array(transpose(D)) * Array(transpose(Asym)) - @test Array(adjoint(Aherm)*adjoint(D)) ≈ Array(adjoint(Aherm)) * Array(adjoint(D)) - @test Array(adjoint(D)*adjoint(Aherm)) ≈ Array(adjoint(D)) * Array(adjoint(Aherm)) - - # Performance specialisations for A*_mul_B! - vvv = similar(vv) - @test (r = Matrix(D) * vv ; mul!(vvv, D, vv) ≈ r ≈ vvv) - @test (r = Matrix(D)' * vv ; mul!(vvv, adjoint(D), vv) ≈ r ≈ vvv) - @test (r = transpose(Matrix(D)) * vv ; mul!(vvv, transpose(D), vv) ≈ r ≈ vvv) - - UUU = similar(UU) - for transformA in (identity, adjoint, transpose) - for transformD in (identity, adjoint, transpose) - @test mul!(UUU, transformA(UU), transformD(D)) ≈ transformA(UU) * Matrix(transformD(D)) - @test mul!(UUU, transformD(D), transformA(UU)) ≈ Matrix(transformD(D)) * transformA(UU) - end - end - - alpha = elty(randn()) # randn(elty) does not work with BigFloat - beta = elty(randn()) - @test begin - vvv = similar(vv) - vvv .= randn(size(vvv)) # randn!(vvv) does not work with BigFloat - r = alpha * Matrix(D) * vv + beta * vvv - mul!(vvv, D, vv, alpha, beta) ≈ r ≈ vvv - end - @test begin - vvv = similar(vv) - vvv .= randn(size(vvv)) # randn!(vvv) does not work with BigFloat - r = alpha * Matrix(D)' * vv + beta * vvv - mul!(vvv, adjoint(D), vv, alpha, beta) ≈ r ≈ vvv - end - @test begin - vvv = similar(vv) - vvv .= randn(size(vvv)) # randn!(vvv) does not work with BigFloat - r = alpha * transpose(Matrix(D)) * vv + beta * vvv - mul!(vvv, transpose(D), vv, alpha, beta) ≈ r ≈ vvv - end - - @test begin - UUU = similar(UU) - UUU .= randn(size(UUU)) # randn!(UUU) does not work with BigFloat - r = alpha * Matrix(D) * UU + beta * UUU - mul!(UUU, D, UU, alpha, beta) ≈ r ≈ UUU - end - @test begin - UUU = similar(UU) - UUU .= randn(size(UUU)) # randn!(UUU) does not work with BigFloat - r = alpha * Matrix(D)' * UU + beta * UUU - mul!(UUU, adjoint(D), UU, alpha, beta) ≈ r ≈ UUU - end - @test begin - UUU = similar(UU) - UUU .= randn(size(UUU)) # randn!(UUU) does not work with BigFloat - r = alpha * transpose(Matrix(D)) * UU + beta * UUU - mul!(UUU, transpose(D), UU, alpha, beta) ≈ r ≈ UUU - end - - # make sure that mul!(A, {Adj|Trans}(B)) works with B as a Diagonal - VV = Array(D) - DD = copy(D) - r = VV * Matrix(D) - @test Array(rmul!(VV, DD)) ≈ r ≈ Array(D)*Array(D) - DD = copy(D) - r = VV * transpose(Array(D)) - @test Array(rmul!(VV, transpose(DD))) ≈ r - DD = copy(D) - r = VV * Array(D)' - @test Array(rmul!(VV, adjoint(DD))) ≈ r - - # kron - D3 = Diagonal(convert(Vector{elty}, rand(n÷2))) - DM3= Matrix(D3) - @test Matrix(kron(D, D3)) ≈ kron(DM, DM3) - M4 = rand(elty, size(D3,1) + 1, size(D3,2) + 2) # choose a different size from D3 - @test kron(D3, M4) ≈ kron(DM3, M4) - @test kron(M4, D3) ≈ kron(M4, DM3) - X = [ones(1,1) for i in 1:2, j in 1:2] - @test kron(I(2), X)[1,3] == zeros(1,1) - X = [ones(2,2) for i in 1:2, j in 1:2] - @test kron(I(2), X)[1,3] == zeros(2,2) - end - @testset "iszero, isone, triu, tril" begin - Dzero = Diagonal(zeros(elty, 10)) - Done = Diagonal(ones(elty, 10)) - Dmix = Diagonal(zeros(elty, 10)) - Dmix[end,end] = one(elty) - @test iszero(Dzero) - @test !isone(Dzero) - @test !iszero(Done) - @test isone(Done) - @test !iszero(Dmix) - @test !isone(Dmix) - @test istriu(D) - @test istril(D) - @test iszero(triu(D,1)) - @test triu(D,0) == D - @test triu(D,-1) == D - @test tril(D,1) == D - @test iszero(tril(D,-1)) - @test tril(D,0) == D - @test_throws ArgumentError tril(D, -n - 2) - @test_throws ArgumentError tril(D, n) - @test_throws ArgumentError triu(D, -n) - @test_throws ArgumentError triu(D, n + 2) - end - - # factorize - @test factorize(D) == D - - @testset "Eigensystem" begin - eigD = eigen(D) - @test Diagonal(eigD.values) == D - @test eigD.vectors == Matrix(I, size(D)) - eigsortD = eigen(D, sortby=LinearAlgebra.eigsortby) - @test eigsortD.values !== D.diag - @test eigsortD.values == sort(D.diag, by=LinearAlgebra.eigsortby) - @test Matrix(eigsortD) == D - end - - @testset "ldiv" begin - v = rand(n + 1) - @test_throws DimensionMismatch D\v - v = rand(n) - @test D\v ≈ DM\v - V = rand(n + 1, n) - @test_throws DimensionMismatch D\V - V = rand(n, n) - @test D\V ≈ DM\V - end - - @testset "conj and transpose" begin - @test transpose(D) == D - if elty <: Real - @test transpose(D) === D - @test adjoint(D) === D - elseif elty <: BlasComplex - @test Array(conj(D)) ≈ conj(DM) - @test adjoint(D) == conj(D) - local D2 = copy(D) - local D2adj = adjoint(D2) - D2adj[1,1] = rand(eltype(D2adj)) - @test D2[1,1] == adjoint(D2adj[1,1]) - @test D2adj' === D2 - end - # Translates to Ac/t_mul_B, which is specialized after issue 21286 - @test(D' * vv == conj(D) * vv) - @test(transpose(D) * vv == D * vv) - end - - # logdet and logabsdet - if relty <: Real - lD = Diagonal(convert(Vector{relty}, rand(n))) - lM = Matrix(lD) - @test logdet(lD) ≈ logdet(lM) - d1, s1 = @inferred logabsdet(lD) - d2, s2 = logabsdet(lM) - @test d1 ≈ d2 - @test s1 == s2 - @test logdet(Diagonal(relty[-1,-2])) ≈ log(2) - @test_throws DomainError logdet(Diagonal(relty[-1,-2,-3])) - end - - @testset "similar" begin - @test isa(similar(D), Diagonal{elty}) - @test isa(similar(D, Int), Diagonal{Int}) - @test isa(similar(D, (3,2)), Matrix{elty}) - @test isa(similar(D, Int, (3,2)), Matrix{Int}) - end - - # Issue number 10036 - # make sure issymmetric/ishermitian work for - # non-real diagonal matrices - @testset "issymmetric/hermitian for complex Diagonal" begin - @test issymmetric(D2) - @test ishermitian(D2) - if elty <: Complex - dc = d .+ elty(1im) - D3 = Diagonal(dc) - @test issymmetric(D3) - @test !ishermitian(D3) - end - end - - @testset "svd (#11120/#11247)" begin - U, s, V = svd(D) - @test (U*Diagonal(s))*V' ≈ D - @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 - v = OffsetArray(1:3) - D = Diagonal(v) - @test axes(D) isa NTuple{2,typeof(axes(v,1))} -end - -@testset "rdiv! (#40887)" begin - @test rdiv!(Matrix(Diagonal([2.0, 3.0])), Diagonal(2:3)) == Diagonal([1.0, 1.0]) - @test rdiv!(fill(3.0, 3, 3), 3.0I(3)) == ones(3,3) -end - -@testset "kron (issue #40595)" begin - # custom array type to test that kron on Diagonal matrices preserves types of the parents if possible - struct KronTestArray{T, N, AT} <: AbstractArray{T, N} - data::AT - end - KronTestArray(data::AbstractArray) = KronTestArray{eltype(data), ndims(data), typeof(data)}(data) - Base.size(A::KronTestArray) = size(A.data) - LinearAlgebra.kron(A::KronTestArray, B::KronTestArray) = KronTestArray(kron(A.data, B.data)) - Base.getindex(K::KronTestArray{<:Any,N}, i::Vararg{Int,N}) where {N} = K.data[i...] - - A = KronTestArray([1, 2, 3]); - @test kron(A, A) isa KronTestArray - Ad = Diagonal(A); - @test kron(Ad, Ad).diag isa KronTestArray - @test kron(Ad, Ad).diag == kron([1, 2, 3], [1, 2, 3]) -end - -# Define a vector type that does not support `deleteat!`, to ensure that `kron` handles this -struct SimpleVector{T} <: AbstractVector{T} - vec::Vector{T} -end -SimpleVector(x::SimpleVector) = SimpleVector(Vector(x.vec)) -SimpleVector{T}(::UndefInitializer, n::Integer) where {T} = SimpleVector(Vector{T}(undef, n)) -Base.:(==)(x::SimpleVector, y::SimpleVector) = x == y -Base.axes(x::SimpleVector) = axes(x.vec) -Base.convert(::Type{Vector{T}}, x::SimpleVector) where {T} = convert(Vector{T}, x.vec) -Base.convert(::Type{Vector}, x::SimpleVector{T}) where {T} = convert(Vector{T}, x) -Base.convert(::Type{Array{T}}, x::SimpleVector) where {T} = convert(Vector{T}, x) -Base.convert(::Type{Array}, x::SimpleVector) = convert(Vector, x) -Base.copyto!(x::SimpleVector, y::SimpleVector) = (copyto!(x.vec, y.vec); x) -Base.eltype(::Type{SimpleVector{T}}) where {T} = T -Base.getindex(x::SimpleVector, ind...) = getindex(x.vec, ind...) -Base.kron(x::SimpleVector, y::SimpleVector) = SimpleVector(kron(x.vec, y.vec)) -Base.promote_rule(::Type{<:AbstractVector{T}}, ::Type{SimpleVector{U}}) where {T,U} = Vector{promote_type(T, U)} -Base.promote_rule(::Type{SimpleVector{T}}, ::Type{SimpleVector{U}}) where {T,U} = SimpleVector{promote_type(T, U)} -Base.setindex!(x::SimpleVector, val, ind...) = (setindex!(x.vec, val, ind...), x) -Base.similar(x::SimpleVector, ::Type{T}) where {T} = SimpleVector(similar(x.vec, T)) -Base.similar(x::SimpleVector, ::Type{T}, dims::Dims{1}) where {T} = SimpleVector(similar(x.vec, T, dims)) -Base.size(x::SimpleVector) = size(x.vec) - -@testset "kron (issue #46456)" for repr in Any[identity, SimpleVector] - A = Diagonal(repr(randn(10))) - BL = Bidiagonal(repr(randn(10)), repr(randn(9)), :L) - BU = Bidiagonal(repr(randn(10)), repr(randn(9)), :U) - C = SymTridiagonal(repr(randn(10)), repr(randn(9))) - Cl = SymTridiagonal(repr(randn(10)), repr(randn(10))) - D = Tridiagonal(repr(randn(9)), repr(randn(10)), repr(randn(9))) - @test kron(A, BL)::Bidiagonal == kron(Array(A), Array(BL)) - @test kron(A, BU)::Bidiagonal == kron(Array(A), Array(BU)) - @test kron(A, C)::SymTridiagonal == kron(Array(A), Array(C)) - @test kron(A, Cl)::SymTridiagonal == kron(Array(A), Array(Cl)) - @test kron(A, D)::Tridiagonal == kron(Array(A), Array(D)) -end - -@testset "svdvals and eigvals (#11120/#11247)" begin - D = Diagonal(Matrix{Float64}[randn(3,3), randn(2,2)]) - @test sort([svdvals(D)...;], rev = true) ≈ svdvals([D.diag[1] zeros(3,2); zeros(2,3) D.diag[2]]) - @test sort([eigvals(D)...;], by=LinearAlgebra.eigsortby) ≈ eigvals([D.diag[1] zeros(3,2); zeros(2,3) D.diag[2]]) -end - -@testset "eigvals should return a copy of the diagonal" begin - D = Diagonal([1, 2, 3]) - lam = eigvals(D) - D[3,3] = 4 # should not affect lam - @test lam == [1, 2, 3] -end - -@testset "eigmin (#27847)" begin - for _ in 1:100 - d = randn(rand(1:10)) - D = Diagonal(d) - @test eigmin(D) == minimum(d) - end -end - -@testset "isposdef" begin - @test isposdef(Diagonal(1.0 .+ rand(n))) - @test !isposdef(Diagonal(-1.0 * rand(n))) - @test isposdef(Diagonal(complex(1.0, 0.0) .+ rand(n))) - @test !isposdef(Diagonal(complex(1.0, 1.0) .+ rand(n))) - @test isposdef(Diagonal([[1 0; 0 1], [1 0; 0 1]])) - @test !isposdef(Diagonal([[1 0; 0 1], [1 0; 1 1]])) -end - -@testset "getindex" begin - d = randn(n) - D = Diagonal(d) - # getindex bounds checking - @test_throws BoundsError D[0, 0] - @test_throws BoundsError D[-1, -2] - @test_throws BoundsError D[n, n + 1] - @test_throws BoundsError D[n + 1, n] - @test_throws BoundsError D[n + 1, n + 1] - # getindex on and off the diagonal - for i in 1:n, j in 1:n - @test D[i, j] == (i == j ? d[i] : 0) - end -end - -@testset "setindex!" begin - d = randn(n) - D = Diagonal(d) - # setindex! bounds checking - @test_throws BoundsError D[0, 0] = 0 - @test_throws BoundsError D[-1 , -2] = 0 - @test_throws BoundsError D[n, n + 1] = 0 - @test_throws BoundsError D[n + 1, n] = 0 - @test_throws BoundsError D[n + 1, n + 1] = 0 - for i in 1:n, j in 1:n - if i == j - # setindex on! the diagonal - @test ((D[i, j] = i) == i; D[i, j] == i) - else - # setindex! off the diagonal - @test ((D[i, j] = 0) == 0; iszero(D[i, j])) - @test_throws ArgumentError D[i, j] = 1 - end - end - # setindex should return the destination - @test setindex!(D, 1, 1, 1) === D -end - -@testset "Test reverse" begin - D = Diagonal(randn(5)) - @test reverse(D, dims=1) == reverse(Matrix(D), dims=1) - @test reverse(D, dims=2) == reverse(Matrix(D), dims=2) - @test reverse(D)::Diagonal == reverse(Matrix(D)) -end - -@testset "inverse" begin - for d in Any[randn(n), Int[], [1, 2, 3], [1im, 2im, 3im], [1//1, 2//1, 3//1], [1+1im//1, 2//1, 3im//1]] - D = Diagonal(d) - @test inv(D) ≈ inv(Array(D)) - end - @test_throws SingularException inv(Diagonal(zeros(n))) - @test_throws SingularException inv(Diagonal([0, 1, 2])) - @test_throws SingularException inv(Diagonal([0im, 1im, 2im])) -end - -@testset "pseudoinverse" begin - for d in Any[randn(n), zeros(n), Int[], [0, 2, 0.003], [0im, 1+2im, 0.003im], [0//1, 2//1, 3//100], [0//1, 1//1+2im, 3im//100]] - D = Diagonal(d) - @test pinv(D) ≈ pinv(Array(D)) - @test pinv(D, 1.0e-2) ≈ pinv(Array(D), 1.0e-2) - end -end - -# allow construct from range -@test all(Diagonal(range(1, stop=3, length=3)) .== Diagonal([1.0,2.0,3.0])) - -# Issue 12803 -for t in (Float32, Float64, Int, ComplexF64, Rational{Int}) - @test Diagonal(Matrix{t}[fill(t(1), 2, 2), fill(t(1), 3, 3)])[2,1] == zeros(t, 3, 2) -end - -# Issue 15401 -@test Matrix(1.0I, 5, 5) \ Diagonal(fill(1.,5)) == Matrix(I, 5, 5) - -@testset "Triangular and Diagonal" begin - function _test_matrix(type) - if type == Int - return rand(1:9, 5, 5) - else - return randn(type, 5, 5) - end - end - types = (Float64, Int, ComplexF64) - for ta in types - D = Diagonal(_test_matrix(ta)) - for tb in types - B = _test_matrix(tb) - Tmats = (LowerTriangular(B), UnitLowerTriangular(B), UpperTriangular(B), UnitUpperTriangular(B)) - restypes = (LowerTriangular, LowerTriangular, UpperTriangular, UpperTriangular) - for (T, rtype) in zip(Tmats, restypes) - adjtype = (rtype == LowerTriangular) ? UpperTriangular : LowerTriangular - - # Triangular * Diagonal - R = T * D - @test R ≈ Array(T) * Array(D) - @test isa(R, rtype) - - # Diagonal * Triangular - R = D * T - @test R ≈ Array(D) * Array(T) - @test isa(R, rtype) - - # Adjoint of Triangular * Diagonal - R = T' * D - @test R ≈ Array(T)' * Array(D) - @test isa(R, adjtype) - - # Diagonal * Adjoint of Triangular - R = D * T' - @test R ≈ Array(D) * Array(T)' - @test isa(R, adjtype) - - # Transpose of Triangular * Diagonal - R = transpose(T) * D - @test R ≈ transpose(Array(T)) * Array(D) - @test isa(R, adjtype) - - # Diagonal * Transpose of Triangular - R = D * transpose(T) - @test R ≈ Array(D) * transpose(Array(T)) - @test isa(R, adjtype) - end - end - end -end - -let D1 = Diagonal(rand(5)), D2 = Diagonal(rand(5)) - @test LinearAlgebra.rmul!(copy(D1),D2) == D1*D2 - @test LinearAlgebra.lmul!(D1,copy(D2)) == D1*D2 - @test LinearAlgebra.rmul!(copy(D1),transpose(D2)) == D1*transpose(D2) - @test LinearAlgebra.lmul!(transpose(D1),copy(D2)) == transpose(D1)*D2 - @test LinearAlgebra.rmul!(copy(D1),adjoint(D2)) == D1*adjoint(D2) - @test LinearAlgebra.lmul!(adjoint(D1),copy(D2)) == adjoint(D1)*D2 -end - -@testset "multiplication of a Diagonal with a Matrix" begin - A = collect(reshape(1:8, 4, 2)); - B = BigFloat.(A); - DL = Diagonal(collect(axes(A, 1))); - DR = Diagonal(Float16.(collect(axes(A, 2)))); - - @test DL * A == collect(DL) * A - @test A * DR == A * collect(DR) - @test DL * B == collect(DL) * B - @test B * DR == B * collect(DR) - - A = reshape([ones(2,2), ones(2,2)*2, ones(2,2)*3, ones(2,2)*4], 2, 2) - Ac = collect(A) - D = Diagonal([collect(reshape(1:4, 2, 2)), collect(reshape(5:8, 2, 2))]) - Dc = collect(D) - @test A * D == Ac * Dc - @test D * A == Dc * Ac - @test D * D == Dc * Dc - - AS = similar(A) - mul!(AS, A, D, true, false) - @test AS == A * D - - D2 = similar(D) - mul!(D2, D, D) - @test D2 == D * D - - copyto!(D2, D) - lmul!(D, D2) - @test D2 == D * D - copyto!(D2, D) - rmul!(D2, D) - @test D2 == D * D -end - -@testset "multiplication of 2 Diagonal and a Matrix (#46400)" begin - A = randn(10, 10) - D = Diagonal(randn(10)) - D2 = Diagonal(randn(10)) - @test D * A * D2 ≈ D * (A * D2) - @test D * A * D2 ≈ (D * A) * D2 - @test_throws DimensionMismatch Diagonal(ones(9)) * A * D2 - @test_throws DimensionMismatch D * A * Diagonal(ones(9)) -end - -@testset "multiplication of QR Q-factor and Diagonal (#16615 spot test)" begin - D = Diagonal(randn(5)) - Q = qr(randn(5, 5)).Q - @test D * Q' == Array(D) * Q' - Q = qr(randn(5, 5), ColumnNorm()).Q - @test_throws ArgumentError lmul!(Q, D) -end - -@testset "block diagonal matrices" begin - D = Diagonal([[1 2; 3 4], [1 2; 3 4]]) - Dherm = Diagonal([[1 1+im; 1-im 1], [1 1+im; 1-im 1]]) - Dsym = Diagonal([[1 1+im; 1+im 1], [1 1+im; 1+im 1]]) - @test adjoint(D) == Diagonal([[1 3; 2 4], [1 3; 2 4]]) - @test transpose(D) == Diagonal([[1 3; 2 4], [1 3; 2 4]]) - @test adjoint(Dherm) == Dherm - @test transpose(Dherm) == Diagonal([[1 1-im; 1+im 1], [1 1-im; 1+im 1]]) - @test adjoint(Dsym) == Diagonal([[1 1-im; 1-im 1], [1 1-im; 1-im 1]]) - @test transpose(Dsym) == Dsym - @test diag(D, 0) == diag(D) == [[1 2; 3 4], [1 2; 3 4]] - @test diag(D, 1) == diag(D, -1) == [zeros(Int,2,2)] - @test diag(D, 2) == diag(D, -2) == [] - - v = [[1, 2], [3, 4]] - @test Dherm' * v == Dherm * v - @test transpose(D) * v == [[7, 10], [15, 22]] - - @test issymmetric(D) == false - @test issymmetric(Dherm) == false - @test issymmetric(Dsym) == true - - @test ishermitian(D) == false - @test ishermitian(Dherm) == true - @test ishermitian(Dsym) == false - - @test exp(D) == Diagonal([exp([1 2; 3 4]), exp([1 2; 3 4])]) - @test cis(D) == Diagonal([cis([1 2; 3 4]), cis([1 2; 3 4])]) - @test log(D) == Diagonal([log([1 2; 3 4]), log([1 2; 3 4])]) - @test sqrt(D) == Diagonal([sqrt([1 2; 3 4]), sqrt([1 2; 3 4])]) - - @test tr(D) == 10 - @test det(D) == 4 - - M = [1 2; 3 4] - for n in 0:1 - D = Diagonal(fill(M, n)) - @test D == Matrix{eltype(D)}(D) - end - - S = SizedArray{(2,3)}(reshape([1:6;],2,3)) - D = Diagonal(fill(S,3)) - @test D * fill(S,2,3)' == fill(S * S', 3, 2) - @test fill(S,3,2)' * D == fill(S' * S, 2, 3) - - @testset "indexing with non-standard-axes" begin - s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - D = Diagonal(fill(s,3)) - @test @inferred(D[1,2]) isa typeof(s) - @test all(iszero, D[1,2]) - end - - @testset "mul!" begin - D1 = Diagonal(fill(ones(2,3), 2)) - D2 = Diagonal(fill(ones(3,2), 2)) - C = similar(D1, size(D1)) - mul!(C, D1, D2) - @test all(x -> size(x) == (2,2), C) - @test C == D1 * D2 - D = similar(D1) - mul!(D, D1, D2) - @test all(x -> size(x) == (2,2), D) - @test D == D1 * D2 - end -end - -@testset "Eigensystem for block diagonal (issue #30681)" begin - I2 = Matrix(I, 2,2) - D = Diagonal([2.0*I2, 3.0*I2]) - eigD = eigen(D) - evals = [ 2.0, 2.0, 3.0, 3.0 ] - evecs = [ [[ 1.0, 0.0 ]] [[ 0.0, 1.0 ]] [[ 0.0, 0.0 ]] [[ 0.0, 0.0 ]]; - [[ 0.0, 0.0 ]] [[ 0.0, 0.0 ]] [[ 1.0, 0.0 ]] [[ 0.0, 1.0 ]] ] - @test eigD.values == evals - @test eigD.vectors == evecs - @test D * eigD.vectors ≈ eigD.vectors * Diagonal(eigD.values) - - I3 = Matrix(I, 3,3) - D = Diagonal([[0.0 -1.0; 1.0 0.0], 2.0*I3]) - eigD = eigen(D) - evals = [ -1.0im, 1.0im, 2.0, 2.0, 2.0 ] - evecs = [ [[ 1/sqrt(2)+0im, 1/sqrt(2)*im ]] [[ 1/sqrt(2)+0im, -1/sqrt(2)*im ]] [[ 0.0, 0.0 ]] [[ 0.0, 0.0 ]] [[ 0.0, 0.0]]; - [[ 0.0, 0.0, 0.0 ]] [[ 0.0, 0.0, 0.0 ]] [[ 1.0, 0.0, 0.0 ]] [[ 0.0, 1.0, 0.0 ]] [[ 0.0, 0.0, 1.0]] ] - @test eigD.values == evals - @test eigD.vectors ≈ evecs - @test D * eigD.vectors ≈ eigD.vectors * Diagonal(eigD.values) -end - -@testset "linear solve for block diagonal matrices" begin - D = Diagonal([rand(2,2) for _ in 1:5]) - b = [rand(2,2) for _ in 1:5] - B = [rand(2,2) for _ in 1:5, _ in 1:5] - @test ldiv!(D, copy(b)) ≈ Diagonal(inv.(D.diag)) * b - @test ldiv!(D, copy(B)) ≈ Diagonal(inv.(D.diag)) * B - @test rdiv!(copy(B), D) ≈ B * Diagonal(inv.(D.diag)) -end - -@testset "multiplication/division with Symmetric/Hermitian" begin - for T in (Float64, ComplexF64) - D = Diagonal(randn(T, n)) - A = randn(T, n, n); A = A'A - S = Symmetric(A) - H = Hermitian(A) - for (transform1, transform2) in ((identity, identity), - (identity, adjoint ), (adjoint, identity ), (adjoint, adjoint ), - (identity, transpose), (transpose, identity ), (transpose, transpose) ) - @test *(transform1(D), transform2(S)) ≈ *(transform1(Matrix(D)), transform2(Matrix(S))) - @test *(transform1(D), transform2(H)) ≈ *(transform1(Matrix(D)), transform2(Matrix(H))) - @test *(transform1(S), transform2(D)) ≈ *(transform1(Matrix(S)), transform2(Matrix(D))) - @test *(transform1(S), transform2(H)) ≈ *(transform1(Matrix(S)), transform2(Matrix(H))) - @test (transform1(H)/D) * D ≈ transform1(H) - @test (transform1(S)/D) * D ≈ transform1(S) - @test D * (D\transform2(H)) ≈ transform2(H) - @test D * (D\transform2(S)) ≈ transform2(S) - end - end -end - -@testset "multiplication of transposes of Diagonal (#22428)" begin - for T in (Float64, ComplexF64) - D = Diagonal(randn(T, 5, 5)) - B = Diagonal(randn(T, 5, 5)) - DD = Diagonal([randn(T, 2, 2), rand(T, 2, 2)]) - BB = Diagonal([randn(T, 2, 2), rand(T, 2, 2)]) - fullDD = copyto!(Matrix{Matrix{T}}(undef, 2, 2), DD) - fullBB = copyto!(Matrix{Matrix{T}}(undef, 2, 2), BB) - for (transform1, transform2) in ((identity, identity), - (identity, adjoint ), (adjoint, identity ), (adjoint, adjoint ), - (identity, transpose), (transpose, identity ), (transpose, transpose)) - @test *(transform1(D), transform2(B))::typeof(D) ≈ *(transform1(Matrix(D)), transform2(Matrix(B))) atol=2 * eps() - @test *(transform1(DD), transform2(BB))::typeof(DD) == *(transform1(fullDD), transform2(fullBB)) - end - M = randn(T, 5, 5) - MM = [randn(T, 2, 2) for _ in 1:2, _ in 1:2] - for transform in (identity, adjoint, transpose) - @test lmul!(transform(D), copy(M)) ≈ *(transform(Matrix(D)), M) - @test rmul!(copy(M), transform(D)) ≈ *(M, transform(Matrix(D))) - @test lmul!(transform(DD), copy(MM)) ≈ *(transform(fullDD), MM) - @test rmul!(copy(MM), transform(DD)) ≈ *(MM, transform(fullDD)) - end - end -end - -@testset "Diagonal of adjoint/transpose vectors (#23649)" begin - @test Diagonal(adjoint([1, 2, 3])) == Diagonal([1 2 3]) - @test Diagonal(transpose([1, 2, 3])) == Diagonal([1 2 3]) -end - -@testset "Multiplication with adjoint and transpose vectors (#26863)" begin - x = collect(1:2) - xt = transpose(x) - A = reshape([[1 2; 3 4], zeros(Int,2,2), zeros(Int, 2, 2), [5 6; 7 8]], 2, 2) - D = Diagonal(A) - @test x'*D == x'*A == collect(x')*D == collect(x')*A - @test xt*D == xt*A == collect(xt)*D == collect(xt)*A - outadjxD = similar(x'*D); outtrxD = similar(xt*D); - mul!(outadjxD, x', D) - @test outadjxD == x'*D - mul!(outtrxD, xt, D) - @test outtrxD == xt*D - - D1 = Diagonal([[1 2; 3 4]]) - @test D1 * x' == D1 * collect(x') == collect(D1) * collect(x') - @test D1 * xt == D1 * collect(xt) == collect(D1) * collect(xt) - outD1adjx = similar(D1 * x'); outD1trx = similar(D1 * xt); - mul!(outadjxD, D1, x') - @test outadjxD == D1*x' - mul!(outtrxD, D1, xt) - @test outtrxD == D1*xt - - y = [x, x] - yt = transpose(y) - @test y'*D*y == (y'*D)*y == (y'*A)*y - @test yt*D*y == (yt*D)*y == (yt*A)*y - outadjyD = similar(y'*D); outtryD = similar(yt*D); - outadjyD2 = similar(collect(y'*D)); outtryD2 = similar(collect(yt*D)); - mul!(outadjyD, y', D) - mul!(outadjyD2, y', D) - @test outadjyD == outadjyD2 == y'*D - mul!(outtryD, yt, D) - mul!(outtryD2, yt, D) - @test outtryD == outtryD2 == yt*D -end - -@testset "Multiplication of single element Diagonal (#36746, #40726)" begin - @test_throws DimensionMismatch Diagonal(randn(1)) * randn(5) - @test_throws DimensionMismatch Diagonal(randn(1)) * Diagonal(randn(3, 3)) - A = [1 0; 0 2] - v = [3, 4] - @test Diagonal(A) * v == A * v - @test Diagonal(A) * Diagonal(A) == A * A - @test_throws DimensionMismatch [1 0;0 1] * Diagonal([2 3]) # Issue #40726 - @test_throws DimensionMismatch lmul!(Diagonal([1]), [1,2,3]) # nearby -end - -@testset "Multiplication of a Diagonal with an OffsetArray" begin - # Offset indices should throw - D = Diagonal(1:4) - A = OffsetArray(rand(4,4), 2, 2) - @test_throws ArgumentError D * A - @test_throws ArgumentError A * D - @test_throws ArgumentError mul!(similar(A, size(A)), A, D) - @test_throws ArgumentError mul!(similar(A, size(A)), D, A) -end - -@testset "Triangular division by Diagonal #27989" begin - K = 5 - for elty in (Float32, Float64, ComplexF32, ComplexF64) - U = UpperTriangular(randn(elty, K, K)) - L = LowerTriangular(randn(elty, K, K)) - D = Diagonal(randn(elty, K)) - @test (U / D)::UpperTriangular{elty} == UpperTriangular(Matrix(U) / Matrix(D)) - @test (L / D)::LowerTriangular{elty} == LowerTriangular(Matrix(L) / Matrix(D)) - @test (D \ U)::UpperTriangular{elty} == UpperTriangular(Matrix(D) \ Matrix(U)) - @test (D \ L)::LowerTriangular{elty} == LowerTriangular(Matrix(D) \ Matrix(L)) - end -end - -@testset "(Sym)Tridiagonal division by Diagonal" begin - for K in (5, 1), elty in (Float64, ComplexF32), overlength in (1, 0) - S = SymTridiagonal(randn(elty, K), randn(elty, K-overlength)) - T = Tridiagonal(randn(elty, K-1), randn(elty, K), randn(elty, K-1)) - D = Diagonal(randn(elty, K)) - D0 = Diagonal(zeros(elty, K)) - @test (D \ S)::Tridiagonal{elty} == Tridiagonal(Matrix(D) \ Matrix(S)) - @test (D \ T)::Tridiagonal{elty} == Tridiagonal(Matrix(D) \ Matrix(T)) - @test (S / D)::Tridiagonal{elty} == Tridiagonal(Matrix(S) / Matrix(D)) - @test (T / D)::Tridiagonal{elty} == Tridiagonal(Matrix(T) / Matrix(D)) - @test_throws SingularException D0 \ S - @test_throws SingularException D0 \ T - @test_throws SingularException S / D0 - @test_throws SingularException T / D0 - end - # 0-length case - S = SymTridiagonal(Float64[], Float64[]) - T = Tridiagonal(Float64[], Float64[], Float64[]) - D = Diagonal(Float64[]) - @test (D \ S)::Tridiagonal{Float64} == T - @test (D \ T)::Tridiagonal{Float64} == T - @test (S / D)::Tridiagonal{Float64} == T - @test (T / D)::Tridiagonal{Float64} == T - # matrix eltype case - K = 5 - for elty in (Float64, ComplexF32), overlength in (1, 0) - S = SymTridiagonal([rand(elty, 2, 2) for _ in 1:K], [rand(elty, 2, 2) for _ in 1:K-overlength]) - T = Tridiagonal([rand(elty, 2, 2) for _ in 1:K-1], [rand(elty, 2, 2) for _ in 1:K], [rand(elty, 2, 2) for _ in 1:K-1]) - D = Diagonal(randn(elty, K)) - SM = fill(zeros(elty, 2, 2), K, K) - TM = copy(SM) - SM[1,1] = S[1,1]; TM[1,1] = T[1,1] - for j in 2:K - SM[j,j-1] = S[j,j-1]; SM[j,j] = S[j,j]; SM[j-1,j] = S[j-1,j] - TM[j,j-1] = T[j,j-1]; TM[j,j] = T[j,j]; TM[j-1,j] = T[j-1,j] - end - for (M, Mm) in ((S, SM), (T, TM)) - DS = D \ M - @test DS isa Tridiagonal - DM = D \ Mm - for i in -1:1; @test diag(DS, i) ≈ diag(DM, i) end - DS = M / D - @test DS isa Tridiagonal - DM = Mm / D - for i in -1:1; @test diag(DS, i) ≈ diag(DM, i) end - end - end - # eltype promotion case - S = SymTridiagonal(rand(-20:20, K), rand(-20:20, K-1)) - T = Tridiagonal(rand(-20:20, K-1), rand(-20:20, K), rand(-20:20, K-1)) - D = Diagonal(rand(1:20, K)) - @test (D \ S)::Tridiagonal{Float64} == Tridiagonal(Matrix(D) \ Matrix(S)) - @test (D \ T)::Tridiagonal{Float64} == Tridiagonal(Matrix(D) \ Matrix(T)) - @test (S / D)::Tridiagonal{Float64} == Tridiagonal(Matrix(S) / Matrix(D)) - @test (T / D)::Tridiagonal{Float64} == Tridiagonal(Matrix(T) / Matrix(D)) -end - -@testset "eigenvalue sorting" begin - D = Diagonal([0.4, 0.2, -1.3]) - @test eigvals(D) == eigen(D).values == [0.4, 0.2, -1.3] # not sorted by default - @test eigvals(Matrix(D)) == eigen(Matrix(D)).values == [-1.3, 0.2, 0.4] # sorted even if diagonal special case is detected - E = eigen(D, sortby=abs) # sortby keyword supported for eigen(::Diagonal) - @test E.values == [0.2, 0.4, -1.3] - @test E.vectors == [0 1 0; 1 0 0; 0 0 1] -end - -@testset "sum, mapreduce" begin - D = Diagonal([1,2,3]) - Ddense = Matrix(D) - @test sum(D) == 6 - @test_throws ArgumentError sum(D, dims=0) - @test sum(D, dims=1) == sum(Ddense, dims=1) - @test sum(D, dims=2) == sum(Ddense, dims=2) - @test sum(D, dims=3) == sum(Ddense, dims=3) - @test typeof(sum(D, dims=1)) == typeof(sum(Ddense, dims=1)) - @test mapreduce(one, min, D, dims=1) == mapreduce(one, min, Ddense, dims=1) - @test mapreduce(one, min, D, dims=2) == mapreduce(one, min, Ddense, dims=2) - @test mapreduce(one, min, D, dims=3) == mapreduce(one, min, Ddense, dims=3) - @test typeof(mapreduce(one, min, D, dims=1)) == typeof(mapreduce(one, min, Ddense, dims=1)) - @test mapreduce(zero, max, D, dims=1) == mapreduce(zero, max, Ddense, dims=1) - @test mapreduce(zero, max, D, dims=2) == mapreduce(zero, max, Ddense, dims=2) - @test mapreduce(zero, max, D, dims=3) == mapreduce(zero, max, Ddense, dims=3) - @test typeof(mapreduce(zero, max, D, dims=1)) == typeof(mapreduce(zero, max, Ddense, dims=1)) - - D = Diagonal(Int[]) - Ddense = Matrix(D) - @test sum(D) == 0 - @test_throws ArgumentError sum(D, dims=0) - @test sum(D, dims=1) == sum(Ddense, dims=1) - @test sum(D, dims=2) == sum(Ddense, dims=2) - @test sum(D, dims=3) == sum(Ddense, dims=3) - @test typeof(sum(D, dims=1)) == typeof(sum(Ddense, dims=1)) - - D = Diagonal(Int[2]) - Ddense = Matrix(D) - @test sum(D) == 2 - @test_throws ArgumentError sum(D, dims=0) - @test sum(D, dims=1) == sum(Ddense, dims=1) - @test sum(D, dims=2) == sum(Ddense, dims=2) - @test sum(D, dims=3) == sum(Ddense, dims=3) - @test typeof(sum(D, dims=1)) == typeof(sum(Ddense, dims=1)) -end - -@testset "logabsdet for generic eltype" begin - d = Any[1, -2.0, -3.0] - D = Diagonal(d) - d1, s1 = logabsdet(D) - @test d1 ≈ sum(log ∘ abs, d) - @test s1 == prod(sign, d) -end - -@testset "Empty (#35424) & size checks (#47060)" begin - @test zeros(0)'*Diagonal(zeros(0))*zeros(0) === 0.0 - @test transpose(zeros(0))*Diagonal(zeros(Complex{Int}, 0))*zeros(0) === 0.0 + 0.0im - @test dot(zeros(Int32, 0), Diagonal(zeros(Int, 0)), zeros(Int16, 0)) === 0 - @test_throws DimensionMismatch zeros(2)' * Diagonal(zeros(2)) * zeros(3) - @test_throws DimensionMismatch zeros(3)' * Diagonal(zeros(2)) * zeros(2) - @test_throws DimensionMismatch dot(zeros(2), Diagonal(zeros(2)), zeros(3)) - @test_throws DimensionMismatch dot(zeros(3), Diagonal(zeros(2)), zeros(2)) -end - -@testset "Diagonal(undef)" begin - d = Diagonal{Float32}(undef, 2) - @test length(d.diag) == 2 -end - -@testset "permutedims (#39447)" begin - for D in (Diagonal(zeros(5)), Diagonal(zeros(5) .+ 1im), Diagonal([[1,2],[3,4]])) - @test permutedims(D) === permutedims(D,(1,2)) === permutedims(D,(2,1)) === D - @test_throws ArgumentError permutedims(D,(1,3)) - end -end - -@testset "Inner product" begin - A = Diagonal(rand(10) .+ im) - B = Diagonal(rand(10) .+ im) - @test dot(A, B) ≈ dot(Matrix(A), B) - @test dot(A, B) ≈ dot(A, Matrix(B)) - @test dot(A, B) ≈ dot(Matrix(A), Matrix(B)) - @test dot(A, B) ≈ conj(dot(B, A)) -end - -@testset "eltype relaxation(#41015)" begin - A = rand(3,3) - for trans in (identity, adjoint, transpose) - @test ldiv!(trans(I(3)), A) == A - @test rdiv!(A, trans(I(3))) == A - 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 - -@testset "Conversion to AbstractArray" begin - # tests corresponding to #34995 - d = ImmutableArray([1, 2, 3, 4]) - D = Diagonal(d) - - @test convert(AbstractArray{Float64}, D)::Diagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == D - @test convert(AbstractMatrix{Float64}, D)::Diagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == D -end - -@testset "divisions functionality" for elty in (Int, Float64, ComplexF64) - B = Diagonal(rand(elty,5,5)) - x = rand(elty) - @test \(x, B) == /(B, x) -end - -@testset "promotion" begin - for (v1, v2) in (([true], [1]), ([zeros(2,2)], [zeros(Int, 2,2)])) - T = promote_type(eltype(v1), eltype(v2)) - V = promote_type(typeof(v1), typeof(v2)) - d1 = Diagonal(v1) - d2 = Diagonal(v2) - v = [d1, d2] - @test (@inferred eltype(v)) == Diagonal{T, V} - end - # test for a type for which promote_type doesn't lead to a concrete eltype - struct MyArrayWrapper{T,N,A<:AbstractArray{T,N}} <: AbstractArray{T,N} - a :: A - end - Base.size(M::MyArrayWrapper) = size(M.a) - Base.axes(M::MyArrayWrapper) = axes(M.a) - Base.length(M::MyArrayWrapper) = length(M.a) - Base.getindex(M::MyArrayWrapper, i::Int...) = M.a[i...] - Base.setindex!(M::MyArrayWrapper, v, i::Int...) = M.a[i...] = v - d1 = Diagonal(MyArrayWrapper(1:3)) - d2 = Diagonal(MyArrayWrapper(1.0:3.0)) - c = [d1, d2] - @test c[1] == d1 - @test c[2] == d2 -end - -@testset "zero and one" begin - D1 = Diagonal(rand(3)) - @test D1 + zero(D1) == D1 - @test D1 * one(D1) == D1 - @test D1 * oneunit(D1) == D1 - @test oneunit(D1) isa typeof(D1) - D2 = Diagonal([collect(reshape(1:4, 2, 2)), collect(reshape(5:8, 2, 2))]) - @test D2 + zero(D2) == D2 - @test D2 * one(D2) == D2 - @test D2 * oneunit(D2) == D2 - @test oneunit(D2) isa typeof(D2) - D3 = Diagonal([D2, D2]); - @test D3 + zero(D3) == D3 - @test D3 * one(D3) == D3 - @test D3 * oneunit(D3) == D3 - @test oneunit(D3) isa typeof(D3) -end - -@testset "$Tri" for (Tri, UTri) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) - A = randn(4, 4) - TriA = Tri(A) - UTriA = UTri(A) - D = Diagonal(1.0:4.0) - DM = Matrix(D) - DMF = factorize(DM) - outTri = similar(TriA) - out = similar(A) - # 2 args - for fun in (*, rmul!, rdiv!, /) - @test fun(copy(TriA), D)::Tri == fun(Matrix(TriA), D) - @test fun(copy(UTriA), D)::Tri == fun(Matrix(UTriA), D) - end - for fun in (*, lmul!, ldiv!, \) - @test fun(D, copy(TriA))::Tri == fun(D, Matrix(TriA)) - @test fun(D, copy(UTriA))::Tri == fun(D, Matrix(UTriA)) - end - # 3 args - @test outTri === ldiv!(outTri, D, TriA)::Tri == ldiv!(out, D, Matrix(TriA)) - @test outTri === ldiv!(outTri, D, UTriA)::Tri == ldiv!(out, D, Matrix(UTriA)) - @test outTri === mul!(outTri, D, TriA)::Tri == mul!(out, D, Matrix(TriA)) - @test outTri === mul!(outTri, D, UTriA)::Tri == mul!(out, D, Matrix(UTriA)) - @test outTri === mul!(outTri, TriA, D)::Tri == mul!(out, Matrix(TriA), D) - @test outTri === mul!(outTri, UTriA, D)::Tri == mul!(out, Matrix(UTriA), D) - # 5 args - @test outTri === mul!(outTri, D, TriA, 2, 1)::Tri == mul!(out, D, Matrix(TriA), 2, 1) - @test outTri === mul!(outTri, D, UTriA, 2, 1)::Tri == mul!(out, D, Matrix(UTriA), 2, 1) - @test outTri === mul!(outTri, TriA, D, 2, 1)::Tri == mul!(out, Matrix(TriA), D, 2, 1) - @test outTri === mul!(outTri, UTriA, D, 2, 1)::Tri == mul!(out, Matrix(UTriA), D, 2, 1) - - # we may write to a Unit triangular if the diagonal is preserved - ID = Diagonal(ones(size(UTriA,2))) - @test mul!(copy(UTriA), UTriA, ID) == UTriA - @test mul!(copy(UTriA), ID, UTriA) == UTriA - - @testset "partly filled parents" begin - M = Matrix{BigFloat}(undef, 2, 2) - M[1,1] = M[2,2] = 3 - isupper = Tri == UpperTriangular - M[1+!isupper, 1+isupper] = 3 - D = Diagonal(1:2) - T = Tri(M) - TA = Array(T) - @test T * D == TA * D - @test D * T == D * TA - @test mul!(copy(T), T, D, 2, 3) == 2T * D + 3T - @test mul!(copy(T), D, T, 2, 3) == 2D * T + 3T - - U = UTri(M) - UA = Array(U) - @test U * D == UA * D - @test D * U == D * UA - @test mul!(copy(T), U, D, 2, 3) == 2 * UA * D + 3TA - @test mul!(copy(T), D, U, 2, 3) == 2 * D * UA + 3TA - - M2 = Matrix{BigFloat}(undef, 2, 2) - M2[1+!isupper, 1+isupper] = 3 - U = UTri(M2) - UA = Array(U) - @test U * D == UA * D - @test D * U == D * UA - ID = Diagonal(ones(size(U,2))) - @test mul!(copy(U), U, ID) == U - @test mul!(copy(U), ID, U) == U - @test mul!(copy(U), U, ID, 2, -1) == U - @test mul!(copy(U), ID, U, 2, -1) == U - end -end - -struct SMatrix1{T} <: AbstractArray{T,2} - elt::T -end -Base.:(==)(A::SMatrix1, B::SMatrix1) = A.elt == B.elt -Base.zero(::Type{SMatrix1{T}}) where {T} = SMatrix1(zero(T)) -Base.iszero(A::SMatrix1) = iszero(A.elt) -Base.getindex(A::SMatrix1, inds...) = A.elt -Base.size(::SMatrix1) = (1, 1) -@testset "map for Diagonal matrices (#46292)" begin - A = Diagonal([1]) - @test A isa Diagonal{Int,Vector{Int}} - @test 2*A isa Diagonal{Int,Vector{Int}} - @test A.+1 isa Matrix{Int} - # Numeric element types remain diagonal - B = map(SMatrix1, A) - @test B == fill(SMatrix1(1), 1, 1) - @test B isa Diagonal{SMatrix1{Int},Vector{SMatrix1{Int}}} - # Non-numeric element types become dense - C = map(a -> SMatrix1(string(a)), A) - @test C == fill(SMatrix1(string(1)), 1, 1) - @test C isa Matrix{SMatrix1{String}} -end - -@testset "show" begin - @test repr(Diagonal([1,2])) == "Diagonal([1, 2])" # 2-arg show - @test contains(repr(MIME"text/plain"(), Diagonal([1,2])), "⋅ 2") # 3-arg show -end - -@testset "copyto! with UniformScaling" begin - @testset "Fill" begin - for len in (4, InfiniteArrays.Infinity()) - d = FillArrays.Fill(1, len) - D = Diagonal(d) - @test copyto!(D, I) === D - end - end - D = Diagonal(fill(2, 2)) - copyto!(D, I) - @test all(isone, diag(D)) -end - -@testset "diagonal triple multiplication (#49005)" begin - n = 10 - @test *(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n))) isa Diagonal - @test_throws DimensionMismatch (*(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n+1)))) - @test_throws DimensionMismatch (*(Diagonal(ones(n)), Diagonal(1:n+1), Diagonal(ones(n+1)))) - @test_throws DimensionMismatch (*(Diagonal(ones(n+1)), Diagonal(1:n), Diagonal(ones(n)))) - - # currently falls back to two-term * - @test *(Diagonal(ones(n)), Diagonal(1:n), Diagonal(ones(n)), Diagonal(1:n)) isa Diagonal -end - -@testset "triple multiplication with a sandwiched BandedMatrix" begin - D = Diagonal(StepRangeLen(NaN, 0, 4)); - B = Bidiagonal(1:4, 1:3, :U) - C = D * B * D - @test iszero(diag(C, 2)) - # test associativity - C1 = (D * B) * D - C2 = D * (B * D) - @test diag(C,2) == diag(C1,2) == diag(C2,2) -end - -@testset "diagind" begin - D = Diagonal(1:4) - M = Matrix(D) - @testset for k in -4:4 - @test D[diagind(D,k)] == M[diagind(M,k)] - end -end - -@testset "avoid matmul ambiguities with ::MyMatrix * ::AbstractMatrix" begin - A = [i+j for i in 1:2, j in 1:2] - S = SizedArrays.SizedArray{(2,2)}(A) - D = Diagonal([1:2;]) - @test S * D == A * D - @test D * S == D * A - C1, C2 = zeros(2,2), zeros(2,2) - @test mul!(C1, S, D) == mul!(C2, A, D) - @test mul!(C1, S, D, 1, 2) == mul!(C2, A, D, 1 ,2) - @test mul!(C1, D, S) == mul!(C2, D, A) - @test mul!(C1, D, S, 1, 2) == mul!(C2, D, A, 1 ,2) - - v = [i for i in 1:2] - sv = SizedArrays.SizedArray{(2,)}(v) - @test D * sv == D * v - C1, C2 = zeros(2), zeros(2) - @test mul!(C1, D, sv) == mul!(C2, D, v) - @test mul!(C1, D, sv, 1, 2) == mul!(C2, D, v, 1 ,2) -end - -@testset "copy" begin - @test copy(Diagonal(1:5)) === Diagonal(1:5) -end - -@testset "kron! for Diagonal" begin - a = Diagonal([2,2]) - b = Diagonal([1,1]) - c = Diagonal([0,0,0,0]) - kron!(c,b,a) - @test c == Diagonal([2,2,2,2]) - c=Diagonal(Vector{Float64}(undef, 4)) - kron!(c,a,b) - @test c == Diagonal([2,2,2,2]) -end - -@testset "uppertriangular/lowertriangular" begin - D = Diagonal([1,2]) - @test LinearAlgebra.uppertriangular(D) === D - @test LinearAlgebra.lowertriangular(D) === D -end - -@testset "mul/div with an adjoint vector" begin - A = [1.0;;] - x = [1.0] - yadj = Diagonal(A) \ x' - @test typeof(yadj) == typeof(x') - @test yadj == x' - yadj = Diagonal(A) * x' - @test typeof(yadj) == typeof(x') - @test yadj == x' -end - -@testset "Matrix conversion for non-numeric" begin - D = Diagonal(fill(Diagonal([1,3]), 2)) - M = Matrix{eltype(D)}(D) - @test M isa Matrix{eltype(D)} - @test M == D -end - -@testset "rmul!/lmul! with banded matrices" begin - @testset "$(nameof(typeof(B)))" for B in ( - Bidiagonal(rand(4), rand(3), :L), - Tridiagonal(rand(3), rand(4), rand(3)) - ) - BA = Array(B) - D = Diagonal(rand(size(B,1))) - DA = Array(D) - @test rmul!(copy(B), D) ≈ B * D ≈ BA * DA - @test lmul!(D, copy(B)) ≈ D * B ≈ DA * BA - end -end - -@testset "rmul!/lmul! with numbers" begin - D = Diagonal(rand(4)) - @test rmul!(copy(D), 0.2) ≈ rmul!(Array(D), 0.2) - @test lmul!(0.2, copy(D)) ≈ lmul!(0.2, Array(D)) - @test_throws ArgumentError rmul!(D, NaN) - @test_throws ArgumentError lmul!(NaN, D) - D = Diagonal(rand(1)) - @test all(isnan, rmul!(copy(D), NaN)) - @test all(isnan, lmul!(NaN, copy(D))) -end - -@testset "+/- with block Symmetric/Hermitian" begin - for p in ([1 2; 3 4], [1 2+im; 2-im 4+2im]) - m = SizedArrays.SizedArray{(2,2)}(p) - D = Diagonal(fill(m, 2)) - for T in (Symmetric, Hermitian) - S = T(fill(m, 2, 2)) - @test D + S == Array(D) + Array(S) - @test S + D == Array(S) + Array(D) - end - end -end - -@testset "bounds-check with CartesianIndex ranges" begin - D = Diagonal(1:typemax(Int)) - @test checkbounds(Bool, D, diagind(D, IndexCartesian())) -end - -@testset "zeros in kron with block matrices" begin - D = Diagonal(1:4) - B = reshape([ones(2,2), ones(3,2), ones(2,3), ones(3,3)], 2, 2) - @test kron(D, B) == kron(Array(D), B) - @test kron(B, D) == kron(B, Array(D)) - D2 = Diagonal([ones(2,2), ones(3,3)]) - @test kron(D, D2) == kron(D, Array{eltype(D2)}(D2)) - @test kron(D2, D) == kron(Array{eltype(D2)}(D2), D) -end - -end # module TestDiagonal diff --git a/stdlib/LinearAlgebra/test/eigen.jl b/stdlib/LinearAlgebra/test/eigen.jl deleted file mode 100644 index a82c745436009..0000000000000 --- a/stdlib/LinearAlgebra/test/eigen.jl +++ /dev/null @@ -1,282 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestEigen - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted, UtiAUi! - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(12343219) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int) - aa = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - asym = aa' + aa # symmetric indefinite - apd = aa' * aa # symmetric positive-definite - for (a, asym, apd) in ((aa, asym, apd), - (view(aa, 1:n, 1:n), - view(asym, 1:n, 1:n), - view(apd, 1:n, 1:n))) - ε = εa = eps(abs(float(one(eltya)))) - - α = rand(eltya) - β = rand(eltya) - eab = eigen(α,β) - @test eab.values == eigvals(fill(α,1,1),fill(β,1,1)) - @test eab.vectors == eigvecs(fill(α,1,1),fill(β,1,1)) - - @testset "non-symmetric eigen decomposition" begin - d, v = eigen(a) - for i in 1:size(a,2) - @test a*v[:,i] ≈ d[i]*v[:,i] - end - f = eigen(a) - @test det(a) ≈ det(f) - @test inv(a) ≈ inv(f) - @test isposdef(a) == isposdef(f) - @test eigvals(f) === f.values - @test eigvecs(f) === f.vectors - @test Array(f) ≈ a - - for T in (Tridiagonal(a), Hermitian(Tridiagonal(a), :U), Hermitian(Tridiagonal(a), :L)) - f = eigen(T) - d, v = f - for i in 1:size(a,2) - @test T*v[:,i] ≈ d[i]*v[:,i] - end - @test eigvals(T) ≈ d - @test det(T) ≈ det(f) - @test inv(T) ≈ inv(f) - end - - num_fact = eigen(one(eltya)) - @test num_fact.values[1] == one(eltya) - h = asym - @test minimum(eigvals(h)) ≈ eigmin(h) - @test maximum(eigvals(h)) ≈ eigmax(h) - @test_throws DomainError eigmin(a - a') - @test_throws DomainError eigmax(a - a') - end - @testset "symmetric generalized eigenproblem" begin - if isa(a, Array) - asym_sg = asym[1:n1, 1:n1] - a_sg = a[:,n1+1:n2] - else - asym_sg = view(asym, 1:n1, 1:n1) - a_sg = view(a, 1:n, n1+1:n2) - end - ASG2 = a_sg'a_sg - f = eigen(asym_sg, ASG2) - @test asym_sg*f.vectors ≈ (ASG2*f.vectors) * Diagonal(f.values) - @test f.values ≈ eigvals(asym_sg, ASG2) - @test prod(f.values) ≈ prod(eigvals(asym_sg/(ASG2))) atol=200ε - @test eigvecs(asym_sg, ASG2) == f.vectors - @test eigvals(f) === f.values - @test eigvecs(f) === f.vectors - @test_throws FieldError f.Z - - d,v = eigen(asym_sg, ASG2) - @test d == f.values - @test v == f.vectors - - # solver for in-place U' \ A / U (#14896) - if !(eltya <: Integer) - for atyp in (eltya <: Real ? (Symmetric, Hermitian) : (Hermitian,)) - for utyp in (UpperTriangular, Diagonal), uplo in (:L, :U) - A = atyp(asym_sg, uplo) - U = utyp(ASG2) - @test UtiAUi!(copy(A), U) ≈ U' \ A / U - end - end - end - - # matrices of different types (#14896) - D = Diagonal(ASG2) - for uplo in (:L, :U) - if eltya <: Real - fs = eigen(Symmetric(asym_sg, uplo), ASG2) - @test fs.values ≈ f.values - @test abs.(fs.vectors) ≈ abs.(f.vectors) # may change sign - gs = eigen(Symmetric(asym_sg, uplo), D) - @test Symmetric(asym_sg, uplo)*gs.vectors ≈ (D*gs.vectors) * Diagonal(gs.values) - end - fh = eigen(Hermitian(asym_sg, uplo), ASG2) - @test fh.values ≈ f.values - @test abs.(fh.vectors) ≈ abs.(f.vectors) # may change sign - gh = eigen(Hermitian(asym_sg, uplo), D) - @test Hermitian(asym_sg, uplo)*gh.vectors ≈ (D*gh.vectors) * Diagonal(gh.values) - gd = eigen(Matrix(Hermitian(ASG2, uplo)), D) - @test Hermitian(ASG2, uplo) * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - gd = eigen(Hermitian(Tridiagonal(ASG2), uplo), D) - @test Hermitian(Tridiagonal(ASG2), uplo) * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - end - gd = eigen(D, D) - @test all(≈(1), gd.values) - @test D * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - gd = eigen(Matrix(D), D) - @test D * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - gd = eigen(D, Matrix(D)) - @test D * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - gd = eigen(Tridiagonal(ASG2), Matrix(D)) - @test Tridiagonal(ASG2) * gd.vectors ≈ D * gd.vectors * Diagonal(gd.values) - end - @testset "Non-symmetric generalized eigenproblem" begin - if isa(a, Array) - a1_nsg = a[1:n1, 1:n1] - a2_nsg = a[n1+1:n2, n1+1:n2] - else - a1_nsg = view(a, 1:n1, 1:n1) - a2_nsg = view(a, n1+1:n2, n1+1:n2) - end - sortfunc = x -> real(x) + imag(x) - f = eigen(a1_nsg, a2_nsg; sortby = sortfunc) - @test a1_nsg*f.vectors ≈ (a2_nsg*f.vectors) * Diagonal(f.values) - @test f.values ≈ eigvals(a1_nsg, a2_nsg; sortby = sortfunc) - @test prod(f.values) ≈ prod(eigvals(a1_nsg/a2_nsg, sortby = sortfunc)) atol=50000ε - @test eigvecs(a1_nsg, a2_nsg; sortby = sortfunc) == f.vectors - @test_throws FieldError f.Z - - g = eigen(a1_nsg, Diagonal(1:n1)) - @test a1_nsg*g.vectors ≈ (Diagonal(1:n1)*g.vectors) * Diagonal(g.values) - - d,v = eigen(a1_nsg, a2_nsg; sortby = sortfunc) - @test d == f.values - @test v == f.vectors - end - end -end - -@testset "eigenvalue computations with NaNs" begin - for eltya in (NaN16, NaN32, NaN) - @test_throws(ArgumentError, eigen(fill(eltya, 1, 1))) - @test_throws(ArgumentError, eigen(fill(eltya, 2, 2))) - test_matrix = rand(typeof(eltya),3,3) - test_matrix[1,3] = eltya - @test_throws(ArgumentError, eigen(test_matrix)) - @test_throws(ArgumentError, eigvals(test_matrix)) - @test_throws(ArgumentError, eigvecs(test_matrix)) - @test_throws(ArgumentError, eigen(Symmetric(test_matrix))) - @test_throws(ArgumentError, eigvals(Symmetric(test_matrix))) - @test_throws(ArgumentError, eigvecs(Symmetric(test_matrix))) - @test_throws(ArgumentError, eigen(Hermitian(test_matrix))) - @test_throws(ArgumentError, eigvals(Hermitian(test_matrix))) - @test_throws(ArgumentError, eigvecs(Hermitian(test_matrix))) - @test_throws(ArgumentError, eigen(Hermitian(complex.(test_matrix)))) - @test_throws(ArgumentError, eigvals(Hermitian(complex.(test_matrix)))) - @test_throws(ArgumentError, eigvecs(Hermitian(complex.(test_matrix)))) - @test eigen(Symmetric(test_matrix, :L)) isa Eigen - @test eigen(Hermitian(test_matrix, :L)) isa Eigen - end -end - -# test a matrix larger than 140-by-140 for #14174 -let aa = rand(200, 200) - for a in (aa, view(aa, 1:n, 1:n)) - f = eigen(a) - @test a ≈ f.vectors * Diagonal(f.values) / f.vectors - end -end - -@testset "rational promotion: issue #24935" begin - A = [1//2 0//1; 0//1 2//3] - for λ in (eigvals(A), @inferred(eigvals(Symmetric(A)))) - @test λ isa Vector{Float64} - @test λ ≈ [0.5, 2/3] - end -end - -@testset "text/plain (REPL) printing of Eigen and GeneralizedEigen" begin - A, B = randn(5,5), randn(5,5) - e = eigen(A) - ge = eigen(A, B) - valsstring = sprint((t, s) -> show(t, "text/plain", s), e.values) - vecsstring = sprint((t, s) -> show(t, "text/plain", s), e.vectors) - factstring = sprint((t, s) -> show(t, "text/plain", s), e) - @test factstring == "$(summary(e))\nvalues:\n$valsstring\nvectors:\n$vecsstring" -end - -@testset "eigen of an Adjoint" begin - Random.seed!(4) - A = randn(3,3) - @test eigvals(A') == eigvals(copy(A')) - @test eigen(A') == eigen(copy(A')) - @test eigmin(A') == eigmin(copy(A')) - @test eigmax(A') == eigmax(copy(A')) -end - -@testset "equality of eigen factorizations" begin - A1 = Float32[1 0; 0 2] - A2 = Float64[1 0; 0 2] - EA1 = eigen(A1) - EA2 = eigen(A2) - @test EA1 == EA2 - @test hash(EA1) == hash(EA2) - @test isequal(EA1, EA2) - - # trivial RHS to ensure that values match exactly - B1 = Float32[1 0; 0 1] - B2 = Float64[1 0; 0 1] - EA1B1 = eigen(A1, B1) - EA2B2 = eigen(A2, B2) - @test EA1B1 == EA2B2 - @test hash(EA1B1) == hash(EA2B2) - @test isequal(EA1B1, EA2B2) -end - -@testset "Float16" begin - A = Float16[4. 12. -16.; 12. 37. -43.; -16. -43. 98.] - B = eigen(A) - B32 = eigen(Float32.(A)) - C = Float16[3 -2; 4 -1] - D = eigen(C) - D32 = eigen(Float32.(C)) - F = eigen(complex(C)) - F32 = eigen(complex(Float32.(C))) - @test B isa Eigen{Float16, Float16, Matrix{Float16}, Vector{Float16}} - @test B.values isa Vector{Float16} - @test B.vectors isa Matrix{Float16} - @test B.values ≈ B32.values - @test B.vectors ≈ B32.vectors - @test D isa Eigen{ComplexF16, ComplexF16, Matrix{ComplexF16}, Vector{ComplexF16}} - @test D.values isa Vector{ComplexF16} - @test D.vectors isa Matrix{ComplexF16} - @test D.values ≈ D32.values - @test D.vectors ≈ D32.vectors - @test F isa Eigen{ComplexF16, ComplexF16, Matrix{ComplexF16}, Vector{ComplexF16}} - @test F.values isa Vector{ComplexF16} - @test F.vectors isa Matrix{ComplexF16} - @test F.values ≈ F32.values - @test F.vectors ≈ F32.vectors - - for T in (Float16, ComplexF16) - D = Diagonal(T[1,2,4]) - A = Array(D) - B = eigen(A) - @test B isa Eigen{Float16, Float16, Matrix{Float16}, Vector{Float16}} - @test B.values isa Vector{Float16} - @test B.vectors isa Matrix{Float16} - end - D = Diagonal(ComplexF16[im,2,4]) - A = Array(D) - B = eigen(A) - @test B isa Eigen{Float16, ComplexF16, Matrix{Float16}, Vector{ComplexF16}} - @test B.values isa Vector{ComplexF16} - @test B.vectors isa Matrix{Float16} -end - -@testset "complex eigen inference (#52289)" begin - A = ComplexF64[1.0 0.0; 0.0 8.0] - TC = Eigen{ComplexF64, ComplexF64, Matrix{ComplexF64}, Vector{ComplexF64}} - TR = Eigen{ComplexF64, Float64, Matrix{ComplexF64}, Vector{Float64}} - λ, v = @inferred Union{TR,TC} eigen(A) - @test λ == [1.0, 8.0] -end - -end # module TestEigen diff --git a/stdlib/LinearAlgebra/test/factorization.jl b/stdlib/LinearAlgebra/test/factorization.jl deleted file mode 100644 index f80c5197836a1..0000000000000 --- a/stdlib/LinearAlgebra/test/factorization.jl +++ /dev/null @@ -1,94 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestFactorization -using Test, LinearAlgebra - -@testset "equality for factorizations - $f" for f in Any[ - bunchkaufman, - cholesky, - x -> cholesky(x, RowMaximum()), - eigen, - hessenberg, - lq, - lu, - qr, - x -> qr(x, ColumnNorm()), - svd, - schur, -] - A = randn(3, 3) - A = A * A' # ensure A is pos. def. and symmetric - F, G = f(A), f(A) - - @test F == G - @test isequal(F, G) - @test hash(F) == hash(G) - - f === hessenberg && continue - - # change all arrays in F to have eltype Float32 - F = typeof(F).name.wrapper(Base.mapany(1:nfields(F)) do i - x = getfield(F, i) - return x isa AbstractArray{Float64} ? Float32.(x) : x - end...) - # round all arrays in G to the nearest Float64 representable as Float32 - G = typeof(G).name.wrapper(Base.mapany(1:nfields(G)) do i - x = getfield(G, i) - return x isa AbstractArray{Float64} ? Float64.(Float32.(x)) : x - end...) - - @test F == G broken=!(f === eigen || f === qr || f == bunchkaufman || f == cholesky || F isa CholeskyPivoted) - @test isequal(F, G) broken=!(f === eigen || f === qr || f == bunchkaufman || f == cholesky || F isa CholeskyPivoted) - @test hash(F) == hash(G) -end - -@testset "size for factorizations - $f" for f in Any[ - bunchkaufman, - cholesky, - x -> cholesky(x, RowMaximum()), - hessenberg, - lq, - lu, - qr, - x -> qr(x, ColumnNorm()), - svd, -] - A = randn(3, 3) - A = A * A' # ensure A is pos. def. and symmetric - F = f(A) - @test size(F) == size(A) - @test size(F') == size(A') -end - -@testset "size for transpose factorizations - $f" for f in Any[ - bunchkaufman, - cholesky, - x -> cholesky(x, RowMaximum()), - hessenberg, - lq, - lu, - svd, -] - A = randn(3, 3) - A = A * A' # ensure A is pos. def. and symmetric - F = f(A) - @test size(F) == size(A) - @test size(transpose(F)) == size(transpose(A)) -end - -@testset "equality of QRCompactWY" begin - A = rand(100, 100) - F, G = qr(A), qr(A) - - @test F == G - @test isequal(F, G) - @test hash(F) == hash(G) - - G.T[28, 100] = 42 - - @test F != G - @test !isequal(F, G) - @test hash(F) != hash(G) -end - -end diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl deleted file mode 100644 index 6d11ec824e538..0000000000000 --- a/stdlib/LinearAlgebra/test/generic.jl +++ /dev/null @@ -1,840 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestGeneric - -using Test, LinearAlgebra, Random -using Test: GenericArray -using LinearAlgebra: isbanded - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") - -isdefined(Main, :Quaternions) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Quaternions.jl")) -using .Main.Quaternions - -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -isdefined(Main, :DualNumbers) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "DualNumbers.jl")) -using .Main.DualNumbers - -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -using .Main.FillArrays - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -Random.seed!(123) - -n = 5 # should be odd - -@testset for elty in (Int, Rational{BigInt}, Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) - # In the long run, these tests should step through Strang's - # axiomatic definition of determinants. - # If all axioms are satisfied and all the composition rules work, - # all determinants will be correct except for floating point errors. - if elty != Rational{BigInt} - @testset "det(A::Matrix)" begin - # The determinant of the identity matrix should always be 1. - for i = 1:10 - A = Matrix{elty}(I, i, i) - @test det(A) ≈ one(elty) - end - - # The determinant of a Householder reflection matrix should always be -1. - for i = 1:10 - A = Matrix{elty}(I, 10, 10) - A[i, i] = -one(elty) - @test det(A) ≈ -one(elty) - end - - # The determinant of a rotation matrix should always be 1. - if elty != Int - for theta = convert(Vector{elty}, pi ./ [1:4;]) - R = [cos(theta) -sin(theta); - sin(theta) cos(theta)] - @test convert(elty, det(R)) ≈ one(elty) - end - end - end - end - if elty <: Int - A = rand(-n:n, n, n) + 10I - elseif elty <: Rational - A = Rational{BigInt}[rand(-n:n)/rand(1:n) for i = 1:n, j = 1:n] + 10I - elseif elty <: Real - A = convert(Matrix{elty}, randn(n,n)) + 10I - else - A = convert(Matrix{elty}, complex.(randn(n,n), randn(n,n))) - end - - @testset "logdet and logabsdet" begin - @test logdet(A[1,1]) == log(det(A[1,1])) - @test logdet(A) ≈ log(det(A)) - @test logabsdet(A)[1] ≈ log(abs(det(A))) - @test logabsdet(Matrix{elty}(-I, n, n))[2] == -1 - infinity = convert(float(elty), Inf) - @test logabsdet(zeros(elty, n, n)) == (-infinity, zero(elty)) - if elty <: Real - @test logabsdet(A)[2] == sign(det(A)) - @test_throws DomainError logdet(Matrix{elty}(-I, n, n)) - else - @test logabsdet(A)[2] ≈ sign(det(A)) - end - # logabsdet for Number" - x = A[1, 1] # getting a number of type elty - X = fill(x, 1, 1) - @test logabsdet(x)[1] ≈ logabsdet(X)[1] - @test logabsdet(x)[2] ≈ logabsdet(X)[2] - # Diagonal, upper, and lower triangular matrices - chksign(s1, s2) = if elty <: Real s1 == s2 else s1 ≈ s2 end - D = Matrix(Diagonal(A)) - v, s = logabsdet(D) - @test v ≈ log(abs(det(D))) && chksign(s, sign(det(D))) - R = triu(A) - v, s = logabsdet(R) - @test v ≈ log(abs(det(R))) && chksign(s, sign(det(R))) - L = tril(A) - v, s = logabsdet(L) - @test v ≈ log(abs(det(L))) && chksign(s, sign(det(L))) - end - - @testset "det with nonstandard Number type" begin - elty <: Real && @test det(Dual.(triu(A), zero(A))) isa Dual - end -end - -@testset "diag" begin - A = Matrix(1.0I, 4, 4) - @test diag(A) == fill(1, 4) - @test diag(view(A, 1:3, 1:3)) == fill(1, 3) - @test diag(view(A, 1:2, 1:2)) == fill(1, 2) - @test_throws ArgumentError diag(rand(10)) -end - -@testset "generic axpy" begin - x = ['a','b','c','d','e'] - y = ['a','b','c','d','e'] - α, β = 'f', 'g' - @test_throws DimensionMismatch axpy!(α, x, ['g']) - @test_throws DimensionMismatch axpby!(α, x, β, ['g']) - @test_throws BoundsError axpy!(α, x, Vector(-1:5), y, Vector(1:7)) - @test_throws BoundsError axpy!(α, x, Vector(1:7), y, Vector(-1:5)) - @test_throws BoundsError axpy!(α, x, Vector(1:7), y, Vector(1:7)) - @test_throws DimensionMismatch axpy!(α, x, Vector(1:3), y, Vector(1:5)) -end - -@test !issymmetric(fill(1,5,3)) -@test !ishermitian(fill(1,5,3)) -@test (x = fill(1,3); cross(x,x) == zeros(3)) -@test_throws DimensionMismatch cross(fill(1,3), fill(1,4)) -@test_throws DimensionMismatch cross(fill(1,2), fill(1,3)) - -@test tr(Bidiagonal(fill(1,5),fill(0,4),:U)) == 5 - - -@testset "array and subarray" begin - for aa in (reshape([1.:6;], (2,3)), fill(float.(rand(Int8,2,2)), 2,3)) - for a in (aa, view(aa, 1:2, 1:2)) - am, an = size(a) - @testset "Scaling with rmul! and lmul" begin - @test rmul!(copy(a), 5.) == a*5 - @test lmul!(5., copy(a)) == a*5 - b = randn(2048) - subB = view(b, :, :) - @test rmul!(copy(b), 5.) == b*5 - @test rmul!(copy(subB), 5.) == subB*5 - @test lmul!(Diagonal([1.; 2.]), copy(a)) == a.*[1; 2] - @test lmul!(Diagonal([1; 2]), copy(a)) == a.*[1; 2] - @test rmul!(copy(a), Diagonal(1.:an)) == a.*Vector(1:an)' - @test rmul!(copy(a), Diagonal(1:an)) == a.*Vector(1:an)' - @test_throws DimensionMismatch lmul!(Diagonal(Vector{Float64}(undef,am+1)), a) - @test_throws DimensionMismatch rmul!(a, Diagonal(Vector{Float64}(undef,an+1))) - end - - @testset "Scaling with rdiv! and ldiv!" begin - @test rdiv!(copy(a), 5.) == a/5 - @test ldiv!(5., copy(a)) == a/5 - @test ldiv!(zero(a), 5., copy(a)) == a/5 - end - - @testset "Scaling with 3-argument mul!" begin - @test mul!(similar(a), 5., a) == a*5 - @test mul!(similar(a), a, 5.) == a*5 - @test mul!(similar(a), Diagonal([1.; 2.]), a) == a.*[1; 2] - @test mul!(similar(a), Diagonal([1; 2]), a) == a.*[1; 2] - @test_throws DimensionMismatch mul!(similar(a), Diagonal(Vector{Float64}(undef, am+1)), a) - @test_throws DimensionMismatch mul!(Matrix{Float64}(undef, 3, 2), a, Diagonal(Vector{Float64}(undef, an+1))) - @test_throws DimensionMismatch mul!(similar(a), a, Diagonal(Vector{Float64}(undef, an+1))) - @test mul!(similar(a), a, Diagonal(1.:an)) == a.*Vector(1:an)' - @test mul!(similar(a), a, Diagonal(1:an)) == a.*Vector(1:an)' - end - - @testset "Scaling with 5-argument mul!" begin - @test mul!(copy(a), 5., a, 10, 100) == a*150 - @test mul!(copy(a), a, 5., 10, 100) == a*150 - @test mul!(vec(copy(a)), 5., a, 10, 100) == vec(a*150) - @test mul!(vec(copy(a)), a, 5., 10, 100) == vec(a*150) - @test_throws DimensionMismatch mul!([vec(copy(a)); 0], 5., a, 10, 100) - @test_throws DimensionMismatch mul!([vec(copy(a)); 0], a, 5., 10, 100) - @test mul!(copy(a), Diagonal([1.; 2.]), a, 10, 100) == 10a.*[1; 2] .+ 100a - @test mul!(copy(a), Diagonal([1; 2]), a, 10, 100) == 10a.*[1; 2] .+ 100a - @test mul!(copy(a), a, Diagonal(1.:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a - @test mul!(copy(a), a, Diagonal(1:an), 10, 100) == 10a.*Vector(1:an)' .+ 100a - end - end - end -end - -@testset "scale real matrix by complex type" begin - @test_throws InexactError rmul!([1.0], 2.0im) - @test isequal([1.0] * 2.0im, ComplexF64[2.0im]) - @test isequal(2.0im * [1.0], ComplexF64[2.0im]) - @test isequal(Float32[1.0] * 2.0f0im, ComplexF32[2.0im]) - @test isequal(Float32[1.0] * 2.0im, ComplexF64[2.0im]) - @test isequal(Float64[1.0] * 2.0f0im, ComplexF64[2.0im]) - @test isequal(Float32[1.0] * big(2.0)im, Complex{BigFloat}[2.0im]) - @test isequal(Float64[1.0] * big(2.0)im, Complex{BigFloat}[2.0im]) - @test isequal(BigFloat[1.0] * 2.0im, Complex{BigFloat}[2.0im]) - @test isequal(BigFloat[1.0] * 2.0f0im, Complex{BigFloat}[2.0im]) -end -@testset "* and mul! for non-commutative scaling" begin - q = Quaternion(0.44567, 0.755871, 0.882548, 0.423612) - qmat = [Quaternion(0.015007, 0.355067, 0.418645, 0.318373)] - @test lmul!(q, copy(qmat)) != rmul!(copy(qmat), q) - @test q*qmat ≉ qmat*q - @test conj(q*qmat) ≈ conj(qmat)*conj(q) - @test q * (q \ qmat) ≈ qmat ≈ (qmat / q) * q - @test q\qmat ≉ qmat/q - alpha = Quaternion(rand(4)...) - beta = Quaternion(0, 0, 0, 0) - @test mul!(copy(qmat), qmat, q, alpha, beta) ≈ qmat * q * alpha - @test mul!(copy(qmat), q, qmat, alpha, beta) ≈ q * qmat * alpha -end -@testset "ops on Numbers" begin - @testset for elty in [Float32,Float64,ComplexF32,ComplexF64] - a = rand(elty) - @test tr(a) == a - @test rank(zero(elty)) == 0 - @test rank(one(elty)) == 1 - @test !isfinite(cond(zero(elty))) - @test cond(a) == one(elty) - @test cond(a,1) == one(elty) - @test issymmetric(a) - @test ishermitian(one(elty)) - @test det(a) == a - @test norm(a) == abs(a) - @test norm(a, 0) == 1 - @test norm(0, 0) == 0 - end - - @test !issymmetric(NaN16) - @test !issymmetric(NaN32) - @test !issymmetric(NaN) - @test norm(NaN) === NaN - @test norm(NaN, 0) === NaN -end - -@test rank(zeros(4)) == 0 -@test rank(1:10) == 1 -@test rank(fill(0, 0, 0)) == 0 -@test rank([1.0 0.0; 0.0 0.9],0.95) == 1 -@test rank([1.0 0.0; 0.0 0.9],rtol=0.95) == 1 -@test rank([1.0 0.0; 0.0 0.9],atol=0.95) == 1 -@test rank([1.0 0.0; 0.0 0.9],atol=0.95,rtol=0.95)==1 -@test qr(big.([0 1; 0 0])).R == [0 1; 0 0] - -@test norm([2.4e-322, 4.4e-323]) ≈ 2.47e-322 -@test norm([2.4e-322, 4.4e-323], 3) ≈ 2.4e-322 -@test_throws ArgumentError opnorm(Matrix{Float64}(undef,5,5),5) - -# operator norm for zero-dimensional domain is zero (see #40370) -@testset "opnorm" begin - for m in (0, 1, 2) - @test @inferred(opnorm(fill(1,0,m))) == 0.0 - @test @inferred(opnorm(fill(1,m,0))) == 0.0 - end - for m in (1, 2) - @test @inferred(opnorm(fill(1im,1,m))) ≈ sqrt(m) - @test @inferred(opnorm(fill(1im,m,1))) ≈ sqrt(m) - end - @test @inferred(opnorm(fill(1,2,2))) ≈ 2 -end - -@testset "generic norm for arrays of arrays" begin - x = Vector{Int}[[1,2], [3,4]] - @test @inferred(norm(x)) ≈ sqrt(30) - @test norm(x, 0) == length(x) - @test norm(x, 1) ≈ 5+sqrt(5) - @test norm(x, 3) ≈ cbrt(5^3 +sqrt(5)^3) -end - -@testset "norm of transpose/adjoint equals norm of parent #32739" begin - for t in (transpose, adjoint), elt in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) - # Vector/matrix of scalars - for sz in ((2,), (2, 3)) - A = rand(elt, sz...) - Aᵀ = t(A) - @test norm(Aᵀ) ≈ norm(Matrix(Aᵀ)) - end - - # Vector/matrix of vectors/matrices - for sz_outer in ((2,), (2, 3)), sz_inner in ((3,), (1, 2)) - A = [rand(elt, sz_inner...) for _ in CartesianIndices(sz_outer)] - Aᵀ = t(A) - @test norm(Aᵀ) ≈ norm(Matrix(Matrix.(Aᵀ))) - end - end -end - -@testset "rotate! and reflect!" begin - x = rand(ComplexF64, 10) - y = rand(ComplexF64, 10) - c = rand(Float64) - s = rand(ComplexF64) - - x2 = copy(x) - y2 = copy(y) - rotate!(x, y, c, s) - @test x ≈ c*x2 + s*y2 - @test y ≈ -conj(s)*x2 + c*y2 - @test_throws DimensionMismatch rotate!([x; x], y, c, s) - - x3 = copy(x) - y3 = copy(y) - reflect!(x, y, c, s) - @test x ≈ c*x3 + s*y3 - @test y ≈ conj(s)*x3 - c*y3 - @test_throws DimensionMismatch reflect!([x; x], y, c, s) -end - -@testset "LinearAlgebra.reflectorApply!" begin - for T in (Float64, ComplexF64) - x = rand(T, 6) - τ = rand(T) - A = rand(T, 6) - B = LinearAlgebra.reflectorApply!(x, τ, copy(A)) - C = LinearAlgebra.reflectorApply!(x, τ, reshape(copy(A), (length(A), 1))) - @test B[1] ≈ C[1] ≈ A[1] - conj(τ)*(A[1] + dot(x[2:end], A[2:end])) - @test B[2:end] ≈ C[2:end] ≈ A[2:end] - conj(τ)*(A[1] + dot(x[2:end], A[2:end]))*x[2:end] - end -end - -@testset "axp(b)y! for element type without commutative multiplication" begin - α = [1 2; 3 4] - β = [5 6; 7 8] - x = fill([ 9 10; 11 12], 3) - y = fill([13 14; 15 16], 3) - axpy = axpy!(α, x, deepcopy(y)) - axpby = axpby!(α, x, β, deepcopy(y)) - @test axpy == x .* [α] .+ y - @test axpy != [α] .* x .+ y - @test axpby == x .* [α] .+ y .* [β] - @test axpby != [α] .* x .+ [β] .* y - axpy = axpy!(zero(α), x, deepcopy(y)) - axpby = axpby!(zero(α), x, one(β), deepcopy(y)) - @test axpy == y - @test axpy == y - @test axpby == y - @test axpby == y -end - -@testset "axpy! for x and y of different dimensions" begin - α = 5 - x = 2:5 - y = fill(1, 2, 4) - rx = [1 4] - ry = [2 8] - @test axpy!(α, x, rx, y, ry) == [1 1 1 1; 11 1 1 26] -end - -@testset "axp(b)y! for non strides input" begin - a = rand(5, 5) - @test axpby!(1, Hermitian(a), 1, zeros(size(a))) == Hermitian(a) - @test axpby!(1, 1.:5, 1, zeros(5)) == 1.:5 - @test axpy!(1, Hermitian(a), zeros(size(a))) == Hermitian(a) - @test axpy!(1, 1.:5, zeros(5)) == 1.:5 -end - -@testset "LinearAlgebra.axp(b)y! for stride-vector like input" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - a = rand(T, 5, 5) - @test axpby!(1, view(a, :, 1:5), 1, zeros(T, size(a))) == a - @test axpy!(1, view(a, :, 1:5), zeros(T, size(a))) == a - b = view(a, 25:-2:1) - @test axpby!(1, b, 1, zeros(T, size(b))) == b - @test axpy!(1, b, zeros(T, size(b))) == b - end -end - -@testset "norm and normalize!" begin - vr = [3.0, 4.0] - for Tr in (Float32, Float64) - for T in (Tr, Complex{Tr}) - v = convert(Vector{T}, vr) - @test norm(v) == 5.0 - w = normalize(v) - @test norm(w - [0.6, 0.8], Inf) < eps(Tr) - @test norm(w) == 1.0 - @test norm(normalize!(copy(v)) - w, Inf) < eps(Tr) - @test isempty(normalize!(T[])) - end - end -end - -@testset "normalize for multidimensional arrays" begin - - for arr in ( - fill(10.0, ()), # 0 dim - [1.0], # 1 dim - [1.0 2.0 3.0; 4.0 5.0 6.0], # 2-dim - rand(1,2,3), # higher dims - rand(1,2,3,4), - Dual.(randn(2,3), randn(2,3)), - OffsetArray([-1,0], (-2,)) # no index 1 - ) - @test normalize(arr) == normalize!(copy(arr)) - @test size(normalize(arr)) == size(arr) - @test axes(normalize(arr)) == axes(arr) - @test vec(normalize(arr)) == normalize(vec(arr)) - end - - @test typeof(normalize([1 2 3; 4 5 6])) == Array{Float64,2} -end - -@testset "normalize for scalars" begin - @test normalize(8.0) == 1.0 - @test normalize(-3.0) == -1.0 - @test normalize(-3.0, 1) == -1.0 - @test isnan(normalize(0.0)) -end - -@testset "Issue #30466" begin - @test norm([typemin(Int), typemin(Int)], Inf) == -float(typemin(Int)) - @test norm([typemin(Int), typemin(Int)], 1) == -2float(typemin(Int)) -end - -@testset "potential overflow in normalize!" begin - δ = inv(prevfloat(typemax(Float64))) - v = [δ, -δ] - - @test norm(v) === 7.866824069956793e-309 - w = normalize(v) - @test w ≈ [1/√2, -1/√2] - @test norm(w) === 1.0 - @test norm(normalize!(v) - w, Inf) < eps() -end - -@testset "normalize with Infs. Issue 29681." begin - @test all(isequal.(normalize([1, -1, Inf]), - [0.0, -0.0, NaN])) - @test all(isequal.(normalize([complex(1), complex(0, -1), complex(Inf, -Inf)]), - [0.0 + 0.0im, 0.0 - 0.0im, NaN + NaN*im])) -end - -@testset "Issue 14657" begin - @test det([true false; false true]) == det(Matrix(1I, 2, 2)) -end - -@test_throws ArgumentError LinearAlgebra.char_uplo(:Z) - -@testset "Issue 17650" begin - @test [0.01311489462160816, Inf] ≈ [0.013114894621608135, Inf] -end - -@testset "Issue 19035" begin - @test LinearAlgebra.promote_leaf_eltypes([1, 2, [3.0, 4.0]]) == Float64 - @test LinearAlgebra.promote_leaf_eltypes([[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]]) == ComplexF64 - @test [1, 2, 3] ≈ [1, 2, 3] - @test [[1, 2], [3, 4]] ≈ [[1, 2], [3, 4]] - @test [[1, 2], [3, 4]] ≈ [[1.0-eps(), 2.0+eps()], [3.0+2eps(), 4.0-1e8eps()]] - @test [[1, 2], [3, 4]] ≉ [[1.0-eps(), 2.0+eps()], [3.0+2eps(), 4.0-1e9eps()]] - @test [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] ≈ [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] -end - -@testset "Issue 40128" begin - @test det(BigInt[9 1 8 0; 0 0 8 7; 7 6 8 3; 2 9 7 7])::BigInt == -1 - @test det(BigInt[1 big(2)^65+1; 3 4])::BigInt == (4 - 3*(big(2)^65+1)) -end - -# Minimal modulo number type - but not subtyping Number -struct ModInt{n} - k - ModInt{n}(k) where {n} = new(mod(k,n)) - ModInt{n}(k::ModInt{n}) where {n} = k -end -Base.:+(a::ModInt{n}, b::ModInt{n}) where {n} = ModInt{n}(a.k + b.k) -Base.:-(a::ModInt{n}, b::ModInt{n}) where {n} = ModInt{n}(a.k - b.k) -Base.:*(a::ModInt{n}, b::ModInt{n}) where {n} = ModInt{n}(a.k * b.k) -Base.:-(a::ModInt{n}) where {n} = ModInt{n}(-a.k) -Base.inv(a::ModInt{n}) where {n} = ModInt{n}(invmod(a.k, n)) -Base.:/(a::ModInt{n}, b::ModInt{n}) where {n} = a*inv(b) - -Base.isfinite(a::ModInt{n}) where {n} = isfinite(a.k) -Base.zero(::Type{ModInt{n}}) where {n} = ModInt{n}(0) -Base.zero(::ModInt{n}) where {n} = ModInt{n}(0) -Base.one(::Type{ModInt{n}}) where {n} = ModInt{n}(1) -Base.one(::ModInt{n}) where {n} = ModInt{n}(1) -Base.conj(a::ModInt{n}) where {n} = a -LinearAlgebra.lupivottype(::Type{ModInt{n}}) where {n} = RowNonZero() -Base.adjoint(a::ModInt{n}) where {n} = ModInt{n}(conj(a)) -Base.transpose(a::ModInt{n}) where {n} = a # see Issue 20978 -LinearAlgebra.Adjoint(a::ModInt{n}) where {n} = adjoint(a) -LinearAlgebra.Transpose(a::ModInt{n}) where {n} = transpose(a) - -@testset "Issue 22042" begin - A = [ModInt{2}(1) ModInt{2}(0); ModInt{2}(1) ModInt{2}(1)] - b = [ModInt{2}(1), ModInt{2}(0)] - - @test A*(A\b) == b - @test A*(lu(A)\b) == b - @test A*(lu(A, NoPivot())\b) == b - @test A*(lu(A, RowNonZero())\b) == b - @test_throws MethodError lu(A, RowMaximum()) - - # Needed for pivoting: - Base.abs(a::ModInt{n}) where {n} = a - Base.:<(a::ModInt{n}, b::ModInt{n}) where {n} = a.k < b.k - @test A*(lu(A, RowMaximum())\b) == b - - A = [ModInt{2}(0) ModInt{2}(1); ModInt{2}(1) ModInt{2}(1)] - @test A*(A\b) == b - @test A*(lu(A)\b) == b - @test A*(lu(A, RowMaximum())\b) == b - @test A*(lu(A, RowNonZero())\b) == b -end - -@testset "Issue 18742" begin - @test_throws DimensionMismatch ones(4,5)/zeros(3,6) - @test_throws DimensionMismatch ones(4,5)\zeros(3,6) -end -@testset "fallback throws properly for AbstractArrays with dimension > 2" begin - @test_throws ErrorException adjoint(rand(2,2,2,2)) - @test_throws ErrorException transpose(rand(2,2,2,2)) -end - -@testset "generic functions for checking whether matrices have banded structure" begin - pentadiag = [1 2 3; 4 5 6; 7 8 9] - tridiag = [1 2 0; 4 5 6; 0 8 9] - tridiagG = GenericArray([1 2 0; 4 5 6; 0 8 9]) - Tridiag = Tridiagonal(tridiag) - ubidiag = [1 2 0; 0 5 6; 0 0 9] - ubidiagG = GenericArray([1 2 0; 0 5 6; 0 0 9]) - uBidiag = Bidiagonal(ubidiag, :U) - lbidiag = [1 0 0; 4 5 0; 0 8 9] - lbidiagG = GenericArray([1 0 0; 4 5 0; 0 8 9]) - lBidiag = Bidiagonal(lbidiag, :L) - adiag = [1 0 0; 0 5 0; 0 0 9] - adiagG = GenericArray([1 0 0; 0 5 0; 0 0 9]) - aDiag = Diagonal(adiag) - @testset "istriu" begin - @test !istriu(pentadiag) - @test istriu(pentadiag, -2) - @test !istriu(tridiag) - @test istriu(tridiag) == istriu(tridiagG) == istriu(Tridiag) - @test istriu(tridiag, -1) - @test istriu(tridiag, -1) == istriu(tridiagG, -1) == istriu(Tridiag, -1) - @test istriu(ubidiag) - @test istriu(ubidiag) == istriu(ubidiagG) == istriu(uBidiag) - @test !istriu(ubidiag, 1) - @test istriu(ubidiag, 1) == istriu(ubidiagG, 1) == istriu(uBidiag, 1) - @test !istriu(lbidiag) - @test istriu(lbidiag) == istriu(lbidiagG) == istriu(lBidiag) - @test istriu(lbidiag, -1) - @test istriu(lbidiag, -1) == istriu(lbidiagG, -1) == istriu(lBidiag, -1) - @test istriu(adiag) - @test istriu(adiag) == istriu(adiagG) == istriu(aDiag) - end - @testset "istril" begin - @test !istril(pentadiag) - @test istril(pentadiag, 2) - @test !istril(tridiag) - @test istril(tridiag) == istril(tridiagG) == istril(Tridiag) - @test istril(tridiag, 1) - @test istril(tridiag, 1) == istril(tridiagG, 1) == istril(Tridiag, 1) - @test !istril(ubidiag) - @test istril(ubidiag) == istril(ubidiagG) == istril(ubidiagG) - @test istril(ubidiag, 1) - @test istril(ubidiag, 1) == istril(ubidiagG, 1) == istril(uBidiag, 1) - @test istril(lbidiag) - @test istril(lbidiag) == istril(lbidiagG) == istril(lBidiag) - @test !istril(lbidiag, -1) - @test istril(lbidiag, -1) == istril(lbidiagG, -1) == istril(lBidiag, -1) - @test istril(adiag) - @test istril(adiag) == istril(adiagG) == istril(aDiag) - end - @testset "isbanded" begin - @test isbanded(pentadiag, -2, 2) - @test !isbanded(pentadiag, -1, 2) - @test !isbanded(pentadiag, -2, 1) - @test isbanded(tridiag, -1, 1) - @test isbanded(tridiag, -1, 1) == isbanded(tridiagG, -1, 1) == isbanded(Tridiag, -1, 1) - @test !isbanded(tridiag, 0, 1) - @test isbanded(tridiag, 0, 1) == isbanded(tridiagG, 0, 1) == isbanded(Tridiag, 0, 1) - @test !isbanded(tridiag, -1, 0) - @test isbanded(tridiag, -1, 0) == isbanded(tridiagG, -1, 0) == isbanded(Tridiag, -1, 0) - @test isbanded(ubidiag, 0, 1) - @test isbanded(ubidiag, 0, 1) == isbanded(ubidiagG, 0, 1) == isbanded(uBidiag, 0, 1) - @test !isbanded(ubidiag, 1, 1) - @test isbanded(ubidiag, 1, 1) == isbanded(ubidiagG, 1, 1) == isbanded(uBidiag, 1, 1) - @test !isbanded(ubidiag, 0, 0) - @test isbanded(ubidiag, 0, 0) == isbanded(ubidiagG, 0, 0) == isbanded(uBidiag, 0, 0) - @test isbanded(lbidiag, -1, 0) - @test isbanded(lbidiag, -1, 0) == isbanded(lbidiagG, -1, 0) == isbanded(lBidiag, -1, 0) - @test !isbanded(lbidiag, 0, 0) - @test isbanded(lbidiag, 0, 0) == isbanded(lbidiagG, 0, 0) == isbanded(lBidiag, 0, 0) - @test !isbanded(lbidiag, -1, -1) - @test isbanded(lbidiag, -1, -1) == isbanded(lbidiagG, -1, -1) == isbanded(lBidiag, -1, -1) - @test isbanded(adiag, 0, 0) - @test isbanded(adiag, 0, 0) == isbanded(adiagG, 0, 0) == isbanded(aDiag, 0, 0) - @test !isbanded(adiag, -1, -1) - @test isbanded(adiag, -1, -1) == isbanded(adiagG, -1, -1) == isbanded(aDiag, -1, -1) - @test !isbanded(adiag, 1, 1) - @test isbanded(adiag, 1, 1) == isbanded(adiagG, 1, 1) == isbanded(aDiag, 1, 1) - end - @testset "isdiag" begin - @test !isdiag(tridiag) - @test isdiag(tridiag) == isdiag(tridiagG) == isdiag(Tridiag) - @test !isdiag(ubidiag) - @test isdiag(ubidiag) == isdiag(ubidiagG) == isdiag(uBidiag) - @test !isdiag(lbidiag) - @test isdiag(lbidiag) == isdiag(lbidiagG) == isdiag(lBidiag) - @test isdiag(adiag) - @test isdiag(adiag) ==isdiag(adiagG) == isdiag(aDiag) - end -end - -@testset "isbanded/istril/istriu with rectangular matrices" begin - @testset "$(size(A))" for A in [zeros(0,4), zeros(2,5), zeros(5,2), zeros(4,0)] - @testset for m in -(size(A,1)-1):(size(A,2)-1) - A .= 0 - A[diagind(A, m)] .= 1 - G = GenericArray(A) - @testset for (kl,ku) in Iterators.product(-6:6, -6:6) - @test isbanded(A, kl, ku) == isbanded(G, kl, ku) == isempty(A) || (m in (kl:ku)) - end - @testset for k in -6:6 - @test istriu(A,k) == istriu(G,k) == isempty(A) || (k <= m) - @test istril(A,k) == istril(G,k) == isempty(A) || (k >= m) - end - end - end -end - -@testset "missing values" begin - @test ismissing(norm(missing)) - x = [5, 6, missing] - y = [missing, 5, 6] - for p in (-Inf, -1, 1, 2, 3, Inf) - @test ismissing(norm(x, p)) - @test ismissing(norm(y, p)) - end - @test_broken ismissing(norm(x, 0)) -end - -@testset "avoid stackoverflow of norm on AbstractChar" begin - @test_throws ArgumentError norm('a') - @test_throws ArgumentError norm(['a', 'b']) - @test_throws ArgumentError norm("s") - @test_throws ArgumentError norm(["s", "t"]) -end - -@testset "peakflops" begin - @test LinearAlgebra.peakflops(1024, eltype=Float32, ntrials=2) > 0 -end - -@testset "NaN handling: Issue 28972" begin - @test all(isnan, rmul!([NaN], 0.0)) - @test all(isnan, rmul!(Any[NaN], 0.0)) - @test all(isnan, lmul!(0.0, [NaN])) - @test all(isnan, lmul!(0.0, Any[NaN])) - - @test all(!isnan, rmul!([NaN], false)) - @test all(!isnan, rmul!(Any[NaN], false)) - @test all(!isnan, lmul!(false, [NaN])) - @test all(!isnan, lmul!(false, Any[NaN])) -end - -@testset "adjtrans dot" begin - for t in (transpose, adjoint), T in (ComplexF64, Quaternion{Float64}) - x, y = t(rand(T, 10)), t(rand(T, 10)) - X, Y = copy(x), copy(y) - @test dot(x, y) ≈ dot(X, Y) - x, y = t([rand(T, 2, 2) for _ in 1:5]), t([rand(T, 2, 2) for _ in 1:5]) - X, Y = copy(x), copy(y) - @test dot(x, y) ≈ dot(X, Y) - x, y = t(rand(T, 10, 5)), t(rand(T, 10, 5)) - X, Y = copy(x), copy(y) - @test dot(x, y) ≈ dot(X, Y) - x = t([rand(T, 2, 2) for _ in 1:5, _ in 1:5]) - y = t([rand(T, 2, 2) for _ in 1:5, _ in 1:5]) - X, Y = copy(x), copy(y) - @test dot(x, y) ≈ dot(X, Y) - x, y = t([rand(T, 2, 2) for _ in 1:5]), t([rand(T, 2, 2) for _ in 1:5]) - end -end - -@testset "avoid stackoverflow in dot" begin - @test_throws "cannot evaluate dot recursively" dot('a', 'c') - @test_throws "cannot evaluate dot recursively" dot('a', 'b':'c') - @test_throws "x and y are of different lengths" dot(1, 1:2) -end - -@testset "generalized dot #32739" begin - for elty in (Int, Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}) - n = 10 - if elty <: Int - A = rand(-n:n, n, n) - x = rand(-n:n, n) - y = rand(-n:n, n) - elseif elty <: Real - A = convert(Matrix{elty}, randn(n,n)) - x = rand(elty, n) - y = rand(elty, n) - else - A = convert(Matrix{elty}, complex.(randn(n,n), randn(n,n))) - x = rand(elty, n) - y = rand(elty, n) - end - @test dot(x, A, y) ≈ dot(A'x, y) ≈ *(x', A, y) ≈ (x'A)*y - @test dot(x, A', y) ≈ dot(A*x, y) ≈ *(x', A', y) ≈ (x'A')*y - elty <: Real && @test dot(x, transpose(A), y) ≈ dot(x, transpose(A)*y) ≈ *(x', transpose(A), y) ≈ (x'*transpose(A))*y - B = reshape([A], 1, 1) - x = [x] - y = [y] - @test dot(x, B, y) ≈ dot(B'x, y) - @test dot(x, B', y) ≈ dot(B*x, y) - elty <: Real && @test dot(x, transpose(B), y) ≈ dot(x, transpose(B)*y) - end -end - -@testset "condskeel #34512" begin - A = rand(3, 3) - @test condskeel(A) ≈ condskeel(A, [8,8,8]) -end - -@testset "copytrito!" begin - n = 10 - @testset "square" begin - for A in (rand(n, n), rand(Int8, n, n)), uplo in ('L', 'U') - for AA in (A, view(A, reverse.(axes(A))...)) - C = uplo == 'L' ? tril(AA) : triu(AA) - for B in (zeros(n, n), zeros(n+1, n+2)) - copytrito!(B, AA, uplo) - @test view(B, 1:n, 1:n) == C - end - end - end - end - @testset "wide" begin - for A in (rand(n, 2n), rand(Int8, n, 2n)) - for AA in (A, view(A, reverse.(axes(A))...)) - C = tril(AA) - for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) - B = zeros(M, N) - copytrito!(B, AA, 'L') - @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) - end - @test_throws DimensionMismatch copytrito!(zeros(n-1, 2n), AA, 'L') - C = triu(AA) - for (M, N) in ((n, 2n), (n+1, 2n), (n, 2n+1), (n+1, 2n+1)) - B = zeros(M, N) - copytrito!(B, AA, 'U') - @test view(B, 1:n, 1:2n) == view(C, 1:n, 1:2n) - end - @test_throws DimensionMismatch copytrito!(zeros(n+1, 2n-1), AA, 'U') - end - end - end - @testset "tall" begin - for A in (rand(2n, n), rand(Int8, 2n, n)) - for AA in (A, view(A, reverse.(axes(A))...)) - C = triu(AA) - for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) - B = zeros(M, N) - copytrito!(B, AA, 'U') - @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) - end - @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'U') - C = tril(AA) - for (M, N) in ((2n, n), (2n, n+1), (2n+1, n), (2n+1, n+1)) - B = zeros(M, N) - copytrito!(B, AA, 'L') - @test view(B, 1:2n, 1:n) == view(C, 1:2n, 1:n) - end - @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'L') - end - end - end - @testset "aliasing" begin - M = Matrix(reshape(1:36, 6, 6)) - A = view(M, 1:5, 1:5) - A2 = Matrix(A) - B = view(M, 2:6, 2:6) - copytrito!(B, A, 'U') - @test UpperTriangular(B) == UpperTriangular(A2) - end -end - -@testset "immutable arrays" begin - A = FillArrays.Fill(big(3), (4, 4)) - M = Array(A) - @test triu(A) == triu(M) - @test triu(A, -1) == triu(M, -1) - @test tril(A) == tril(M) - @test tril(A, 1) == tril(M, 1) - @test det(A) == det(M) -end - -@testset "tril/triu" begin - @testset "with partly initialized matrices" begin - function test_triu(M, k=nothing) - M[1,1] = M[2,2] = M[1,2] = M[1,3] = M[2,3] = 3 - if isnothing(k) - MU = triu(M) - else - MU = triu(M, k) - end - @test iszero(MU[2,1]) - @test MU[1,1] == MU[2,2] == MU[1,2] == MU[1,3] == MU[2,3] == 3 - end - test_triu(Matrix{BigInt}(undef, 2, 3)) - test_triu(Matrix{BigInt}(undef, 2, 3), 0) - test_triu(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3))) - test_triu(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3)), 0) - - function test_tril(M, k=nothing) - M[1,1] = M[2,2] = M[2,1] = 3 - if isnothing(k) - ML = tril(M) - else - ML = tril(M, k) - end - @test ML[1,2] == ML[1,3] == ML[2,3] == 0 - @test ML[1,1] == ML[2,2] == ML[2,1] == 3 - end - test_tril(Matrix{BigInt}(undef, 2, 3)) - test_tril(Matrix{BigInt}(undef, 2, 3), 0) - test_tril(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3))) - test_tril(SizedArrays.SizedArray{(2,3)}(Matrix{BigInt}(undef, 2, 3)), 0) - end - - @testset "block arrays" begin - for nrows in 0:3, ncols in 0:3 - M = [randn(2,2) for _ in 1:nrows, _ in 1:ncols] - Mu = triu(M) - for col in axes(M,2) - rowcutoff = min(col, size(M,1)) - @test @views Mu[1:rowcutoff, col] == M[1:rowcutoff, col] - @test @views Mu[rowcutoff+1:end, col] == zero.(M[rowcutoff+1:end, col]) - end - Ml = tril(M) - for col in axes(M,2) - @test @views Ml[col:end, col] == M[col:end, col] - rowcutoff = min(col-1, size(M,1)) - @test @views Ml[1:rowcutoff, col] == zero.(M[1:rowcutoff, col]) - end - end - end -end - -end # module TestGeneric diff --git a/stdlib/LinearAlgebra/test/givens.jl b/stdlib/LinearAlgebra/test/givens.jl deleted file mode 100644 index 62d677cf086ad..0000000000000 --- a/stdlib/LinearAlgebra/test/givens.jl +++ /dev/null @@ -1,124 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestGivens - -using Test, LinearAlgebra, Random -using LinearAlgebra: Givens, Rotation, givensAlgorithm - -# Test givens rotations -@testset "Test Givens for $elty" for elty in (Float32, Float64, ComplexF32, ComplexF64) - if elty <: Real - raw_A = convert(Matrix{elty}, randn(10,10)) - else - raw_A = convert(Matrix{elty}, complex.(randn(10,10),randn(10,10))) - end - @testset for A in (raw_A, view(raw_A, 1:10, 1:10)) - Ac = copy(A) - R = Rotation(Givens{elty}[]) - T = Rotation(Givens{elty}[]) - for j = 1:8 - for i = j+2:10 - G, _ = givens(A, j+1, i, j) - lmul!(G, A) - rmul!(A, adjoint(G)) - lmul!(G, R) - rmul!(T, G) - - @test lmul!(G, Matrix{elty}(I, 10, 10)) == [G[i,j] for i=1:10,j=1:10] - - @testset "transposes" begin - @test (@inferred G'*G)*Matrix(elty(1)I, 10, 10) ≈ Matrix(I, 10, 10) - @test (G*Matrix(elty(1)I, 10, 10))*G' ≈ Matrix(I, 10, 10) - @test (@inferred copy(R'))*(R*Matrix(elty(1)I, 10, 10)) ≈ Matrix(I, 10, 10) - @test_throws ErrorException transpose(G) - @test_throws ErrorException transpose(R) - end - end - end - @test (R')' === R - # test products of Givens and Rotations - for r in (R, T, *(R.rotations...), *(R.rotations[1], *(R.rotations[2:end]...))) - @test r * A ≈ (A' * r')' ≈ lmul!(r, copy(A)) - @test A * r ≈ (r' * A')' ≈ rmul!(copy(A), r) - @test r' * A ≈ lmul!(r', copy(A)) - @test A * r' ≈ rmul!(copy(A), r') - end - @test_throws ArgumentError givens(A, 3, 3, 2) - @test_throws ArgumentError givens(one(elty),zero(elty),2,2) - G, _ = givens(one(elty),zero(elty),11,12) - @test_throws DimensionMismatch lmul!(G, A) - @test_throws DimensionMismatch rmul!(A, adjoint(G)) - @test abs.(A) ≈ abs.(hessenberg(Ac).H) - @test opnorm(R*Matrix{elty}(I, 10, 10)) ≈ one(elty) - - I10 = Matrix{elty}(I, 10, 10) - G, _ = givens(one(elty),zero(elty),9,10) - @test (G*I10)' * (G*I10) ≈ I10 - K, _ = givens(zero(elty),one(elty),9,10) - @test (K*I10)' * (K*I10) ≈ I10 - end - - @testset "Givens * vectors" begin - for x in (raw_A[:,1], view(raw_A, :, 1)) - G, r = @inferred givens(x[2], x[4], 2, 4) - @test (G*x)[2] ≈ r - @test abs((G*x)[4]) < eps(real(elty)) - - G, r = @inferred givens(x, 2, 4) - @test (G*x)[2] ≈ r - @test abs((G*x)[4]) < eps(real(elty)) - - G, r = givens(x, 4, 2) - @test (G*x)[4] ≈ r - @test abs((G*x)[2]) < eps(real(elty)) - end - d = rand(4) - l = d[1] - g2, l = givens(l, d[2], 1, 2) - g3, l = givens(l, d[3], 1, 3) - g4, l = givens(l, d[4], 1, 4) - @test g2*(g3*d) ≈ g2*g3*d ≈ (g2*g3)*d - @test g2*g3*g4 isa Rotation - 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 - MockUnitful(data::T) where T<:TNumber = new{T}(data) -end -import Base: *, /, one, oneunit -*(a::MockUnitful{T}, b::T) where T<:TNumber = MockUnitful(a.data * b) -*(a::T, b::MockUnitful{T}) where T<:TNumber = MockUnitful(a * b.data) -*(a::MockUnitful{T}, b::MockUnitful{T}) where T<:TNumber = MockUnitful(a.data * b.data) -/(a::MockUnitful{T}, b::MockUnitful{T}) where T<:TNumber = a.data / b.data -one(::Type{<:MockUnitful{T}}) where T = one(T) -oneunit(::Type{<:MockUnitful{T}}) where T = MockUnitful(one(T)) - -@testset "unitful givens rotation unitful $T " for T in (Float64, ComplexF64) - g, r = givens(MockUnitful(T(3)), MockUnitful(T(4)), 1, 2) - @test g.c ≈ 3/5 - @test g.s ≈ 4/5 - @test r.data ≈ 5.0 -end - -# 51554 -# avoid infinite loop on Inf inputs -@testset "givensAlgorithm - Inf inputs" for T in (Float64, ComplexF64) - cs, sn, r = givensAlgorithm(T(Inf), T(1.0)) - @test !isfinite(r) - cs, sn, r = givensAlgorithm(T(1.0), T(Inf)) - @test !isfinite(r) -end - -end # module TestGivens diff --git a/stdlib/LinearAlgebra/test/hessenberg.jl b/stdlib/LinearAlgebra/test/hessenberg.jl deleted file mode 100644 index de58fea9fb27e..0000000000000 --- a/stdlib/LinearAlgebra/test/hessenberg.jl +++ /dev/null @@ -1,308 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -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 - -# for tuple tests below -≅(x,y) = all(p -> p[1] ≈ p[2], zip(x,y)) - -let n = 10 - Random.seed!(1234321) - - Areal = randn(n,n)/2 - Aimg = randn(n,n)/2 - b_ = randn(n) - B_ = randn(n,3) - - # UpperHessenberg methods not covered by the tests below - @testset "UpperHessenberg" begin - A = Areal - H = UpperHessenberg(A) - AH = triu(A,-1) - for k in -2:2 - @test istril(H, k) == istril(AH, k) - @test istriu(H, k) == istriu(AH, k) - @test (k <= -1 ? istriu(H, k) : !istriu(H, k)) - end - @test UpperHessenberg(H) === H - @test parent(H) === A - @test Matrix(H) == Array(H) == H == AH - @test real(H) == real(AH) - @test real(UpperHessenberg{ComplexF64}(A)) == H - @test real(UpperHessenberg{ComplexF64}(H)) == H - sim = similar(H, ComplexF64) - @test sim isa UpperHessenberg{ComplexF64} - @test size(sim) == size(H) - for x in (2,2+3im) - @test x*H == H*x == x*AH - for op in (+,-) - @test op(H,x*I) == op(AH,x*I) == op(op(x*I,H)) - @test op(H,x*I)*x == op(AH,x*I)*x == x*op(H,x*I) - end - end - @test [H[i,j] for i=1:size(H,1), j=1:size(H,2)] == triu(A,-1) - H1 = LinearAlgebra.fillstored!(copy(H), 1) - @test H1 == triu(fill(1, n,n), -1) - @test tril(H1.data,-2) == tril(H.data,-2) - A2, H2 = copy(A), copy(H) - A2[1:4,3]=H2[1:4,3]=1:4 - H2[5,3]=0 - @test H2 == triu(A2,-1) - @test_throws ArgumentError H[5,3]=1 - Hc = UpperHessenberg(Areal + im .* Aimg) - AHc = triu(Areal + im .* Aimg,-1) - @test real(Hc) == real(AHc) - @test imag(Hc) == imag(AHc) - @test Array(copy(adjoint(Hc))) == adjoint(Array(Hc)) - @test Array(copy(transpose(Hc))) == transpose(Array(Hc)) - @test rmul!(copy(Hc), 2.0) == lmul!(2.0, copy(Hc)) - H = UpperHessenberg(Areal) - @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 - end - end - H = UpperHessenberg(Areal) - A = randn(n,n) - d = randn(n) - dl = randn(n-1) - @testset "Multiplication/division" begin - for x = (5, 5I, Diagonal(d), Bidiagonal(d,dl,:U), - 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 isa UpperHessenberg - @test x\H isa UpperHessenberg - 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 - 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 - - @testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int), herm in (false, true) - A_ = eltya == Int ? - rand(1:7, n, n) : - convert(Matrix{eltya}, eltya <: Complex ? - complex.(Areal, Aimg) : - Areal) - A = herm ? Hermitian(A_ + A_') : A_ - - H = hessenberg(A) - @test Hessenberg(H) === H - eltyh = eltype(H) - @test size(H.Q, 1) == size(A, 1) - @test size(H.Q, 2) == size(A, 2) - @test size(H.Q) == size(A) - @test size(H) == size(A) - @test_throws FieldError H.Z - @test convert(Array, H) ≈ A - @test (H.Q * H.H) * H.Q' ≈ A ≈ (Matrix(H.Q) * Matrix(H.H)) * Matrix(H.Q)' - @test (H.Q' * A) * H.Q ≈ H.H - #getindex for HessenbergQ - @test H.Q[1,1] ≈ Array(H.Q)[1,1] - @test det(H.Q) ≈ det(Matrix(H.Q)) - @test logabsdet(H.Q)[1] ≈ logabsdet(Matrix(H.Q))[1] atol=2n*eps(float(real(eltya))) - - # REPL show - hessstring = sprint((t, s) -> show(t, "text/plain", s), H) - qstring = sprint((t, s) -> show(t, "text/plain", s), H.Q) - hstring = sprint((t, s) -> show(t, "text/plain", s), H.H) - @test hessstring == "$(summary(H))\nQ factor: $qstring\nH factor:\n$hstring" - - #iterate - q,h = H - @test q == H.Q - @test h == H.H - - @test convert(Array, 2 * H) ≈ 2 * A ≈ convert(Array, H * 2) - @test convert(Array, H + 2I) ≈ A + 2I ≈ convert(Array, 2I + H) - @test convert(Array, H + (2+4im)I) ≈ A + (2+4im)I ≈ convert(Array, (2+4im)I + H) - @test convert(Array, H - 2I) ≈ A - 2I ≈ -convert(Array, 2I - H) - @test convert(Array, -H) == -convert(Array, H) - @test convert(Array, 2*(H + (2+4im)I)) ≈ 2A + (4+8im)I - - b = convert(Vector{eltype(H)}, b_) - B = convert(Matrix{eltype(H)}, B_) - @test H \ b ≈ A \ b ≈ H \ complex(b) - @test H \ B ≈ A \ B ≈ H \ complex(B) - @test (H - I) \ B ≈ (A - I) \ B - @test (H - (3+4im)I) \ B ≈ (A - (3+4im)I) \ B - @test b' / H ≈ b' / A ≈ complex(b') / H - @test transpose(b) / H ≈ transpose(b) / A ≈ transpose(complex(b)) / H - @test B' / H ≈ B' / A ≈ complex(B') / H - @test b' / H' ≈ complex(b)' / H' - @test B' / (H - I) ≈ B' / (A - I) - @test B' / (H - (3+4im)I) ≈ B' / (A - (3+4im)I) - @test (H - (3+4im)I)' \ B ≈ (A - (3+4im)I)' \ B - @test B' / (H - (3+4im)I)' ≈ B' / (A - (3+4im)I)' - - for shift in (0,1,3+4im) - @test det(H + shift*I) ≈ det(A + shift*I) - @test logabsdet(H + shift*I) ≅ logabsdet(A + shift*I) - end - - HM = Matrix(h) - @test dot(b, h, b) ≈ dot(h'b, b) ≈ dot(b, HM, b) ≈ dot(HM'b, b) - c = b .+ 1 - @test dot(b, h, c) ≈ dot(h'b, c) ≈ dot(b, HM, c) ≈ dot(HM'b, c) - end -end - -@testset "Reverse operation on UpperHessenberg" begin - A = UpperHessenberg(randn(5, 5)) - @test reverse(A, dims=1) == reverse(Matrix(A), dims=1) - @test reverse(A, dims=2) == reverse(Matrix(A), dims=2) - @test reverse(A) == reverse(Matrix(A)) -end - -@testset "hessenberg(::AbstractMatrix)" begin - n = 10 - A = Tridiagonal(rand(n-1), rand(n), rand(n-1)) - H = hessenberg(A) - @test convert(Array, H) ≈ A -end - -# check logdet on a matrix that has a positive determinant -let A = [0.5 0.1 0.9 0.4; 0.9 0.7 0.5 0.4; 0.3 0.4 0.9 0.0; 0.4 0.0 0.0 0.5] - @test logdet(hessenberg(A)) ≈ logdet(A) ≈ -3.5065578973199822 -end - -@testset "Base.propertynames" begin - F = hessenberg([4. 9. 7.; 4. 4. 1.; 4. 3. 2.]) - @test Base.propertynames(F) == (:Q, :H, :μ) - @test Base.propertynames(F, true) == (:Q, :H, :μ, :τ, :factors, :uplo) -end - -@testset "adjoint of Hessenberg" begin - Ar = randn(5, 5) - Ac = complex.(randn(5, 5), randn(5, 5)) - b = ones(size(Ar, 1)) - - for A in (Ar, Ac) - F = hessenberg(A) - @test A'\b ≈ F'\b - end -end - -isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) -using .Main.ImmutableArrays - -@testset "Conversion to AbstractArray" begin - # tests corresponding to #34995 - A = ImmutableArray([1 2 3; 4 5 6; 7 8 9]) - H = UpperHessenberg(A) - - @test convert(AbstractArray{Float64}, H)::UpperHessenberg{Float64,ImmutableArray{Float64,2,Array{Float64,2}}} == H - @test convert(AbstractMatrix{Float64}, H)::UpperHessenberg{Float64,ImmutableArray{Float64,2,Array{Float64,2}}} == H -end - -@testset "custom axes" begin - SZA = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - S = UpperHessenberg(SZA) - r = SizedArrays.SOneTo(2) - @test axes(S) === (r,r) -end - -@testset "copyto! with aliasing (#39460)" begin - M = Matrix(reshape(1:36, 6, 6)) - A = UpperHessenberg(view(M, 1:5, 1:5)) - A2 = copy(A) - B = UpperHessenberg(view(M, 2:6, 2:6)) - @test copyto!(B, A) == A2 -end - -@testset "getindex with Integers" begin - M = reshape(1:9, 3, 3) - S = UpperHessenberg(M) - @test_throws "invalid index" S[3, true] - @test S[1,2] == S[Int8(1),UInt16(2)] == S[big(1), Int16(2)] -end - -@testset "complex Symmetric" begin - D = diagm(0=>ComplexF64[1,2]) - S = Symmetric(D) - H = hessenberg(S) - @test H.H == D -end - -@testset "istriu/istril forwards to parent" begin - n = 10 - @testset "$(nameof(typeof(M)))" for M in [Tridiagonal(rand(n-1), rand(n), rand(n-1)), - Tridiagonal(zeros(n-1), zeros(n), zeros(n-1)), - Diagonal(randn(n)), - Diagonal(zeros(n)), - ] - U = UpperHessenberg(M) - A = Array(U) - for k in -n:n - @test istriu(U, k) == istriu(A, k) - @test istril(U, k) == istril(A, k) - end - end - z = zeros(n,n) - P = Matrix{BigFloat}(undef, n, n) - copytrito!(P, z, 'U') - P[diagind(P,-1)] .= 0 - U = UpperHessenberg(P) - A = Array(U) - @testset for k in -n:n - @test istriu(U, k) == istriu(A, k) - @test istril(U, k) == istril(A, k) - end -end - -end # module TestHessenberg diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl deleted file mode 100644 index f05d7d99c2437..0000000000000 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ /dev/null @@ -1,902 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestLAPACK - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasInt - -@test_throws ArgumentError LinearAlgebra.LAPACK.chkuplo('Z') -@test_throws ArgumentError LinearAlgebra.LAPACK.chkside('Z') -@test_throws ArgumentError LinearAlgebra.LAPACK.chkdiag('Z') -@test_throws ArgumentError LinearAlgebra.LAPACK.chktrans('Z') -@test_throws ArgumentError LinearAlgebra.LAPACK.chkvalidparam(1, "job", 2, (0,1)) - -@testset "syevr" begin - Random.seed!(123) - Ainit = randn(5,5) - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - if elty == ComplexF32 || elty == ComplexF64 - A = complex.(Ainit, Ainit) - else - A = Ainit - end - A = convert(Array{elty, 2}, A) - Asym = A'A - vals, Z = LAPACK.syevr!('V', copy(Asym)) - @test Z*(Diagonal(vals)*Z') ≈ Asym - @test all(vals .> 0.0) - @test LAPACK.syevr!('N', 'V', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[vals .< 1.0] - @test LAPACK.syevr!('N', 'I', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[4:5] - @test vals ≈ LAPACK.syev!('N', 'U', copy(Asym)) - @test vals ≈ LAPACK.syevd!('N', 'U', copy(Asym)) - vals_test, Z_test = LAPACK.syev!('V', 'U', copy(Asym)) - @test vals_test ≈ vals - @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym - vals_test, Z_test = LAPACK.syevd!('V', 'U', copy(Asym)) - @test vals_test ≈ vals - @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym - @test_throws DimensionMismatch LAPACK.sygvd!(1, 'V', 'U', copy(Asym), zeros(elty, 6, 6)) - - @test_throws "jobz must be one of ('N', 'V'), but 'X' was passed" LAPACK.syevr!('X', Asym) - @test_throws "jobz must be one of ('N', 'V'), but 'X' was passed" LAPACK.syev!('X', 'U', Asym) - @test_throws "uplo argument must be 'U' (upper) or 'L' (lower), got 'M'" LAPACK.syev!('N', 'M', Asym) - @test_throws "jobz must be one of ('N', 'V'), but 'X' was passed" LAPACK.syevd!('X', 'U', Asym) - @test_throws "uplo argument must be 'U' (upper) or 'L' (lower), got 'M'" LAPACK.syevd!('N', 'M', Asym) - end -end - -@testset "gglse" begin - let - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = convert(Array{elty, 2}, [1 1 1 1; 1 3 1 1; 1 -1 3 1; 1 1 1 3; 1 1 1 -1]) - c = convert(Array{elty, 1}, [2, 1, 6, 3, 1]) - B = convert(Array{elty, 2}, [1 1 1 -1; 1 -1 1 1; 1 1 -1 1]) - d = convert(Array{elty, 1}, [1, 3, -1]) - @test LAPACK.gglse!(A, c, B, d)[1] ≈ convert(Array{elty}, [0.5, -0.5, 1.5, 0.5]) - end - end -end - -@testset "gebrd, bdsqr, throw for bdsdc" begin - let - n = 10 - @testset for elty in (Float32, Float64) - d, e = convert(Vector{elty}, randn(n)), convert(Vector{elty}, randn(n - 1)) - U, Vt, C = Matrix{elty}(I, n, n), Matrix{elty}(I, n, n), Matrix{elty}(I, n, n) - s, _ = LAPACK.bdsqr!('U', copy(d), copy(e), Vt, U, C) - @test Array(Bidiagonal(d, e, :U)) ≈ U*Diagonal(s)*Vt - - @test_throws ArgumentError LAPACK.bdsqr!('A', d, e, Vt, U, C) - @test_throws DimensionMismatch LAPACK.bdsqr!('U', d, [e; 1], Vt, U, C) - @test_throws DimensionMismatch LAPACK.bdsqr!('U', d, e, Vt[1:end - 1, :], U, C) - @test_throws DimensionMismatch LAPACK.bdsqr!('U', d, e, Vt, U[:,1:end - 1], C) - @test_throws DimensionMismatch LAPACK.bdsqr!('U', d, e, Vt, U, C[1:end - 1, :]) - - @test_throws ArgumentError LAPACK.bdsdc!('U','Z',d,e) - - A = rand(elty,n,n) - B = copy(A) - B, d, e, tauq, taup = LAPACK.gebrd!(B) - U, Vt, C = Matrix{elty}(I, n, n), Matrix{elty}(I, n, n), Matrix{elty}(I, n, n) - s, _ = LAPACK.bdsqr!('U',d,e[1:n-1],Vt, U, C) - @test s ≈ svdvals(A) - end - end -end - -@testset "Issue #7886" begin - let - x, r = LAPACK.gelsy!([0 1; 0 2; 0 3.], [2, 4, 6.]) - @test x ≈ [0,2] - @test r == 1 - end -end - -@testset "geqrt(3)" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - B = copy(A) - C,T = LAPACK.geqrt!(A,zeros(elty,10,10)) - D,S = LAPACK.geqrt3!(A,zeros(elty,10,10)) - @test C ≈ D - end -end - -@testset "gbtrf and gbtrs" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - d = rand(elty,6) - dl = rand(elty,5) - du = rand(elty,5) - dl2 = rand(elty,4) - AB = zeros(elty,6,6) - AB[6,1:4] = dl2 - AB[5,1:5] = dl - AB[4,:] = d - AB[3,2:6] = du - AB,ipiv = LAPACK.gbtrf!(2,1,6,AB) - C = rand(elty,6,6) - D = copy(C) - D = LAPACK.gbtrs!('N',2,1,6,AB,ipiv,D) - A = diagm(-2 => dl2, -1 => dl, 0 => d, 1 => du) - @test A\C ≈ D - M = Matrix{elty}(undef,7,6) - @test_throws DimensionMismatch LAPACK.gbtrs!('N',2,1,6,AB,ipiv,M) - @test_throws ArgumentError LAPACK.gbtrs!('M',2,1,6,AB,ipiv,M) - @test_throws LinearAlgebra.LAPACKException LAPACK.gbtrf!(2,1,6,zeros(elty,6,6)) - end -end - - -@testset "geqp3, geqrt error handling" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - x10, x11 = Vector{elty}.(undef, (10, 11)) - y10, y11 = Vector{LinearAlgebra.BlasInt}.(undef, (10, 11)) - A10x10, A11x10, A10x11, A11x11 = Matrix{elty}.(undef, ((10,10), (11,10), (10,11), (11,11))) - @test_throws DimensionMismatch LAPACK.geqlf!(A10x10, x11) - @test_throws DimensionMismatch LAPACK.gelqf!(A10x10, x11) - @test_throws DimensionMismatch LAPACK.geqp3!(A10x10, y11, x10) - @test_throws DimensionMismatch LAPACK.geqp3!(A10x10, y10, x11) - @test_throws ArgumentError LAPACK.geqrt!(A10x10, A11x10) - @test_throws DimensionMismatch LAPACK.geqrt3!(A10x10, A11x10) - @test_throws DimensionMismatch LAPACK.geqrt3!(A10x11, A11x11) - @test_throws DimensionMismatch LAPACK.geqrf!(A10x10, x11) - @test_throws DimensionMismatch LAPACK.gerqf!(A10x10, x11) - end -end - -@testset "gels, gesv, getrs, getri error handling" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A10x10, B11x11 = Matrix{elty}.(undef, ((10,10), (11,11))) - x10, x11 = Vector{LinearAlgebra.BlasInt}.(undef, (10, 11)) - @test_throws DimensionMismatch LAPACK.gels!('N',A10x10,B11x11) - @test_throws DimensionMismatch LAPACK.gels!('T',A10x10,B11x11) - @test_throws ArgumentError LAPACK.gels!('X',A10x10,B11x11) - @test_throws DimensionMismatch LAPACK.gesv!(A10x10,B11x11) - @test_throws DimensionMismatch LAPACK.getrs!('N',A10x10,x10,B11x11) - @test_throws DimensionMismatch LAPACK.getrs!('T',A10x10,x10,B11x11) - @test_throws ArgumentError LAPACK.getrs!('X',A10x10,x10,B11x11) - @test_throws DimensionMismatch LAPACK.getri!(A10x10,x11) - end -end - -@testset "gelsy, gelsd" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty, 10, 10) - B = rand(elty, 10, 10) - C, j = LAPACK.gelsd!(copy(A),copy(B)) - D, k = LAPACK.gelsy!(copy(A),copy(B)) - @test C ≈ D rtol=4*eps(cond(A)) - @test_throws DimensionMismatch LAPACK.gelsd!(A,rand(elty,12,10)) - @test_throws DimensionMismatch LAPACK.gelsy!(A,rand(elty,12,10)) - end -end - -@testset "gglse errors" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - @test_throws DimensionMismatch LAPACK.gglse!(A,zeros(elty,10),rand(elty,12,11),zeros(elty,12)) - @test_throws DimensionMismatch LAPACK.gglse!(A,zeros(elty,11),rand(elty,10,10),zeros(elty,10)) - @test_throws DimensionMismatch LAPACK.gglse!(A,zeros(elty,10),rand(elty,10,10),zeros(elty,11)) - end -end - -@testset "gesvd, ggsvd" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,5) - U,S,V = svd(A) - lU,lS,lVt = LAPACK.gesvd!('S','S',A) - @test U ≈ lU - @test S ≈ lS - @test V' ≈ lVt - @test_throws ArgumentError LAPACK.gesvd!('X','S',A) - @test_throws ArgumentError LAPACK.gesvd!('S','X',A) - B = rand(elty,10,10) - # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.version() < v"3.6.0" - @test_throws DimensionMismatch LAPACK.ggsvd!('N','N','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd!('X','N','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd!('N','X','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd!('N','N','X',A,B) - else - @test_throws DimensionMismatch LAPACK.ggsvd3!('N','N','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd3!('X','N','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd3!('N','X','N',A,B) - @test_throws ArgumentError LAPACK.ggsvd3!('N','N','X',A,B) - end - end -end - -@testset "geevx, ggev, ggev3 errors" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - B = rand(elty,10,10) - @test_throws ArgumentError LAPACK.geevx!('M','N','N','N',A) - @test_throws ArgumentError LAPACK.geevx!('N','Z','N','N',A) - @test_throws ArgumentError LAPACK.geevx!('N','N','Z','N',A) - @test_throws ArgumentError LAPACK.geevx!('N','N','N','Z',A) - @test_throws ArgumentError LAPACK.ggev!('N','B',A,B) - @test_throws ArgumentError LAPACK.ggev!('B','N',A,B) - @test_throws DimensionMismatch LAPACK.ggev!('N','N',A,zeros(elty,12,12)) - @test_throws ArgumentError LAPACK.ggev3!('N','B',A,B) - @test_throws ArgumentError LAPACK.ggev3!('B','N',A,B) - @test_throws DimensionMismatch LAPACK.ggev3!('N','N',A,zeros(elty,12,12)) - end -end - -@testset "gebal/gebak" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - typescale = log10(eps(real(elty))) / 3 * 2 - A = rand(elty,10,10) * Diagonal(exp10.(range(typescale, stop=-typescale, length=10))) - B = copy(A) - ilo, ihi, scale = LAPACK.gebal!('S',B) - Bvs = eigvecs(B) - Avs = eigvecs(A) - Bvs = LAPACK.gebak!('S','R',ilo,ihi,scale,Bvs) - @test norm(diff(Avs ./ Bvs, dims=1)) < 100 * eps(abs(float(one(elty)))) - end -end - -@testset "gels" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - Random.seed!(913) - A = rand(elty,10,10) - X = rand(elty,10) - B,Y,z = LAPACK.gels!('N',copy(A),copy(X)) - @test A\X ≈ Y - @test_throws ArgumentError LAPACK.gels!('X',A,X) - end -end - -@testset "getrf/getri" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - iA = inv(A) - A, ipiv, info = LAPACK.getrf!(A) - A = LAPACK.getri!(A, ipiv) - @test A ≈ iA - - B = rand(elty,10,10) - iB = inv(B) - ipiv = rand(BlasInt,10) - B, ipiv, info = LAPACK.getrf!(B, ipiv) - B = LAPACK.getri!(B, ipiv) - @test B ≈ iB - end -end - -@testset "geev" begin - # complex is easier for now - @testset for elty in (ComplexF32, ComplexF64) - A = rand(elty,10,10) - Aw, Avl, Avr = LAPACK.geev!('N','V',copy(A)) - fA = eigen(A, sortby=nothing) - @test fA.values ≈ Aw - @test fA.vectors ≈ Avr - - @test_throws ArgumentError LAPACK.geev!('X','V',A) - @test_throws ArgumentError LAPACK.geev!('N','X',A) - end -end - -@testset "gtsv" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - du = rand(elty,9) - d = rand(elty,10) - dl = rand(elty,9) - b = rand(elty,10) - c = Tridiagonal(dl,d,du) \ b - b = LAPACK.gtsv!(dl,d,du,b) - @test b ≈ c - @test_throws DimensionMismatch LAPACK.gtsv!(zeros(elty,11),d,du,b) - @test_throws DimensionMismatch LAPACK.gtsv!(dl,d,zeros(elty,11),b) - @test_throws DimensionMismatch LAPACK.gtsv!(dl,d,du,zeros(elty,11)) - @test LAPACK.gtsv!(elty[],elty[],elty[],elty[]) == elty[] - end -end - -@testset "gttrs,gttrf errors" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - du = rand(elty,9) - d = rand(elty,10) - dl = rand(elty,9) - b = rand(elty,10) - y10 = Vector{BlasInt}(undef, 10) - x9, x11 = Vector{elty}.(undef, (9, 11)) - @test_throws DimensionMismatch LAPACK.gttrf!(x11, d, du) - @test_throws DimensionMismatch LAPACK.gttrf!(dl, d, x11) - @test_throws DimensionMismatch LAPACK.gttrs!('N', x11, d, du, x9, y10, b) - @test_throws DimensionMismatch LAPACK.gttrs!('N', dl, d, x11, x9, y10, b) - @test_throws DimensionMismatch LAPACK.gttrs!('N', dl, d, du, x9, y10, x11) - @test_throws ArgumentError LAPACK.gttrs!('X', dl, d, du, x9, y10, x11) - A = lu(Tridiagonal(dl,d,du)) - b = rand(elty,10,5) - c = copy(b) - dl,d,du,du2,ipiv = LAPACK.gttrf!(dl,d,du) - c = LAPACK.gttrs!('N',dl,d,du,du2,ipiv,c) - @test A\b ≈ c - end -end - -@testset "orglq and friends errors" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - A,tau = LAPACK.gelqf!(A) - @test_throws DimensionMismatch LAPACK.orglq!(A,tau,11) - temp = rand(elty,11,11) - @test_throws DimensionMismatch LAPACK.ormlq!('R','N',A,tau,temp) - @test_throws DimensionMismatch LAPACK.ormlq!('L','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormlq!('X','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormlq!('R','X',A,tau,temp) - temp = zeros(elty,11) - B = copy(A) - @test_throws DimensionMismatch LAPACK.ormlq!('R','N',A,temp,B) - @test_throws DimensionMismatch LAPACK.ormlq!('L','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormlq!('X','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormlq!('L','X',A,temp,B) - - B = copy(A) - C = LAPACK.orglq!(B,tau) - @test LAPACK.ormlq!('R','N',A,tau, Matrix{elty}(I, 10, 10)) ≈ C - - A = rand(elty,10,10) - A,tau = LAPACK.geqrf!(A) - @test_throws DimensionMismatch LAPACK.orgqr!(A,tau,11) - B = copy(A) - @test LAPACK.orgqr!(B,tau) ≈ LAPACK.ormqr!('R','N',A,tau,Matrix{elty}(I, 10, 10)) - temp = rand(elty,11,11) - @test_throws DimensionMismatch LAPACK.ormqr!('R','N',A,tau,temp) - @test_throws DimensionMismatch LAPACK.ormqr!('L','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormqr!('X','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormqr!('L','X',A,tau,temp) - B = copy(A) - temp = zeros(elty,11) - @test_throws DimensionMismatch LAPACK.ormqr!('R','N',A,temp,B) - @test_throws DimensionMismatch LAPACK.ormqr!('L','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormqr!('X','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormqr!('L','X',A,temp,B) - - A = rand(elty,10,10) - A,tau = LAPACK.geqlf!(A) - @test_throws DimensionMismatch LAPACK.orgql!(A,tau,11) - B = copy(A) - @test LAPACK.orgql!(B,tau) ≈ LAPACK.ormql!('R','N',A,tau,Matrix{elty}(I, 10, 10)) - temp = rand(elty,11,11) - @test_throws DimensionMismatch LAPACK.ormql!('R','N',A,tau,temp) - @test_throws DimensionMismatch LAPACK.ormql!('L','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormql!('X','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormql!('L','X',A,tau,temp) - temp = zeros(elty,11) - B = copy(A) - @test_throws DimensionMismatch LAPACK.ormql!('R','N',A,temp,B) - @test_throws DimensionMismatch LAPACK.ormql!('L','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormql!('X','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormql!('L','X',A,temp,B) - - A = rand(elty,10,10) - A,tau = LAPACK.gerqf!(A) - @test_throws DimensionMismatch LAPACK.orgrq!(A,tau,11) - B = copy(A) - @test LAPACK.orgrq!(B,tau) ≈ LAPACK.ormrq!('R','N',A,tau,Matrix{elty}(I, 10, 10)) - temp = rand(elty,11,11) - @test_throws DimensionMismatch LAPACK.ormrq!('R','N',A,tau,temp) - @test_throws DimensionMismatch LAPACK.ormrq!('L','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormrq!('X','N',A,tau,temp) - @test_throws ArgumentError LAPACK.ormrq!('L','X',A,tau,temp) - B = copy(A) - temp = zeros(elty,11) - @test_throws DimensionMismatch LAPACK.ormrq!('R','N',A,temp,B) - @test_throws DimensionMismatch LAPACK.ormrq!('L','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormrq!('X','N',A,temp,B) - @test_throws ArgumentError LAPACK.ormrq!('L','X',A,temp,B) - - A = rand(elty,10,11) - Q = copy(A) - Q,tau = LAPACK.gerqf!(Q) - R = triu(Q[:,2:11]) - LAPACK.orgrq!(Q,tau) - @test Q*Q' ≈ Matrix(I, 10, 10) - @test R*Q ≈ A - @test_throws DimensionMismatch LAPACK.orgrq!(zeros(elty,11,10),zeros(elty,10)) - - C = rand(elty,10,10) - V = rand(elty,10,10) - T = zeros(elty,10,11) - @test_throws DimensionMismatch LAPACK.gemqrt!('L','N',V,T,C) - @test_throws DimensionMismatch LAPACK.gemqrt!('R','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('X','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('R','X',V,T,C) - - C = rand(elty,10,10) - V = rand(elty,11,10) - T = zeros(elty,10,10) - @test_throws DimensionMismatch LAPACK.gemqrt!('R','N',V,T,C) - @test_throws DimensionMismatch LAPACK.gemqrt!('L','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('X','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('L','X',V,T,C) - - # test size(T) = (nb,k) ensures 1 <= nb <= k - T = zeros(elty,10,10) - V = rand(elty,5,10) - @test_throws DimensionMismatch LAPACK.gemqrt!('L','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('X','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('L','X',V,T,C) - C = rand(elty,10,10) - V = rand(elty,10,10) - T = zeros(elty,11,10) - @test_throws DimensionMismatch LAPACK.gemqrt!('R','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('X','N',V,T,C) - @test_throws ArgumentError LAPACK.gemqrt!('R','X',V,T,C) - - @test_throws DimensionMismatch LAPACK.orghr!(1, 10, C, zeros(elty,11)) - end -end - -@testset "sytri, sytrs, and sytrf" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - A = A + transpose(A) #symmetric! - B = copy(A) - B,ipiv = LAPACK.sytrf!('U',B) - @test_throws ArgumentError LAPACK.sytrf!('X',B) - @test triu(inv(A)) ≈ triu(LAPACK.sytri!('U',B,ipiv)) rtol=eps(cond(A)) - @test_throws ArgumentError LAPACK.sytri!('X',B,ipiv) - temp = rand(elty,11,5) - @test_throws DimensionMismatch LAPACK.sytrs!('U',B,ipiv,temp) - @test_throws ArgumentError LAPACK.sytrs!('X',B,ipiv,temp) - @test LAPACK.sytrf!('U',zeros(elty,0,0)) == (zeros(elty,0,0),zeros(BlasInt,0),zero(BlasInt)) - end - - # Rook-pivoting variants - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty, 10, 10) - A = A + transpose(A) #symmetric! - B = copy(A) - B,ipiv = LAPACK.sytrf_rook!('U', B) - @test triu(inv(A)) ≈ triu(LAPACK.sytri_rook!('U', B, ipiv)) rtol=eps(cond(A)) - @test_throws ArgumentError LAPACK.sytri_rook!('X', B, ipiv) - temp = rand(elty, 11, 5) - @test_throws DimensionMismatch LAPACK.sytrs_rook!('U', B, ipiv, temp) - @test_throws ArgumentError LAPACK.sytrs_rook!('X', B, ipiv, temp) - @test LAPACK.sytrf_rook!('U',zeros(elty, 0, 0)) == (zeros(elty, 0, 0),zeros(BlasInt, 0),zero(BlasInt)) - A = rand(elty, 10, 10) - A = A + transpose(A) #symmetric! - b = rand(elty, 10) - c = A \ b - cnd = cond(A) - b,A = LAPACK.sysv_rook!('U', A, b) - @test b ≈ c rtol=eps(cnd) - temp = rand(elty,11) - @test_throws DimensionMismatch LAPACK.sysv_rook!('U',A,temp) - @test_throws ArgumentError LAPACK.sysv_rook!('X',A,temp) - - # syconvf_rook error handling - # way argument is wrong - @test_throws ArgumentError LAPACK.syconvf_rook!('U', 'U', A, rand(BlasInt, 10)) - # ipiv has wrong length - @test_throws ArgumentError LAPACK.syconvf_rook!('U', 'R', A, rand(BlasInt, 9)) - # e has wrong length - @test_throws ArgumentError LAPACK.syconvf_rook!('U', 'R', A, rand(BlasInt, 10), rand(elty, 9)) - end -end - -@testset "hetrf, hetrs" begin - @testset for elty in (ComplexF32, ComplexF64) - A = rand(elty,10,10) - A = A + A' #hermitian! - B = copy(A) - B,ipiv = LAPACK.hetrf!('U',B) - temp = rand(elty,11,5) - @test_throws DimensionMismatch LAPACK.hetrs!('U',B,ipiv,temp) - @test_throws ArgumentError LAPACK.hetrs!('X',B,ipiv,temp) - @test_throws DimensionMismatch LAPACK.hetrs_rook!('U',B,ipiv,temp) - @test_throws ArgumentError LAPACK.hetrs_rook!('X',B,ipiv,temp) - end -end - -@testset "stev, stebz, stein, stegr" begin - @testset for elty in (Float32, Float64) - d = rand(elty,10) - e = rand(elty,9) - temp = rand(elty,11) - @test_throws DimensionMismatch LAPACK.stev!('N',d,temp) - @test_throws ArgumentError LAPACK.stev!('X',d,temp) - temp = rand(elty,10) - @test_throws DimensionMismatch LAPACK.stebz!('A','B',zero(elty),zero(elty),0,0,-1.,d,temp) - @test_throws ArgumentError LAPACK.stebz!('X','B',zero(elty),zero(elty),0,0,-1.,d,temp) - @test_throws ArgumentError LAPACK.stebz!('A','X',zero(elty),zero(elty),0,0,-1.,d,temp) - temp11 = rand(elty,11) - @test_throws DimensionMismatch LAPACK.stegr!('N','A',d,temp11,zero(elty),zero(elty),0,0) - @test_throws ArgumentError LAPACK.stegr!('X','A',d,temp11,zero(elty),zero(elty),0,0) - @test_throws ArgumentError LAPACK.stegr!('N','X',d,temp11,zero(elty),zero(elty),0,0) - tempblasint10 = zeros(BlasInt,10) - tempblasint10_2 = zeros(BlasInt,10) - @test_throws DimensionMismatch LAPACK.stein!(d,temp11,temp,tempblasint10,tempblasint10_2) - @test_throws DimensionMismatch LAPACK.stein!(d,e,temp11,tempblasint10,tempblasint10_2) - end -end - -@testset "trtri & trtrs" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - A = triu(A) - B = copy(A) - @test inv(A) ≈ LAPACK.trtri!('U','N',B) - @test_throws ArgumentError LAPACK.trtri!('X','N',B) - @test_throws ArgumentError LAPACK.trtri!('U','X',B) - temp = zeros(elty,11,10) - @test_throws DimensionMismatch LAPACK.trtrs!('U','N','N',B,temp) - @test_throws ArgumentError LAPACK.trtrs!('X','N','N',B,temp) - @test_throws ArgumentError LAPACK.trtrs!('U','X','N',B,temp) - @test_throws ArgumentError LAPACK.trtrs!('U','N','X',B,temp) - end -end - -@testset "larfg & larf" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - ## larfg - Random.seed!(0) - x = rand(elty, 5) - v = copy(x) - τ = LinearAlgebra.LAPACK.larfg!(v) - H = (I - τ*v*v') - # for complex input, LAPACK wants a conjugate transpose of H (check clarfg docs) - y = elty <: Complex ? H'*x : H*x - # we have rotated a vector - @test norm(y) ≈ norm(x) - # an annihilated almost all the first column - @test norm(y[2:end], Inf) < 10*eps(real(one(elty))) - - ## larf - C = rand(elty, 5, 5) - C_norm = norm(C, 2) - v = C[1:end, 1] - τ = LinearAlgebra.LAPACK.larfg!(v) - LinearAlgebra.LAPACK.larf!('L', v, conj(τ), C) - # we have applied a unitary transformation - @test norm(C, 2) ≈ C_norm - # an annihilated almost all the first column - @test norm(C[2:end, 1], Inf) < 10*eps(real(one(elty))) - - # apply left and right - C1 = rand(elty, 5, 5) - C2 = rand(elty, 5, 5) - C = C2*C1 - - v = C1[1:end, 1] - τ = LinearAlgebra.LAPACK.larfg!(v) - LinearAlgebra.LAPACK.larf!('L', v, τ, C1) - LinearAlgebra.LAPACK.larf!('R', v, conj(τ), C2) - @test C ≈ C2*C1 - - @test_throws ArgumentError LAPACK.larf!('X', v, τ, C1) - end -end - -@testset "tgsen, tzrzf, & trsyl" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - Z = zeros(elty,10,10) - @test_throws DimensionMismatch LAPACK.tgsen!(zeros(BlasInt,10),Z,zeros(elty,11,11),Z,Z) - @test_throws DimensionMismatch LAPACK.tgsen!(zeros(BlasInt,10),Z,Z,zeros(elty,11,11),Z) - @test_throws DimensionMismatch LAPACK.tgsen!(zeros(BlasInt,10),Z,Z,Z,zeros(elty,11,11)) - @test_throws DimensionMismatch LAPACK.trsyl!('N','N',Z,Z,zeros(elty,11,11)) - @test_throws ArgumentError LAPACK.trsyl!('X','N',Z,Z,zeros(elty,11,11)) - @test_throws ArgumentError LAPACK.trsyl!('N','X',Z,Z,zeros(elty,11,11)) - @test_throws DimensionMismatch LAPACK.tzrzf!(zeros(elty,10,5)) - - A = triu(rand(elty,4,4)) - V = view(A, 1:2, :) - M = Matrix(V) - @test LAPACK.tzrzf!(V) == LAPACK.tzrzf!(M) - end -end - -@testset "sysv" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - Random.seed!(123) - A = rand(elty,10,10) - A = A + transpose(A) #symmetric! - b = rand(elty,10) - c = A \ b - b,A = LAPACK.sysv!('U',A,b) - @test b ≈ c - @test_throws DimensionMismatch LAPACK.sysv!('U',A,rand(elty,11)) - @test_throws ArgumentError LAPACK.sysv!('X',A,rand(elty,11)) - end -end - -@testset "hesv" begin - @testset for elty in (ComplexF32, ComplexF64) - Random.seed!(935) - A = rand(elty,10,10) - A = A + A' #hermitian! - b = rand(elty,10) - c = A \ b - b,A = LAPACK.hesv!('U',A,b) - @test b ≈ c - temp = rand(elty,11) - @test_throws DimensionMismatch LAPACK.hesv!('U',A,temp) - @test_throws ArgumentError LAPACK.hesv!('X',A,temp) - A = rand(elty,10,10) - A = A + A' #hermitian! - b = rand(elty,10) - c = A \ b - b,A = LAPACK.hesv_rook!('U',A,b) - @test b ≈ c - @test_throws DimensionMismatch LAPACK.hesv_rook!('U',A,temp) - @test_throws ArgumentError LAPACK.hesv_rook!('X',A,temp) - end -end - -@testset "ptsv" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - dv = fill(elty(1),10) - ev = zeros(elty,9) - rdv = real(dv) - A = SymTridiagonal(dv,ev) - if elty <: Complex - A = Tridiagonal(conj(ev),dv,ev) - end - B = rand(elty,10,10) - C = copy(B) - @test A\B ≈ LAPACK.ptsv!(rdv,ev,C) - @test_throws DimensionMismatch LAPACK.ptsv!(rdv,Vector{elty}(undef,10),C) - @test_throws DimensionMismatch LAPACK.ptsv!(rdv,ev,Matrix{elty}(undef,11,11)) - end -end - -@testset "pttrf and pttrs" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - dv = fill(elty(1),10) - ev = zeros(elty,9) - rdv = real(dv) - A = SymTridiagonal(dv,ev) - if elty <: Complex - A = Tridiagonal(conj(ev),dv,ev) - end - rdv,ev = LAPACK.pttrf!(rdv,ev) - @test_throws DimensionMismatch LAPACK.pttrf!(rdv,dv) - B = rand(elty,10,10) - C = copy(B) - if elty <: Complex - @test A\B ≈ LAPACK.pttrs!('U',rdv,ev,C) - tempvec = Vector{elty}(undef,10) - tempmat = Matrix{elty}(undef,11,11) - @test_throws DimensionMismatch LAPACK.pttrs!('U',rdv,tempvec,C) - @test_throws DimensionMismatch LAPACK.pttrs!('U',rdv,ev,tempmat) - @test_throws ArgumentError LAPACK.pttrs!('X',rdv,tempvec,C) - @test_throws ArgumentError LAPACK.pttrs!('X',rdv,ev,tempmat) - else - @test A\B ≈ LAPACK.pttrs!(rdv,ev,C) - @test_throws DimensionMismatch LAPACK.pttrs!(rdv,Vector{elty}(undef,10),C) - @test_throws DimensionMismatch LAPACK.pttrs!(rdv,ev,Matrix{elty}(undef,11,11)) - end - end -end - -@testset "posv and some errors for friends" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - local n = 10 - A = rand(elty,n,n)/100 - A += real(diagm(0 => n*real(rand(elty,n)))) - if elty <: Complex - A = A + A' - else - A = A + transpose(A) - end - B = rand(elty,n,n) - D = copy(A) - C = copy(B) - D,C = LAPACK.posv!('U',D,C) - @test A\B ≈ C - offsizemat = Matrix{elty}(undef, n+1, n+1) - @test_throws DimensionMismatch LAPACK.posv!('U', D, offsizemat) - @test_throws DimensionMismatch LAPACK.potrs!('U', D, offsizemat) - @test_throws ArgumentError LAPACK.posv!('X', D, offsizemat) - @test_throws ArgumentError LAPACK.potrs!('X', D, offsizemat) - - @test LAPACK.potrs!('U',Matrix{elty}(undef,0,0),elty[]) == elty[] - end -end - -@testset "gesvx" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - B = rand(elty,10,5) - C = copy(A) - D = copy(B) - X, rcond, f, b, r = LAPACK.gesvx!(C,D) - @test X ≈ A\B rtol=inv(rcond)*eps(real(elty)) - end -end - -@testset "gees, gges, gges3 error throwing" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - A = rand(elty,10,10) - B = rand(elty,11,11) - @test_throws DimensionMismatch LAPACK.gges!('V','V',A,B) - @test_throws DimensionMismatch LAPACK.gges3!('V','V',A,B) - @test_throws ArgumentError LAPACK.gges!('X','V',A,B) - @test_throws ArgumentError LAPACK.gges3!('X','V',A,B) - @test_throws ArgumentError LAPACK.gges!('V','X',A,B) - @test_throws ArgumentError LAPACK.gges3!('V','X',A,B) - end -end - -@testset "trrfs & trevc" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - T = triu(rand(elty,10,10)) - v = eigvecs(T, sortby=nothing)[:,1] - select = zeros(LinearAlgebra.BlasInt,10) - select[1] = 1 - select,Vr = LAPACK.trevc!('R','S',select,copy(T)) - @test Vr ≈ v - select = zeros(LinearAlgebra.BlasInt,10) - select[1] = 1 - select,Vl = LAPACK.trevc!('L','S',select,copy(T)) - select = zeros(LinearAlgebra.BlasInt,10) - select[1] = 1 - select,Vln,Vrn = LAPACK.trevc!('B','S',select,copy(T)) - @test Vrn ≈ v - @test Vln ≈ Vl - @test_throws ArgumentError LAPACK.trevc!('V','S',select,T) - @test_throws ArgumentError LAPACK.trevc!('R','X',select,T) - temp1010 = rand(elty,10,10) - temp1011 = rand(elty,10,11) - @test_throws DimensionMismatch LAPACK.trrfs!('U','N','N',T,temp1010,temp1011) - @test_throws ArgumentError LAPACK.trrfs!('X','N','N',T,temp1010,temp1011) - @test_throws ArgumentError LAPACK.trrfs!('U','X','N',T,temp1010,temp1011) - @test_throws ArgumentError LAPACK.trrfs!('U','N','X',T,temp1010,temp1011) - end -end - -@testset "laic1" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - @test_throws DimensionMismatch LAPACK.laic1!(1,rand(elty,10),real(rand(elty)),rand(elty,11),rand(elty)) - end -end - -@testset "trsen" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - for job in ('N', 'E', 'V', 'B') - for c in ('V', 'N') - A = convert(Matrix{elty}, [7 2 2 1; 1 5 2 0; 0 3 9 4; 1 1 1 4]) - T,Q,d = schur(A) - s, sep = LinearAlgebra.LAPACK.trsen!(job,c,Array{LinearAlgebra.BlasInt}([0,1,0,0]),T,Q)[4:5] - @test d[1] ≈ T[2,2] - @test d[2] ≈ T[1,1] - if c == 'V' - @test Q*T*Q' ≈ A - end - if job == 'N' || job == 'V' - @test iszero(s) - else - @test s ≈ 0.8080423 atol=1e-6 - end - if job == 'N' || job == 'E' - @test iszero(sep) - else - @test sep ≈ 2. atol=3e-1 - end - end - end - end -end - -@testset "trexc" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - for c in ('V', 'N') - A = convert(Matrix{elty}, [7 2 2 1; 1 5 2 0; 0 3 9 4; 1 1 1 4]) - T,Q,d = schur(A) - LinearAlgebra.LAPACK.trexc!(c,LinearAlgebra.BlasInt(1),LinearAlgebra.BlasInt(2),T,Q) - @test d[1] ≈ T[2,2] - @test d[2] ≈ T[1,1] - if c == 'V' - @test Q*T*Q' ≈ A - end - end - end -end - -@testset "lacpy!" begin - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - n = 10 - A = rand(elty, n, n) - for uplo in ('L', 'U', 'N') - B = zeros(elty, n, n) - LinearAlgebra.LAPACK.lacpy!(B, A, uplo) - C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) - @test B == C - B = zeros(elty, n+1, n+1) - LinearAlgebra.LAPACK.lacpy!(B, A, uplo) - C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) - @test view(B, 1:n, 1:n) == C - end - A = rand(elty, n, n+1) - B = zeros(elty, n, n) - LinearAlgebra.LAPACK.lacpy!(B, A, 'L') - @test B == view(tril(A), 1:n, 1:n) - B = zeros(elty, n, n+1) - LinearAlgebra.LAPACK.lacpy!(B, A, 'U') - @test B == triu(A) - A = rand(elty, n+1, n) - B = zeros(elty, n, n) - LinearAlgebra.LAPACK.lacpy!(B, A, 'U') - @test B == view(triu(A), 1:n, 1:n) - B = zeros(elty, n+1, n) - LinearAlgebra.LAPACK.lacpy!(B, A, 'L') - @test B == tril(A) - end -end - -@testset "Julia vs LAPACK" begin - # Test our own linear algebra functionality against LAPACK - @testset for elty in (Float32, Float64, ComplexF32, ComplexF64) - for nn in (5,10,15) - if elty <: Real - A = convert(Matrix{elty}, randn(10,nn)) - else - A = convert(Matrix{elty}, complex.(randn(10,nn),randn(10,nn))) - end ## LU (only equal for real because LAPACK uses different absolute value when choosing permutations) - if elty <: Real - FJulia = LinearAlgebra.generic_lufact!(copy(A)) - FLAPACK = LinearAlgebra.LAPACK.getrf!(copy(A)) - @test FJulia.factors ≈ FLAPACK[1] - @test FJulia.ipiv ≈ FLAPACK[2] - @test FJulia.info ≈ FLAPACK[3] - end - - ## QR - FJulia = LinearAlgebra.qrfactUnblocked!(copy(A)) - FLAPACK = LinearAlgebra.LAPACK.geqrf!(copy(A)) - @test FJulia.factors ≈ FLAPACK[1] - @test FJulia.τ ≈ FLAPACK[2] - end - end -end - -# Issue 13976 -let A = [NaN 0.0 NaN; 0 0 0; NaN 0 NaN] - @test_throws ArgumentError exp(A) -end - -# Issue 14065 (and 14220) -let A = [NaN NaN; NaN NaN] - @test_throws ArgumentError eigen(A) -end - -# Issue #42762 https://github.com/JuliaLang/julia/issues/42762 -# Tests geqrf! and gerqf! with null column dimensions -a = zeros(2,0), zeros(0) -@test LinearAlgebra.LAPACK.geqrf!(a...) === a -@test LinearAlgebra.LAPACK.gerqf!(a...) === a - -# Issue #49489: https://github.com/JuliaLang/julia/issues/49489 -# Dimension mismatch between A and ipiv causes segfaults -@testset "issue #49489" begin - A = randn(23,23) - b = randn(23) - ipiv = collect(1:20) - @test_throws DimensionMismatch LinearAlgebra.LAPACK.getrs!('N', A, ipiv, b) -end - -@testset "hetrd ignore non-filled half" begin - A = rand(3,3) - B = copy(A) - B[2,1] = NaN - B[3,1] = Inf - LAPACK.hetrd!('U', A) - LAPACK.hetrd!('U', B) - @test UpperTriangular(A) == UpperTriangular(B) -end - -@testset "inference in syev!/syevd!" begin - for T in (Float32, Float64), CT in (T, Complex{T}) - A = rand(CT, 4,4) - @inferred (A -> LAPACK.syev!('N', 'U', A))(A) - @inferred (A -> LAPACK.syev!('V', 'U', A))(A) - @inferred (A -> LAPACK.syevd!('N', 'U', A))(A) - @inferred (A -> LAPACK.syevd!('V', 'U', A))(A) - end -end - -end # module TestLAPACK diff --git a/stdlib/LinearAlgebra/test/ldlt.jl b/stdlib/LinearAlgebra/test/ldlt.jl deleted file mode 100644 index 51abf31086091..0000000000000 --- a/stdlib/LinearAlgebra/test/ldlt.jl +++ /dev/null @@ -1,41 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestLDLT - -using Test, LinearAlgebra, Random - -Random.seed!(123) - -@testset "Factorization conversions of LDLT" begin - S = SymTridiagonal(randn(5), randn(4)) - F = ldlt(S) - @test Factorization{eltype(S)}(F) === F - @test Array(Factorization{complex(eltype(S))}(F)) ≈ Array(ldlt(complex(S))) - @test eltype(Factorization{complex(eltype(S))}) == complex(eltype(S)) -end - -@testset "eltype conversions of LDLT" begin - S = SymTridiagonal(randn(5), randn(4)) - F = ldlt(S) - Fc = LDLt{ComplexF32}(F.data) - @test Fc isa LDLt{ComplexF32} - @test Array(Fc) ≈ ComplexF32.(Array(S)) -end - -@testset "Accessing fields of LDLT" begin - S = SymTridiagonal(randn(5), randn(4)) - F = ldlt(S) - @test getproperty(F, :L) == transpose(getproperty(F, :Lt)) - @test getproperty(F, :d) == diag(getproperty(F, :D), 0) -end - -@testset "REPL printing of LDLT" begin - S = SymTridiagonal(randn(5), randn(4)) - F = ldlt(S) - ldltstring = sprint((t, s) -> show(t, "text/plain", s), F) - lstring = sprint((t, s) -> show(t, "text/plain", s), F.L) - dstring = sprint((t, s) -> show(t, "text/plain", s), F.D) - @test ldltstring == "$(summary(F))\nL factor:\n$lstring\nD factor:\n$dstring" -end - -end # module TestLDLT diff --git a/stdlib/LinearAlgebra/test/lq.jl b/stdlib/LinearAlgebra/test/lq.jl deleted file mode 100644 index c3499f7f46fa6..0000000000000 --- a/stdlib/LinearAlgebra/test/lq.jl +++ /dev/null @@ -1,237 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestLQ - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, rmul!, lmul! - -m = 10 - -Random.seed!(1234321) - -asquare = randn(ComplexF64, m, m) / 2 -awide = randn(ComplexF64, m, m+3) / 2 -bcomplex = randn(ComplexF64, m, 2) / 2 - -# helper functions to unambiguously recover explicit forms of an LQPackedQ -squareQ(Q::LinearAlgebra.LQPackedQ) = (n = size(Q.factors, 2); lmul!(Q, Matrix{eltype(Q)}(I, n, n))) -rectangularQ(Q::LinearAlgebra.LQPackedQ) = convert(Array, Q) - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64), n in (m, size(awide, 2)) - adata = m == n ? asquare : awide - a = convert(Matrix{eltya}, eltya <: Complex ? adata : real(adata)) - ε = εa = eps(abs(float(one(eltya)))) - n1 = n ÷ 2 - - α = rand(eltya) - aα = fill(α,1,1) - @test lq(α).L*lq(α).Q ≈ lq(aα).L*lq(aα).Q - @test abs(lq(α).Q[1,1]) ≈ one(eltya) - - @testset for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int) - b = eltyb == Int ? rand(1:5, m, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? bcomplex : real(bcomplex)) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - - tab = promote_type(eltya,eltyb) - - @testset for isview in (false,true) - let a = isview ? view(a, 1:m - 1, 1:n - 1) : a, b = isview ? view(b, 1:m - 1) : b, m = m - isview, n = n - isview - lqa = lq(a) - x = lqa\b - l, q = lqa.L, lqa.Q - qra = qr(a, ColumnNorm()) - @testset "Basic ops" begin - @test size(lqa,1) == size(a,1) - @test size(lqa,3) == 1 - @test size(lqa.Q,3) == 1 - @test Base.propertynames(lqa) == (:L, :Q) - ref_obs = (l, q) - for (ii, lq_obj) in enumerate(lqa) - @test ref_obs[ii] == lq_obj - end - @test_throws FieldError lqa.Z - @test Array(copy(adjoint(lqa))) ≈ a' - @test q*squareQ(q)' ≈ Matrix(I, n, n) - @test l*q ≈ a - @test Array(lqa) ≈ a - @test Array(copy(lqa)) ≈ a - @test LinearAlgebra.Factorization{eltya}(lqa) === lqa - @test Matrix{eltya}(q) isa Matrix{eltya} - # test Array{T}(LQPackedQ{T}) - @test Array{eltya}(q) ≈ Matrix(q) - end - @testset "Binary ops" begin - k = size(a, 2) - T = Tridiagonal(rand(eltya, k-1), rand(eltya, k), rand(eltya, k-1)) - @test lq(T) * T ≈ T * T rtol=3000ε - @test lqa * T ≈ a * T rtol=3000ε - @test a*x ≈ b rtol=3000ε - @test x ≈ qra \ b rtol=3000ε - @test lqa*x ≈ a*x rtol=3000ε - @test (sq = size(q.factors, 2); *(Matrix{eltyb}(I, sq, sq), adjoint(q))*squareQ(q)) ≈ Matrix(I, n, n) rtol=5000ε - if eltya != Int - @test Matrix{eltyb}(I, n, n)*q ≈ Matrix(I, n, n) * convert(LinearAlgebra.AbstractQ{tab}, q) - end - @test q*x ≈ squareQ(q)*x rtol=100ε - @test q'*x ≈ squareQ(q)'*x rtol=100ε - @test a*q ≈ a*squareQ(q) rtol=100ε - @test a*q' ≈ a*squareQ(q)' rtol=100ε - @test q*a'≈ squareQ(q)*a' rtol=100ε - @test q'*a' ≈ squareQ(q)'*a' rtol=100ε - @test_throws DimensionMismatch q*x[1:n1 + 1] - @test_throws DimensionMismatch adjoint(q) * Matrix{eltya}(undef,m+2,m+2) - @test_throws DimensionMismatch Matrix{eltyb}(undef,m+2,m+2)*q - if isa(a, DenseArray) && isa(b, DenseArray) - # use this to test 2nd branch in mult code - pad_a = vcat(I, a) - pad_x = hcat(I, x) - @test pad_a*q ≈ pad_a*squareQ(q) rtol=100ε - @test q'*pad_x ≈ squareQ(q)'*pad_x rtol=100ε - end - end - end - end - - @testset "Matmul with LQ factorizations" begin - lqa = lq(a[:,1:n1]) - l,q = lqa.L, lqa.Q - @test rectangularQ(q)*rectangularQ(q)' ≈ Matrix(I, n1, n1) - @test squareQ(q)'*squareQ(q) ≈ Matrix(I, n1, n1) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1),q) - @test lmul!(adjoint(q), rectangularQ(q)) ≈ Matrix(I, n1, n1) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1), adjoint(q)) - @test_throws BoundsError size(q,-1) - end - end -end - -@testset "getindex on LQPackedQ (#23733)" begin - local m, n - function getqs(F::LinearAlgebra.LQ) - implicitQ = F.Q - sq = size(implicitQ.factors, 2) - explicitQ = lmul!(implicitQ, Matrix{eltype(implicitQ)}(I, sq, sq)) - return implicitQ, explicitQ - end - - m, n = 3, 3 # reduced Q 3-by-3, full Q 3-by-3 - implicitQ, explicitQ = getqs(lq(randn(m, n))) - @test implicitQ[1, 1] == explicitQ[1, 1] - @test implicitQ[m, 1] == explicitQ[m, 1] - @test implicitQ[1, n] == explicitQ[1, n] - @test implicitQ[m, n] == explicitQ[m, n] - - m, n = 3, 4 # reduced Q 3-by-4, full Q 4-by-4 - implicitQ, explicitQ = getqs(lq(randn(m, n))) - @test implicitQ[1, 1] == explicitQ[1, 1] - @test implicitQ[m, 1] == explicitQ[m, 1] - @test implicitQ[1, n] == explicitQ[1, n] - @test implicitQ[m, n] == explicitQ[m, n] - @test implicitQ[m+1, 1] == explicitQ[m+1, 1] - @test implicitQ[m+1, n] == explicitQ[m+1, n] - - m, n = 4, 3 # reduced Q 3-by-3, full Q 3-by-3 - implicitQ, explicitQ = getqs(lq(randn(m, n))) - @test implicitQ[1, 1] == explicitQ[1, 1] - @test implicitQ[n, 1] == explicitQ[n, 1] - @test implicitQ[1, n] == explicitQ[1, n] - @test implicitQ[n, n] == explicitQ[n, n] -end - -@testset "size on LQPackedQ (#23780)" begin - # size(Q::LQPackedQ) yields the shape of Q's full/square form - for ((mA, nA), nQ) in ( - ((3, 3), 3), # A 3-by-3 => full/square Q 3-by-3 - ((3, 4), 4), # A 3-by-4 => full/square Q 4-by-4 - ((4, 3), 3) )# A 4-by-3 => full/square Q 3-by-3 - @test size(lq(randn(mA, nA)).Q) == (nQ, nQ) - end -end - -@testset "postmultiplication with / right-application of LQPackedQ (#23779)" begin - function getqs(F::LinearAlgebra.LQ) - implicitQ = F.Q - explicitQ = lmul!(implicitQ, Matrix{eltype(implicitQ)}(I, size(implicitQ)...)) - return implicitQ, explicitQ - end - # for any shape m-by-n of LQ-factored matrix, where Q is an LQPackedQ - # A_mul_B*(C, Q) (Ac_mul_B*(C, Q)) operations should work for - # *-by-n (n-by-*) C, which we test below via n-by-n C - for (mA, nA) in ((3, 3), (3, 4), (4, 3)) - implicitQ, explicitQ = getqs(lq(randn(mA, nA))) - C = randn(nA, nA) - @test *(C, implicitQ) ≈ *(C, explicitQ) - @test *(C, adjoint(implicitQ)) ≈ *(C, adjoint(explicitQ)) - @test *(adjoint(C), implicitQ) ≈ *(adjoint(C), explicitQ) - @test *(adjoint(C), adjoint(implicitQ)) ≈ *(adjoint(C), adjoint(explicitQ)) - end - # where the LQ-factored matrix has at least as many rows m as columns n, - # Q's full/square and reduced/rectangular forms have the same shape (n-by-n). hence we expect - # _only_ *-by-n (n-by-*) C to work in A_mul_B*(C, Q) (Ac_mul_B*(C, Q)) ops. - # and hence the n-by-n C tests above suffice. - # - # where the LQ-factored matrix has more columns n than rows m, - # Q's full/square form is n-by-n whereas its reduced/rectangular form is m-by-n. - # hence we need also test *-by-m C with - # A*_mul_B(C, Q) ops, as below via m-by-m C. - mA, nA = 3, 4 - implicitQ, explicitQ = getqs(lq(randn(mA, nA))) - C = randn(mA, mA) - zeroextCright = hcat(C, zeros(eltype(C), mA)) - zeroextCdown = vcat(C, zeros(eltype(C), (1, mA))) - @test *(C, implicitQ) ≈ *(zeroextCright, explicitQ) - @test *(adjoint(C), implicitQ) ≈ *(adjoint(zeroextCdown), explicitQ) - @test_throws DimensionMismatch C * adjoint(implicitQ) - @test_throws DimensionMismatch adjoint(C) * adjoint(implicitQ) -end - -@testset "det(Q::LQPackedQ)" begin - @testset for n in 1:3, m in 1:3 - @testset "real" begin - _, Q = lq(randn(n, m)) - @test det(Q) ≈ det(Q*I) - @test abs(det(Q)) ≈ 1 - end - @testset "complex" begin - _, Q = lq(randn(ComplexF64, n, m)) - @test det(Q) ≈ det(Q*I) - @test abs(det(Q)) ≈ 1 - end - end -end - -@testset "REPL printing" begin - bf = IOBuffer() - show(bf, "text/plain", lq(Matrix(I, 4, 4))) - seekstart(bf) - @test String(take!(bf)) == """ -$(LinearAlgebra.LQ){Float64, Matrix{Float64}, Vector{Float64}} -L factor: -4×4 Matrix{Float64}: - 1.0 0.0 0.0 0.0 - 0.0 1.0 0.0 0.0 - 0.0 0.0 1.0 0.0 - 0.0 0.0 0.0 1.0 -Q factor: 4×4 $(LinearAlgebra.LQPackedQ){Float64, Matrix{Float64}, Vector{Float64}}""" -end - -@testset "adjoint of LQ" begin - n = 5 - - for b in (ones(n), ones(n, 2), ones(Complex{Float64}, n, 2)) - for A in ( - randn(n, n), - # Tall problems become least squares problems similarly to QR - randn(n - 2, n), - complex.(randn(n, n), randn(n, n))) - - F = lq(A) - @test A'\b ≈ F'\b - end - @test_throws DimensionMismatch lq(randn(n, n + 2))'\b - end - -end - -end # module TestLQ diff --git a/stdlib/LinearAlgebra/test/lu.jl b/stdlib/LinearAlgebra/test/lu.jl deleted file mode 100644 index 56a402d70493e..0000000000000 --- a/stdlib/LinearAlgebra/test/lu.jl +++ /dev/null @@ -1,502 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestLU - -using Test, LinearAlgebra, Random -using LinearAlgebra: ldiv!, BlasReal, BlasInt, BlasFloat, rdiv! - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(1234324) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 -breal = randn(n,2)/2 -bimg = randn(n,2)/2 -creal = randn(n)/2 -cimg = randn(n)/2 -dureal = randn(n-1)/2 -duimg = randn(n-1)/2 -dlreal = randn(n-1)/2 -dlimg = randn(n-1)/2 -dreal = randn(n)/2 -dimg = randn(n)/2 - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - a = eltya == Int ? rand(1:7, n, n) : - convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - d = if eltya == Int - Tridiagonal(rand(1:7, n-1), rand(1:7, n), rand(1:7, n-1)) - elseif eltya <: Complex - convert(Tridiagonal{eltya}, Tridiagonal( - complex.(dlreal, dlimg), complex.(dreal, dimg), complex.(dureal, duimg))) - else - convert(Tridiagonal{eltya}, Tridiagonal(dlreal, dreal, dureal)) - end - εa = eps(abs(float(one(eltya)))) - - if eltya <: BlasFloat - @testset "LU factorization for Number" begin - num = rand(eltya) - @test (lu(num)...,) == (hcat(one(eltya)), hcat(num), [1]) - @test convert(Array, lu(num)) ≈ eltya[num] - end - @testset "Balancing in eigenvector calculations" begin - A = convert(Matrix{eltya}, [ 3.0 -2.0 -0.9 2*eps(real(one(eltya))); - -2.0 4.0 1.0 -eps(real(one(eltya))); - -eps(real(one(eltya)))/4 eps(real(one(eltya)))/2 -1.0 0; - -0.5 -0.5 0.1 1.0]) - F = eigen(A, permute=false, scale=false) - @test F.vectors*Diagonal(F.values)/F.vectors ≈ A - F = eigen(A) - # @test norm(F.vectors*Diagonal(F.values)/F.vectors - A) > 0.01 - end - end - κ = cond(a,1) - @testset "(Automatic) Square LU decomposition" begin - lua = factorize(a) - @test_throws FieldError lua.Z - l,u,p = lua.L, lua.U, lua.p - ll,ul,pl = @inferred lu(a) - @test ll * ul ≈ a[pl,:] - @test l*u ≈ a[p,:] - @test (l*u)[invperm(p),:] ≈ a - @test a * inv(lua) ≈ Matrix(I, n, n) - @test copy(lua) == lua - if eltya <: BlasFloat - # test conversion of LU factorization's numerical type - bft = eltya <: Real ? LinearAlgebra.LU{BigFloat} : LinearAlgebra.LU{Complex{BigFloat}} - bflua = convert(bft, lua) - @test bflua.L*bflua.U ≈ big.(a)[p,:] rtol=εa*norm(a) - @test Factorization{eltya}(lua) === lua - # test Factorization with different eltype - if eltya <: BlasReal - @test Array(Factorization{Float16}(lua)) ≈ Array(lu(convert(Matrix{Float16}, a))) - @test eltype(Factorization{Float16}(lua)) == Float16 - end - end - # compact printing - lstring = sprint(show,l) - ustring = sprint(show,u) - end - κd = cond(Array(d),1) - @testset "Tridiagonal LU" begin - lud = @inferred lu(d) - @test LinearAlgebra.issuccess(lud) - @test @inferred(lu(lud)) == lud - @test_throws FieldError lud.Z - @test lud.L*lud.U ≈ lud.P*Array(d) - @test lud.L*lud.U ≈ Array(d)[lud.p,:] - @test AbstractArray(lud) ≈ d - @test Array(lud) ≈ d - if eltya != Int - dlu = convert.(eltya, [1, 1]) - dia = convert.(eltya, [-2, -2, -2]) - tri = Tridiagonal(dlu, dia, dlu) - L = lu(tri) - @test lu!(tri) == L - @test UpperTriangular(tri) == L.U - end - end - @testset for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int) - b = eltyb == Int ? rand(1:5, n, 2) : - convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) - c = eltyb == Int ? rand(1:5, n) : - convert(Vector{eltyb}, eltyb <: Complex ? complex.(creal, cimg) : creal) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - @testset "(Automatic) Square LU decomposition" begin - lua = factorize(a) - let Bs = copy(b), Cs = copy(c) - for (bb, cc) in ((Bs, Cs), (view(Bs, 1:n, 1), view(Cs, 1:n))) - @test norm(a*(lua\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a'*(lua'\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a'*(lua'\a') - a', 1) < ε*κ*n^2 - @test norm(a*(lua\cc) - cc, 1) < ε*κ*n # cc is a vector - @test norm(a'*(lua'\cc) - cc, 1) < ε*κ*n # cc is a vector - @test AbstractArray(lua) ≈ a - @test norm(transpose(a)*(transpose(lua)\bb) - bb,1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(transpose(a)*(transpose(lua)\cc) - cc,1) < ε*κ*n - end - - # Test whether Ax_ldiv_B!(y, LU, x) indeed overwrites y - resultT = typeof(oneunit(eltyb) / oneunit(eltya)) - - b_dest = similar(b, resultT) - c_dest = similar(c, resultT) - - ldiv!(b_dest, lua, b) - ldiv!(c_dest, lua, c) - @test norm(b_dest - lua \ b, 1) < ε*κ*2n - @test norm(c_dest - lua \ c, 1) < ε*κ*n - - ldiv!(b_dest, transpose(lua), b) - ldiv!(c_dest, transpose(lua), c) - @test norm(b_dest - transpose(lua) \ b, 1) < ε*κ*2n - @test norm(c_dest - transpose(lua) \ c, 1) < ε*κ*n - - ldiv!(b_dest, adjoint(lua), b) - ldiv!(c_dest, adjoint(lua), c) - @test norm(b_dest - lua' \ b, 1) < ε*κ*2n - @test norm(c_dest - lua' \ c, 1) < ε*κ*n - - if eltyb != Int && !(eltya <: Complex) || eltya <: Complex && eltyb <: Complex - p = Matrix(b') - q = Matrix(c') - p_dest = copy(p) - q_dest = copy(q) - rdiv!(p_dest, lua) - rdiv!(q_dest, lua) - @test norm(p_dest - p / lua, 1) < ε*κ*2n - @test norm(q_dest - q / lua, 1) < ε*κ*n - end - end - if eltya <: BlasFloat && eltyb <: BlasFloat - e = rand(eltyb,n,n) - @test norm(e/lua - e/a,1) < ε*κ*n^2 - end - end - @testset "Tridiagonal LU" begin - lud = factorize(d) - f = zeros(eltyb, n+1) - @test_throws DimensionMismatch lud\f - @test_throws DimensionMismatch transpose(lud)\f - @test_throws DimensionMismatch lud'\f - @test_throws DimensionMismatch LinearAlgebra.ldiv!(transpose(lud), f) - let Bs = copy(b) - for bb in (Bs, view(Bs, 1:n, 1)) - @test norm(d*(lud\bb) - bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns - if eltya <: Real - @test norm((transpose(lud)\bb) - Array(transpose(d))\bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns - if eltya != Int && eltyb != Int - @test norm(LinearAlgebra.ldiv!(transpose(lud), copy(bb)) - Array(transpose(d))\bb, 1) < ε*κd*n*2 - end - end - if eltya <: Complex - dummy_factor = 2.5 - # TODO: Remove dummy_factor, this test started failing when the RNG stream changed - # so the factor was added. - @test norm((lud'\bb) - Array(d')\bb, 1) < ε*κd*n*2*dummy_factor # Two because the right hand side has two columns - end - end - end - if eltya <: BlasFloat && eltyb <: BlasFloat - e = rand(eltyb,n,n) - @test norm(e/lud - e/d,1) < ε*κ*n^2 - @test norm((transpose(lud)\e') - Array(transpose(d))\e',1) < ε*κd*n^2 - #test singular - du = rand(eltya,n-1) - dl = rand(eltya,n-1) - dd = rand(eltya,n) - dd[1] = zero(eltya) - du[1] = zero(eltya) - dl[1] = zero(eltya) - zT = Tridiagonal(dl,dd,du) - @test !LinearAlgebra.issuccess(lu(zT; check = false)) - end - end - @testset "Thin LU" begin - lua = @inferred lu(a[:,1:n1]) - @test lua.L*lua.U ≈ lua.P*a[:,1:n1] - end - @testset "Fat LU" begin - lua = @inferred lu(a[1:n1,:]) - @test lua.L*lua.U ≈ lua.P*a[1:n1,:] - end - end - - @testset "LU of Symmetric/Hermitian" begin - for HS in (Hermitian(a'a), Symmetric(a'a)) - luhs = @inferred lu(HS) - @test luhs.L*luhs.U ≈ luhs.P*Matrix(HS) - end - end - - @testset "Factorization of symtridiagonal dense matrix with zero ldlt-pivot (#38026)" begin - A = [0.0 -1.0 0.0 0.0 - -1.0 0.0 0.0 0.0 - 0.0 0.0 0.0 -1.0 - 0.0 0.0 -1.0 0.0] - F = factorize(A) - @test all((!isnan).(Matrix(F))) - end -end - -@testset "Small tridiagonal matrices" for T in (Float64, ComplexF64) - A = Tridiagonal(T[], T[1], T[]) - @test inv(A) == A -end - -@testset "Singular matrices" for T in (Float64, ComplexF64) - A = T[1 2; 0 0] - @test_throws SingularException lu(A) - @test_throws SingularException lu!(copy(A)) - @test_throws SingularException lu(A; check = true) - @test_throws SingularException lu!(copy(A); check = true) - @test !issuccess(lu(A; check = false)) - @test !issuccess(lu!(copy(A); check = false)) - @test_throws ZeroPivotException lu(A, NoPivot()) - @test_throws ZeroPivotException lu!(copy(A), NoPivot()) - @test_throws ZeroPivotException lu(A, NoPivot(); check = true) - @test_throws ZeroPivotException lu!(copy(A), NoPivot(); check = true) - @test !issuccess(lu(A, NoPivot(); check = false)) - @test !issuccess(lu!(copy(A), NoPivot(); check = false)) - F = lu(A, NoPivot(); check = false) - @test sprint((io, x) -> show(io, "text/plain", x), F) == - "Failed factorization of type $(typeof(F))" - F2 = lu(A; allowsingular = true) - @test !issuccess(F2) - @test issuccess(F2, allowsingular = true) - @test occursin("U factor (rank-deficient)", sprint((io, x) -> show(io, "text/plain", x), F2)) -end - -@testset "conversion" begin - Random.seed!(4) - a = Tridiagonal(rand(9),rand(10),rand(9)) - fa = Array(a) - falu = lu(fa) - alu = lu(a) - falu = convert(typeof(falu),alu) - @test Array(alu) == fa - @test AbstractArray(alu) == fa -end - -@testset "Rational Matrices" begin - ## Integrate in general tests when more linear algebra is implemented in julia - a = convert(Matrix{Rational{BigInt}}, rand(1:10//1,n,n))/n - b = rand(1:10,n,2) - @inferred lu(a) - lua = factorize(a) - l,u,p = lua.L, lua.U, lua.p - @test l*u ≈ a[p,:] - @test l[invperm(p),:]*u ≈ a - @test a*inv(lua) ≈ Matrix(I, n, n) - let Bs = b - for b in (Bs, view(Bs, 1:n, 1)) - @test a*(lua\b) ≈ b - end - end - @test @inferred(det(a)) ≈ det(Array{Float64}(a)) -end - -@testset "Rational{BigInt} and BigFloat Hilbert Matrix" begin - ## Hilbert Matrix (very ill conditioned) - ## Testing Rational{BigInt} and BigFloat version - nHilbert = 50 - H = Rational{BigInt}[1//(i+j-1) for i = 1:nHilbert,j = 1:nHilbert] - Hinv = Rational{BigInt}[(-1)^(i+j)*(i+j-1)*binomial(nHilbert+i-1,nHilbert-j)* - binomial(nHilbert+j-1,nHilbert-i)*binomial(i+j-2,i-1)^2 - for i = big(1):nHilbert,j=big(1):nHilbert] - @test inv(H) == Hinv - setprecision(2^10) do - @test norm(Array{Float64}(inv(float(H)) - float(Hinv))) < 1e-100 - end -end - -@testset "logdet" begin - @test @inferred(logdet(ComplexF32[1.0f0 0.5f0; 0.5f0 -1.0f0])) === 0.22314355f0 + 3.1415927f0im - @test_throws DomainError logdet([1 1; 1 -1]) -end - -@testset "REPL printing" begin - bf = IOBuffer() - show(bf, "text/plain", lu(Matrix(I, 4, 4))) - seekstart(bf) - @test String(take!(bf)) == """ -$(LinearAlgebra.LU){Float64, Matrix{Float64}, Vector{$Int}} -L factor: -4×4 Matrix{Float64}: - 1.0 0.0 0.0 0.0 - 0.0 1.0 0.0 0.0 - 0.0 0.0 1.0 0.0 - 0.0 0.0 0.0 1.0 -U factor: -4×4 Matrix{Float64}: - 1.0 0.0 0.0 0.0 - 0.0 1.0 0.0 0.0 - 0.0 0.0 1.0 0.0 - 0.0 0.0 0.0 1.0""" -end - -@testset "propertynames" begin - names = sort!(collect(string.(Base.propertynames(lu(rand(3,3)))))) - @test names == ["L", "P", "U", "p"] - allnames = sort!(collect(string.(Base.propertynames(lu(rand(3,3)), true)))) - @test allnames == ["L", "P", "U", "factors", "info", "ipiv", "p"] -end - -include("trickyarithmetic.jl") - -@testset "lu with type whose sum is another type" begin - A = TrickyArithmetic.A[1 2; 3 4] - ElT = TrickyArithmetic.D{TrickyArithmetic.C,TrickyArithmetic.C} - B = lu(A, NoPivot()) - @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 - 1 0 0 1 0 0 1 0 - 0 1 1 1 0 0 0 0 - 0 1 0 0 0 0 1 1 - 0 0 1 0 1 0 0 1 - 0 0 0 1 1 1 0 0 - 0 0 0 0 1 1 0 1]) ≈ 6 -end - -@testset "Issue #33177. No ldiv!(LU, Adjoint)" begin - A = [1 0; 1 1] - B = [1 2; 2 8] - F = lu(B) - @test (A / F') * B == A - @test (A' / F') * B == A' - - a = complex.(randn(2), randn(2)) - @test (a' / F') * B ≈ a' - @test (transpose(a) / F') * B ≈ transpose(a) - - A = complex.(randn(2, 2), randn(2, 2)) - @test (A' / F') * B ≈ A' - @test (transpose(A) / F') * B ≈ transpose(A) -end - -@testset "0x0 matrix" begin - A = ones(0, 0) - F = lu(A) - @test F.U == ones(0, 0) - @test F.L == ones(0, 0) - @test F.P == ones(0, 0) - @test F.p == [] -end - -@testset "more rdiv! methods" begin - for elty in (Float16, Float64, ComplexF64), transform in (transpose, adjoint) - A = randn(elty, 5, 5) - C = copy(A) - B = randn(elty, 5, 5) - @test rdiv!(transform(A), transform(lu(B))) ≈ transform(C) / transform(B) - end - for elty in (Float32, Float64, ComplexF64), transF in (identity, transpose), - transB in (transpose, adjoint), transT in (identity, complex) - A = randn(elty, 5, 5) - F = lu(A) - b = randn(transT(elty), 5) - @test rdiv!(transB(copy(b)), transF(F)) ≈ transB(b) / transF(F) ≈ transB(b) / transF(A) - B = randn(transT(elty), 5, 5) - @test rdiv!(copy(B), transF(F)) ≈ B / transF(F) ≈ B / transF(A) - end -end - -@testset "transpose(A) / lu(B)' should not overwrite A (#36657)" begin - for elty in (Float16, Float64, ComplexF64) - A = randn(elty, 5, 5) - B = randn(elty, 5, 5) - C = copy(A) - a = randn(elty, 5) - c = copy(a) - @test transpose(A) / lu(B)' ≈ transpose(A) / B' - @test transpose(a) / lu(B)' ≈ transpose(a) / B' - @test A == C - @test a == c - end -end - -@testset "lu on *diagonal matrices" begin - dl = rand(3) - d = rand(4) - Bl = Bidiagonal(d, dl, :L) - Bu = Bidiagonal(d, dl, :U) - Tri = Tridiagonal(dl, d, dl) - Sym = SymTridiagonal(d, dl) - D = Diagonal(d) - b = ones(4) - B = rand(4,4) - for A in (Bl, Bu, Tri, Sym, D), pivot in (NoPivot(), RowMaximum()) - @test A\b ≈ lu(A, pivot)\b - @test B/A ≈ B/lu(A, pivot) - @test B/A ≈ B/Matrix(A) - @test Matrix(lu(A, pivot)) ≈ A - @test @inferred(lu(A)) isa LU - if A isa Union{Bidiagonal, Diagonal, Tridiagonal, SymTridiagonal} - @test lu(A) isa LU{Float64, Tridiagonal{Float64, Vector{Float64}}} - @test lu(A, pivot) isa LU{Float64, Tridiagonal{Float64, Vector{Float64}}} - @test lu(A, pivot; check = false) isa LU{Float64, Tridiagonal{Float64, Vector{Float64}}} - end - end -end - -@testset "can push to vector after 3-arg ldiv! (#43507)" begin - u = rand(3) - A = rand(3,3) - b = rand(3) - ldiv!(u,lu(A),b) - push!(b,4.0) - @test length(b) == 4 -end - -@testset "NaN matrix should throw error" begin - for eltya in (NaN16, NaN32, NaN64, BigFloat(NaN)) - r = fill(eltya, 2, 3) - c = fill(complex(eltya, eltya), 2, 3) - @test_throws ArgumentError lu(r) - @test_throws ArgumentError lu(c) - end -end - -@testset "more generic ldiv! #35419" begin - A = rand(3, 3) - b = rand(3) - @test A * ldiv!(lu(A), Base.ReshapedArray(copy(b)', (3,), ())) ≈ b -end - -@testset "generic lu!" begin - A = rand(3,3); B = deepcopy(A); C = A[2:3,2:3] - Asub1 = @view(A[2:3,2:3]) - F1 = lu!(Asub1) - Asub2 = @view(B[[2,3],[2,3]]) - F2 = lu!(Asub2) - @test Matrix(F1) ≈ Matrix(F2) ≈ C -end - -@testset "matrix with Nonfinite" begin - lu(fill(NaN, 2, 2), check=false) - lu(fill(Inf, 2, 2), check=false) - LinearAlgebra.generic_lufact!(fill(NaN, 2, 2), check=false) - LinearAlgebra.generic_lufact!(fill(Inf, 2, 2), check=false) -end - -@testset "lu for empty matrices" begin - for T in (Float64, BigFloat) - A = fill(T(0.0), 0, 0) - v = fill(T(1.0), 0, 10) - @test A \ v ≈ lu(A) \ v - vt = permutedims(v) - @test vt / A ≈ vt / lu(A) - B = UpperTriangular(transpose(fill(complex(T(0.0)), 0, 0)')) - @test B \ v ≈ v - @test vt / B ≈ vt - end -end - -end # module TestLU diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl deleted file mode 100644 index 1294e97c2a30c..0000000000000 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ /dev/null @@ -1,1151 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestMatmul - -using Base: rtoldefault -using Test, LinearAlgebra, Random -using LinearAlgebra: mul!, Symmetric, Hermitian - -## Test Julia fallbacks to BLAS routines - -mul_wrappers = [ - m -> m, - m -> Symmetric(m, :U), - m -> Symmetric(m, :L), - m -> Hermitian(m, :U), - m -> Hermitian(m, :L), - m -> adjoint(m), - m -> transpose(m)] - -@testset "wrap" begin - f(A) = LinearAlgebra.wrap(A, 'N') - A = ones(1,1) - @test @inferred(f(A)) === A - g(A) = LinearAlgebra.wrap(A, 'T') - @test @inferred(g(A)) === transpose(A) - # https://github.com/JuliaLang/julia/issues/52202 - @test Base.infer_return_type((Vector{Float64},)) do v - LinearAlgebra.wrap(v, 'N') - end == Vector{Float64} - h(A) = LinearAlgebra.wrap(LinearAlgebra._unwrap(A), LinearAlgebra.wrapper_char(A)) - @test @inferred(h(transpose(A))) === transpose(A) - @test @inferred(h(adjoint(A))) === transpose(A) - - M = rand(2,2) - for S in (Symmetric(M), Hermitian(M)) - @test @inferred((A -> LinearAlgebra.wrap(parent(A), LinearAlgebra.wrapper_char(A)))(S)) === Symmetric(M) - end - M = rand(ComplexF64,2,2) - for S in (Symmetric(M), Hermitian(M)) - @test @inferred((A -> LinearAlgebra.wrap(parent(A), LinearAlgebra.wrapper_char(A)))(S)) === S - end - - @testset "WrapperChar" begin - @test LinearAlgebra.WrapperChar('c') == 'c' - @test LinearAlgebra.WrapperChar('C') == 'C' - @testset "constant propagation in uppercase/lowercase" begin - v = @inferred (() -> Val(uppercase(LinearAlgebra.WrapperChar('C'))))() - @test v isa Val{'C'} - v = @inferred (() -> Val(uppercase(LinearAlgebra.WrapperChar('s'))))() - @test v isa Val{'S'} - v = @inferred (() -> Val(lowercase(LinearAlgebra.WrapperChar('C'))))() - @test v isa Val{'c'} - v = @inferred (() -> Val(lowercase(LinearAlgebra.WrapperChar('s'))))() - @test v isa Val{'s'} - end - end -end - -@testset "matrices with zero dimensions" begin - for (dimsA, dimsB, dimsC) in ( - ((0, 5), (5, 3), (0, 3)), - ((3, 5), (5, 0), (3, 0)), - ((3, 0), (0, 4), (3, 4)), - ((0, 5), (5, 0), (0, 0)), - ((0, 0), (0, 4), (0, 4)), - ((3, 0), (0, 0), (3, 0)), - ((0, 0), (0, 0), (0, 0))) - @test Matrix{Float64}(undef, dimsA) * Matrix{Float64}(undef, dimsB) == zeros(dimsC) - end - @test Matrix{Float64}(undef, 5, 0) |> t -> t't == zeros(0, 0) - @test Matrix{Float64}(undef, 5, 0) |> t -> t * t' == zeros(5, 5) - @test Matrix{ComplexF64}(undef, 5, 0) |> t -> t't == zeros(0, 0) - @test Matrix{ComplexF64}(undef, 5, 0) |> t -> t * t' == zeros(5, 5) -end -@testset "2x2 matmul" begin - AA = [1 2; 3 4] - BB = [5 6; 7 8] - AAi = AA + (0.5 * im) .* BB - BBi = BB + (2.5 * im) .* AA[[2, 1], [2, 1]] - for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) - @test A * B == [19 22; 43 50] - @test *(transpose(A), B) == [26 30; 38 44] - @test *(A, transpose(B)) == [17 23; 39 53] - @test *(transpose(A), transpose(B)) == [23 31; 34 46] - end - for Ai in (copy(AAi), view(AAi, 1:2, 1:2)), Bi in (copy(BBi), view(BBi, 1:2, 1:2)) - @test Ai * Bi == [-21+53.5im -4.25+51.5im; -12+95.5im 13.75+85.5im] - @test *(adjoint(Ai), Bi) == [68.5-12im 57.5-28im; 88-3im 76.5-25im] - @test *(Ai, adjoint(Bi)) == [64.5+5.5im 43+31.5im; 104-18.5im 80.5+31.5im] - @test *(adjoint(Ai), adjoint(Bi)) == [-28.25-66im 9.75-58im; -26-89im 21-73im] - @test_throws DimensionMismatch [1 2; 0 0; 0 0] * [1 2] - end - for wrapper_a in mul_wrappers, wrapper_b in mul_wrappers - @test wrapper_a(AA) * wrapper_b(BB) == Array(wrapper_a(AA)) * Array(wrapper_b(BB)) - end - @test_throws DimensionMismatch mul!(Matrix{Float64}(undef, 3, 3), AA, BB) -end -@testset "3x3 matmul" begin - AA = [1 2 3; 4 5 6; 7 8 9] .- 5 - BB = [1 0 5; 6 -10 3; 2 -4 -1] - AAi = AA + (0.5 * im) .* BB - BBi = BB + (2.5 * im) .* AA[[2, 1, 3], [2, 3, 1]] - for A in (copy(AA), view(AA, 1:3, 1:3)), B in (copy(BB), view(BB, 1:3, 1:3)) - @test A * B == [-26 38 -27; 1 -4 -6; 28 -46 15] - @test *(adjoint(A), B) == [-6 2 -25; 3 -12 -18; 12 -26 -11] - @test *(A, adjoint(B)) == [-14 0 6; 4 -3 -3; 22 -6 -12] - @test *(adjoint(A), adjoint(B)) == [6 -8 -6; 12 -9 -9; 18 -10 -12] - end - for Ai in (copy(AAi), view(AAi, 1:3, 1:3)), Bi in (copy(BBi), view(BBi, 1:3, 1:3)) - @test Ai * Bi == [-44.75+13im 11.75-25im -38.25+30im; -47.75-16.5im -51.5+51.5im -56+6im; 16.75-4.5im -53.5+52im -15.5im] - @test *(adjoint(Ai), Bi) == [-21+2im -1.75+49im -51.25+19.5im; 25.5+56.5im -7-35.5im 22+35.5im; -3+12im -32.25+43im -34.75-2.5im] - @test *(Ai, adjoint(Bi)) == [-20.25+15.5im -28.75-54.5im 22.25+68.5im; -12.25+13im -15.5+75im -23+27im; 18.25+im 1.5+94.5im -27-54.5im] - @test *(adjoint(Ai), adjoint(Bi)) == [1+2im 20.75+9im -44.75+42im; 19.5+17.5im -54-36.5im 51-14.5im; 13+7.5im 11.25+31.5im -43.25-14.5im] - @test_throws DimensionMismatch [1 2 3; 0 0 0; 0 0 0] * [1 2 3] - end - for wrapper_a in mul_wrappers, wrapper_b in mul_wrappers - @test wrapper_a(AA) * wrapper_b(BB) == Array(wrapper_a(AA)) * Array(wrapper_b(BB)) - end - @test_throws DimensionMismatch mul!(Matrix{Float64}(undef, 4, 4), AA, BB) -end - -# Generic AbstractArrays -module MyArray15367 -using Test, Random - -struct MyArray{T,N} <: AbstractArray{T,N} - data::Array{T,N} -end - -Base.size(A::MyArray) = size(A.data) -Base.getindex(A::MyArray, indices...) = A.data[indices...] - -A = MyArray(rand(4, 5)) -b = rand(5) -@test A * b ≈ A.data * b -end - -@testset "Generic integer matrix multiplication" begin - AA = [1 2 3; 4 5 6] .- 3 - BB = [2 -2; 3 -5; -4 7] - for A in (copy(AA), view(AA, 1:2, 1:3)), B in (copy(BB), view(BB, 1:3, 1:2)) - @test A * B == [-7 9; -4 9] - @test *(transpose(A), transpose(B)) == [-6 -11 15; -6 -13 18; -6 -15 21] - end - AA = fill(1, 2, 100) - BB = fill(1, 100, 3) - for A in (copy(AA), view(AA, 1:2, 1:100)), B in (copy(BB), view(BB, 1:100, 1:3)) - @test A * B == [100 100 100; 100 100 100] - end - AA = rand(1:20, 5, 5) .- 10 - BB = rand(1:20, 5, 5) .- 10 - CC = Matrix{Int}(undef, size(AA, 1), size(BB, 2)) - for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5, 1:5)), C in (copy(CC), view(CC, 1:5, 1:5)) - @test *(transpose(A), B) == A' * B - @test *(A, transpose(B)) == A * B' - # Preallocated - @test mul!(C, A, B) == A * B - @test mul!(C, transpose(A), B) == A' * B - @test mul!(C, A, transpose(B)) == A * B' - @test mul!(C, transpose(A), transpose(B)) == A' * B' - @test mul!(C, adjoint(A), transpose(B)) == A' * transpose(B) - - # Inplace multiply-add - α = rand(-10:10) - β = rand(-10:10) - rand!(C, -10:10) - βC = β * C - _C0 = copy(C) - C0() = (C .= _C0; C) # reset C but don't change the container type - @test mul!(C0(), A, B, α, β) == α * A * B .+ βC - @test mul!(C0(), transpose(A), B, α, β) == α * A' * B .+ βC - @test mul!(C0(), A, transpose(B), α, β) == α * A * B' .+ βC - @test mul!(C0(), transpose(A), transpose(B), α, β) == α * A' * B' .+ βC - @test mul!(C0(), adjoint(A), transpose(B), α, β) == α * A' * transpose(B) .+ βC - - #test DimensionMismatch for generic_matmatmul - @test_throws DimensionMismatch mul!(C, adjoint(A), transpose(fill(1, 4, 4))) - @test_throws DimensionMismatch mul!(C, adjoint(fill(1, 4, 4)), transpose(B)) - end - vv = [1, 2] - CC = Matrix{Int}(undef, 2, 2) - for v in (copy(vv), view(vv, 1:2)), C in (copy(CC), view(CC, 1:2, 1:2)) - @test @inferred(mul!(C, v, adjoint(v))) == [1 2; 2 4] - - C .= [1 0; 0 1] - @test @inferred(mul!(C, v, adjoint(v), 2, 3)) == [5 4; 4 11] - end -end - -@testset "generic_matvecmul" begin - AA = rand(5, 5) - BB = rand(5) - for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5)) - @test_throws DimensionMismatch LinearAlgebra.generic_matvecmul!(zeros(6), 'N', A, B) - @test_throws DimensionMismatch LinearAlgebra.generic_matvecmul!(B, 'N', A, zeros(6)) - end - vv = [1, 2, 3] - CC = Matrix{Int}(undef, 3, 3) - for v in (copy(vv), view(vv, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) - @test mul!(C, v, transpose(v)) == v * v' - C .= C0 = rand(-10:10, size(C)) - @test mul!(C, v, transpose(v), 2, 3) == 2v * v' .+ 3C0 - end - vvf = map(Float64, vv) - CC = Matrix{Float64}(undef, 3, 3) - for vf in (copy(vvf), view(vvf, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) - @test mul!(C, vf, transpose(vf)) == vf * vf' - C .= C0 = rand(eltype(C), size(C)) - @test mul!(C, vf, transpose(vf), 2, 3) ≈ 2vf * vf' .+ 3C0 - end - - @testset "zero stride" begin - for AAv in (view(AA, StepRangeLen(2,0,size(AA,1)), :), - view(AA, StepRangeLen.(2,0,size(AA))...), - view(complex.(AA, AA), StepRangeLen.(2,0,size(AA))...),) - for BB2 in (BB, complex.(BB, BB)) - C = AAv * BB2 - @test allequal(C) - @test C ≈ Array(AAv) * BB2 - end - end - end -end - -@testset "generic_matvecmul for vectors of vectors" begin - @testset "matrix of scalars" begin - u = [[1, 2], [3, 4]] - A = [1 2; 3 4] - v = [[0, 0], [0, 0]] - Au = [[7, 10], [15, 22]] - @test A * u == Au - mul!(v, A, u) - @test v == Au - mul!(v, A, u, 2, -1) - @test v == Au - end - - @testset "matrix of matrices" begin - u = [[1, 2], [3, 4]] - A = Matrix{Matrix{Int}}(undef, 2, 2) - A[1, 1] = [1 2; 3 4] - A[1, 2] = [5 6; 7 8] - A[2, 1] = [9 10; 11 12] - A[2, 2] = [13 14; 15 16] - v = [[0, 0], [0, 0]] - Au = [[44, 64], [124, 144]] - @test A * u == Au - mul!(v, A, u) - @test v == Au - mul!(v, A, u, 2, -1) - @test v == Au - end -end - -@testset "generic_matvecmul for vectors of matrices" begin - x = [1 2 3; 4 5 6] - A = reshape([x,2x,3x,4x],2,2) - b = [x, 2x] - for f in (adjoint, transpose) - c = f(A) * b - for i in eachindex(c) - @test c[i] == sum(f(A)[i, j] * b[j] for j in eachindex(b)) - end - end -end - -@testset "generic_matmatmul for matrices of vectors" begin - B = Matrix{Vector{Int}}(undef, 2, 2) - B[1, 1] = [1, 2] - B[2, 1] = [3, 4] - B[1, 2] = [5, 6] - B[2, 2] = [7, 8] - A = [1 2; 3 4] - C = Matrix{Vector{Int}}(undef, 2, 2) - AB = Matrix{Vector{Int}}(undef, 2, 2) - AB[1, 1] = [7, 10] - AB[2, 1] = [15, 22] - AB[1, 2] = [19, 22] - AB[2, 2] = [43, 50] - @test A * B == AB - mul!(C, A, B) - @test C == AB - mul!(C, A, B, 2, -1) - @test C == AB - LinearAlgebra.generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(2, -1)) - @test C == AB -end - -@testset "fallbacks & such for BlasFloats" begin - AA = rand(Float64, 6, 6) - BB = rand(Float64, 6, 6) - CC = zeros(Float64, 6, 6) - for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) - @test mul!(C, transpose(A), transpose(B)) == transpose(A) * transpose(B) - @test mul!(C, A, adjoint(B)) == A * transpose(B) - @test mul!(C, adjoint(A), B) == transpose(A) * B - - # Inplace multiply-add - α = rand(Float64) - β = rand(Float64) - rand!(C) - βC = β * C - _C0 = copy(C) - C0() = (C .= _C0; C) # reset C but don't change the container type - @test mul!(C0(), transpose(A), transpose(B), α, β) ≈ α * transpose(A) * transpose(B) .+ βC - @test mul!(C0(), A, adjoint(B), α, β) ≈ α * A * transpose(B) .+ βC - @test mul!(C0(), adjoint(A), B, α, β) ≈ α * transpose(A) * B .+ βC - end -end - -@testset "allocations in BLAS-mul" begin - for n in (2, 3, 6) - A = rand(Float64, n, n) - B = rand(Float64, n, n) - C = zeros(Float64, n, n) - # gemm - for t in (identity, adjoint, transpose) - At = t(A) - Bt = t(B) - mul!(C, At, B) - @test 0 == @allocations mul!(C, At, B) - mul!(C, A, Bt) - @test 0 == @allocations mul!(C, A, Bt) - mul!(C, At, Bt) - @test 0 == @allocations mul!(C, At, Bt) - end - # syrk/herk - @test 0 == @allocations mul!(C, transpose(A), A) - @test 0 == @allocations mul!(C, adjoint(A), A) - @test 0 == @allocations mul!(C, A, transpose(A)) - @test 0 == @allocations mul!(C, A, adjoint(A)) - # complex times real - Cc = complex(C) - Ac = complex(A) - for t in (identity, adjoint, transpose) - Bt = t(B) - @test 0 == @allocations mul!(Cc, Ac, Bt) - end - end -end - -@testset "mixed Blas-non-Blas matmul" begin - AA = rand(-10:10, 6, 6) - BB = ones(Float64, 6, 6) - CC = zeros(Float64, 6, 6) - for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) - @test mul!(C, A, B) == A * B - @test mul!(C, transpose(A), transpose(B)) == transpose(A) * transpose(B) - @test mul!(C, A, adjoint(B)) == A * transpose(B) - @test mul!(C, adjoint(A), B) == transpose(A) * B - end -end - -@testset "allocations in mixed Blas-non-Blas matmul" begin - for n in (2, 3, 6) - A = rand(-10:10, n, n) - B = ones(Float64, n, n) - C = zeros(Float64, n, n) - @test 0 == @allocations mul!(C, A, B) - @test 0 == @allocations mul!(C, A, transpose(B)) - @test 0 == @allocations mul!(C, adjoint(A), B) - end -end - -@testset "matrix algebra with subarrays of floats (stride != 1)" begin - A = reshape(map(Float64, 1:20), 5, 4) - Aref = A[1:2:end, 1:2:end] - Asub = view(A, 1:2:5, 1:2:4) - b = [1.2, -2.5] - @test (Aref * b) == (Asub * b) - @test *(transpose(Asub), Asub) == *(transpose(Aref), Aref) - @test *(Asub, transpose(Asub)) == *(Aref, transpose(Aref)) - Ai = A .+ im - Aref = Ai[1:2:end, 1:2:end] - Asub = view(Ai, 1:2:5, 1:2:4) - @test *(adjoint(Asub), Asub) == *(adjoint(Aref), Aref) - @test *(Asub, adjoint(Asub)) == *(Aref, adjoint(Aref)) -end - -@testset "matrix x matrix with negative stride" begin - M = reshape(map(Float64, 1:77), 7, 11) - N = reshape(map(Float64, 1:63), 9, 7) - U = view(M, 7:-1:1, 11:-2:1) - V = view(N, 7:-1:2, 7:-1:1) - @test U * V ≈ Matrix(U) * Matrix(V) -end - -@testset "dot product of subarrays of vectors (floats, negative stride, issue #37767)" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - a = Vector{T}(3:2:7) - b = Vector{T}(1:10) - v = view(b, 7:-2:3) - @test dot(a, Vector(v)) ≈ 67.0 - @test dot(a, v) ≈ 67.0 - @test dot(v, a) ≈ 67.0 - @test dot(Vector(v), Vector(v)) ≈ 83.0 - @test dot(v, v) ≈ 83.0 - end -end - -@testset "dot product of stride-vector like input" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - a = randn(T, 10) - b = view(a, 1:10) - c = reshape(b, 5, 2) - d = view(c, :, 1:2) - r = sum(abs2, a) - for x in (a,b,c,d), y in (a,b,c,d) - @test dot(x, y) ≈ r - end - end -end - -@testset "Complex matrix x real MatOrVec etc (issue #29224)" for T in (Float32, Float64) - A0 = randn(complex(T), 10, 10) - B0 = randn(T, 10, 10) - @testset "Combination Mat{$(complex(T))} Mat{$T}" for Bax1 in (1:5, 2:2:10), Bax2 in (1:5, 2:2:10) - B = view(A0, Bax1, Bax2) - tB = transpose(B) - Bd, tBd = copy(B), copy(tB) - for Aax1 in (1:5, 2:2:10, (:)), Aax2 in (1:5, 2:2:10) - A = view(A0, Aax1, Aax2) - AB_correct = copy(A) * Bd - AtB_correct = copy(A) * tBd - @test A*Bd ≈ AB_correct # view times matrix - @test A*B ≈ AB_correct # view times view - @test A*tBd ≈ AtB_correct # view times transposed matrix - @test A*tB ≈ AtB_correct # view times transposed view - end - end - x = randn(T, 10) - y0 = similar(A0, 20) - @testset "Combination Mat{$(complex(T))} Vec{$T}" for Aax1 in (1:5, 2:2:10, (:)), Aax2 in (1:5, 2:2:10) - A = view(A0, Aax1, Aax2) - Ad = copy(A) - for indx in (1:5, 1:2:10, 6:-1:2) - vx = view(x, indx) - dx = x[indx] - Ax_correct = Ad*dx - @test A*vx ≈ A*dx ≈ Ad*vx ≈ Ax_correct # view/matrix times view/vector - for indy in (1:2:2size(A,1), size(A,1):-1:1) - y = view(y0, indy) - @test mul!(y, A, vx) ≈ mul!(y, A, dx) ≈ mul!(y, Ad, vx) ≈ - mul!(y, Ad, dx) ≈ Ax_correct # test for uncontiguous dest - end - end - end -end - -@testset "real matrix x complex vec" begin - _matmulres(M, v) = [mapreduce(*, +, row, v) for row in eachrow(M)] - testmatmul(M, v) = @test M * v ≈ _matmulres(M, v) - - @testset for T in (Float32, Float64), n = (4, 5) - M1 = reshape(Vector{T}(1:n^2), n, n) - M2 = reinterpret(reshape, T, [Tuple(T(i + j) for j in 1:n) for i in 1:n]) - v = convert(Vector{Complex{T}}, (1:n) .+ im .* (4 .+ (1:n))) - - for M in (M1, M2) - M_view_cont = @view M[:, :] - v_view_cont = @view v[:] - for _M in (M, M_view_cont), _v in (v, v_view_cont) - testmatmul(_M, _v) - end - - # construct a view with strides(M, 1) == 1 and strides(M, 2) != 1 - ax_noncont = 1:2:n - n1 = length(ax_noncont) - M_view_noncont = @view M[1:n1, ax_noncont] - v_view_noncont = @view v[ax_noncont] - testmatmul(M_view_noncont, v_view_noncont) - - @testset for op in (transpose, adjoint) - for _M in (M, M_view_cont), _v in (v, v_view_cont) - _M2 = op(_M) - testmatmul(_M2, _v) - end - _M2 = op(M_view_noncont) - testmatmul(_M2, v_view_noncont) - end - end - end -end - -@testset "matrix x vector with negative lda or 0 stride" for T in (Float32, Float64) - for TA in (T, complex(T)), TB in (T, complex(T)) - A = view(randn(TA, 10, 10), 1:10, 10:-1:1) # negative lda - v = view([randn(TB)], 1 .+ 0(1:10)) # 0 stride - Ad, vd = copy(A), copy(v) - @test Ad * vd ≈ A * vd ≈ Ad * v ≈ A * v - end -end - -@testset "issue #15286" begin - A = reshape(map(Float64, 1:20), 5, 4) - C = zeros(8, 8) - sC = view(C, 1:2:8, 1:2:8) - B = reshape(map(Float64, -9:10), 5, 4) - @test mul!(sC, transpose(A), A) == A' * A - @test mul!(sC, transpose(A), B) == A' * B - - Aim = A .- im - C = zeros(ComplexF64, 8, 8) - sC = view(C, 1:2:8, 1:2:8) - B = reshape(map(Float64, -9:10), 5, 4) .+ im - @test mul!(sC, adjoint(Aim), Aim) == Aim' * Aim - @test mul!(sC, adjoint(Aim), B) == Aim' * B -end - -@testset "syrk & herk" begin - AA = reshape(1:1503, 501, 3) .- 750.0 - res = Float64[135228751 9979252 -115270247; 9979252 10481254 10983256; -115270247 10983256 137236759] - for A in (copy(AA), view(AA, 1:501, 1:3)) - @test *(transpose(A), A) == res - @test *(adjoint(A), transpose(copy(A'))) == res - end - cutoff = 501 - A = reshape(1:6*cutoff, 2 * cutoff, 3) .- (6 * cutoff) / 2 - Asub = view(A, 1:2:2*cutoff, 1:3) - Aref = A[1:2:2*cutoff, 1:3] - @test *(transpose(Asub), Asub) == *(transpose(Aref), Aref) - Ai = A .- im - Asub = view(Ai, 1:2:2*cutoff, 1:3) - Aref = Ai[1:2:2*cutoff, 1:3] - @test *(adjoint(Asub), Asub) == *(adjoint(Aref), Aref) - - A5x5, A6x5 = Matrix{Float64}.(undef, ((5, 5), (6, 5))) - @test_throws DimensionMismatch LinearAlgebra.syrk_wrapper!(A5x5, 'N', A6x5) - @test_throws DimensionMismatch LinearAlgebra.herk_wrapper!(A5x5, 'N', A6x5) -end - -@testset "matmul for types w/o sizeof (issue #1282)" begin - AA = fill(complex(1, 1), 10, 10) - for A in (copy(AA), view(AA, 1:10, 1:10)) - A2 = A^2 - @test A2[1, 1] == 20im - end -end - -@testset "mul! (scaling)" begin - A5x5, b5, C5x6 = Array{Float64}.(undef, ((5, 5), 5, (5, 6))) - for A in (A5x5, view(A5x5, :, :)), b in (b5, view(b5, :)), C in (C5x6, view(C5x6, :, :)) - @test_throws DimensionMismatch mul!(A, Diagonal(b), C) - end -end - -@testset "muladd" begin - A23 = reshape(1:6, 2, 3) .+ 0 - B34 = reshape(1:12, 3, 4) .+ im - u2 = [10, 20] - v3 = [3, 5, 7] .+ im - w4 = [11, 13, 17, 19im] - - @testset "matrix-matrix" begin - @test muladd(A23, B34, 0) == A23 * B34 - @test muladd(A23, B34, 100) == A23 * B34 .+ 100 - @test muladd(A23, B34, u2) == A23 * B34 .+ u2 - @test muladd(A23, B34, w4') == A23 * B34 .+ w4' - @test_throws DimensionMismatch muladd(B34, A23, 1) - @test muladd(ones(1, 3), ones(3, 4), ones(1, 4)) == fill(4.0, 1, 4) - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3, 4), ones(9, 4)) - - # broadcasting fallback method allows trailing dims - @test muladd(A23, B34, ones(2, 4, 1)) == A23 * B34 + ones(2, 4, 1) - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3, 4), ones(9, 4, 1)) - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3, 4), ones(1, 4, 9)) - # and catches z::Array{T,0} - @test muladd(A23, B34, fill(0)) == A23 * B34 - end - @testset "matrix-vector" begin - @test muladd(A23, v3, 0) == A23 * v3 - @test muladd(A23, v3, 100) == A23 * v3 .+ 100 - @test muladd(A23, v3, u2) == A23 * v3 .+ u2 - @test muladd(A23, v3, im) isa Vector{Complex{Int}} - @test muladd(ones(1, 3), ones(3), ones(1)) == [4] - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3), ones(7)) - - # fallback - @test muladd(A23, v3, ones(2, 1, 1)) == A23 * v3 + ones(2, 1, 1) - @test_throws DimensionMismatch muladd(A23, v3, ones(2, 2)) - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3), ones(7, 1)) - @test_throws DimensionMismatch muladd(ones(1, 3), ones(3), ones(1, 7)) - @test muladd(A23, v3, fill(0)) == A23 * v3 - end - @testset "adjoint-matrix" begin - @test muladd(v3', B34, 0) isa Adjoint - @test muladd(v3', B34, 2im) == v3' * B34 .+ 2im - @test muladd(v3', B34, w4') == v3' * B34 .+ w4' - - # via fallback - @test muladd(v3', B34, ones(1, 4)) == (B34' * v3 + ones(4, 1))' - @test_throws DimensionMismatch muladd(v3', B34, ones(7, 4)) - @test_throws DimensionMismatch muladd(v3', B34, ones(1, 4, 7)) - @test muladd(v3', B34, fill(0)) == v3' * B34 # does not make an Adjoint - end - @testset "vector-adjoint" begin - @test muladd(u2, v3', 0) isa Matrix - @test muladd(u2, v3', 99) == u2 * v3' .+ 99 - @test muladd(u2, v3', A23) == u2 * v3' .+ A23 - - @test muladd(u2, v3', ones(2, 3, 1)) == u2 * v3' + ones(2, 3, 1) - @test_throws DimensionMismatch muladd(u2, v3', ones(2, 3, 4)) - @test_throws DimensionMismatch muladd([1], v3', ones(7, 3)) - @test muladd(u2, v3', fill(0)) == u2 * v3' - end - @testset "dot" begin # all use muladd(::Any, ::Any, ::Any) - @test muladd(u2', u2, 0) isa Number - @test muladd(v3', v3, im) == dot(v3, v3) + im - @test muladd(u2', u2, [1]) == [dot(u2, u2) + 1] - @test_throws DimensionMismatch muladd(u2', u2, [1, 1]) == [dot(u2, u2) + 1] - @test muladd(u2', u2, fill(0)) == dot(u2, u2) - end - @testset "arrays of arrays" begin - vofm = [rand(1:9, 2, 2) for _ in 1:3] - Mofm = [rand(1:9, 2, 2) for _ in 1:3, _ in 1:3] - - @test muladd(vofm', vofm, vofm[1]) == vofm' * vofm .+ vofm[1] # inner - @test muladd(vofm, vofm', Mofm) == vofm * vofm' .+ Mofm # outer - @test muladd(vofm', Mofm, vofm') == vofm' * Mofm .+ vofm' # bra-mat - @test muladd(Mofm, Mofm, vofm) == Mofm * Mofm .+ vofm # mat-mat - @test muladd(Mofm, vofm, vofm) == Mofm * vofm .+ vofm # mat-vec - end -end - -@testset "muladd & structured matrices" begin - A33 = reshape(1:9, 3, 3) .+ im - v3 = [3, 5, 7im] - - # no special treatment - @test muladd(Symmetric(A33), Symmetric(A33), 1) == Symmetric(A33) * Symmetric(A33) .+ 1 - @test muladd(Hermitian(A33), Hermitian(A33), v3) == Hermitian(A33) * Hermitian(A33) .+ v3 - @test muladd(adjoint(A33), transpose(A33), A33) == A33' * transpose(A33) .+ A33 - - u1 = muladd(UpperTriangular(A33), UpperTriangular(A33), Diagonal(v3)) - @test u1 isa UpperTriangular - @test u1 == UpperTriangular(A33) * UpperTriangular(A33) + Diagonal(v3) - - # diagonal - @test muladd(Diagonal(v3), Diagonal(A33), Diagonal(v3)).diag == ([1, 5, 9] .+ im .+ 1) .* v3 - - # uniformscaling - @test muladd(Diagonal(v3), I, I).diag == v3 .+ 1 - @test muladd(2 * I, 3 * I, I).λ == 7 - @test muladd(A33, A33', I) == A33 * A33' + I - - # https://github.com/JuliaLang/julia/issues/38426 - @test @evalpoly(A33, 1.0 * I, 1.0 * I) == I + A33 - @test @evalpoly(A33, 1.0 * I, 1.0 * I, 1.0 * I) == I + A33 + A33^2 -end - -# issue #6450 -@test dot(Any[1.0, 2.0], Any[3.5, 4.5]) === 12.5 - -@testset "dot" for elty in (Float32, Float64, ComplexF32, ComplexF64) - x = convert(Vector{elty}, [1.0, 2.0, 3.0]) - y = convert(Vector{elty}, [3.5, 4.5, 5.5]) - @test_throws DimensionMismatch dot(x, 1:2, y, 1:3) - @test_throws BoundsError dot(x, 1:4, y, 1:4) - @test_throws BoundsError dot(x, 1:3, y, 2:4) - @test dot(x, 1:2, y, 1:2) == convert(elty, 12.5) - @test transpose(x) * y == convert(elty, 29.0) - X = convert(Matrix{elty}, [1.0 2.0; 3.0 4.0]) - Y = convert(Matrix{elty}, [1.5 2.5; 3.5 4.5]) - @test dot(X, Y) == convert(elty, 35.0) - Z = Matrix{elty}[reshape(1:4, 2, 2), fill(1, 2, 2)] - @test dot(Z, Z) == convert(elty, 34.0) -end - -dot1(x, y) = invoke(dot, Tuple{Any,Any}, x, y) -dot2(x, y) = invoke(dot, Tuple{AbstractArray,AbstractArray}, x, y) -@testset "generic dot" begin - AA = [1+2im 3+4im; 5+6im 7+8im] - BB = [2+7im 4+1im; 3+8im 6+5im] - for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) - @test dot(A, B) == dot(vec(A), vec(B)) == dot1(A, B) == dot2(A, B) == dot(float.(A), float.(B)) - @test dot(Int[], Int[]) == 0 == dot1(Int[], Int[]) == dot2(Int[], Int[]) - @test_throws MethodError dot(Any[], Any[]) - @test_throws MethodError dot1(Any[], Any[]) - @test_throws MethodError dot2(Any[], Any[]) - for n1 = 0:2, n2 = 0:2, d in (dot, dot1, dot2) - if n1 != n2 - @test_throws DimensionMismatch d(1:n1, 1:n2) - else - @test d(1:n1, 1:n2) ≈ norm(1:n1)^2 - end - end - end -end - -@testset "Issue 11978" begin - A = Matrix{Matrix{Float64}}(undef, 2, 2) - A[1, 1] = Matrix(1.0I, 3, 3) - A[2, 2] = Matrix(1.0I, 2, 2) - A[1, 2] = Matrix(1.0I, 3, 2) - A[2, 1] = Matrix(1.0I, 2, 3) - b = Vector{Vector{Float64}}(undef, 2) - b[1] = fill(1.0, 3) - b[2] = fill(1.0, 2) - @test A * b == Vector{Float64}[[2, 2, 1], [2, 2]] -end - -@test_throws ArgumentError LinearAlgebra.copytri!(Matrix{Float64}(undef, 10, 10), 'Z') - -@testset "Issue 30055" begin - B = [1+im 2+im 3+im; 4+im 5+im 6+im; 7+im 9+im im] - A = UpperTriangular(B) - @test copy(transpose(A)) == transpose(A) - @test copy(A') == A' - A = LowerTriangular(B) - @test copy(transpose(A)) == transpose(A) - @test copy(A') == A' - B = Matrix{Matrix{Complex{Int}}}(undef, 2, 2) - B[1, 1] = [1+im 2+im; 3+im 4+im] - B[2, 1] = [1+2im 1+3im; 1+3im 1+4im] - B[1, 2] = [7+im 8+2im; 9+3im 4im] - B[2, 2] = [9+im 8+im; 7+im 6+im] - A = UpperTriangular(B) - @test copy(transpose(A)) == transpose(A) - @test copy(A') == A' - A = LowerTriangular(B) - @test copy(transpose(A)) == transpose(A) - @test copy(A') == A' -end - -@testset "gemv! and gemm_wrapper for $elty" for elty in [Float32, Float64, ComplexF64, ComplexF32] - A10x10, x10, x11 = Array{elty}.(undef, ((10, 10), 10, 11)) - @test_throws DimensionMismatch LinearAlgebra.gemv!(x10, 'N', A10x10, x11) - @test_throws DimensionMismatch LinearAlgebra.gemv!(x11, 'N', A10x10, x10) - @test LinearAlgebra.gemv!(elty[], 'N', Matrix{elty}(undef, 0, 0), elty[]) == elty[] - @test LinearAlgebra.gemv!(x10, 'N', Matrix{elty}(undef, 10, 0), elty[]) == zeros(elty, 10) - - I0x0 = Matrix{elty}(I, 0, 0) - I10x10 = Matrix{elty}(I, 10, 10) - I10x11 = Matrix{elty}(I, 10, 11) - @test LinearAlgebra.gemm_wrapper('N', 'N', I10x10, I10x10) == I10x10 - @test_throws DimensionMismatch LinearAlgebra.gemm_wrapper!(I10x10, 'N', 'N', I10x11, I10x10) - @test_throws DimensionMismatch LinearAlgebra.gemm_wrapper!(I10x10, 'N', 'N', I0x0, I0x0) - - A = rand(elty, 3, 3) - @test LinearAlgebra.matmul3x3('T', 'N', A, Matrix{elty}(I, 3, 3)) == transpose(A) -end - -@testset "#13593, #13488" begin - aa = rand(3, 3) - bb = rand(3, 3) - for a in (copy(aa), view(aa, 1:3, 1:3)), b in (copy(bb), view(bb, 1:3, 1:3)) - @test_throws ArgumentError mul!(a, a, b) - @test_throws ArgumentError mul!(a, b, a) - @test_throws ArgumentError mul!(a, a, a) - end -end - -@testset "#35163" begin - # typemax(Int32) * Int32(1) + Int32(1) * Int32(1) should wrap around - # not promote to Int64, convert to Int32 and throw inexacterror - val = mul!(Int32[1], fill(typemax(Int32), 1, 1), Int32[1], Int32(1), Int32(1)) - @test val[1] == typemin(Int32) -end - -# Number types that lack conversion to the destination type -struct RootInt - i::Int -end -import Base: *, adjoint, transpose -import LinearAlgebra: Adjoint, Transpose -(*)(x::RootInt, y::RootInt) = x.i * y.i -(*)(x::RootInt, y::Integer) = x.i * y -adjoint(x::RootInt) = x -transpose(x::RootInt) = x - -@test Base.promote_op(*, RootInt, RootInt) === Int - -@testset "#14293" begin - a = [RootInt(3)] - C = [0;;] - mul!(C, a, transpose(a)) - @test C[1] == 9 - C = [1;;] - mul!(C, a, transpose(a), 2, 3) - @test C[1] == 21 - a = [RootInt(2), RootInt(10)] - @test a * adjoint(a) == [4 20; 20 100] - A = [RootInt(3) RootInt(5)] - @test A * a == [56] -end - -function test_mul(C, A, B, S) - mul!(C, A, B) - @test Array(A) * Array(B) ≈ C - @test A * B ≈ C - - # This is similar to how `isapprox` choose `rtol` (when `atol=0`) - # but consider all number types involved: - rtol = max(rtoldefault.(real.(eltype.((C, A, B))))...) - - rand!(C, S) - T = promote_type(eltype.((A, B))...) - α = T <: AbstractFloat ? rand(T) : rand(T(-10):T(10)) - β = T <: AbstractFloat ? rand(T) : rand(T(-10):T(10)) - βArrayC = β * Array(C) - βC = β * C - mul!(C, A, B, α, β) - @test α * Array(A) * Array(B) .+ βArrayC ≈ C rtol = rtol - @test α * A * B .+ βC ≈ C rtol = rtol -end - -@testset "mul! vs * for special types" begin - eltypes = [Float32, Float64, Int64(-100):Int64(100)] - for k in [3, 4, 10] - T = rand(eltypes) - bi1 = Bidiagonal(rand(T, k), rand(T, k - 1), rand([:U, :L])) - bi2 = Bidiagonal(rand(T, k), rand(T, k - 1), rand([:U, :L])) - tri1 = Tridiagonal(rand(T, k - 1), rand(T, k), rand(T, k - 1)) - tri2 = Tridiagonal(rand(T, k - 1), rand(T, k), rand(T, k - 1)) - stri1 = SymTridiagonal(rand(T, k), rand(T, k - 1)) - stri2 = SymTridiagonal(rand(T, k), rand(T, k - 1)) - C = rand(T, k, k) - specialmatrices = (bi1, bi2, tri1, tri2, stri1, stri2) - for A in specialmatrices - B = specialmatrices[rand(1:length(specialmatrices))] - test_mul(C, A, B, T) - end - for S in specialmatrices - l = rand(1:6) - B = randn(k, l) - C = randn(k, l) - test_mul(C, S, B, T) - A = randn(l, k) - C = randn(l, k) - test_mul(C, A, S, T) - end - end - for T in eltypes - A = Bidiagonal(rand(T, 2), rand(T, 1), rand([:U, :L])) - B = Bidiagonal(rand(T, 2), rand(T, 1), rand([:U, :L])) - C = randn(2, 2) - test_mul(C, A, B, T) - B = randn(2, 9) - C = randn(2, 9) - test_mul(C, A, B, T) - end - let - tri44 = Tridiagonal(randn(3), randn(4), randn(3)) - tri33 = Tridiagonal(randn(2), randn(3), randn(2)) - full43 = randn(4, 3) - full24 = randn(2, 4) - full33 = randn(3, 3) - full44 = randn(4, 4) - @test_throws DimensionMismatch mul!(full43, tri44, tri33) - @test_throws DimensionMismatch mul!(full44, tri44, tri33) - @test_throws DimensionMismatch mul!(full44, tri44, full43) - @test_throws DimensionMismatch mul!(full43, tri33, full43) - @test_throws DimensionMismatch mul!(full43, full43, tri44) - end -end - -# #18218 -module TestPR18218 -using Test -import Base.*, Base.+, Base.zero -struct TypeA - x::Int -end -Base.convert(::Type{TypeA}, x::Int) = TypeA(x) -struct TypeB - x::Int -end -struct TypeC - x::Int -end -Base.convert(::Type{TypeC}, x::Int) = TypeC(x) -zero(c::TypeC) = TypeC(0) -zero(::Type{TypeC}) = TypeC(0) -(*)(x::Int, a::TypeA) = TypeB(x * a.x) -(*)(a::TypeA, x::Int) = TypeB(a.x * x) -(+)(a::Union{TypeB,TypeC}, b::Union{TypeB,TypeC}) = TypeC(a.x + b.x) -A = TypeA[1 2; 3 4] -b = [1, 2] -d = A * b -@test typeof(d) == Vector{TypeC} -@test d == TypeC[5, 11] -end - -@testset "VecOrMat of Vectors" begin - X = rand(ComplexF64, 3, 3) - Xv1 = [X[:, j] for i in 1:1, j in 1:3] - Xv2 = [transpose(X[i, :]) for i in 1:3] - Xv3 = [transpose(X[i, :]) for i in 1:3, j in 1:1] - - XX = X * X - XtX = transpose(X) * X - XcX = X' * X - XXt = X * transpose(X) - XtXt = transpose(XX) - XcXt = X' * transpose(X) - XXc = X * X' - XtXc = transpose(X) * X' - XcXc = X' * X' - - @test (Xv1*Xv2)[1] ≈ XX - @test (Xv1*Xv3)[1] ≈ XX - @test transpose(Xv1) * Xv1 ≈ XtX - @test transpose(Xv2) * Xv2 ≈ XtX - @test (transpose(Xv3)*Xv3)[1] ≈ XtX - @test Xv1' * Xv1 ≈ XcX - @test Xv2' * Xv2 ≈ XcX - @test (Xv3'*Xv3)[1] ≈ XcX - @test (Xv1*transpose(Xv1))[1] ≈ XXt - @test Xv2 * transpose(Xv2) ≈ XXt - @test Xv3 * transpose(Xv3) ≈ XXt - @test transpose(Xv1) * transpose(Xv2) ≈ XtXt - @test transpose(Xv1) * transpose(Xv3) ≈ XtXt - @test Xv1' * transpose(Xv2) ≈ XcXt - @test Xv1' * transpose(Xv3) ≈ XcXt - @test (Xv1*Xv1')[1] ≈ XXc - @test Xv2 * Xv2' ≈ XXc - @test Xv3 * Xv3' ≈ XXc - @test transpose(Xv1) * Xv2' ≈ XtXc - @test transpose(Xv1) * Xv3' ≈ XtXc - @test Xv1' * Xv2' ≈ XcXc - @test Xv1' * Xv3' ≈ XcXc -end - -@testset "copyto! for matrices of matrices" begin - A = [randn(ComplexF64, 2,3) for _ in 1:2, _ in 1:3] - for (tfun, tM) in ((identity, 'N'), (transpose, 'T'), (adjoint, 'C')) - At = copy(tfun(A)) - B = zero.(At) - copyto!(B, axes(B, 1), axes(B, 2), tM, A, axes(A, tM == 'N' ? 1 : 2), axes(A, tM == 'N' ? 2 : 1)) - @test B == At - end -end - -@testset "method ambiguity" begin - # Ambiguity test is run inside a clean process. - # https://github.com/JuliaLang/julia/issues/28804 - script = joinpath(@__DIR__, "ambiguous_exec.jl") - cmd = `$(Base.julia_cmd()) --startup-file=no $script` - @test success(pipeline(cmd; stdout = stdout, stderr = stderr)) -end - -struct A32092 - x::Float64 -end -Base.:+(x::Float64, a::A32092) = x + a.x -Base.:*(x::Float64, a::A32092) = x * a.x -@testset "Issue #32092" begin - @test ones(2, 2) * [A32092(1.0), A32092(2.0)] == fill(3.0, (2,)) -end - -@testset "strong zero" begin - @testset for α in Any[false, 0.0, 0], n in 1:4 - C = ones(n, n) - A = fill!(zeros(n, n), NaN) - B = ones(n, n) - @test mul!(copy(C), A, B, α, 1.0) == C - end -end - -@testset "CartesianIndex handling in _modify!" begin - C = rand(10, 10) - A = rand(10, 10) - @test mul!(view(C, 1:10, 1:10), A, 0.5) == A * 0.5 -end - -@testset "Issue #33214: tiled generic mul!" begin - n = 100 - A = rand(n, n) - B = rand(n, n) - C = zeros(n, n) - mul!(C, A, B, -1 + 0im, 0) - D = -A * B - @test D ≈ C - - # Just in case dispatching on the surface API `mul!` is changed in the future, - # let's test the function where the tiled multiplication is defined. - fill!(C, 0) - LinearAlgebra.generic_matmatmul!(C, 'N', 'N', A, B, LinearAlgebra.MulAddMul(-1, 0)) - @test D ≈ C -end - -@testset "size zero types in matrix mult (see issue 39362)" begin - A = [missing missing; missing missing] - v = [missing, missing] - @test (A * v == v) === missing - M = fill(1.0, 2, 2) - a = fill(missing, 2, 1) - @test (a' * M * a == fill(missing, 1, 1)) === missing -end - - -@testset "multiplication of empty matrices without calling zero" begin - r, c = rand(0:9, 2) - A = collect(Number, rand(r, c)) - B = rand(c, 0) - C = A * B - @test size(C) == (r, 0) - @test_throws MethodError zero(eltype(C)) -end - -@testset "Issue #33873: genmatmul! with empty operands" begin - @test Matrix{Any}(undef, 0, 2) * Matrix{Any}(undef, 2, 3) == Matrix{Any}(undef, 0, 3) - @test_throws MethodError Matrix{Any}(undef, 2, 0) * Matrix{Any}(undef, 0, 3) - @test Matrix{Int}(undef, 2, 0) * Matrix{Int}(undef, 0, 3) == zeros(Int, 2, 3) -end - -@testset "3-arg *, order by type" begin - x = [1, 2im] - y = [im, 20, 30 + 40im] - z = [-1, 200 + im, -3] - A = [1 2 3im; 4 5 6+im] - B = [-10 -20; -30 -40] - a = 3 + im * round(Int, 10^6 * (pi - 3)) - b = 123 - - @test x' * A * y == (x' * A) * y == x' * (A * y) - @test y' * A' * x == (y' * A') * x == y' * (A' * x) - @test y' * transpose(A) * x == (y' * transpose(A)) * x == y' * (transpose(A) * x) - - @test B * A * y == (B * A) * y == B * (A * y) - - @test a * A * y == (a * A) * y == a * (A * y) - @test A * y * a == (A * y) * a == A * (y * a) - - @test a * B * A == (a * B) * A == a * (B * A) - @test B * A * a == (B * A) * a == B * (A * a) - - @test a * y' * z == (a * y') * z == a * (y' * z) - @test y' * z * a == (y' * z) * a == y' * (z * a) - - @test a * y * z' == (a * y) * z' == a * (y * z') - @test y * z' * a == (y * z') * a == y * (z' * a) - - @test a * x' * A == (a * x') * A == a * (x' * A) - @test x' * A * a == (x' * A) * a == x' * (A * a) - @test a * x' * A isa Adjoint{<:Any,<:Vector} - - @test a * transpose(x) * A == (a * transpose(x)) * A == a * (transpose(x) * A) - @test transpose(x) * A * a == (transpose(x) * A) * a == transpose(x) * (A * a) - @test a * transpose(x) * A isa Transpose{<:Any,<:Vector} - - @test x' * B * A == (x' * B) * A == x' * (B * A) - @test x' * B * A isa Adjoint{<:Any,<:Vector} - - @test y * x' * A == (y * x') * A == y * (x' * A) - y31 = reshape(y, 3, 1) - @test y31 * x' * A == (y31 * x') * A == y31 * (x' * A) - - vm = [rand(1:9, 2, 2) for _ in 1:3] - Mm = [rand(1:9, 2, 2) for _ in 1:3, _ in 1:3] - - @test vm' * Mm * vm == (vm' * Mm) * vm == vm' * (Mm * vm) - @test Mm * Mm' * vm == (Mm * Mm') * vm == Mm * (Mm' * vm) - @test vm' * Mm * Mm == (vm' * Mm) * Mm == vm' * (Mm * Mm) - @test Mm * Mm' * Mm == (Mm * Mm') * Mm == Mm * (Mm' * Mm) -end - -@testset "3-arg *, order by size" begin - M44 = randn(4, 4) - M24 = randn(2, 4) - M42 = randn(4, 2) - @test M44 * M44 * M44 ≈ (M44 * M44) * M44 ≈ M44 * (M44 * M44) - @test M42 * M24 * M44 ≈ (M42 * M24) * M44 ≈ M42 * (M24 * M44) - @test M44 * M42 * M24 ≈ (M44 * M42) * M24 ≈ M44 * (M42 * M24) -end - -@testset "4-arg *, by type" begin - y = [im, 20, 30 + 40im] - z = [-1, 200 + im, -3] - a = 3 + im * round(Int, 10^6 * (pi - 3)) - b = 123 - M = rand(vcat(1:9, im .* [1, 2, 3]), 3, 3) - N = rand(vcat(1:9, im .* [1, 2, 3]), 3, 3) - - @test a * b * M * y == (a * b) * (M * y) - @test a * b * M * N == (a * b) * (M * N) - @test a * M * N * y == (a * M) * (N * y) - @test a * y' * M * z == (a * y') * (M * z) - @test a * y' * M * N == (a * y') * (M * N) - - @test M * y * a * b == (M * y) * (a * b) - @test M * N * a * b == (M * N) * (a * b) - @test M * N * y * a == (a * M) * (N * y) - @test y' * M * z * a == (a * y') * (M * z) - @test y' * M * N * a == (a * y') * (M * N) - - @test M * N * conj(M) * y == (M * N) * (conj(M) * y) - @test y' * M * N * conj(M) == (y' * M) * (N * conj(M)) - @test y' * M * N * z == (y' * M) * (N * z) -end - -@testset "4-arg *, by size" begin - for shift in 1:5 - s1, s2, s3, s4, s5 = circshift(3:7, shift) - a = randn(s1, s2) - b = randn(s2, s3) - c = randn(s3, s4) - d = randn(s4, s5) - - # _quad_matmul - @test *(a, b, c, d) ≈ (a * b) * (c * d) - - # _tri_matmul(A,B,B,δ) - @test *(11.1, b, c, d) ≈ (11.1 * b) * (c * d) - @test *(a, b, c, 99.9) ≈ (a * b) * (c * 99.9) - end -end - -#46865 -@testset "mul!() with non-const alpha, beta" begin - f!(C,A,B,alphas,betas) = mul!(C, A, B, alphas[1], betas[1]) - alphas = [1.0] - betas = [0.5] - for d in [2,3,4] # test native small-matrix cases as well as BLAS - A = rand(d,d) - B = copy(A) - C = copy(A) - f!(C, A, B, alphas, betas) - @test (@allocated f!(C, A, B, alphas, betas)) == 0 - end -end - -@testset "vector-matrix multiplication" begin - a = [1,2] - A = reshape([1,2], 2, 1) - B = [1 2] - @test a * B ≈ A * B - B = reshape([1,2], 2, 1) - @test a * B' ≈ A * B' - @test a * transpose(B) ≈ A * transpose(B) -end - -@testset "issue #56085" begin - struct Thing - data::Float64 - end - - Base.zero(::Type{Thing}) = Thing(0.) - Base.zero(::Thing) = Thing(0.) - Base.one(::Type{Thing}) = Thing(1.) - Base.one(::Thing) = Thing(1.) - Base.:+(t1::Thing, t::Thing...) = +(getfield.((t1, t...), :data)...) - Base.:*(t1::Thing, t::Thing...) = *(getfield.((t1, t...), :data)...) - - M = Float64[1 2; 3 4] - A = Thing.(M) - - @test A * A ≈ M * M -end - -end # module TestMatmul diff --git a/stdlib/LinearAlgebra/test/pinv.jl b/stdlib/LinearAlgebra/test/pinv.jl deleted file mode 100644 index c7268865a0505..0000000000000 --- a/stdlib/LinearAlgebra/test/pinv.jl +++ /dev/null @@ -1,186 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestPinv - -using Test, LinearAlgebra, Random - -Random.seed!(12345) - -function hilb(T::Type, n::Integer) - a = Matrix{T}(undef, n, n) - for i=1:n - for j=1:n - a[j,i]=one(T)/(i+j-one(T)) - end - end - return a -end -hilb(n::Integer) = hilb(Float64,n) - -function hilb(T::Type, m::Integer, n::Integer) - a = Matrix{T}(undef, m, n) - for i=1:n - for j=1:m - a[j,i]=one(T)/(i+j-one(T)) - end - end - return a -end -hilb(m::Integer, n::Integer) = hilb(Float64,m,n) - -function onediag(T::Type, m::Integer, n::Integer) - a=zeros(T,m,n) - for i=1:min(n,m) - a[i,i]=one(T)/(float(i)^5) - end - a[1,1] = 0 - a[min(m,n),min(m,n)] = 0 - return a -end -onediag(m::Integer, n::Integer) = onediag(Float64, m::Integer, n::Integer) - -function onediag_sparse(T::Type, n::Integer) - a=zeros(T,n) - for i=1:n - a[i]=one(T)/(float(i)^5) - end - a[1] = 0 - a[n] = 0 - return Diagonal(a) -end -onediag_sparse(n::Integer) = onediag_sparse(Float64, n::Integer) - -function tridiag(T::Type, m::Integer, n::Integer) - a=zeros(T,m,n) - for i=1:min(n,m) - a[i,i]=one(T)/(float(i)^5) - end - for i=1:min(n,m)-1 - a[i+1,i]=2*one(T)/(float(i)^5) - a[1,i+1]=2*one(T)/(float(i)^5) - end - return a -end -tridiag(m::Integer, n::Integer) = tridiag(Float64, m::Integer, n::Integer) - -function test_pinv(a,tol1,tol2) - m,n = size(a) - - apinv = @inferred pinv(a) - @test size(apinv) == (n,m) - @test norm(a*apinv*a-a)/norm(a) ≈ 0 atol=tol1 - @test norm(apinv*a*apinv-apinv)/norm(apinv) ≈ 0 atol=tol1 - b = a*randn(n) - x = apinv*b - @test norm(a*x-b)/norm(b) ≈ 0 atol=tol1 - - apinv = @inferred pinv(a,sqrt(eps(real(one(eltype(a)))))) - @test size(apinv) == (n,m) - @test norm(a*apinv*a-a)/norm(a) ≈ 0 atol=tol2 - @test norm(apinv*a*apinv-apinv)/norm(apinv) ≈ 0 atol=tol2 - b = a*randn(n) - x = apinv*b - @test norm(a*x-b)/norm(b) ≈ 0 atol=tol2 -end - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64) - @testset for (m, n) in [(1000, 100), (100, 100), (100, 1000)] - default_tol = (real(one(eltya))) * max(m,n) * 10 - tol1 = 1e-2 - tol2 = 1e-5 - if real(eltya) == Float32 - tol1 = 1e0 - tol2 = 1e-2 - end - @testset "dense/ill-conditioned matrix" begin - a = hilb(eltya, m, n) - test_pinv(a, tol1, tol2) - end - @testset "dense/diagonal matrix" begin - a = onediag(eltya, m, n) - test_pinv(a, default_tol, default_tol) - end - @testset "dense/tri-diagonal matrix" begin - a = tridiag(eltya, m, n) - test_pinv(a, default_tol, tol2) - end - @testset "Diagonal matrix" begin - a = onediag_sparse(eltya, m) - test_pinv(a, default_tol, default_tol) - end - @testset "Vector" begin - a = rand(eltya, m) - apinv = @inferred pinv(a) - @test pinv(hcat(a)) ≈ apinv - @test isa(apinv, eltya <: Complex ? Adjoint{eltya} : Transpose{eltya}) - end - @testset "Adjoint/Transpose vector" begin - a = rand(eltya, m)' - apinv = @inferred pinv(a) - @test pinv(vcat(a)) ≈ apinv - @test apinv isa Vector{eltya} - end - end - - @testset "zero valued numbers/vectors/matrices" begin - a = pinv(zero(eltya)) - @test a ≈ 0.0 - - a = pinv([zero(eltya); zero(eltya)]) - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - - a = pinv([zero(eltya); zero(eltya)]') - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - - a = pinv(Diagonal([zero(eltya); zero(eltya)])) - @test a.diag[1] ≈ 0.0 - @test a.diag[2] ≈ 0.0 - end - - @testset "hermitian matrices" begin - Q = ones(2,2) - C = pinv(Hermitian(Q))/0.25 - @test C ≈ ones(2,2) - end - - @testset "non-square diagonal matrices" begin - A = eltya[1 0 ; 0 1 ; 0 0] - B = pinv(A) - @test A*B*A ≈ A - @test B*A*B ≈ B - - A = eltya[1 0 0 ; 0 1 0] - B = pinv(A) - @test A*B*A ≈ A - @test B*A*B ≈ B - end - - if eltya <: LinearAlgebra.BlasReal - @testset "sub-normal numbers/vectors/matrices" begin - a = pinv(floatmin(eltya)/100) - @test a ≈ 0.0 - # Complex subnormal - a = pinv(floatmin(eltya)/100*(1+1im)) - @test a ≈ 0.0 - - a = pinv([floatmin(eltya); floatmin(eltya)]/100) - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - # Complex subnormal - a = pinv([floatmin(eltya); floatmin(eltya)]/100*(1+1im)) - @test a[1] ≈ 0.0 - @test a[2] ≈ 0.0 - a = pinv(Diagonal([floatmin(eltya); floatmin(eltya)]/100)) - @test a.diag[1] ≈ 0.0 - @test a.diag[2] ≈ 0.0 - # Complex subnormal - a = pinv(Diagonal([floatmin(eltya); floatmin(eltya)]/100*(1+1im))) - @test a.diag[1] ≈ 0.0 - @test a.diag[2] ≈ 0.0 - end - end -end - -end # module TestPinv diff --git a/stdlib/LinearAlgebra/test/qr.jl b/stdlib/LinearAlgebra/test/qr.jl deleted file mode 100644 index b6e9ce3a82743..0000000000000 --- a/stdlib/LinearAlgebra/test/qr.jl +++ /dev/null @@ -1,543 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestQR - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted, rmul!, lmul! - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(1234325) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 -a2real = randn(n,n)/2 -a2img = randn(n,n)/2 -breal = randn(n,2)/2 -bimg = randn(n,2)/2 - -# helper functions to unambiguously recover explicit forms of an implicit QR Q -squareQ(Q::LinearAlgebra.AbstractQ) = Q*I -rectangularQ(Q::LinearAlgebra.AbstractQ) = Matrix(Q) - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - raw_a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - raw_a2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - asym = raw_a' + raw_a # symmetric indefinite - apd = raw_a' * raw_a # symmetric positive-definite - ε = εa = eps(abs(float(one(eltya)))) - - @testset for eltyb in (Float32, Float64, ComplexF32, ComplexF64, Int) - raw_b = eltyb == Int ? rand(1:5, n, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa, εb) - tab = promote_type(eltya, eltyb) - - @testset "QR decomposition of a Number" begin - α = rand(eltyb) - aα = fill(α, 1, 1) - @test qr(α).Q * qr(α).R ≈ qr(aα).Q * qr(aα).R - @test abs(qr(α).Q[1,1]) ≈ one(eltyb) - end - - for (a, b) in ((raw_a, raw_b), - (view(raw_a, 1:n-1, 1:n-1), view(raw_b, 1:n-1, 1))) - a_1 = size(a, 1) - @testset "QR decomposition (without pivoting)" begin - qra = @inferred qr(a) - q, r = qra.Q, qra.R - @test_throws FieldError qra.Z - @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) - @test q*squareQ(q)' ≈ Matrix(I, a_1, a_1) - @test q'*Matrix(1.0I, a_1, a_1)' ≈ squareQ(q)' - @test squareQ(q)'q ≈ Matrix(I, a_1, a_1) - @test Matrix(1.0I, a_1, a_1)'q' ≈ squareQ(q)' - @test q*r ≈ a - @test a*(qra\b) ≈ b atol=3000ε - @test Array(qra) ≈ a - sq = size(q.factors, 2) - @test *(Matrix{eltyb}(I, sq, sq), adjoint(q)) * squareQ(q) ≈ Matrix(I, sq, sq) atol=5000ε - if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab}, q)) - ac = copy(a) - @test qr!(a[:, 1:5])\b == qr!(view(ac, :, 1:5))\b - end - qrstring = sprint((t, s) -> show(t, "text/plain", s), qra) - rstring = sprint((t, s) -> show(t, "text/plain", s), r) - qstring = sprint((t, s) -> show(t, "text/plain", s), q) - @test qrstring == "$(summary(qra))\nQ factor: $qstring\nR factor:\n$rstring" - # iterate - q, r = qra - @test q*r ≈ a - # property names - @test Base.propertynames(qra) == (:R, :Q) - end - @testset "Thin QR decomposition (without pivoting)" begin - qra = @inferred qr(a[:, 1:n1], NoPivot()) - q,r = qra.Q, qra.R - @test_throws FieldError qra.Z - @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) - @test q'*rectangularQ(q) ≈ Matrix(I, a_1, n1) - @test q*r ≈ a[:, 1:n1] - @test q*b[1:n1] ≈ rectangularQ(q)*b[1:n1] atol=100ε - @test q*b ≈ squareQ(q)*b atol=100ε - if eltya != Int - @test Array{eltya}(q) ≈ rectangularQ(q) - end - @test_throws DimensionMismatch q*b[1:n1 + 1] - @test_throws DimensionMismatch b[1:n1 + 1]*q' - sq = size(q.factors, 2) - @test *(UpperTriangular(Matrix{eltyb}(I, sq, sq)), adjoint(q))*squareQ(q) ≈ Matrix(I, n1, a_1) atol=5000ε - if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) - end - # iterate - q, r = qra - @test q*r ≈ a[:, 1:n1] - # property names - @test Base.propertynames(qra) == (:R, :Q) - end - @testset "(Automatic) Fat (pivoted) QR decomposition" begin - @inferred qr(a, ColumnNorm()) - - qrpa = factorize(a[1:n1,:]) - q,r = qrpa.Q, qrpa.R - @test_throws FieldError qrpa.Z - p = qrpa.p - @test q'*squareQ(q) ≈ Matrix(I, n1, n1) - @test q*squareQ(q)' ≈ Matrix(I, n1, n1) - sq = size(q, 2); - @test (UpperTriangular(Matrix{eltya}(I, sq, sq))*q')*squareQ(q) ≈ Matrix(I, n1, n1) - @test q*r ≈ (isa(qrpa,QRPivoted) ? a[1:n1,p] : a[1:n1,:]) - @test q*r[:,invperm(p)] ≈ a[1:n1,:] - @test q*r*transpose(qrpa.P) ≈ a[1:n1,:] - @test a[1:n1,:]*(qrpa\b[1:n1]) ≈ b[1:n1] atol=5000ε - @test Array(qrpa) ≈ a[1:5,:] - if eltya != Int - @test Array{eltya}(q) ≈ Matrix(q) - end - @test_throws DimensionMismatch q*b[1:n1+1] - @test_throws DimensionMismatch b[1:n1+1]*q' - if eltya != Int - @test Matrix{eltyb}(I, n1, n1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) - end - # iterate - q, r, p = qrpa - @test q*r[:,invperm(p)] ≈ a[1:n1,:] - # property names - @test Base.propertynames(qrpa) == (:R, :Q, :p, :P) - end - @testset "(Automatic) Thin (pivoted) QR decomposition" begin - qrpa = factorize(a[:,1:n1]) - q,r = qrpa.Q, qrpa.R - @test_throws FieldError qrpa.Z - p = qrpa.p - @test q'*squareQ(q) ≈ Matrix(I, a_1, a_1) - @test q*squareQ(q)' ≈ Matrix(I, a_1, a_1) - @test q*r ≈ a[:,p] - @test q*r[:,invperm(p)] ≈ a[:,1:n1] - @test Array(qrpa) ≈ a[:,1:5] - if eltya != Int - @test Array{eltya}(q) ≈ Matrix(q) - end - @test_throws DimensionMismatch q*b[1:n1+1] - @test_throws DimensionMismatch b[1:n1+1]*q' - sq = size(q.factors, 2) - @test *(UpperTriangular(Matrix{eltyb}(I, sq, sq)), adjoint(q))*squareQ(q) ≈ Matrix(I, n1, a_1) atol=5000ε - if eltya != Int - @test Matrix{eltyb}(I, a_1, a_1)*q ≈ squareQ(convert(LinearAlgebra.AbstractQ{tab},q)) - end - qrstring = sprint((t, s) -> show(t, "text/plain", s), qrpa) - rstring = sprint((t, s) -> show(t, "text/plain", s), r) - qstring = sprint((t, s) -> show(t, "text/plain", s), q) - pstring = sprint((t, s) -> show(t, "text/plain", s), p) - @test qrstring == "$(summary(qrpa))\nQ factor: $qstring\nR factor:\n$rstring\npermutation:\n$pstring" - # iterate - q, r, p = qrpa - @test q*r[:,invperm(p)] ≈ a[:,1:n1] - # property names - @test Base.propertynames(qrpa) == (:R, :Q, :p, :P) - end - end - if eltya != Int - @testset "Matmul with QR factorizations" begin - a = raw_a - qrpa = factorize(a[:,1:n1]) - q, r = qrpa.Q, qrpa.R - @test rmul!(copy(squareQ(q)'), q) ≈ Matrix(I, n, n) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1),q) - @test rmul!(squareQ(q), adjoint(q)) ≈ Matrix(I, n, n) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1), adjoint(q)) - @test_throws ErrorException size(q,-1) - @test_throws DimensionMismatch LinearAlgebra.lmul!(q,zeros(eltya,n1+1)) - @test_throws DimensionMismatch LinearAlgebra.lmul!(adjoint(q), zeros(eltya,n1+1)) - - b = similar(a); rand!(b) - c = similar(a) - d = similar(a[:,1:n1]) - @test mul!(c, q, b) ≈ q*b - @test mul!(d, q, r) ≈ q*r ≈ a[:,qrpa.p] - @test mul!(c, q', b) ≈ q'*b - @test mul!(d, q', a[:,qrpa.p])[1:n1,:] ≈ r - @test all(x -> abs(x) < ε*norm(a), d[n1+1:end,:]) - @test mul!(c, b, q) ≈ b*q - @test mul!(c, b, q') ≈ b*q' - @test_throws DimensionMismatch mul!(Matrix{eltya}(I, n+1, n), q, b) - - qra = qr(a[:,1:n1], NoPivot()) - q, r = qra.Q, qra.R - @test rmul!(copy(squareQ(q)'), q) ≈ Matrix(I, n, n) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1),q) - @test rmul!(squareQ(q), adjoint(q)) ≈ Matrix(I, n, n) - @test_throws DimensionMismatch rmul!(Matrix{eltya}(I, n+1, n+1),adjoint(q)) - @test_throws ErrorException size(q,-1) - @test_throws DimensionMismatch q * Matrix{Int8}(I, n+4, n+4) - - @test mul!(c, q, b) ≈ q*b - @test mul!(d, q, r) ≈ a[:,1:n1] - @test mul!(c, q', b) ≈ q'*b - @test mul!(d, q', a[:,1:n1])[1:n1,:] ≈ r - @test all(x -> abs(x) < ε*norm(a), d[n1+1:end,:]) - @test mul!(c, b, q) ≈ b*q - @test mul!(c, b, q') ≈ b*q' - @test_throws DimensionMismatch mul!(Matrix{eltya}(I, n+1, n), q, b) - - b = similar(a[:,1]); rand!(b) - c = similar(a[:,1]) - d = similar(a[:,1]) - @test mul!(c, q, b) ≈ q*b - @test mul!(c, q', b) ≈ q'*b - @test_throws DimensionMismatch mul!(Vector{eltya}(undef, n+1), q, b) - end - end - end -end - -@testset "transpose errors" begin - @test_throws ArgumentError transpose(qr(randn(ComplexF64,3,3))) - @test_throws ArgumentError transpose(qr(randn(ComplexF64,3,3), NoPivot())) - @test_throws ArgumentError transpose(qr(big.(randn(ComplexF64,3,3)))) -end - -@testset "Issue 7304" begin - A = [-√.5 -√.5; -√.5 √.5] - Q = rectangularQ(qr(A).Q) - @test norm(A-Q) < eps() -end - -@testset "qr on AbstractVector" begin - vr = [3.0, 4.0] - for Tr in (Float32, Float64) - for T in (Tr, Complex{Tr}) - v = convert(Vector{T}, vr) - nv, nm = qr(v) - @test norm(nv*Matrix(I, (2,2)) - [-0.6 -0.8; -0.8 0.6], Inf) < eps(Tr) - @test nm == fill(-5.0, 1, 1) - end - end -end - -@testset "QR on Ints" begin - # not sure what to do about this edge case now that we build decompositions - # for qr(...), so for now just commenting this out - # @test qr(Int[]) == (Int[],1) - - B = rand(7,2) - @test (1:7)\B ≈ Vector(1:7)\B -end - -@testset "Issue 16520" begin - @test_throws DimensionMismatch rand(3,2)\(1:5) -end - -@testset "Issue 22810" begin - A = zeros(1, 2) - B = zeros(1, 1) - @test A \ B == zeros(2, 1) - @test qr(A, ColumnNorm()) \ B == zeros(2, 1) -end - -@testset "Issue 24107" begin - A = rand(200,2) - @test A \ range(0, stop=1, length=200) == A \ Vector(range(0, stop=1, length=200)) -end - -@testset "Issue 24589. Promotion of rational matrices" begin - A = rand(1//1:5//5, 4,3) - @test Matrix(first(qr(A))) == Matrix(first(qr(float(A)))) -end - -@testset "Issue Test Factorization fallbacks for rectangular problems" begin - A = randn(3,2) - Ac = copy(A') - b = randn(3) - b0 = copy(b) - c = randn(2) - B = randn(3,3) - B0 = copy(B) - C = randn(2,3) - @test A \b ≈ ldiv!(c, qr(A ), b) - @test b == b0 - @test A \B ≈ ldiv!(C, qr(A ), B) - @test B == B0 - c0 = copy(c) - C0 = copy(C) - @test Ac\c ≈ ldiv!(b, qr(Ac, ColumnNorm()), c) - @test c0 == c - @test Ac\C ≈ ldiv!(B, qr(Ac, ColumnNorm()), C) - @test C0 == C -end - -@testset "Issue reflector of zero-length vector" begin - a = [2.0] - x = view(a,1:0) - τ = LinearAlgebra.reflector!(view(x,1:0)) - @test τ == 0.0 - - b = reshape([3.0],1,1) - @test isempty(LinearAlgebra.reflectorApply!(x, τ, view(b,1:0,:))) - @test b[1] == 3.0 -end - -@testset "det(Q::Union{QRCompactWYQ, QRPackedQ})" begin - # 40 is the number larger than the default block size 36 of QRCompactWY - @testset for n in [1:3; 40], m in [1:3; 40], pivot in (NoPivot(), ColumnNorm()) - @testset "real" begin - @testset for k in 0:min(n, m, 5) - A = cat(Array(I(k)), randn(n - k, m - k); dims=(1, 2)) - Q, = qr(A, pivot) - @test det(Q) ≈ det(Q*Matrix(I, size(Q, 1), size(Q, 1))) - @test abs(det(Q)) ≈ 1 - end - end - @testset "complex" begin - @testset for k in 0:min(n, m, 5) - A = cat(Array(I(k)), randn(ComplexF64, n - k, m - k); dims=(1, 2)) - Q, = qr(A, pivot) - @test det(Q) ≈ det(Q*Matrix(I, size(Q, 1), size(Q, 1))) - @test abs(det(Q)) ≈ 1 - end - end - end -end - -@testset "inv(::AbstractQ)" begin - for T in (Float64, ComplexF64) - Q = qr(randn(T,5,5)).Q - @test inv(Q) === Q' - @test inv(Q)' === inv(Q') === Q - end -end - -@testset "QR factorization of Q" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - Q1, R1 = qr(randn(T,5,5)) - Q2, R2 = qr(Q1) - @test Matrix(Q1) ≈ Matrix(Q2) - @test R2 ≈ I - end -end - -@testset "Generation of orthogonal matrices" begin - for T in (Float32, Float64) - n = 5 - Q, R = qr(randn(T,n,n)) - O = Q * Diagonal(sign.(diag(R))) - @test O' * O ≈ I - end -end - -@testset "Multiplication of Q by special matrices" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - n = 5 - Q, R = qr(randn(T,n,n)) - Qmat = Matrix(Q) - D = Diagonal(randn(T,n)) - @test Q * D ≈ Qmat * D - @test D * Q ≈ D * Qmat - J = 2*I - @test Q * J ≈ Qmat * J - @test J * Q ≈ J * Qmat - end -end - -@testset "copyto! for Q" begin - for T in (Float32, Float64, ComplexF32, ComplexF64) - n = 5 - Q, R = qr(randn(T,n,n)) - Qmat = Matrix(Q) - dest1 = Matrix{T}(undef, size(Q)) - copyto!(dest1, Q) - @test dest1 ≈ Qmat - dest2 = PermutedDimsArray(Matrix{T}(undef, size(Q)), (1, 2)) - copyto!(dest2, Q) - @test dest2 ≈ Qmat - dest3 = PermutedDimsArray(Matrix{T}(undef, size(Q)), (2, 1)) - copyto!(dest3, Q) - @test dest3 ≈ Qmat - end -end - -@testset "adjoint of QR" begin - n = 5 - B = randn(5, 2) - - @testset "size(b)=$(size(b))" for b in (B[:, 1], B) - @testset "size(A)=$(size(A))" for A in ( - randn(n, n), - # Wide problems become minimum norm (in x) problems similarly to LQ - randn(n + 2, n), - complex.(randn(n, n), randn(n, n))) - - @testset "QRCompactWY" begin - F = qr(A) - x = F'\b - @test x ≈ A'\b - @test length(size(x)) == length(size(b)) - end - - @testset "QR" begin - F = LinearAlgebra.qrfactUnblocked!(copy(A)) - x = F'\b - @test x ≈ A'\b - @test length(size(x)) == length(size(b)) - end - - @testset "QRPivoted" begin - F = LinearAlgebra.qr(A, ColumnNorm()) - x = F'\b - @test x ≈ A'\b - @test length(size(x)) == length(size(b)) - end - end - @test_throws DimensionMismatch("overdetermined systems are not supported") qr(randn(n - 2, n))'\b - @test_throws DimensionMismatch("arguments must have the same number of rows") qr(randn(n, n + 1))'\b - @test_throws DimensionMismatch("overdetermined systems are not supported") LinearAlgebra.qrfactUnblocked!(randn(n - 2, n))'\b - @test_throws DimensionMismatch("arguments must have the same number of rows") LinearAlgebra.qrfactUnblocked!(randn(n, n + 1))'\b - @test_throws DimensionMismatch("overdetermined systems are not supported") qr(randn(n - 2, n), ColumnNorm())'\b - @test_throws DimensionMismatch("arguments must have the same number of rows") qr(randn(n, n + 1), ColumnNorm())'\b - end -end - -@testset "issue #38974" begin - A = qr(ones(3, 1)) - B = I(3) - C = B*A.Q' - @test C ≈ A.Q * Matrix(I, 3, 3) - @test A.Q' * B ≈ A.Q * Matrix(I, 3, 3) -end - -@testset "convert between eltypes" begin - a = rand(Float64, 10, 5) - qra = qr(a) - qrwy = LinearAlgebra.QRCompactWY{Float32}(qra.factors, qra.T) - @test Array(qrwy) ≈ Array(qr(Float32.(a))) - @test eltype(qrwy.factors) == eltype(qrwy.T) == Float32 - qra = qr(a, ColumnNorm()) - qrp = QRPivoted{Float32}(qra.factors, qra.τ, qra.jpvt) - @test Array(qrp) ≈ Array(qr(Float32.(a), ColumnNorm())) - @test eltype(qrp.factors) == eltype(qrp.τ) == Float32 - a = rand(Float16, 10, 5) - qra = qr(a) - qrnonblas = QR{ComplexF16}(qra.factors, qra.τ) - @test Array(qrnonblas) ≈ Array(qr(ComplexF16.(a))) - @test eltype(qrnonblas.factors) == eltype(qrnonblas.τ) == ComplexF16 -end - -# We use approximate equals to get MKL.jl tests to pass. -@testset "optimized getindex for an AbstractQ" begin - for T in [Float64, ComplexF64] - Q = qr(rand(T, 4, 4)) - Q2 = Q.Q - M = Matrix(Q2) - for j in axes(M, 2) - @test Q2[:, j] ≈ M[:, j] - for i in axes(M, 1) - @test Q2[i, :] ≈ M[i, :] - @test Q2[i, j] ≈ M[i, j] - end - end - @test Q2[:] ≈ M[:] - @test Q2[:, :] ≈ M[:, :] - @test Q2[:, :, :] ≈ M[:, :, :] - end - # Check that getindex works if copy returns itself (#44729) - struct MyIdentity{T} <: LinearAlgebra.AbstractQ{T} end - Base.size(::MyIdentity, dim::Integer) = dim in (1,2) ? 2 : 1 - Base.size(::MyIdentity) = (2, 2) - Base.copy(J::MyIdentity) = J - LinearAlgebra.lmul!(::MyIdentity{T}, M::Array{T}) where {T} = M - @test MyIdentity{Float64}()[1,:] == [1.0, 0.0] -end - -@testset "issue #48911" begin - # testcase in the original issue - # test ldiv!(::QRPivoted, ::AbstractVector) - A = Complex{BigFloat}[1+im 1-im] - b = Complex{BigFloat}[3+im] - x = A\b - AF = Complex{Float64}[1+im 1-im] - bf = Complex{Float64}[3+im] - xf = AF\bf - @test x ≈ xf - - # test ldiv!(::QRPivoted, ::AbstractVector) - A = Complex{BigFloat}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] - b = Complex{BigFloat}[1+im; 0] - x = A\b - AF = Complex{Float64}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] - bf = Complex{Float64}[1+im; 0] - xf = AF\bf - @test x ≈ xf - - # test ldiv!(::QRPivoted, ::AbstractMatrix) - C = Complex{BigFloat}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] - D = Complex{BigFloat}[1+im 1-im; 0 0] - x = C\D - CF = Complex{Float64}[1+im 2-2im 3+3im; 4-4im 5+5im 6-6im] - DF = Complex{Float64}[1+im 1-im; 0 0] - xf = CF\DF - @test x ≈ xf -end - -@testset "issue #53451" begin - # in the issue it was noted that QR factorizations of zero-column matrices - # were possible, but zero row-matrices errored, because LAPACK does not - # accept these empty matrices. now, the `geqrt!` call should be forwarded only - # if both matrix dimensions are positive. - - for dimA in (0, 1, 2, 4) - for F in (Float32, Float64, ComplexF32, ComplexF64, BigFloat) - # this should have worked before, Q is square, and R is 0 × 0: - A_zero_cols = rand(F, dimA, 0) - qr_zero_cols = qr(A_zero_cols) - @test size(qr_zero_cols.Q) == (dimA, dimA) - @test size(qr_zero_cols.R) == (0, 0) - @test qr_zero_cols.Q == LinearAlgebra.I(dimA) - - # this should work now, Q is 0 × 0, and R has `dimA` columns: - A_zero_rows = rand(F, 0, dimA) - qr_zero_rows = qr(A_zero_rows) - @test size(qr_zero_rows.Q) == (0, 0) - @test size(qr_zero_rows.R) == (0, dimA) - end - end -end - -@testset "issue #53214" begin - # Test that the rank of a QRPivoted matrix is computed correctly - @test rank(qr([1.0 0.0; 0.0 1.0], ColumnNorm())) == 2 - @test rank(qr([1.0 0.0; 0.0 0.9], ColumnNorm()), rtol=0.95) == 1 - @test rank(qr([1.0 0.0; 0.0 0.9], ColumnNorm()), atol=0.95) == 1 - @test rank(qr([1.0 0.0; 0.0 1.0], ColumnNorm()), rtol=1.01) == 0 - @test rank(qr([1.0 0.0; 0.0 1.0], ColumnNorm()), atol=1.01) == 0 - - @test rank(qr([1.0 2.0; 2.0 4.0], ColumnNorm())) == 1 - @test rank(qr([1.0 2.0 3.0; 4.0 5.0 6.0 ; 7.0 8.0 9.0], ColumnNorm())) == 2 -end - -end # module TestQR diff --git a/stdlib/LinearAlgebra/test/runtests.jl b/stdlib/LinearAlgebra/test/runtests.jl deleted file mode 100644 index d64da9899ca86..0000000000000 --- a/stdlib/LinearAlgebra/test/runtests.jl +++ /dev/null @@ -1,10 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license -using Test, LinearAlgebra - -for file in readlines(joinpath(@__DIR__, "testgroups")) - include(file * ".jl") -end - -@testset "Docstrings" begin - @test isempty(Docs.undocumented_names(LinearAlgebra)) -end diff --git a/stdlib/LinearAlgebra/test/schur.jl b/stdlib/LinearAlgebra/test/schur.jl deleted file mode 100644 index f3d494fba7942..0000000000000 --- a/stdlib/LinearAlgebra/test/schur.jl +++ /dev/null @@ -1,221 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestSchur - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted - -n = 10 - -# Split n into 2 parts for tests needing two matrices -n1 = div(n, 2) -n2 = 2*n1 - -Random.seed!(1234321) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int) - a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - asym = a' + a # symmetric indefinite - apd = a' * a # symmetric positive-definite - for (a, asym, apd) in ((a, asym, apd), - (view(a, 1:n, 1:n), - view(asym, 1:n, 1:n), - view(apd, 1:n, 1:n))) - ε = εa = eps(abs(float(one(eltya)))) - - d,v = eigen(a) - f = schur(a) - @test f.vectors*f.Schur*f.vectors' ≈ a - @test sort(real(f.values)) ≈ sort(real(d)) - @test sort(imag(f.values)) ≈ sort(imag(d)) - @test istriu(f.Schur) || eltype(a)<:Real - @test convert(Array, f) ≈ a - @test_throws FieldError f.A - - sch, vecs, vals = schur(UpperTriangular(triu(a))) - @test vecs*sch*vecs' ≈ triu(a) - sch, vecs, vals = schur(UnitUpperTriangular(triu(a))) - @test vecs*sch*vecs' ≈ UnitUpperTriangular(triu(a)) - sch, vecs, vals = schur(LowerTriangular(tril(a))) - @test vecs*sch*vecs' ≈ tril(a) - sch, vecs, vals = schur(UnitLowerTriangular(tril(a))) - @test vecs*sch*vecs' ≈ UnitLowerTriangular(tril(a)) - sch, vecs, vals = schur(Hermitian(asym)) - @test vecs*sch*vecs' ≈ asym - sch, vecs, vals = schur(Symmetric(a + transpose(a))) - @test vecs*sch*vecs' ≈ a + transpose(a) - sch, vecs, vals = schur(Tridiagonal(a + transpose(a))) - @test vecs*sch*vecs' ≈ Tridiagonal(a + transpose(a)) - sch, vecs, vals = schur(Bidiagonal(a, :U)) - @test vecs*sch*vecs' ≈ Bidiagonal(a, :U) - sch, vecs, vals = schur(Bidiagonal(a, :L)) - @test vecs*sch*vecs' ≈ Bidiagonal(a, :L) - - tstring = sprint((t, s) -> show(t, "text/plain", s), f.T) - zstring = sprint((t, s) -> show(t, "text/plain", s), f.Z) - vstring = sprint((t, s) -> show(t, "text/plain", s), f.values) - fstring = sprint((t, s) -> show(t, "text/plain", s), f) - @test fstring == "$(summary(f))\nT factor:\n$tstring\nZ factor:\n$(zstring)\neigenvalues:\n$vstring" - @testset "Reorder Schur" begin - # use asym for real schur to enforce tridiag structure - # avoiding partly selection of conj. eigenvalues - ordschura = eltya <: Complex ? a : asym - S = schur(ordschura) - select = bitrand(n) - O = ordschur(S, select) - sum(select) != 0 && @test S.values[findall(select)] ≈ O.values[1:sum(select)] - @test O.vectors*O.Schur*O.vectors' ≈ ordschura - @test_throws FieldError f.A - Snew = LinearAlgebra.Schur(S.T, S.Z, S.values) - SchurNew = ordschur!(copy(Snew), select) - @test O.vectors ≈ SchurNew.vectors - @test O.Schur ≈ SchurNew.Schur - end - - if isa(a, Array) - a1_sf = a[1:n1, 1:n1] - a2_sf = a[n1+1:n2, n1+1:n2] - else - a1_sf = view(a, 1:n1, 1:n1) - a2_sf = view(a, n1+1:n2, n1+1:n2) - end - @testset "Generalized Schur" begin - f = schur(a1_sf, a2_sf) - @test f.Q*f.S*f.Z' ≈ a1_sf - @test f.Q*f.T*f.Z' ≈ a2_sf - @test istriu(f.S) || eltype(a)<:Real - @test istriu(f.T) || eltype(a)<:Real - @test_throws FieldError f.A - - sstring = sprint((t, s) -> show(t, "text/plain", s), f.S) - tstring = sprint((t, s) -> show(t, "text/plain", s), f.T) - qstring = sprint((t, s) -> show(t, "text/plain", s), f.Q) - zstring = sprint((t, s) -> show(t, "text/plain", s), f.Z) - αstring = sprint((t, s) -> show(t, "text/plain", s), f.α) - βstring = sprint((t, s) -> show(t, "text/plain", s), f.β) - fstring = sprint((t, s) -> show(t, "text/plain", s), f) - @test fstring == "$(summary(f))\nS factor:\n$sstring\nT factor:\n$(tstring)\nQ factor:\n$(qstring)\nZ factor:\n$(zstring)\nα:\n$αstring\nβ:\n$βstring" - end - @testset "Reorder Generalized Schur" begin - NS = schur(a1_sf, a2_sf) - # Currently just testing with selecting gen eig values < 1 - select = abs2.(NS.values) .< 1 - m = sum(select) - S = ordschur(NS, select) - # Make sure that the new factorization still factors matrix - @test S.Q*S.S*S.Z' ≈ a1_sf - @test S.Q*S.T*S.Z' ≈ a2_sf - # Make sure that we have sorted it correctly - @test NS.values[findall(select)] ≈ S.values[1:m] - - Snew = LinearAlgebra.GeneralizedSchur(NS.S, NS.T, NS.alpha, NS.beta, NS.Q, NS.Z) - SchurNew = ordschur!(copy(Snew), select) - @test S.Q ≈ SchurNew.Q - @test S.S ≈ SchurNew.S - @test S.T ≈ SchurNew.T - @test S.Z ≈ SchurNew.Z - @test S.alpha ≈ SchurNew.alpha - @test S.beta ≈ SchurNew.beta - sS,sT,sQ,sZ = schur(a1_sf,a2_sf) - @test NS.Q ≈ sQ - @test NS.T ≈ sT - @test NS.S ≈ sS - @test NS.Z ≈ sZ - end - end - @testset "0x0 matrix" for A in (zeros(eltya, 0, 0), view(rand(eltya, 2, 2), 1:0, 1:0)) - T, Z, λ = LinearAlgebra.schur(A) - @test T == A - @test Z == A - @test λ == zeros(0) - end - - if eltya <: Real - @testset "quasitriangular to triangular" begin - S = schur(a) - SC = Schur{Complex}(S) - @test eltype(SC) == complex(eltype(S)) - @test istriu(SC.T) - @test SC.Z*SC.Z' ≈ I - @test SC.Z*SC.T*SC.Z' ≈ a - @test sort(SC.values,by=LinearAlgebra.eigsortby) ≈ sort(S.values,by=LinearAlgebra.eigsortby) - @test Schur{Complex}(SC) === SC === Schur{eltype(SC)}(SC) - @test Schur{eltype(S)}(S) === S - if eltype(S) === Float32 - S64 = Schur{Float64}(S) - @test eltype(S64) == Float64 - @test S64.Z == S.Z - @test S64.T == S.T - @test S64.values == S.values - end - end - end - - @testset "0x0 $eltya matrices" begin - A = zeros(eltya, 0, 0) - B = zeros(eltya, 0, 0) - S = LinearAlgebra.schur(A, B) - @test S.S == A - @test S.T == A - @test S.Q == A - @test S.Z == A - @test S.alpha == zeros(0) - @test S.beta == zeros(0) - end -end - -@testset "Generalized Schur convergence" begin - # Check for convergence issues, #40279 - problematic_pencils = [ - ( ComplexF64[0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 3.7796350217469814 -3.3125635598133054 0.0 0.0 0.0 0.0 0.0 0.0 6.418270043493963 -6.625127119626611 0.0 0.0 0.0 0.0 0.0 -1.0; -3.312563559813306 3.779635021746982 0.0 0.0 0.0 0.0 0.0 0.0 -6.625127119626612 6.418270043493964 -1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 3.7796350217469814 0.0 0.0 -3.3125635598133054 0.0 0.0 0.0 -1.0 6.418270043493963 0.0 0.0 -6.625127119626611 0.0 0.0; 0.0 0.0 0.0 3.779635021746982 -3.312563559813306 0.0 0.0 0.0 0.0 0.0 0.0 6.418270043493964 -6.625127119626612 0.0 -1.0 0.0; 0.0 0.0 0.0 -3.3125635598133054 3.7796350217469814 0.0 0.0 0.0 0.0 0.0 0.0 -6.625127119626611 6.418270043493963 -1.0 0.0 0.0; 0.0 0.0 -3.312563559813306 0.0 0.0 3.779635021746982 0.0 0.0 0.0 0.0 -6.625127119626612 0.0 -1.0 6.418270043493964 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 3.7796350217469814 -3.3125635598133054 0.0 0.0 0.0 -1.0 0.0 0.0 6.418270043493963 -6.625127119626611; 0.0 0.0 0.0 0.0 0.0 0.0 -3.312563559813306 3.779635021746982 -1.0 0.0 0.0 0.0 0.0 0.0 -6.625127119626612 6.418270043493964], - ComplexF64[1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -3.7796350217469814 3.312563559813306 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.3125635598133054 -3.779635021746982 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -3.7796350217469814 0.0 0.0 3.312563559813306 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -3.779635021746982 3.3125635598133054 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.312563559813306 -3.7796350217469814 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.3125635598133054 0.0 0.0 -3.779635021746982 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -3.7796350217469814 3.312563559813306; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 3.3125635598133054 -3.779635021746982] - ), - ( ComplexF64[0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -2.62 -1.0 0.0 0.0 0.0 0.0 -1.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 -1.0 -2.62 0.0 0.0 0.0 0.0 0.0; 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62 0.0; 0.0 0.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0 -1.0 0.0 0.0 0.0 0.0 0.0 -2.62], - ComplexF64[1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0] - ), - ( ComplexF64[0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 0.33748484079831426 -0.10323794456968927 0.0 0.0 0.0 0.0 0.0 0.0 -2.5940303184033713 -0.20647588913937853 0.0 0.0 0.0 0.0 0.0 -1.0; -0.10323794456968927 0.3374848407983142 0.0 0.0 0.0 0.0 0.0 0.0 -0.20647588913937853 -2.5940303184033713 -1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.33748484079831426 0.0 0.0 -0.10323794456968927 0.0 0.0 0.0 -1.0 -2.5940303184033713 0.0 0.0 -0.20647588913937853 0.0 0.0; 0.0 0.0 0.0 0.3374848407983142 -0.10323794456968927 0.0 0.0 0.0 0.0 0.0 0.0 -2.5940303184033713 -0.20647588913937853 0.0 -1.0 0.0; 0.0 0.0 0.0 -0.10323794456968927 0.33748484079831426 0.0 0.0 0.0 0.0 0.0 0.0 -0.20647588913937853 -2.5940303184033713 -1.0 0.0 0.0; 0.0 0.0 -0.10323794456968927 0.0 0.0 0.3374848407983142 0.0 0.0 0.0 0.0 -0.20647588913937853 0.0 -1.0 -2.5940303184033713 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.33748484079831426 -0.10323794456968927 0.0 0.0 0.0 -1.0 0.0 0.0 -2.5940303184033713 -0.20647588913937853; 0.0 0.0 0.0 0.0 0.0 0.0 -0.10323794456968927 0.3374848407983142 -1.0 0.0 0.0 0.0 0.0 0.0 -0.20647588913937853 -2.5940303184033713], - ComplexF64[1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.33748484079831426 0.10323794456968927 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.10323794456968927 -0.3374848407983142 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.33748484079831426 0.0 0.0 0.10323794456968927 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.3374848407983142 0.10323794456968927 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.10323794456968927 -0.33748484079831426 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.10323794456968927 0.0 0.0 -0.3374848407983142 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -0.33748484079831426 0.10323794456968927; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.10323794456968927 -0.3374848407983142] - ), - ( ComplexF64[0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 1.7391668762048442 -1.309613611600033 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.150333752409688 -2.619227223200066 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0; -1.3096136116000332 1.739166876204844 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -2.6192272232000664 2.150333752409688 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.739166876204844 0.0 0.0 -1.3096136116000332 0.0 0.0 0.0 0.0 0.0 -1.0 2.150333752409688 0.0 0.0 -2.6192272232000664 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.739166876204844 0.0 0.0 0.0 0.0 -1.3096136116000332 0.0 -1.0 0.0 0.0 2.150333752409688 0.0 0.0 0.0 0.0 -2.6192272232000664 0.0; 0.0 0.0 0.0 0.0 1.7391668762048442 0.0 0.0 0.0 0.0 -1.309613611600033 0.0 0.0 0.0 0.0 2.150333752409688 -1.0 0.0 0.0 0.0 -2.619227223200066; 0.0 0.0 -1.309613611600033 0.0 0.0 1.7391668762048442 0.0 0.0 0.0 0.0 0.0 0.0 -2.619227223200066 0.0 -1.0 2.150333752409688 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.739166876204844 -1.3096136116000332 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.150333752409688 -2.6192272232000664 0.0 -1.0; 0.0 0.0 0.0 0.0 0.0 0.0 -1.309613611600033 1.7391668762048442 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -2.619227223200066 2.150333752409688 -1.0 0.0; 0.0 0.0 0.0 -1.309613611600033 0.0 0.0 0.0 0.0 1.7391668762048442 0.0 0.0 0.0 0.0 -2.619227223200066 0.0 0.0 0.0 -1.0 2.150333752409688 0.0; 0.0 0.0 0.0 0.0 -1.3096136116000332 0.0 0.0 0.0 0.0 1.739166876204844 0.0 0.0 0.0 0.0 -2.6192272232000664 0.0 -1.0 0.0 0.0 2.150333752409688], - ComplexF64[1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.7391668762048442 1.3096136116000332 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.309613611600033 -1.739166876204844 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.739166876204844 0.0 0.0 1.309613611600033 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.739166876204844 0.0 0.0 0.0 0.0 1.309613611600033 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.7391668762048442 0.0 0.0 0.0 0.0 1.3096136116000332; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.3096136116000332 0.0 0.0 -1.7391668762048442 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.739166876204844 1.309613611600033 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.3096136116000332 -1.7391668762048442 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.3096136116000332 0.0 0.0 0.0 0.0 -1.7391668762048442 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.309613611600033 0.0 0.0 0.0 0.0 -1.739166876204844] - ), - ( ComplexF64[0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0; 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230788 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007; 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769246 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230784 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769246 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230784 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.90076923076925 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -1.0000000000000007 -12.019230769230788; -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 -6.009615384615393 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384622 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769244 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615393 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384622 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769244 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.490384615384624 -1.0000000000000007 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -12.019230769230784 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 11.900769230769248], - ComplexF64[1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615393 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615393 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384622 0.0 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615392 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384622 0.0 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624 0.0; 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6.009615384615394 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 -6.490384615384624] - )] - - for (A, B) in problematic_pencils - f = schur(A, B) - @test f.Q*f.S*f.Z' ≈ A - @test f.Q*f.T*f.Z' ≈ B - end -end - -@testset "adjoint and transpose for schur (#40941)" begin - A = rand(3, 3) - B = schur(A', A) - C = B.left*B.S*B.right' - D = schur(transpose(A), A) - E = D.left*D.S*D.right' - @test A' ≈ C ≈ E -end - -@testset "UpperHessenberg schur" begin - A = UpperHessenberg(rand(ComplexF64, 100, 100)) - B = Array(A) - fact1 = schur(A) - fact2 = schur(B) - @test fact1.values ≈ fact2.values - @test fact1.Z * fact1.T * fact1.Z' ≈ B - - A = UpperHessenberg(rand(Int32, 50, 50)) - B = Array(A) - fact1 = schur(A) - fact2 = schur(B) - @test fact1.values ≈ fact2.values - @test fact1.Z * fact1.T * fact1.Z' ≈ B -end - -end # module TestSchur diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl deleted file mode 100644 index 4b91bcfc1a4d5..0000000000000 --- a/stdlib/LinearAlgebra/test/special.jl +++ /dev/null @@ -1,862 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestSpecial - -using Test, LinearAlgebra, Random -using LinearAlgebra: rmul!, BandIndex - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -n= 10 #Size of matrix to test -Random.seed!(1) - -@testset "Interconversion between special matrix types" begin - a = [1.0:n;] - A = Diagonal(a) - @testset for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - @test Matrix(convert(newtype, Diagonal(GenericArray(a)))) == Matrix(A) - end - - @testset for isupper in (true, false) - A = Bidiagonal(a, [1.0:n-1;], ifelse(isupper, :U, :L)) - for newtype in [Bidiagonal, Tridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - @test Matrix(newtype(A)) == Matrix(A) - end - @test_throws ArgumentError convert(SymTridiagonal, A) - tritype = isupper ? UpperTriangular : LowerTriangular - @test Matrix(tritype(A)) == Matrix(A) - - A = Bidiagonal(a, zeros(n-1), ifelse(isupper, :U, :L)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - @test Matrix(newtype(A)) == Matrix(A) - end - @test Matrix(tritype(A)) == Matrix(A) - end - - A = SymTridiagonal(a, [1.0:n-1;]) - for newtype in [Tridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - end - for newtype in [Diagonal, Bidiagonal] - @test_throws ArgumentError convert(newtype,A) - end - A = SymTridiagonal(a, zeros(n-1)) - @test Matrix(convert(Bidiagonal,A)) == Matrix(A) - - A = Tridiagonal(zeros(n-1), [1.0:n;], zeros(n-1)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - end - A = Tridiagonal(fill(1., n-1), [1.0:n;], fill(1., n-1)) #not morally Diagonal - for newtype in [SymTridiagonal, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - end - for newtype in [Diagonal, Bidiagonal] - @test_throws ArgumentError convert(newtype,A) - end - A = Tridiagonal(zeros(n-1), [1.0:n;], fill(1., n-1)) #not morally Diagonal - @test Matrix(convert(Bidiagonal, A)) == Matrix(A) - A = UpperTriangular(Tridiagonal(zeros(n-1), [1.0:n;], fill(1., n-1))) - @test Matrix(convert(Bidiagonal, A)) == Matrix(A) - A = Tridiagonal(fill(1., n-1), [1.0:n;], zeros(n-1)) #not morally Diagonal - @test Matrix(convert(Bidiagonal, A)) == Matrix(A) - A = LowerTriangular(Tridiagonal(fill(1., n-1), [1.0:n;], zeros(n-1))) - @test Matrix(convert(Bidiagonal, A)) == Matrix(A) - @test_throws ArgumentError convert(SymTridiagonal,A) - - A = LowerTriangular(Matrix(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, LowerTriangular, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - end - A = UpperTriangular(Matrix(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, UpperTriangular, Matrix] - @test Matrix(convert(newtype, A)) == Matrix(A) - end - A = UpperTriangular(triu(rand(n,n))) - for newtype in [Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal] - @test_throws ArgumentError convert(newtype,A) - end - - - # test operations/constructors (not conversions) permitted in the docs - dl = [1., 1.] - d = [-2., -2., -2.] - T = Tridiagonal(dl, d, -dl) - S = SymTridiagonal(d, dl) - Bu = Bidiagonal(d, dl, :U) - Bl = Bidiagonal(d, dl, :L) - D = Diagonal(d) - M = [-2. 0. 0.; 1. -2. 0.; -1. 1. -2.] - U = UpperTriangular(M) - L = LowerTriangular(Matrix(M')) - - for A in (T, S, Bu, Bl, D, U, L, M) - Adense = Matrix(A) - B = Symmetric(A) - Bdense = Matrix(B) - for (C,Cdense) in ((A,Adense), (B,Bdense)) - @test Diagonal(C) == Diagonal(Cdense) - @test Bidiagonal(C, :U) == Bidiagonal(Cdense, :U) - @test Bidiagonal(C, :L) == Bidiagonal(Cdense, :L) - @test Tridiagonal(C) == Tridiagonal(Cdense) - @test UpperTriangular(C) == UpperTriangular(Cdense) - @test LowerTriangular(C) == LowerTriangular(Cdense) - end - end - - @testset "Matrix constructor for !isa(zero(T), T)" begin - # the following models JuMP.jl's VariableRef and AffExpr, resp. - struct TypeWithoutZero end - struct TypeWithZero end - Base.promote_rule(::Type{TypeWithoutZero}, ::Type{TypeWithZero}) = TypeWithZero - Base.convert(::Type{TypeWithZero}, ::TypeWithoutZero) = TypeWithZero() - Base.zero(x::Union{TypeWithoutZero, TypeWithZero}) = zero(typeof(x)) - Base.zero(::Type{<:Union{TypeWithoutZero, TypeWithZero}}) = TypeWithZero() - LinearAlgebra.symmetric(::TypeWithoutZero, ::Symbol) = TypeWithoutZero() - LinearAlgebra.symmetric_type(::Type{TypeWithoutZero}) = TypeWithoutZero - Base.copy(A::TypeWithoutZero) = A - Base.transpose(::TypeWithoutZero) = TypeWithoutZero() - d = fill(TypeWithoutZero(), 3) - du = fill(TypeWithoutZero(), 2) - dl = fill(TypeWithoutZero(), 2) - D = Diagonal(d) - Bu = Bidiagonal(d, du, :U) - Bl = Bidiagonal(d, dl, :L) - Tri = Tridiagonal(dl, d, du) - Sym = SymTridiagonal(d, dl) - for M in (D, Bu, Bl, Tri, Sym) - @test Matrix(M) == zeros(TypeWithZero, 3, 3) - end - - mutable struct MTypeWithZero end - Base.convert(::Type{MTypeWithZero}, ::TypeWithoutZero) = MTypeWithZero() - Base.convert(::Type{MTypeWithZero}, ::TypeWithZero) = MTypeWithZero() - Base.zero(x::MTypeWithZero) = zero(typeof(x)) - Base.zero(::Type{MTypeWithZero}) = MTypeWithZero() - U = UpperTriangular(Symmetric(fill(TypeWithoutZero(), 2, 2))) - M = Matrix{MTypeWithZero}(U) - @test all(x -> x isa MTypeWithZero, M) - end -end - -@testset "Binary ops among special types" begin - a=[1.0:n;] - A=Diagonal(a) - Spectypes = [Diagonal, Bidiagonal, Tridiagonal, Matrix] - for (idx, type1) in enumerate(Spectypes) - for type2 in Spectypes - B = convert(type1,A) - C = convert(type2,A) - @test Matrix(B + C) ≈ Matrix(A + A) - @test Matrix(B - C) ≈ Matrix(A - A) - end - end - B = SymTridiagonal(a, fill(1., n-1)) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test Matrix(B + convert(Spectype,A)) ≈ Matrix(B + A) - @test Matrix(convert(Spectype,A) + B) ≈ Matrix(B + A) - @test Matrix(B - convert(Spectype,A)) ≈ Matrix(B - A) - @test Matrix(convert(Spectype,A) - B) ≈ Matrix(A - B) - end - - C = rand(n,n) - for TriType in [LinearAlgebra.UnitLowerTriangular, LinearAlgebra.UnitUpperTriangular, UpperTriangular, LowerTriangular] - D = TriType(C) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test Matrix(D + convert(Spectype,A)) ≈ Matrix(D + A) - @test Matrix(convert(Spectype,A) + D) ≈ Matrix(A + D) - @test Matrix(D - convert(Spectype,A)) ≈ Matrix(D - A) - @test Matrix(convert(Spectype,A) - D) ≈ Matrix(A - D) - end - end - - UpTri = UpperTriangular(rand(20,20)) - LoTri = LowerTriangular(rand(20,20)) - Diag = Diagonal(rand(20,20)) - Tridiag = Tridiagonal(rand(20, 20)) - UpBi = Bidiagonal(rand(20,20), :U) - LoBi = Bidiagonal(rand(20,20), :L) - Sym = SymTridiagonal(rand(20), rand(19)) - Dense = rand(20, 20) - mats = Any[UpTri, LoTri, Diag, Tridiag, UpBi, LoBi, Sym, Dense] - - for op in (+,-,*) - for A in mats - for B in mats - @test (op)(A, B) ≈ (op)(Matrix(A), Matrix(B)) ≈ Matrix((op)(A, B)) - end - end - end -end - -@testset "+ and - among structured matrices with different container types" begin - diag = 1:5 - offdiag = 1:4 - uniformscalingmats = [UniformScaling(3), UniformScaling(1.0), UniformScaling(3//5), UniformScaling(ComplexF64(1.3, 3.5))] - mats = Any[Diagonal(diag), Bidiagonal(diag, offdiag, 'U'), Bidiagonal(diag, offdiag, 'L'), Tridiagonal(offdiag, diag, offdiag), SymTridiagonal(diag, offdiag)] - for T in [ComplexF64, Int64, Rational{Int64}, Float64] - push!(mats, Diagonal(Vector{T}(diag))) - push!(mats, Bidiagonal(Vector{T}(diag), Vector{T}(offdiag), 'U')) - push!(mats, Bidiagonal(Vector{T}(diag), Vector{T}(offdiag), 'L')) - push!(mats, Tridiagonal(Vector{T}(offdiag), Vector{T}(diag), Vector{T}(offdiag))) - push!(mats, SymTridiagonal(Vector{T}(diag), Vector{T}(offdiag))) - end - - for op in (+,-,*) - for A in mats - for B in mats - @test (op)(A, B) ≈ (op)(Matrix(A), Matrix(B)) ≈ Matrix((op)(A, B)) - end - end - end - for op in (+,-) - for A in mats - for B in uniformscalingmats - @test (op)(A, B) ≈ (op)(Matrix(A), B) ≈ Matrix((op)(A, B)) - @test (op)(B, A) ≈ (op)(B, Matrix(A)) ≈ Matrix((op)(B, A)) - end - end - end - diag = [randn(ComplexF64, 2, 2) for _ in 1:3] - odiag = [randn(ComplexF64, 2, 2) for _ in 1:2] - for A in (Diagonal(diag), - Bidiagonal(diag, odiag, :U), - Bidiagonal(diag, odiag, :L), - Tridiagonal(odiag, diag, odiag), - SymTridiagonal(diag, odiag)), B in uniformscalingmats - @test (A + B)::typeof(A) == (B + A)::typeof(A) - @test (A - B)::typeof(A) == ((A + (-B))::typeof(A)) - @test (B - A)::typeof(A) == ((B + (-A))::typeof(A)) - end -end - - -@testset "Triangular Types and QR" begin - for typ in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - a = rand(n,n) - atri = typ(a) - matri = Matrix(atri) - b = rand(n,n) - for pivot in (ColumnNorm(), NoPivot()) - qrb = qr(b, pivot) - @test atri * qrb.Q ≈ matri * qrb.Q - @test atri * qrb.Q' ≈ matri * qrb.Q' - @test qrb.Q * atri ≈ qrb.Q * matri - @test qrb.Q' * atri ≈ qrb.Q' * matri - end - end -end - -@testset "Multiplication of Qs" begin - for pivot in (ColumnNorm(), NoPivot()), A in (rand(5, 3), rand(5, 5), rand(3, 5)) - Q = qr(A, pivot).Q - m = size(A, 1) - C = Matrix{Float64}(undef, (m, m)) - @test Q*Q ≈ (Q*I) * (Q*I) ≈ mul!(C, Q, Q) - @test size(Q*Q) == (m, m) - @test Q'Q ≈ (Q'*I) * (Q*I) ≈ mul!(C, Q', Q) - @test size(Q'Q) == (m, m) - @test Q*Q' ≈ (Q*I) * (Q'*I) ≈ mul!(C, Q, Q') - @test size(Q*Q') == (m, m) - @test Q'Q' ≈ (Q'*I) * (Q'*I) ≈ mul!(C, Q', Q') - @test size(Q'Q') == (m, m) - end -end - -@testset "concatenations of combinations of special and other matrix types" begin - N = 4 - # Test concatenating pairwise combinations of special matrices - 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)) - abstractq = qr(tridiagmat).Q - specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat, abstractq, zeros(Int,N,N)) - for specialmata in specialmats, specialmatb in specialmats - MA = collect(specialmata); MB = collect(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 dense matrices or dense vectors - densevec = fill(1., N) - densemat = diagm(0 => densevec) - for specialmat in specialmats - SM = Matrix(specialmat) - # --> Tests applicable only to pairs of matrices - @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 (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 - -@testset "concatenations of annotated types" begin - N = 4 - # The tested annotation types - testfull = Base.get_bool_env("JULIA_TESTFULL", false) - utriannotations = (UpperTriangular, UnitUpperTriangular) - ltriannotations = (LowerTriangular, UnitLowerTriangular) - triannotations = (utriannotations..., ltriannotations...) - symannotations = (Symmetric, Hermitian) - annotations = testfull ? (triannotations..., symannotations...) : (LowerTriangular, Symmetric) - # 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)) - 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 specialconcatmats] - # Test concatenations of pairwise combinations of annotated special matrices - for annospcmata in annospcmats, annospcmatb in annospcmats - 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 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..., 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 (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 - # 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...) - 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...) - 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 - -# 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)) - Bu = Bidiagonal(rand(elty, 10), rand(elty, 9), 'U') - Bl = Bidiagonal(rand(elty, 10), rand(elty, 9), 'L') - T = Tridiagonal(rand(elty, 9),rand(elty, 10), rand(elty, 9)) - S = SymTridiagonal(rand(elty, 10), rand(elty, 9)) - mats = Any[D, Bu, Bl, T, S] - for A in mats - @test iszero(zero(A)) - @test isone(one(A)) - @test zero(A) == zero(Matrix(A)) - @test one(A) == one(Matrix(A)) - end - - @test zero(D) isa Diagonal - @test one(D) isa Diagonal - - @test zero(Bu) isa Bidiagonal - @test one(Bu) isa Bidiagonal - @test zero(Bl) isa Bidiagonal - @test one(Bl) isa Bidiagonal - @test zero(Bu).uplo == one(Bu).uplo == Bu.uplo - @test zero(Bl).uplo == one(Bl).uplo == Bl.uplo - - @test zero(T) isa Tridiagonal - @test one(T) isa Tridiagonal - @test zero(S) isa SymTridiagonal - @test one(S) isa SymTridiagonal - end - - # ranges - D = Diagonal(1:10) - Bu = Bidiagonal(1:10, 1:9, 'U') - Bl = Bidiagonal(1:10, 1:9, 'L') - T = Tridiagonal(1:9, 1:10, 1:9) - S = SymTridiagonal(1:10, 1:9) - mats = [D, Bu, Bl, T, S] - for A in mats - @test iszero(zero(A)) - @test isone(one(A)) - @test zero(A) == zero(Matrix(A)) - @test one(A) == one(Matrix(A)) - end - - @test zero(D) isa Diagonal - @test one(D) isa Diagonal - - @test zero(Bu) isa Bidiagonal - @test one(Bu) isa Bidiagonal - @test zero(Bl) isa Bidiagonal - @test one(Bl) isa Bidiagonal - @test zero(Bu).uplo == one(Bu).uplo == Bu.uplo - @test zero(Bl).uplo == one(Bl).uplo == Bl.uplo - - @test zero(T) isa Tridiagonal - @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 - diag = rand(10) - offdiag = rand(9) - D = Diagonal(rand(10)) - Bup = Bidiagonal(diag, offdiag, 'U') - Blo = Bidiagonal(diag, offdiag, 'L') - Bupd = Bidiagonal(diag, zeros(9), 'U') - Blod = Bidiagonal(diag, zeros(9), 'L') - T = Tridiagonal(offdiag, diag, offdiag) - Td = Tridiagonal(zeros(9), diag, zeros(9)) - Tu = Tridiagonal(zeros(9), diag, offdiag) - Tl = Tridiagonal(offdiag, diag, zeros(9)) - S = SymTridiagonal(diag, offdiag) - Sd = SymTridiagonal(diag, zeros(9)) - - mats = [D, Bup, Blo, Bupd, Blod, T, Td, Tu, Tl, S, Sd] - - for a in mats - for b in mats - @test (a == b) == (Matrix(a) == Matrix(b)) == (b == a) == (Matrix(b) == Matrix(a)) - end - end -end - -@testset "BiTriSym*Q' and Q'*BiTriSym" begin - dl = [1, 1, 1] - d = [1, 1, 1, 1] - D = Diagonal(d) - Bi = Bidiagonal(d, dl, :L) - Tri = Tridiagonal(dl, d, dl) - Sym = SymTridiagonal(d, dl) - F = qr(ones(4, 1)) - A = F.Q' - for A in (F.Q, F.Q'), B in (D, Bi, Tri, Sym) - @test B*A ≈ Matrix(B)*A - @test A*B ≈ A*Matrix(B) - end -end - -@testset "Ops on SymTridiagonal ev has the same length as dv" begin - x = rand(3) - y = rand(3) - z = rand(2) - - S = SymTridiagonal(x, y) - T = Tridiagonal(z, x, z) - Bu = Bidiagonal(x, z, :U) - Bl = Bidiagonal(x, z, :L) - - Ms = Matrix(S) - Mt = Matrix(T) - Mbu = Matrix(Bu) - Mbl = Matrix(Bl) - - @test S + T ≈ Ms + Mt - @test T + S ≈ Mt + Ms - @test S + Bu ≈ Ms + Mbu - @test Bu + S ≈ Mbu + Ms - @test S + Bl ≈ Ms + Mbl - @test Bl + S ≈ Mbl + Ms -end - -@testset "Ensure Strided * (Sym)Tridiagonal is Dense" begin - x = rand(3) - y = rand(3) - z = rand(2) - - l = rand(12, 12) - # strided but not a Matrix - v = @view l[1:4:end, 1:4:end] - M_v = Matrix(v) - m = rand(3, 3) - - S = SymTridiagonal(x, y) - T = Tridiagonal(z, x, z) - M_S = Matrix(S) - M_T = Matrix(T) - - @test m * T ≈ m * M_T - @test m * S ≈ m * M_S - @test v * T ≈ M_v * T - @test v * S ≈ M_v * S - - @test m * T isa Matrix - @test m * S isa Matrix - @test v * T isa Matrix - @test v * S isa Matrix -end - -@testset "copyto! between matrix types" begin - dl, d, du = zeros(Int,4), [1:5;], zeros(Int,4) - d_ones = ones(Int,size(du)) - - @testset "from Diagonal" begin - D = Diagonal(d) - @testset "to Bidiagonal" begin - BU = Bidiagonal(similar(d, BigInt), similar(du, BigInt), :U) - BL = Bidiagonal(similar(d, BigInt), similar(dl, BigInt), :L) - for B in (BL, BU) - copyto!(B, D) - @test B == D - end - - @testset "mismatched size" begin - for B in (BU, BL) - B .= 0 - copyto!(B, Diagonal(Int[1])) - @test B[1,1] == 1 - B[1,1] = 0 - @test iszero(B) - end - end - end - @testset "to Tridiagonal" begin - T = Tridiagonal(similar(dl, BigInt), similar(d, BigInt), similar(du, BigInt)) - copyto!(T, D) - @test T == D - - @testset "mismatched size" begin - T .= 0 - copyto!(T, Diagonal([1])) - @test T[1,1] == 1 - T[1,1] = 0 - @test iszero(T) - end - end - @testset "to SymTridiagonal" begin - for du2 in (similar(du, BigInt), similar(d, BigInt)) - S = SymTridiagonal(similar(d), du2) - copyto!(S, D) - @test S == D - end - - @testset "mismatched size" begin - S = SymTridiagonal(zero(d), zero(du)) - copyto!(S, Diagonal([1])) - @test S[1,1] == 1 - S[1,1] = 0 - @test iszero(S) - end - end - end - - @testset "from Bidiagonal" begin - BU = Bidiagonal(d, du, :U) - BUones = Bidiagonal(d, oneunit.(du), :U) - BL = Bidiagonal(d, dl, :L) - BLones = Bidiagonal(d, oneunit.(dl), :L) - @testset "to Diagonal" begin - D = Diagonal(zero(d)) - for B in (BL, BU) - @test copyto!(D, B) == B - D .= 0 - end - for B in (BLones, BUones) - errmsg = "cannot copy a Bidiagonal with a non-zero off-diagonal band to a Diagonal" - @test_throws errmsg copyto!(D, B) - @test iszero(D) - end - - @testset "mismatched size" begin - for uplo in (:L, :U) - D .= 0 - copyto!(D, Bidiagonal(Int[1], Int[], uplo)) - @test D[1,1] == 1 - D[1,1] = 0 - @test iszero(D) - end - end - end - @testset "to Tridiagonal" begin - T = Tridiagonal(similar(dl, BigInt), similar(d, BigInt), similar(du, BigInt)) - for B in (BL, BU, BLones, BUones) - copyto!(T, B) - @test T == B - end - - @testset "mismatched size" begin - T = Tridiagonal(oneunit.(dl), zero(d), oneunit.(du)) - for uplo in (:L, :U) - T .= 0 - copyto!(T, Bidiagonal([1], Int[], uplo)) - @test T[1,1] == 1 - T[1,1] = 0 - @test iszero(T) - end - end - end - @testset "to SymTridiagonal" begin - for du2 in (similar(du, BigInt), similar(d, BigInt)) - S = SymTridiagonal(similar(d, BigInt), du2) - for B in (BL, BU) - copyto!(S, B) - @test S == B - end - errmsg = "cannot copy a non-symmetric Bidiagonal matrix to a SymTridiagonal" - @test_throws errmsg copyto!(S, BUones) - @test_throws errmsg copyto!(S, BLones) - end - - @testset "mismatched size" begin - S = SymTridiagonal(zero(d), zero(du)) - for uplo in (:L, :U) - copyto!(S, Bidiagonal([1], Int[], uplo)) - @test S[1,1] == 1 - S[1,1] = 0 - @test iszero(S) - end - end - end - end - - @testset "from Tridiagonal" begin - T = Tridiagonal(dl, d, du) - TU = Tridiagonal(dl, d, d_ones) - TL = Tridiagonal(d_ones, d, dl) - @testset "to Diagonal" begin - D = Diagonal(zero(d)) - @test copyto!(D, T) == Diagonal(d) - errmsg = "cannot copy a Tridiagonal with a non-zero off-diagonal band to a Diagonal" - D .= 0 - @test_throws errmsg copyto!(D, TU) - @test iszero(D) - errmsg = "cannot copy a Tridiagonal with a non-zero off-diagonal band to a Diagonal" - @test_throws errmsg copyto!(D, TL) - @test iszero(D) - - @testset "mismatched size" begin - D .= 0 - copyto!(D, Tridiagonal(Int[], Int[1], Int[])) - @test D[1,1] == 1 - D[1,1] = 0 - @test iszero(D) - end - end - @testset "to Bidiagonal" begin - BU = Bidiagonal(zero(d), zero(du), :U) - BL = Bidiagonal(zero(d), zero(du), :L) - @test copyto!(BU, T) == Bidiagonal(d, du, :U) - @test copyto!(BL, T) == Bidiagonal(d, du, :L) - - BU .= 0 - BL .= 0 - errmsg = "cannot copy a Tridiagonal with a non-zero superdiagonal to a Bidiagonal with uplo=:L" - @test_throws errmsg copyto!(BL, TU) - @test iszero(BL) - @test copyto!(BU, TU) == Bidiagonal(d, d_ones, :U) - - BU .= 0 - BL .= 0 - @test copyto!(BL, TL) == Bidiagonal(d, d_ones, :L) - errmsg = "cannot copy a Tridiagonal with a non-zero subdiagonal to a Bidiagonal with uplo=:U" - @test_throws errmsg copyto!(BU, TL) - @test iszero(BU) - - @testset "mismatched size" begin - for B in (BU, BL) - B .= 0 - copyto!(B, Tridiagonal(Int[], Int[1], Int[])) - @test B[1,1] == 1 - B[1,1] = 0 - @test iszero(B) - end - end - end - end - - @testset "from SymTridiagonal" begin - S2 = SymTridiagonal(d, ones(Int,size(d))) - for S in (SymTridiagonal(d, du), SymTridiagonal(d, zero(d))) - @testset "to Diagonal" begin - D = Diagonal(zero(d)) - @test copyto!(D, S) == Diagonal(d) - D .= 0 - errmsg = "cannot copy a SymTridiagonal with a non-zero off-diagonal band to a Diagonal" - @test_throws errmsg copyto!(D, S2) - @test iszero(D) - - @testset "mismatched size" begin - D .= 0 - copyto!(D, SymTridiagonal(Int[1], Int[])) - @test D[1,1] == 1 - D[1,1] = 0 - @test iszero(D) - end - end - @testset "to Bidiagonal" begin - BU = Bidiagonal(zero(d), zero(du), :U) - BL = Bidiagonal(zero(d), zero(du), :L) - @test copyto!(BU, S) == Bidiagonal(d, du, :U) - @test copyto!(BL, S) == Bidiagonal(d, du, :L) - - BU .= 0 - BL .= 0 - errmsg = "cannot copy a SymTridiagonal with a non-zero off-diagonal band to a Bidiagonal" - @test_throws errmsg copyto!(BU, S2) - @test iszero(BU) - @test_throws errmsg copyto!(BL, S2) - @test iszero(BL) - - @testset "mismatched size" begin - for B in (BU, BL) - B .= 0 - copyto!(B, SymTridiagonal(Int[1], Int[])) - @test B[1,1] == 1 - B[1,1] = 0 - @test iszero(B) - end - end - end - end - end -end - -@testset "BandIndex indexing" begin - for D in (Diagonal(1:3), Bidiagonal(1:3, 2:3, :U), Bidiagonal(1:3, 2:3, :L), - Tridiagonal(2:3, 1:3, 1:2), SymTridiagonal(1:3, 2:3)) - M = Matrix(D) - for band in -size(D,1)+1:size(D,1)-1 - for idx in 1:size(D,1)-abs(band) - @test D[BandIndex(band, idx)] == M[BandIndex(band, idx)] - end - end - @test_throws BoundsError D[BandIndex(size(D,1),1)] - end -end - -@testset "Partly filled Hermitian and Diagonal algebra" begin - D = Diagonal([1,2]) - for S in (Symmetric, Hermitian), uplo in (:U, :L) - M = Matrix{BigInt}(undef, 2, 2) - M[1,1] = M[2,2] = M[1+(uplo == :L), 1 + (uplo == :U)] = 3 - H = S(M, uplo) - HM = Matrix(H) - @test H + D == D + H == HM + D - @test H - D == HM - D - @test D - H == D - HM - end -end - -@testset "block SymTridiagonal" begin - m = SizedArrays.SizedArray{(2,2)}(reshape([1:4;;],2,2)) - S = SymTridiagonal(fill(m,4), fill(m,3)) - SA = Array(S) - D = Diagonal(fill(m,4)) - DA = Array(D) - BU = Bidiagonal(fill(m,4), fill(m,3), :U) - BUA = Array(BU) - BL = Bidiagonal(fill(m,4), fill(m,3), :L) - BLA = Array(BL) - T = Tridiagonal(fill(m,3), fill(m,4), fill(m,3)) - TA = Array(T) - IA = Array(Diagonal(fill(one(m), 4))) - @test S + D == D + S == SA + DA - @test S - D == -(D - S) == SA - DA - @test S + BU == SA + BUA - @test S - BU == -(BU - S) == SA - BUA - @test S + BL == SA + BLA - @test S - BL == -(BL - S) == SA - BLA - @test S + T == SA + TA - @test S - T == -(T - S) == SA - TA - @test S + S == SA + SA - @test S - S == -(S - S) == SA - SA - @test S + I == I + S == SA + IA - @test S - I == -(I - S) == SA - IA - - @test S == S - @test S != D - @test S != BL - @test S != BU - @test S != T - - @test_throws ArgumentError fill!(S, m) - S_small = SymTridiagonal(fill(m,2), fill(m,1)) - @test_throws "cannot fill a SymTridiagonal with an asymmetric value" fill!(S, m) - fill!(S_small, Symmetric(m)) - @test all(==(Symmetric(m)), S_small) - - @testset "diag" begin - m = SizedArrays.SizedArray{(2,2)}([1 3; 3 4]) - D = Diagonal(fill(m,4)) - z = fill(zero(m),3) - d = fill(m,4) - BU = Bidiagonal(d, z, :U) - BL = Bidiagonal(d, z, :L) - T = Tridiagonal(z, d, z) - for ev in (fill(zero(m),3), fill(zero(m),4)) - SD = SymTridiagonal(fill(m,4), ev) - @test SD == D == SD - @test SD == BU == SD - @test SD == BL == SD - @test SD == T == SD - end - end -end - -end # module TestSpecial diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl deleted file mode 100644 index 71494aedcbef5..0000000000000 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ /dev/null @@ -1,379 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestStructuredBroadcast -using Test, LinearAlgebra - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -@testset "broadcast[!] over combinations of scalars, structured matrices, and dense vectors/matrices" begin - N = 10 - s = rand() - fV = rand(N) - fA = rand(N, N) - Z = copy(fA) - D = Diagonal(rand(N)) - B = Bidiagonal(rand(N), rand(N - 1), :U) - T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) - S = SymTridiagonal(rand(N), rand(N - 1)) - U = UpperTriangular(rand(N,N)) - L = LowerTriangular(rand(N,N)) - M = Matrix(rand(N,N)) - structuredarrays = (D, B, T, U, L, M, S) - fstructuredarrays = map(Array, structuredarrays) - for (X, fX) in zip(structuredarrays, fstructuredarrays) - @test (Q = broadcast(sin, X); typeof(Q) == typeof(X) && Q == broadcast(sin, fX)) - @test broadcast!(sin, Z, X) == broadcast(sin, fX) - @test (Q = broadcast(cos, X); Q isa Matrix && Q == broadcast(cos, fX)) - @test broadcast!(cos, Z, X) == broadcast(cos, fX) - @test (Q = broadcast(*, s, X); typeof(Q) == typeof(X) && Q == broadcast(*, s, fX)) - @test broadcast!(*, Z, s, X) == broadcast(*, s, fX) - @test (Q = broadcast(+, fV, fA, X); Q isa Matrix && Q == broadcast(+, fV, fA, fX)) - @test broadcast!(+, Z, fV, fA, X) == broadcast(+, fV, fA, fX) - @test (Q = broadcast(*, s, fV, fA, X); Q isa Matrix && Q == broadcast(*, s, fV, fA, fX)) - @test broadcast!(*, Z, s, fV, fA, X) == broadcast(*, s, fV, fA, fX) - - @test X .* 2.0 == X .* (2.0,) == fX .* 2.0 - @test X .* 2.0 isa typeof(X) - @test X .* (2.0,) isa typeof(X) - @test isequal(X .* Inf, fX .* Inf) - - two = 2 - @test X .^ 2 == X .^ (2,) == fX .^ 2 == X .^ two - @test X .^ 2 isa typeof(X) - @test X .^ (2,) isa typeof(X) - @test X .^ two isa typeof(X) - @test X .^ 0 == fX .^ 0 - @test X .^ -1 == fX .^ -1 - - for (Y, fY) in zip(structuredarrays, fstructuredarrays) - @test broadcast(+, X, Y) == broadcast(+, fX, fY) - @test broadcast!(+, Z, X, Y) == broadcast(+, fX, fY) - @test broadcast(*, X, Y) == broadcast(*, fX, fY) - @test broadcast!(*, Z, X, Y) == broadcast(*, fX, fY) - end - end - diagonals = (D, B, T) - fdiagonals = map(Array, diagonals) - for (X, fX) in zip(diagonals, fdiagonals) - for (Y, fY) in zip(diagonals, fdiagonals) - @test broadcast(+, X, Y)::Union{Diagonal,Bidiagonal,Tridiagonal} == broadcast(+, fX, fY) - @test broadcast!(+, Z, X, Y) == broadcast(+, fX, fY) - @test broadcast(*, X, Y)::Union{Diagonal,Bidiagonal,Tridiagonal} == broadcast(*, fX, fY) - @test broadcast!(*, Z, X, Y) == broadcast(*, fX, fY) - end - end - UU = UnitUpperTriangular(rand(N,N)) - UL = UnitLowerTriangular(rand(N,N)) - unittriangulars = (UU, UL) - Ttris = typeof.((UpperTriangular(parent(UU)), LowerTriangular(parent(UU)))) - funittriangulars = map(Array, unittriangulars) - for (X, fX, Ttri) in zip(unittriangulars, funittriangulars, Ttris) - @test (Q = broadcast(sin, X); typeof(Q) == Ttri && Q == broadcast(sin, fX)) - @test broadcast!(sin, Z, X) == broadcast(sin, fX) - @test (Q = broadcast(cos, X); Q isa Matrix && Q == broadcast(cos, fX)) - @test broadcast!(cos, Z, X) == broadcast(cos, fX) - @test (Q = broadcast(*, s, X); typeof(Q) == Ttri && Q == broadcast(*, s, fX)) - @test broadcast!(*, Z, s, X) == broadcast(*, s, fX) - @test (Q = broadcast(+, fV, fA, X); Q isa Matrix && Q == broadcast(+, fV, fA, fX)) - @test broadcast!(+, Z, fV, fA, X) == broadcast(+, fV, fA, fX) - @test (Q = broadcast(*, s, fV, fA, X); Q isa Matrix && Q == broadcast(*, s, fV, fA, fX)) - @test broadcast!(*, Z, s, fV, fA, X) == broadcast(*, s, fV, fA, fX) - - @test X .* 2.0 == X .* (2.0,) == fX .* 2.0 - @test X .* 2.0 isa Ttri - @test X .* (2.0,) isa Ttri - @test isequal(X .* Inf, fX .* Inf) - - two = 2 - @test X .^ 2 == X .^ (2,) == fX .^ 2 == X .^ two - @test X .^ 2 isa typeof(X) # special cased, as isstructurepreserving - @test X .^ (2,) isa Ttri - @test X .^ two isa Ttri - @test X .^ 0 == fX .^ 0 - @test X .^ -1 == fX .^ -1 - - for (Y, fY) in zip(unittriangulars, funittriangulars) - @test broadcast(+, X, Y) == broadcast(+, fX, fY) - @test broadcast!(+, Z, X, Y) == broadcast(+, fX, fY) - @test broadcast(*, X, Y) == broadcast(*, fX, fY) - @test broadcast!(*, Z, X, Y) == broadcast(*, fX, fY) - end - end - - @testset "type-stability in Bidiagonal" begin - B2 = @inferred (B -> .- B)(B) - @test B2 isa Bidiagonal - @test B2 == -1 * B - B2 = @inferred (B -> B .* 2)(B) - @test B2 isa Bidiagonal - @test B2 == B + B - B2 = @inferred (B -> 2 .* B)(B) - @test B2 isa Bidiagonal - @test B2 == B + B - B2 = @inferred (B -> B ./ 1)(B) - @test B2 isa Bidiagonal - @test B2 == B - B2 = @inferred (B -> 1 .\ B)(B) - @test B2 isa Bidiagonal - @test B2 == B - end -end - -@testset "broadcast! where the destination is a structured matrix" begin - N = 5 - A = rand(N, N) - sA = A + copy(A') - D = Diagonal(rand(N)) - Bu = Bidiagonal(rand(N), rand(N - 1), :U) - Bl = Bidiagonal(rand(N), rand(N - 1), :L) - T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) - ◣ = LowerTriangular(rand(N,N)) - ◥ = UpperTriangular(rand(N,N)) - M = Matrix(rand(N,N)) - - @test broadcast!(sin, copy(D), D) == Diagonal(sin.(D)) - @test broadcast!(sin, copy(Bu), Bu) == Bidiagonal(sin.(Bu), :U) - @test broadcast!(sin, copy(Bl), Bl) == Bidiagonal(sin.(Bl), :L) - @test broadcast!(sin, copy(T), T) == Tridiagonal(sin.(T)) - @test broadcast!(sin, copy(◣), ◣) == LowerTriangular(sin.(◣)) - @test broadcast!(sin, copy(◥), ◥) == UpperTriangular(sin.(◥)) - @test broadcast!(sin, copy(M), M) == Matrix(sin.(M)) - @test broadcast!(*, copy(D), D, A) == Diagonal(broadcast(*, D, A)) - @test broadcast!(*, copy(Bu), Bu, A) == Bidiagonal(broadcast(*, Bu, A), :U) - @test broadcast!(*, copy(Bl), Bl, A) == Bidiagonal(broadcast(*, Bl, A), :L) - @test broadcast!(*, copy(T), T, A) == Tridiagonal(broadcast(*, T, A)) - @test broadcast!(*, copy(◣), ◣, A) == LowerTriangular(broadcast(*, ◣, A)) - @test broadcast!(*, copy(◥), ◥, A) == UpperTriangular(broadcast(*, ◥, A)) - @test broadcast!(*, copy(M), M, A) == Matrix(broadcast(*, M, A)) - - @test_throws ArgumentError broadcast!(cos, copy(D), D) == Diagonal(sin.(D)) - @test_throws ArgumentError broadcast!(cos, copy(Bu), Bu) == Bidiagonal(sin.(Bu), :U) - @test_throws ArgumentError broadcast!(cos, copy(Bl), Bl) == Bidiagonal(sin.(Bl), :L) - @test_throws ArgumentError broadcast!(cos, copy(T), T) == Tridiagonal(sin.(T)) - @test_throws ArgumentError broadcast!(cos, copy(◣), ◣) == LowerTriangular(sin.(◣)) - @test_throws ArgumentError broadcast!(cos, copy(◥), ◥) == UpperTriangular(sin.(◥)) - @test_throws ArgumentError broadcast!(+, copy(D), D, A) == Diagonal(broadcast(*, D, A)) - @test_throws ArgumentError broadcast!(+, copy(Bu), Bu, A) == Bidiagonal(broadcast(*, Bu, A), :U) - @test_throws ArgumentError broadcast!(+, copy(Bl), Bl, A) == Bidiagonal(broadcast(*, Bl, A), :L) - @test_throws ArgumentError broadcast!(+, copy(T), T, A) == Tridiagonal(broadcast(*, T, A)) - @test_throws ArgumentError broadcast!(+, copy(◣), ◣, A) == LowerTriangular(broadcast(*, ◣, A)) - @test_throws ArgumentError broadcast!(+, copy(◥), ◥, A) == UpperTriangular(broadcast(*, ◥, A)) - @test_throws ArgumentError broadcast!(*, copy(◥), ◣, 2) - @test_throws ArgumentError broadcast!(*, copy(Bu), Bl, 2) -end - -@testset "map[!] over combinations of structured matrices" begin - N = 10 - fA = rand(N, N) - Z = copy(fA) - D = Diagonal(rand(N)) - B = Bidiagonal(rand(N), rand(N - 1), :U) - T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) - S = SymTridiagonal(rand(N), rand(N - 1)) - U = UpperTriangular(rand(N,N)) - L = LowerTriangular(rand(N,N)) - M = Matrix(rand(N,N)) - structuredarrays = (M, D, B, T, S, U, L) - fstructuredarrays = map(Array, structuredarrays) - for (X, fX) in zip(structuredarrays, fstructuredarrays) - @test (Q = map(sin, X); typeof(Q) == typeof(X) && Q == map(sin, fX)) - @test map!(sin, Z, X) == map(sin, fX) - @test (Q = map(cos, X); Q isa Matrix && Q == map(cos, fX)) - @test map!(cos, Z, X) == map(cos, fX) - @test (Q = map(+, fA, X); Q isa Matrix && Q == map(+, fA, fX)) - @test map!(+, Z, fA, X) == map(+, fA, fX) - for (Y, fY) in zip(structuredarrays, fstructuredarrays) - @test map(+, X, Y) == map(+, fX, fY) - @test map!(+, Z, X, Y) == map(+, fX, fY) - @test map(*, X, Y) == map(*, fX, fY) - @test map!(*, Z, X, Y) == map(*, fX, fY) - @test map(+, X, fA, Y) == map(+, fX, fA, fY) - @test map!(+, Z, X, fA, Y) == map(+, fX, fA, fY) - end - end - diagonals = (D, B, T) - fdiagonals = map(Array, diagonals) - for (X, fX) in zip(diagonals, fdiagonals) - for (Y, fY) in zip(diagonals, fdiagonals) - @test map(+, X, Y)::Union{Diagonal,Bidiagonal,Tridiagonal} == broadcast(+, fX, fY) - @test map!(+, Z, X, Y) == broadcast(+, fX, fY) - @test map(*, X, Y)::Union{Diagonal,Bidiagonal,Tridiagonal} == broadcast(*, fX, fY) - @test map!(*, Z, X, Y) == broadcast(*, fX, fY) - end - end - # these would be valid for broadcast, but not for map - @test_throws DimensionMismatch map(+, D, Diagonal(rand(1))) - @test_throws DimensionMismatch map(+, D, Diagonal(rand(1)), D) - @test_throws DimensionMismatch map(+, D, D, Diagonal(rand(1))) - @test_throws DimensionMismatch map(+, Diagonal(rand(1)), D, D) -end - -@testset "Issue #33397" begin - N = 5 - U = UpperTriangular(rand(N, N)) - L = LowerTriangular(rand(N, N)) - UnitU = UnitUpperTriangular(rand(N, N)) - UnitL = UnitLowerTriangular(rand(N, N)) - D = Diagonal(rand(N)) - @test U .+ L .+ D == U + L + D - @test L .+ U .+ D == L + U + D - @test UnitU .+ UnitL .+ D == UnitU + UnitL + D - @test UnitL .+ UnitU .+ D == UnitL + UnitU + D - @test U .+ UnitL .+ D == U + UnitL + D - @test L .+ UnitU .+ D == L + UnitU + D - @test L .+ U .+ L .+ U == L + U + L + U - @test U .+ L .+ U .+ L == U + L + U + L - @test L .+ UnitL .+ UnitU .+ U .+ D == L + UnitL + UnitU + U + D - @test L .+ U .+ D .+ D .+ D .+ D == L + U + D + D + D + D -end -@testset "Broadcast Returned Types" begin - # Issue 35245 - N = 3 - dV = rand(N) - evu = rand(N-1) - evl = rand(N-1) - - Bu = Bidiagonal(dV, evu, :U) - Bl = Bidiagonal(dV, evl, :L) - T = Tridiagonal(evl, dV * 2, evu) - - @test typeof(Bu .+ Bl) <: Tridiagonal - @test typeof(Bl .+ Bu) <: Tridiagonal - @test typeof(Bu .+ Bu) <: Bidiagonal - @test typeof(Bl .+ Bl) <: Bidiagonal - @test Bu .+ Bl == T - @test Bl .+ Bu == T - @test Bu .+ Bu == Bidiagonal(dV * 2, evu * 2, :U) - @test Bl .+ Bl == Bidiagonal(dV * 2, evl * 2, :L) - - - @test typeof(Bu .* Bl) <: Tridiagonal - @test typeof(Bl .* Bu) <: Tridiagonal - @test typeof(Bu .* Bu) <: Bidiagonal - @test typeof(Bl .* Bl) <: Bidiagonal - - @test Bu .* Bl == Tridiagonal(zeros(N-1), dV .* dV, zeros(N-1)) - @test Bl .* Bu == Tridiagonal(zeros(N-1), dV .* dV, zeros(N-1)) - @test Bu .* Bu == Bidiagonal(dV .* dV, evu .* evu, :U) - @test Bl .* Bl == Bidiagonal(dV .* dV, evl .* evl, :L) - - Bu2 = Bu .* 2 - @test typeof(Bu2) <: Bidiagonal && Bu2.uplo == 'U' - Bu2 = 2 .* Bu - @test typeof(Bu2) <: Bidiagonal && Bu2.uplo == 'U' - Bl2 = Bl .* 2 - @test typeof(Bl2) <: Bidiagonal && Bl2.uplo == 'L' - Bu2 = 2 .* Bl - @test typeof(Bl2) <: Bidiagonal && Bl2.uplo == 'L' - - # Example of Nested Broadcasts - tmp = (1 .* 2) .* (Bidiagonal(1:3, 1:2, 'U') .* (3 .* 4)) .* (5 .* Bidiagonal(1:3, 1:2, 'L')) - @test typeof(tmp) <: Tridiagonal - -end - -struct Zero36193 end -Base.iszero(::Zero36193) = true -LinearAlgebra.iszerodefined(::Type{Zero36193}) = true -@testset "PR #36193" begin - f(::Union{Int, Zero36193}) = Zero36193() - function test(el) - M = [el el - el el] - v = [el, el] - U = UpperTriangular(M) - L = LowerTriangular(M) - D = Diagonal(v) - for (T, A) in [(UpperTriangular, U), (LowerTriangular, L), (Diagonal, D)] - @test identity.(A) isa typeof(A) - @test map(identity, A) isa typeof(A) - @test f.(A) isa T{Zero36193} - @test map(f, A) isa T{Zero36193} - end - end - # This should not need `zero(::Type{Zero36193})` to be defined - test(1) - Base.zero(::Type{Zero36193}) = Zero36193() - # This should not need `==(::Zero36193, ::Int)` to be defined as `iszerodefined` - # returns true. - test(Zero36193()) -end - -# structured broadcast with function returning non-number type -@test tuple.(Diagonal([1, 2])) == [(1,) (0,); (0,) (2,)] - -@testset "Broadcast with missing (#54467)" begin - select_first(x, y) = x - diag = Diagonal([1,2]) - @test select_first.(diag, missing) == diag - @test select_first.(diag, missing) isa Diagonal{Int} - @test isequal(select_first.(missing, diag), fill(missing, 2, 2)) - @test select_first.(missing, diag) isa Matrix{Missing} -end - -@testset "broadcast over structured matrices with matrix elements" begin - function standardbroadcastingtests(D, T) - M = [x for x in D] - Dsum = D .+ D - @test Dsum isa T - @test Dsum == M .+ M - Dcopy = copy.(D) - @test Dcopy isa T - @test Dcopy == D - Df = float.(D) - @test Df isa T - @test Df == D - @test eltype(eltype(Df)) <: AbstractFloat - @test (x -> (x,)).(D) == (x -> (x,)).(M) - @test (x -> 1).(D) == ones(Int,size(D)) - @test all(==(2), ndims.(D)) - @test_throws MethodError size.(D) - end - @testset "Diagonal" begin - @testset "square" begin - A = [1 3; 2 4] - D = Diagonal([A, A]) - standardbroadcastingtests(D, Diagonal) - @test sincos.(D) == sincos.(Matrix{eltype(D)}(D)) - M = [x for x in D] - @test cos.(D) == cos.(M) - end - - @testset "different-sized square blocks" begin - D = Diagonal([ones(3,3), fill(3.0,2,2)]) - standardbroadcastingtests(D, Diagonal) - end - - @testset "rectangular blocks" begin - D = Diagonal([ones(Bool,3,4), ones(Bool,2,3)]) - standardbroadcastingtests(D, Diagonal) - end - - @testset "incompatible sizes" begin - A = reshape(1:12, 4, 3) - B = reshape(1:12, 3, 4) - D1 = Diagonal(fill(A, 2)) - D2 = Diagonal(fill(B, 2)) - @test_throws DimensionMismatch D1 .+ D2 - end - end - @testset "Bidiagonal" begin - A = [1 3; 2 4] - B = Bidiagonal(fill(A,3), fill(A,2), :U) - standardbroadcastingtests(B, Bidiagonal) - end - @testset "UpperTriangular" begin - A = [1 3; 2 4] - U = UpperTriangular([(i+j)*A for i in 1:3, j in 1:3]) - standardbroadcastingtests(U, UpperTriangular) - end - @testset "SymTridiagonal" begin - m = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - S = SymTridiagonal(fill(m,4), fill(m,3)) - standardbroadcastingtests(S, SymTridiagonal) - end -end - -end diff --git a/stdlib/LinearAlgebra/test/svd.jl b/stdlib/LinearAlgebra/test/svd.jl deleted file mode 100644 index 9e8b5d5cda7d2..0000000000000 --- a/stdlib/LinearAlgebra/test/svd.jl +++ /dev/null @@ -1,297 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestSVD - -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasComplex, BlasFloat, BlasReal, QRPivoted - -@testset "Simple svdvals / svd tests" begin - ≊(x,y) = isapprox(x,y,rtol=1e-15) - - m = [2, 0] - @test @inferred(svdvals(m)) ≊ [2] - @test @inferred(svdvals!(float(m))) ≊ [2] - for sf in (@inferred(svd(m)), @inferred(svd!(float(m)))) - @test sf.S ≊ [2] - @test sf.U'sf.U ≊ [1] - @test sf.Vt'sf.Vt ≊ [1] - @test sf.U*Diagonal(sf.S)*sf.Vt' ≊ m - end - F = @inferred svd(m, full=true) - @test size(F.U) == (2, 2) - @test F.S ≊ [2] - @test F.U'F.U ≊ Matrix(I, 2, 2) - @test F.Vt'*F.Vt ≊ [1] - @test @inferred(svdvals(3:4)) ≊ [5] - A = Matrix(1.0I, 2, 2) - Z = svd(Hermitian(A); full=true) - @test Z.S ≈ ones(2) - @test Z.U'Z.U ≈ I(2) - - m1 = [2 0; 0 0] - m2 = [2 -2; 1 1]/sqrt(2) - m2c = Complex.([2 -2; 1 1]/sqrt(2)) - @test @inferred(svdvals(m1)) ≊ [2, 0] - @test @inferred(svdvals(m2)) ≊ [2, 1] - @test @inferred(svdvals(m2c)) ≊ [2, 1] - - sf1 = @inferred svd(m1) - sf2 = @inferred svd(m2) - @test sf1.S ≊ [2, 0] - @test sf2.S ≊ [2, 1] - # U & Vt are unitary - I22 = Matrix(I, 2, 2) - @test sf1.U*sf1.U' ≊ I22 - @test sf1.Vt*sf1.Vt' ≊ I22 - @test sf2.U*sf2.U' ≊ I22 - @test sf2.Vt*sf2.Vt' ≊ I22 - # SVD not uniquely determined, so just test we can reconstruct the - # matrices from the factorization as expected. - @test sf1.U*Diagonal(sf1.S)*sf1.Vt' ≊ m1 - @test sf2.U*Diagonal(sf2.S)*sf2.Vt' ≊ m2 - - @test ldiv!([0., 0.], svd(Matrix(I, 2, 2)), [1., 1.]) ≊ [1., 1.] - @test inv(svd(Matrix(I, 2, 2))) ≈ I - @test inv(svd([1 2; 3 4])) ≈ [-2.0 1.0; 1.5 -0.5] - @test inv(svd([1 0 1; 0 1 0])) ≈ [0.5 0.0; 0.0 1.0; 0.5 0.0] - @test_throws SingularException inv(svd([0 0; 0 0])) - @test inv(svd([1+2im 3+4im; 5+6im 7+8im])) ≈ [-0.5 + 0.4375im 0.25 - 0.1875im; 0.375 - 0.3125im -0.125 + 0.0625im] -end - -n = 10 - -Random.seed!(1234321) - -areal = randn(n,n)/2 -aimg = randn(n,n)/2 - -@testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, Int) - aa = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - asym = aa' + aa # symmetric indefinite - for a in (aa, view(aa, 1:n, 1:n)) - usv = svd(a) - @testset "singular value decomposition" begin - @test usv.S === svdvals(usv) - @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ a - @test convert(Array, usv) ≈ a - @test usv.Vt' ≈ usv.V - @test_throws FieldError usv.Z - b = rand(eltya,n) - @test usv\b ≈ a\b - @test Base.propertynames(usv) == (:U, :S, :V, :Vt) - @test size(usv) == size(a) - if eltya <: BlasFloat - svdz = svd!(Matrix{eltya}(undef,0,0)) - @test svdz.U ≈ Matrix{eltya}(I, 0, 0) - @test svdz.S ≈ real(zeros(eltya,0)) - @test svdz.Vt ≈ Matrix{eltya}(I, 0, 0) - end - end - @testset "singular value decomposition of adjoint/transpose" begin - for transform in (adjoint, transpose) - usv = svd(transform(a)) - @test usv.S === svdvals(usv) - @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ transform(a) - @test convert(Array, usv) ≈ transform(a) - @test usv.Vt' ≈ usv.V - @test_throws FieldError usv.Z - b = rand(eltya,n) - @test usv\b ≈ transform(a)\b - end - end - @testset "Generalized svd" begin - a_svd = a[1:div(n, 2), :] - gsvd = svd(a,a_svd) - @test Base.propertynames(gsvd) == (:alpha, :beta, :vals, :S, :D1, :D2, :R0, :U, :V, :Q, :a, :b, :k, :l, :R) - @test gsvd.U*gsvd.D1*gsvd.R*gsvd.Q' ≈ a - @test gsvd.V*gsvd.D2*gsvd.R*gsvd.Q' ≈ a_svd - @test usv.Vt' ≈ usv.V - @test_throws FieldError usv.Z - @test_throws FieldError gsvd.Z - @test gsvd.vals ≈ svdvals(a,a_svd) - α = eltya == Int ? -1 : rand(eltya) - β = svd(α) - @test β.S == [abs(α)] - @test svdvals(α) == abs(α) - u,v,q,d1,d2,r0 = svd(a,a_svd) - @test u ≈ gsvd.U - @test v ≈ gsvd.V - @test d1 ≈ gsvd.D1 - @test d2 ≈ gsvd.D2 - @test q ≈ gsvd.Q - @test gsvd.a.^2 + gsvd.b.^2 ≈ fill(1, length(gsvd.a)) - @test gsvd.alpha.^2 + gsvd.beta.^2 ≈ ones(eltya, length(gsvd.a)) - #testing the other layout for D1 & D2 - b = rand(eltya,n,2*n) - c = rand(eltya,n,2*n) - gsvd = svd(b,c) - @test gsvd.U*gsvd.D1*gsvd.R*gsvd.Q' ≈ b - @test gsvd.V*gsvd.D2*gsvd.R*gsvd.Q' ≈ c - # AbstractMatrix svd - T = Tridiagonal(a) - asvd = svd(T, a) - @test asvd.U*asvd.D1*asvd.R*asvd.Q' ≈ T - @test asvd.V*asvd.D2*asvd.R*asvd.Q' ≈ a - @test all(≈(1), svdvals(T, T)) - end - end - @testset "singular value decomposition of AbstractMatrix" begin - A = Tridiagonal(aa) - F = svd(A) - @test Matrix(F) ≈ A - @test svdvals(A) ≈ F.S - end - @testset "singular value decomposition of Hermitian/real-Symmetric" begin - for T in (eltya <: Real ? (Symmetric, Hermitian) : (Hermitian,)) - usv = svd(T(asym)) - @test usv.S === svdvals(usv) - @test usv.U * (Diagonal(usv.S) * usv.Vt) ≈ T(asym) - @test convert(Array, usv) ≈ T(asym) - @test usv.Vt' ≈ usv.V - @test_throws FieldError usv.Z - b = rand(eltya,n) - @test usv\b ≈ T(asym)\b - end - end - if eltya <: LinearAlgebra.BlasReal - @testset "Number input" begin - x, y = randn(eltya, 2) - @test svd(x) == svd(fill(x, 1, 1)) - @test svdvals(x) == first(svdvals(fill(x, 1, 1))) - @test svd(x, y) == svd(fill(x, 1, 1), fill(y, 1, 1)) - @test svdvals(x, y) ≈ first(svdvals(fill(x, 1, 1), fill(y, 1, 1))) - end - end - if eltya != Int - @testset "isequal, ==, and hash" begin - x, y = rand(eltya), convert(eltya, NaN) - Fx, Fy = svd(x), svd(y) - @test Fx == Fx - @test !(Fy == Fy) - @test isequal(Fy, Fy) - @test hash(Fx) == hash(Fx) - @test hash(Fx, UInt(1)) == hash(Fx, UInt(1)) - @test hash(Fy) == hash(Fy) - @test hash(Fy, UInt(1)) == hash(Fy, UInt(1)) - end - end -end - - - -@testset "SVD Algorithms" begin - ≊(x,y) = isapprox(x,y,rtol=1e-15) - - x = [0.1 0.2; 0.3 0.4] - - for alg in [LinearAlgebra.QRIteration(), LinearAlgebra.DivideAndConquer()] - sx1 = svd(x, alg = alg) - @test sx1.U * Diagonal(sx1.S) * sx1.Vt ≊ x - @test sx1.V * sx1.Vt ≊ I - @test sx1.U * sx1.U' ≊ I - @test all(sx1.S .≥ 0) - - sx2 = svd!(copy(x), alg = alg) - @test sx2.U * Diagonal(sx2.S) * sx2.Vt ≊ x - @test sx2.V * sx2.Vt ≊ I - @test sx2.U * sx2.U' ≊ I - @test all(sx2.S .≥ 0) - end -end - -@testset "REPL printing of SVD" begin - svdd = svd(randn(3, 3)) - svdstring = sprint((t, s) -> show(t, "text/plain", s), svdd) - ustring = sprint((t, s) -> show(t, "text/plain", s), svdd.U) - sstring = sprint((t, s) -> show(t, "text/plain", s), svdd.S) - vtstring = sprint((t, s) -> show(t, "text/plain", s), svdd.Vt) - @test svdstring == "$(summary(svdd))\nU factor:\n$ustring\nsingular values:\n$sstring\nVt factor:\n$vtstring" -end - -@testset "REPL printing of Generalized SVD" begin - a = randn(3, 3) - b = randn(3, 3) - svdd = svd(a, b) - svdstring = sprint((t, s) -> show(t, "text/plain", s), svdd) - ustring = sprint((t, s) -> show(t, "text/plain", s), svdd.U) - qstring = sprint((t, s) -> show(t, "text/plain", s), svdd.Q) - vstring = sprint((t, s) -> show(t, "text/plain", s), svdd.V) - d1string = sprint((t, s) -> show(t, "text/plain", s), svdd.D1) - d2string = sprint((t, s) -> show(t, "text/plain", s), svdd.D2) - r0string = sprint((t, s) -> show(t, "text/plain", s), svdd.R0) - @test svdstring == "$(summary(svdd))\nU factor:\n$ustring\nV factor:\n$vstring\nQ factor:\n$qstring\nD1 factor:\n$d1string\nD2 factor:\n$d2string\nR0 factor:\n$r0string" -end - -@testset "c-tor with varying input eltypes" begin - A = randn(Float64, 10, 10) - U, S, V = svd(A) - Ut = convert.(Float16, U) - Vt = convert.(Float32, V) - svdc = SVD{ComplexF32}(Ut, S, Vt) - @test svdc isa SVD{ComplexF32} - Uc, Sc, Vc = svdc - @test Uc * diagm(0=>Sc) * transpose(V) ≈ complex.(A) rtol=1e-3 -end - -@testset "Issue 40944. ldiV!(SVD) should update rhs" begin - F = svd(randn(2, 2)) - b = randn(2) - x = ldiv!(F, b) - @test x === b -end - -@testset "adjoint of SVD" begin - n = 5 - B = randn(5, 2) - - @testset "size(b)=$(size(b))" for b in (B[:, 1], B) - @testset "size(A)=$(size(A))" for A in ( - randn(n, n), - # Wide problems become minimum norm (in x) problems similarly to LQ - randn(n + 2, n), - randn(n - 2, n), - complex.(randn(n, n), randn(n, n))) - - F = svd(A) - x = F'\b - @test x ≈ A'\b - @test length(size(x)) == length(size(b)) - end - end -end - -@testset "Float16" begin - A = Float16[4. 12. -16.; 12. 37. -43.; -16. -43. 98.] - B = svd(A) - B32 = svd(Float32.(A)) - @test B isa SVD{Float16, Float16, Matrix{Float16}} - @test B.U isa Matrix{Float16} - @test B.Vt isa Matrix{Float16} - @test B.S isa Vector{Float16} - @test B.U ≈ B32.U - @test B.Vt ≈ B32.Vt - @test B.S ≈ B32.S - C = Symmetric(A'A) - D = svd(C) - D32 = svd(Symmetric(Float32.(C))) - @test D isa SVD{Float16, Float16, Matrix{Float16}} - @test D.U isa Matrix{Float16} - @test D.Vt isa Matrix{Float16} - @test D.S isa Vector{Float16} - @test D.U ≈ D32.U - @test D.Vt ≈ D32.Vt - @test D.S ≈ D32.S - A = randn(ComplexF16, 3, 3) - E = Hermitian(A'A) - F = svd(E) - F32 = svd(Hermitian(ComplexF32.(E))) - @test F isa SVD{ComplexF16, Float16, Matrix{ComplexF16}, Vector{Float16}} - @test F.U isa Matrix{ComplexF16} - @test F.Vt isa Matrix{ComplexF16} - @test F.S isa Vector{Float16} - @test F.U ≈ F32.U - @test F.Vt ≈ F32.Vt - @test F.S ≈ F32.S -end - -end # module TestSVD diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl deleted file mode 100644 index edd3af483b5f6..0000000000000 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ /dev/null @@ -1,1181 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestSymmetric - -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")) -using .Main.Quaternions - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -Random.seed!(1010) - -@testset "Pauli σ-matrices: $σ" for σ in map(Hermitian, - Any[ [1 0; 0 1], [0 1; 1 0], [0 -im; im 0], [1 0; 0 -1] ]) - @test ishermitian(σ) -end - -@testset "Two-dimensional Euler formula for Hermitian" begin - @test cis(Hermitian([π 0; 0 π])) ≈ -I -end - -@testset "Hermitian matrix exponential/log" begin - A1 = randn(4,4) + im*randn(4,4) - A2 = A1 + A1' - @test exp(A2) ≈ exp(Hermitian(A2)) - @test cis(A2) ≈ cis(Hermitian(A2)) - @test log(A2) ≈ log(Hermitian(A2)) - A3 = A1 * A1' # posdef - @test exp(A3) ≈ exp(Hermitian(A3)) - @test cis(A3) ≈ cis(Hermitian(A3)) - @test log(A3) ≈ log(Hermitian(A3)) - - A1 = randn(4,4) - A3 = A1 * A1' - A4 = A1 + transpose(A1) - @test exp(A4) ≈ exp(Symmetric(A4)) - @test log(A3) ≈ log(Symmetric(A3)) - @test log(A3) ≈ log(Hermitian(A3)) -end - -@testset "Core functionality" begin - n = 10 - areal = randn(n,n)/2 - aimg = randn(n,n)/2 - @testset for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) - asym = transpose(a) + a # symmetric indefinite - aherm = a' + a # Hermitian indefinite - apos = a' * a # Hermitian positive definite - aposs = apos + transpose(apos) # Symmetric positive definite - ε = εa = eps(abs(float(one(eltya)))) - - x = randn(n) - y = randn(n) - b = randn(n,n)/2 - x = eltya == Int ? rand(1:7, n) : convert(Vector{eltya}, eltya <: Complex ? complex.(x, zeros(n)) : x) - y = eltya == Int ? rand(1:7, n) : convert(Vector{eltya}, eltya <: Complex ? complex.(y, zeros(n)) : y) - b = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(b, zeros(n,n)) : b) - @testset "basic ops" begin - @testset "constructor" begin - @test Symmetric(Symmetric(asym, :U)) === Symmetric(asym, :U) - @test Hermitian(Hermitian(aherm, :U)) === Hermitian(aherm, :U) - @test Symmetric(Symmetric(asym, :U), :U) === Symmetric(asym, :U) - @test Hermitian(Hermitian(aherm, :U), :U) === Hermitian(aherm, :U) - @test_throws ArgumentError Symmetric(Symmetric(asym, :U), :L) - @test_throws ArgumentError Hermitian(Hermitian(aherm, :U), :L) - - @test_throws ArgumentError Symmetric(asym, :R) - @test_throws ArgumentError Hermitian(asym, :R) - - @test_throws MethodError Symmetric{eltya,typeof(asym)}(asym, :L) - @test_throws MethodError Hermitian{eltya,typeof(aherm)}(aherm, :L) - - # mixed cases with Hermitian/Symmetric - if eltya <: Real - @test Symmetric(Hermitian(aherm, :U)) === Symmetric(aherm, :U) - @test Hermitian(Symmetric(asym, :U)) === Hermitian(asym, :U) - @test Symmetric(Hermitian(aherm, :U), :U) === Symmetric(aherm, :U) - @test Hermitian(Symmetric(asym, :U), :U) === Hermitian(asym, :U) - @test_throws ArgumentError Symmetric(Hermitian(aherm, :U), :L) - @test_throws ArgumentError Hermitian(Symmetric(aherm, :U), :L) - end - end - @testset "diag" begin - D = Diagonal(x) - DM = Matrix(D) - B = diagm(-1 => x, 1 => x) - for uplo in (:U, :L) - @test diag(Symmetric(D, uplo))::Vector == x - @test diag(Hermitian(D, uplo))::Vector == real(x) - @test isdiag(Symmetric(DM, uplo)) - @test isdiag(Hermitian(DM, uplo)) - @test !isdiag(Symmetric(B, uplo)) - @test !isdiag(Hermitian(B, uplo)) - end - end - @testset "similar" begin - @test isa(similar(Symmetric(asym)), Symmetric{eltya}) - @test isa(similar(Hermitian(aherm)), Hermitian{eltya}) - @test isa(similar(Symmetric(asym), Int), Symmetric{Int}) - @test isa(similar(Hermitian(aherm), Int), Hermitian{Int}) - @test isa(similar(Symmetric(asym), (3,2)), Matrix{eltya}) - @test isa(similar(Hermitian(aherm), (3,2)), Matrix{eltya}) - @test isa(similar(Symmetric(asym), Int, (3,2)), Matrix{Int}) - @test isa(similar(Hermitian(aherm), Int, (3,2)), Matrix{Int}) - end - - @testset "Array/Matrix constructor from Symmetric/Hermitian" begin - @test asym == Matrix(Symmetric(asym)) == Array(Symmetric(asym)) - @test aherm == Matrix(Hermitian(aherm)) == Array(Hermitian(aherm)) - end - - @testset "parent" begin - @test asym === parent(Symmetric(asym)) - @test aherm === parent(Hermitian(aherm)) - end - # Unary minus for Symmetric/Hermitian matrices - @testset "Unary minus for Symmetric/Hermitian matrices" begin - @test (-Symmetric(asym))::typeof(Symmetric(asym)) == -asym - @test (-Hermitian(aherm))::typeof(Hermitian(aherm)) == -aherm - @test (-Symmetric([true true; false false]))::Symmetric{Int,Matrix{Int}} == [-1 -1; -1 0] - @test (-Hermitian([true false; true false]))::Hermitian{Int,Matrix{Int}} == [-1 0; 0 0] - end - - @testset "Addition and subtraction for Symmetric/Hermitian matrices" begin - for f in (+, -) - @test (f(Symmetric(asym), Symmetric(aposs)))::typeof(Symmetric(asym)) == f(asym, aposs) - @test (f(Hermitian(aherm), Hermitian(apos)))::typeof(Hermitian(aherm)) == f(aherm, apos) - @test (f(Symmetric(real(asym)), Hermitian(aherm)))::typeof(Hermitian(aherm)) == f(real(asym), aherm) - @test (f(Hermitian(aherm), Symmetric(real(asym))))::typeof(Hermitian(aherm)) == f(aherm, real(asym)) - @test (f(Symmetric(asym), Hermitian(aherm))) == f(asym, aherm) - @test (f(Hermitian(aherm), Symmetric(asym))) == f(aherm, asym) - end - end - - @testset "getindex and unsafe_getindex" begin - @test aherm[1,1] == Hermitian(aherm)[1,1] - @test asym[1,1] == Symmetric(asym)[1,1] - @test Symmetric(asym)[1:2,1:2] == asym[1:2,1:2] - @test Hermitian(aherm)[1:2,1:2] == aherm[1:2,1:2] - end - - @testset "conversion" begin - @test Symmetric(asym) == convert(Symmetric,Symmetric(asym)) - if eltya <: Real - typs = [Float16,Float32,Float64] - for typ in typs - @test Symmetric(convert(Matrix{typ},asym)) == convert(Symmetric{typ,Matrix{typ}},Symmetric(asym)) - end - end - if eltya <: Complex - typs = [ComplexF32,ComplexF64] - for typ in typs - @test Symmetric(convert(Matrix{typ},asym)) == convert(Symmetric{typ,Matrix{typ}},Symmetric(asym)) - @test Hermitian(convert(Matrix{typ},aherm)) == convert(Hermitian{typ,Matrix{typ}},Hermitian(aherm)) - end - end - @test Symmetric{eltya, Matrix{eltya}}(Symmetric(asym, :U)) === Symmetric(asym, :U) - @test Hermitian{eltya, Matrix{eltya}}(Hermitian(aherm, :U)) === Hermitian(aherm, :U) - end - - @testset "issymmetric, ishermitian" begin - @test issymmetric(Symmetric(asym)) - @test ishermitian(Hermitian(aherm)) - if eltya <: Real - @test ishermitian(Symmetric(asym)) - @test issymmetric(Hermitian(asym)) - elseif eltya <: Complex - # test that zero imaginary component is - # handled properly - @test ishermitian(Symmetric(b + b')) - end - end - - @testset "tril/triu" begin - for (op, validks) in ( - (triu, (-n + 1):(n + 1)), - (tril, (-n - 1):(n - 1)) ) - for di in validks - @test op(Symmetric(asym), di) == op(asym, di) - @test op(Hermitian(aherm), di) == op(aherm, di) - @test op(Symmetric(asym, :L), di) == op(asym, di) - @test op(Hermitian(aherm, :L), di) == op(aherm, di) - end - end - end - - @testset "transpose, adjoint" begin - S = Symmetric(asym) - H = Hermitian(aherm) - @test transpose(S) === S == asym - @test adjoint(H) === H == aherm - if eltya <: Real - @test adjoint(S) === S == asym - @test transpose(H) === H == aherm - else - @test adjoint(S) == Symmetric(conj(asym)) - @test transpose(H) == Hermitian(copy(transpose(aherm))) - end - @test copy(adjoint(H)) == copy(aherm) - @test copy(transpose(S)) == copy(asym) - end - - @testset "real, imag" begin - S = Symmetric(asym) - H = Hermitian(aherm) - @test issymmetric(real(S)) - @test ishermitian(real(H)) - if eltya <: Real - @test real(S) === S == asym - @test real(H) === H == aherm - elseif eltya <: Complex - @test issymmetric(imag(S)) - @test !ishermitian(imag(H)) - end - end - - end - - @testset "linalg unary ops" begin - @testset "tr" begin - @test tr(asym) ≈ tr(Symmetric(asym)) - @test tr(aherm) ≈ tr(Hermitian(aherm)) - end - - @testset "isposdef[!]" begin - @test isposdef(Symmetric(asym)) == isposdef(asym) - @test isposdef(Symmetric(aposs)) == isposdef(aposs) == true - @test isposdef(Hermitian(aherm)) == isposdef(aherm) - @test isposdef(Hermitian(apos)) == isposdef(apos) == true - if eltya != Int #chol! won't work with Int - @test isposdef!(Symmetric(copy(asym))) == isposdef(asym) - @test isposdef!(Symmetric(copy(aposs))) == isposdef(aposs) == true - @test isposdef!(Hermitian(copy(aherm))) == isposdef(aherm) - @test isposdef!(Hermitian(copy(apos))) == isposdef(apos) == true - end - end - - @testset "$f" for f in (det, logdet, logabsdet) - for uplo in (:U, :L) - @test all(f(apos) .≈ f(Hermitian(apos, uplo))) - @test all(f(aposs) .≈ f(Symmetric(aposs, uplo))) - if f != logdet - @test all(f(aherm) .≈ f(Hermitian(aherm, uplo))) - @test all(f(asym) .≈ f(Symmetric(asym, uplo))) - end - end - end - - @testset "inversion" begin - for uplo in (:U, :L) - @test inv(Symmetric(asym, uplo))::Symmetric ≈ inv(asym) - @test inv(Hermitian(aherm, uplo))::Hermitian ≈ inv(aherm) - @test inv(Symmetric(a, uplo))::Symmetric ≈ inv(Matrix(Symmetric(a, uplo))) - if eltya <: Real - @test inv(Hermitian(a, uplo))::Hermitian ≈ inv(Matrix(Hermitian(a, uplo))) - end - end - if eltya <: LinearAlgebra.BlasComplex - @testset "inverse edge case with complex Hermitian" begin - # Hermitian matrix, where inv(lu(A)) generates non-real diagonal elements - for T in (ComplexF32, ComplexF64) - # data should have nonvanishing imaginary parts on the diagonal - M = T[0.279982+0.988074im 0.770011+0.870555im - 0.138001+0.889728im 0.177242+0.701413im] - H = Hermitian(M) - A = Matrix(H) - @test inv(H) ≈ inv(A) - @test ishermitian(Matrix(inv(H))) - end - end - end - if eltya <: AbstractFloat - @testset "inv should error with NaNs/Infs" begin - h = Hermitian(fill(eltya(NaN), 2, 2)) - @test_throws ArgumentError inv(h) - s = Symmetric(fill(eltya(NaN), 2, 2)) - @test_throws ArgumentError inv(s) - end - end - end - - # Revisit when implemented in julia - if eltya != BigFloat - @testset "cond" begin - if eltya <: Real #svdvals! has no method for Symmetric{Complex} - @test cond(Symmetric(asym)) ≈ cond(asym) - end - @test cond(Hermitian(aherm)) ≈ cond(aherm) - end - - @testset "symmetric eigendecomposition" begin - if eltya <: Real # the eigenvalues are only real and ordered for Hermitian matrices - d, v = eigen(asym) - @test asym*v[:,1] ≈ d[1]*v[:,1] - @test v*Diagonal(d)*transpose(v) ≈ asym - @test isequal(eigvals(asym[1]), eigvals(asym[1:1,1:1])[1]) - @test abs.(eigen(Symmetric(asym), 1:2).vectors'v[:,1:2]) ≈ Matrix(I, 2, 2) - @test abs.(eigen(Symmetric(asym), d[1] - 1, (d[2] + d[3])/2).vectors'v[:,1:2]) ≈ Matrix(I, 2, 2) - @test eigvals(Symmetric(asym), 1:2) ≈ d[1:2] - @test eigvals(Symmetric(asym), sortby= x -> -x) ≈ eigvals(eigen(Symmetric(asym), sortby = x -> -x)) - @test eigvals(Symmetric(asym), d[1] - 1, (d[2] + d[3])/2) ≈ d[1:2] - # eigen doesn't support Symmetric{Complex} - @test Matrix(eigen(asym)) ≈ asym - @test eigvecs(Symmetric(asym)) ≈ eigvecs(asym) - end - - d, v = eigen(aherm) - @test aherm*v[:,1] ≈ d[1]*v[:,1] - @test v*Diagonal(d)*v' ≈ aherm - @test isequal(eigvals(aherm[1]), eigvals(aherm[1:1,1:1])[1]) - @test abs.(eigen(Hermitian(aherm), 1:2).vectors'v[:,1:2]) ≈ Matrix(I, 2, 2) - @test abs.(eigen(Hermitian(aherm), d[1] - 1, (d[2] + d[3])/2).vectors'v[:,1:2]) ≈ Matrix(I, 2, 2) - @test eigvals(Hermitian(aherm), 1:2) ≈ d[1:2] - @test eigvals(Hermitian(aherm), sortby= x -> -x) ≈ eigvals(eigen(Hermitian(aherm), sortby = x -> -x)) - @test eigvals(Hermitian(aherm), d[1] - 1, (d[2] + d[3])/2) ≈ d[1:2] - @test Matrix(eigen(aherm)) ≈ aherm - @test eigvecs(Hermitian(aherm)) ≈ eigvecs(aherm) - - # relation to svdvals - if eltya <: Real #svdvals! has no method for Symmetric{Complex} - @test sum(sort(abs.(eigvals(Symmetric(asym))))) == sum(sort(svdvals(Symmetric(asym)))) - end - @test sum(sort(abs.(eigvals(Hermitian(aherm))))) == sum(sort(svdvals(Hermitian(aherm)))) - end - - @testset "rank" begin - let A = a[:,1:5]*a[:,1:5]' - # Make sure A is Hermitian even in the presence of rounding error - # xianyi/OpenBLAS#729 - A = (A + A') / 2 - @test rank(A) == rank(Hermitian(A)) - end - end - - @testset "pow" begin - # Integer power - @test (asym)^2 ≈ (Symmetric(asym)^2)::Symmetric - @test (asym)^-2 ≈ (Symmetric(asym)^-2)::Symmetric - @test (aposs)^2 ≈ (Symmetric(aposs)^2)::Symmetric - @test (aherm)^2 ≈ (Hermitian(aherm)^2)::Hermitian - @test (aherm)^-2 ≈ (Hermitian(aherm)^-2)::Hermitian - @test (apos)^2 ≈ (Hermitian(apos)^2)::Hermitian - # integer floating point power - @test (asym)^2.0 ≈ (Symmetric(asym)^2.0)::Symmetric - @test (asym)^-2.0 ≈ (Symmetric(asym)^-2.0)::Symmetric - @test (aposs)^2.0 ≈ (Symmetric(aposs)^2.0)::Symmetric - @test (aherm)^2.0 ≈ (Hermitian(aherm)^2.0)::Hermitian - @test (aherm)^-2.0 ≈ (Hermitian(aherm)^-2.0)::Hermitian - @test (apos)^2.0 ≈ (Hermitian(apos)^2.0)::Hermitian - # non-integer floating point power - @test (asym)^2.5 ≈ (Symmetric(asym)^2.5)::Symmetric - @test (asym)^-2.5 ≈ (Symmetric(asym)^-2.5)::Symmetric - @test (aposs)^2.5 ≈ (Symmetric(aposs)^2.5)::Symmetric - @test (aherm)^2.5 ≈ (Hermitian(aherm)^2.5)#::Hermitian - @test (aherm)^-2.5 ≈ (Hermitian(aherm)^-2.5)#::Hermitian - @test (apos)^2.5 ≈ (Hermitian(apos)^2.5)::Hermitian - end - end - end - - @testset "linalg binary ops" begin - @testset "mat * vec" begin - @test Symmetric(asym)*x+y ≈ asym*x+y - # testing fallbacks for transpose-vector * transpose(SymHerm) - xadj = transpose(x) - @test xadj * transpose(Symmetric(asym)) ≈ xadj * asym - @test x' * Symmetric(asym) ≈ x' * asym - - @test Hermitian(aherm)*x+y ≈ aherm*x+y - # testing fallbacks for adjoint-vector * SymHerm' - xadj = x' - @test x' * Hermitian(aherm) ≈ x' * aherm - @test xadj * Hermitian(aherm)' ≈ xadj * aherm - end - - @testset "mat * mat" begin - C = zeros(eltya,n,n) - @test Hermitian(aherm) * a ≈ aherm * a - @test a * Hermitian(aherm) ≈ a * aherm - # rectangular multiplication - @test [a; a] * Hermitian(aherm) ≈ [a; a] * aherm - @test Hermitian(aherm) * [a a] ≈ aherm * [a a] - @test Hermitian(aherm) * Hermitian(aherm) ≈ aherm*aherm - @test_throws DimensionMismatch Hermitian(aherm) * Vector{eltya}(undef, n+1) - LinearAlgebra.mul!(C,a,Hermitian(aherm)) - @test C ≈ a*aherm - - @test Symmetric(asym) * Symmetric(asym) ≈ asym*asym - @test Symmetric(asym) * a ≈ asym * a - @test a * Symmetric(asym) ≈ a * asym - # rectangular multiplication - @test Symmetric(asym) * [a a] ≈ asym * [a a] - @test [a; a] * Symmetric(asym) ≈ [a; a] * asym - @test_throws DimensionMismatch Symmetric(asym) * Vector{eltya}(undef, n+1) - LinearAlgebra.mul!(C,a,Symmetric(asym)) - @test C ≈ a*asym - - tri_b = UpperTriangular(triu(b)) - @test Array(transpose(Hermitian(aherm)) * tri_b) ≈ transpose(aherm) * Array(tri_b) - @test Array(tri_b * transpose(Hermitian(aherm))) ≈ Array(tri_b) * transpose(aherm) - @test Array(Hermitian(aherm)' * tri_b) ≈ aherm' * Array(tri_b) - @test Array(tri_b * Hermitian(aherm)') ≈ Array(tri_b) * aherm' - - @test Array(transpose(Symmetric(asym)) * tri_b) ≈ transpose(asym) * Array(tri_b) - @test Array(tri_b * transpose(Symmetric(asym))) ≈ Array(tri_b) * transpose(asym) - @test Array(Symmetric(asym)' * tri_b) ≈ asym' * Array(tri_b) - @test Array(tri_b * Symmetric(asym)') ≈ Array(tri_b) * asym' - end - @testset "solver" begin - @test Hermitian(aherm)\x ≈ aherm\x - @test Hermitian(aherm)\b ≈ aherm\b - @test Symmetric(asym)\x ≈ asym\x - @test Symmetric(asym)\b ≈ asym\b - @test Hermitian(Diagonal(aherm))\x ≈ Diagonal(aherm)\x - @test Hermitian(Matrix(Diagonal(aherm)))\b ≈ Diagonal(aherm)\b - @test Symmetric(Diagonal(asym))\x ≈ Diagonal(asym)\x - @test Symmetric(Matrix(Diagonal(asym)))\b ≈ Diagonal(asym)\b - end - end - @testset "generalized dot product" begin - for uplo in (:U, :L) - @test dot(x, Hermitian(aherm, uplo), y) ≈ dot(x, Hermitian(aherm, uplo)*y) ≈ dot(x, Matrix(Hermitian(aherm, uplo)), y) - @test dot(x, Hermitian(aherm, uplo), x) ≈ dot(x, Hermitian(aherm, uplo)*x) ≈ dot(x, Matrix(Hermitian(aherm, uplo)), x) - end - @test dot(x, Hermitian(Diagonal(a)), y) ≈ dot(x, Hermitian(Diagonal(a))*y) ≈ dot(x, Matrix(Hermitian(Diagonal(a))), y) - @test dot(x, Hermitian(Diagonal(a)), x) ≈ dot(x, Hermitian(Diagonal(a))*x) ≈ dot(x, Matrix(Hermitian(Diagonal(a))), x) - if eltya <: Real - for uplo in (:U, :L) - @test dot(x, Symmetric(aherm, uplo), y) ≈ dot(x, Symmetric(aherm, uplo)*y) ≈ dot(x, Matrix(Symmetric(aherm, uplo)), y) - @test dot(x, Symmetric(aherm, uplo), x) ≈ dot(x, Symmetric(aherm, uplo)*x) ≈ dot(x, Matrix(Symmetric(aherm, uplo)), x) - end - end - end - - @testset "dot product of symmetric and Hermitian matrices" begin - for mtype in (Symmetric, Hermitian) - symau = mtype(a, :U) - symal = mtype(a, :L) - msymau = Matrix(symau) - msymal = Matrix(symal) - @test_throws DimensionMismatch dot(symau, mtype(zeros(eltya, n-1, n-1))) - for eltyc in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - creal = randn(n, n)/2 - cimag = randn(n, n)/2 - c = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(creal, cimag) : creal) - symcu = mtype(c, :U) - symcl = mtype(c, :L) - msymcu = Matrix(symcu) - msymcl = Matrix(symcl) - @test dot(symau, symcu) ≈ dot(msymau, msymcu) - @test dot(symau, symcl) ≈ dot(msymau, msymcl) - @test dot(symal, symcu) ≈ dot(msymal, msymcu) - @test dot(symal, symcl) ≈ dot(msymal, msymcl) - end - - # block matrices - blockm = [eltya == Int ? rand(1:7, 3, 3) : convert(Matrix{eltya}, eltya <: Complex ? complex.(randn(3, 3)/2, randn(3, 3)/2) : randn(3, 3)/2) for _ in 1:3, _ in 1:3] - symblockmu = mtype(blockm, :U) - symblockml = mtype(blockm, :L) - msymblockmu = Matrix(symblockmu) - msymblockml = Matrix(symblockml) - @test dot(symblockmu, symblockmu) ≈ dot(msymblockmu, msymblockmu) - @test dot(symblockmu, symblockml) ≈ dot(msymblockmu, msymblockml) - @test dot(symblockml, symblockmu) ≈ dot(msymblockml, msymblockmu) - @test dot(symblockml, symblockml) ≈ dot(msymblockml, msymblockml) - end - end - - @testset "kronecker product of symmetric and Hermitian matrices" begin - for mtype in (Symmetric, Hermitian) - symau = mtype(a, :U) - symal = mtype(a, :L) - msymau = Matrix(symau) - msymal = Matrix(symal) - for eltyc in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - creal = randn(n, n)/2 - cimag = randn(n, n)/2 - c = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(creal, cimag) : creal) - symcu = mtype(c, :U) - symcl = mtype(c, :L) - msymcu = Matrix(symcu) - msymcl = Matrix(symcl) - @test kron(symau, symcu) ≈ kron(msymau, msymcu) - @test kron(symau, symcl) ≈ kron(msymau, msymcl) - @test kron(symal, symcu) ≈ kron(msymal, msymcu) - @test kron(symal, symcl) ≈ kron(msymal, msymcl) - end - end - end - end -end - -@testset "non-isbits algebra" begin - for ST in (Symmetric, Hermitian), uplo in (:L, :U) - M = Matrix{Complex{BigFloat}}(undef,2,2) - M[1,1] = rand() - M[2,2] = rand() - M[1+(uplo==:L), 1+(uplo==:U)] = rand(ComplexF64) - S = ST(M, uplo) - MS = Matrix(S) - @test real(S) == real(MS) - @test imag(S) == imag(MS) - @test conj(S) == conj(MS) - @test conj!(copy(S)) == conj(MS) - @test -S == -MS - @test S + S == MS + MS - @test S - S == MS - MS - @test S*2 == 2*S == 2*MS - @test S/2 == MS/2 - @test kron(S,S) == kron(MS,MS) - end - @testset "mixed uplo" begin - Mu = Matrix{Complex{BigFloat}}(undef,2,2) - Mu[1,1] = Mu[2,2] = 3 - Mu[1,2] = 2 + 3im - Ml = Matrix{Complex{BigFloat}}(undef,2,2) - Ml[1,1] = Ml[2,2] = 4 - Ml[2,1] = 4 + 5im - for ST in (Symmetric, Hermitian) - Su = ST(Mu, :U) - MSu = Matrix(Su) - Sl = ST(Ml, :L) - MSl = Matrix(Sl) - @test Su + Sl == Sl + Su == MSu + MSl - @test Su - Sl == -(Sl - Su) == MSu - MSl - @test kron(Su,Sl) == kron(MSu,MSl) - @test kron(Sl,Su) == kron(MSl,MSu) - end - end - @testset "non-strided" begin - @testset "diagonal" begin - for ST1 in (Symmetric, Hermitian), uplo1 in (:L, :U) - m = ST1(Matrix{BigFloat}(undef,2,2), uplo1) - m.data[1,1] = 1 - m.data[2,2] = 3 - m.data[1+(uplo1==:L), 1+(uplo1==:U)] = 2 - A = Array(m) - for ST2 in (Symmetric, Hermitian), uplo2 in (:L, :U) - id = ST2(I(2), uplo2) - @test m + id == id + m == A + id - end - end - end - @testset "unit triangular" begin - for ST1 in (Symmetric, Hermitian), uplo1 in (:L, :U) - H1 = ST1(UnitUpperTriangular(big.(rand(Int8,4,4))), uplo1) - M1 = Matrix(H1) - for ST2 in (Symmetric, Hermitian), uplo2 in (:L, :U) - H2 = ST2(UnitUpperTriangular(big.(rand(Int8,4,4))), uplo2) - @test H1 + H2 == M1 + Matrix(H2) - end - end - end - end -end - -@testset "Reverse operation on Symmetric" begin - for uplo in (:U, :L) - A = Symmetric(randn(5, 5), uplo) - @test reverse(A, dims=1) == reverse(Matrix(A), dims=1) - @test reverse(A, dims=2) == reverse(Matrix(A), dims=2) - @test reverse(A)::Symmetric == reverse(Matrix(A)) - end -end - -@testset "Reverse operation on Hermitian" begin - for uplo in (:U, :L) - A = Hermitian(randn(ComplexF64, 5, 5), uplo) - @test reverse(A, dims=1) == reverse(Matrix(A), dims=1) - @test reverse(A, dims=2) == reverse(Matrix(A), dims=2) - @test reverse(A)::Hermitian == reverse(Matrix(A)) - end -end - - -# bug identified in PR #52318: dot products of quaternionic Hermitian matrices, -# or any number type where conj(a)*conj(b) ≠ conj(a*b): -@testset "dot Hermitian quaternion #52318" begin - A, B = [Quaternion.(randn(3,3), randn(3, 3), randn(3, 3), randn(3,3)) |> t -> t + t' for i in 1:2] - @test A == Hermitian(A) && B == Hermitian(B) - @test dot(A, B) ≈ dot(Hermitian(A), Hermitian(B)) - A, B = [Quaternion.(randn(3,3), randn(3, 3), randn(3, 3), randn(3,3)) |> t -> t + transpose(t) for i in 1:2] - @test A == Symmetric(A) && B == Symmetric(B) - @test dot(A, B) ≈ dot(Symmetric(A), Symmetric(B)) -end - -# let's make sure the analogous bug will not show up with kronecker products -@testset "kron Hermitian quaternion #52318" begin - A, B = [Quaternion.(randn(3,3), randn(3, 3), randn(3, 3), randn(3,3)) |> t -> t + t' for i in 1:2] - @test A == Hermitian(A) && B == Hermitian(B) - @test kron(A, B) ≈ kron(Hermitian(A), Hermitian(B)) - A, B = [Quaternion.(randn(3,3), randn(3, 3), randn(3, 3), randn(3,3)) |> t -> t + transpose(t) for i in 1:2] - @test A == Symmetric(A) && B == Symmetric(B) - @test kron(A, B) ≈ kron(Symmetric(A), Symmetric(B)) -end - -@testset "kron with symmetric/hermitian matrices of matrices" begin - M = fill(ones(2,2), 2, 2) - for W in (Symmetric, Hermitian) - for (t1, t2) in ((W(M, :U), W(M, :U)), (W(M, :U), W(M, :L)), (W(M, :L), W(M, :L))) - @test kron(t1, t2) ≈ kron(Matrix(t1), Matrix(t2)) - end - end -end - -#Issue #7647: test xsyevr, xheevr, xstevr drivers. -@testset "Eigenvalues in interval for $(typeof(Mi7647))" for Mi7647 in - (Symmetric(diagm(0 => 1.0:3.0)), - Hermitian(diagm(0 => 1.0:3.0)), - Hermitian(diagm(0 => complex(1.0:3.0))), - SymTridiagonal([1.0:3.0;], zeros(2))) - @test eigmin(Mi7647) == eigvals(Mi7647, 0.5, 1.5)[1] == 1.0 - @test eigmax(Mi7647) == eigvals(Mi7647, 2.5, 3.5)[1] == 3.0 - @test eigvals(Mi7647) == eigvals(Mi7647, 0.5, 3.5) == [1.0:3.0;] -end - -@testset "Hermitian wrapper ignores imaginary parts on diagonal" begin - A = [1.0+im 2.0; 2.0 0.0] - @test !ishermitian(A) - @test Hermitian(A)[1,1] == 1 -end - -@testset "Issue #7933" begin - A7933 = [1 2; 3 4] - B7933 = copy(A7933) - C7933 = Matrix(Symmetric(A7933)) - @test A7933 == B7933 -end - -@testset "Issues #8057 and #8058. f=$f, A=$A" for f in - (eigen, eigvals), - A in (Symmetric([0 1; 1 0]), Hermitian([0 im; -im 0])) - @test_throws ArgumentError f(A, 3, 2) - @test_throws ArgumentError f(A, 1:4) -end - -@testset "Ignore imaginary part of Hermitian diagonal" begin - A = [1.0+im 2.0; 2.0 0.0] - @test !ishermitian(A) - @test diag(Hermitian(A)) == real(diag(A)) -end - -@testset "Issue #17780" begin - a = randn(2,2) - a = a'a - b = complex.(a,a) - c = Symmetric(b) - @test conj(c) == conj(Array(c)) - cc = copy(c) - @test conj!(c) == conj(Array(cc)) - c = Hermitian(b + b') - @test conj(c) == conj(Array(c)) - cc = copy(c) - @test conj!(c) == conj(Array(cc)) -end - -@testset "Issue # 19225" begin - X = [1 -1; -1 1] - for T in (Symmetric, Hermitian) - Y = T(copy(X)) - _Y = similar(Y) - copyto!(_Y, Y) - @test _Y == Y - - W = T(copy(X), :L) - copyto!(W, Y) - @test W.data == Y.data - @test W.uplo != Y.uplo - - W[1,1] = 4 - @test W == T([4 -1; -1 1]) - @test_throws ArgumentError (W[1,2] = 2) - if T == Hermitian - @test_throws ArgumentError (W[2,2] = 3+4im) - end - - @test Y + I == T([2 -1; -1 2]) - @test Y - I == T([0 -1; -1 0]) - @test Y * I == Y - - @test Y .+ 1 == T([2 0; 0 2]) - @test Y .- 1 == T([0 -2; -2 0]) - @test Y * 2 == T([2 -2; -2 2]) - @test Y / 1 == Y - - @test T([true false; false true]) .+ true == T([2 1; 1 2]) - end -end - -@testset "Issue #21981" begin - B = complex(rand(4,4)) - B[4,1] += 1im; - @test ishermitian(Symmetric(B, :U)) - @test issymmetric(Hermitian(B, :U)) - B[4,1] = real(B[4,1]) - B[1,4] += 1im - @test ishermitian(Symmetric(B, :L)) - @test issymmetric(Hermitian(B, :L)) -end - -@testset "$HS solver with $RHS RHS - $T" for HS in (Hermitian, Symmetric), - RHS in (Hermitian, Symmetric, Diagonal, UpperTriangular, LowerTriangular), - T in (Float64, ComplexF64) - D = rand(T, 10, 10); D = D'D - A = HS(D) - B = RHS(D) - @test A\B ≈ Matrix(A)\Matrix(B) -end - -@testset "inversion of Hilbert matrix" begin - for T in (Float64, ComplexF64) - H = T[1/(i + j - 1) for i in 1:8, j in 1:8] - @test norm(inv(Symmetric(H))*(H*fill(1., 8)) .- 1) ≈ 0 atol = 1e-5 - @test norm(inv(Hermitian(H))*(H*fill(1., 8)) .- 1) ≈ 0 atol = 1e-5 - end -end - -@testset "eigendecomposition Algorithms" begin - using LinearAlgebra: DivideAndConquer, QRIteration, RobustRepresentations - for T in (Float64, ComplexF64, Float32, ComplexF32) - n = 4 - A = T <: Real ? Symmetric(randn(T, n, n)) : Hermitian(randn(T, n, n)) - d, v = eigen(A) - for alg in (DivideAndConquer(), QRIteration(), RobustRepresentations()) - @test (@inferred eigvals(A, alg)) ≈ d - d2, v2 = @inferred eigen(A, alg) - @test d2 ≈ d - @test A * v2 ≈ v2 * Diagonal(d2) - end - 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 - -@testset "Conversion to AbstractArray" begin - # tests corresponding to #34995 - immutablemat = ImmutableArray([1 2 3; 4 5 6; 7 8 9]) - for SymType in (Symmetric, Hermitian) - S = Float64 - symmat = SymType(immutablemat) - @test convert(AbstractArray{S}, symmat).data isa ImmutableArray{S} - @test convert(AbstractMatrix{S}, symmat).data isa ImmutableArray{S} - @test AbstractArray{S}(symmat).data isa ImmutableArray{S} - @test AbstractMatrix{S}(symmat).data isa ImmutableArray{S} - @test convert(AbstractArray{S}, symmat) == symmat - @test convert(AbstractMatrix{S}, symmat) == symmat - end -end - - -@testset "#24572: eltype(A::HermOrSym) === eltype(parent(A))" begin - A = rand(Float32, 3, 3) - @test_throws TypeError Symmetric{Float64,Matrix{Float32}}(A, 'U') - @test_throws TypeError Hermitian{Float64,Matrix{Float32}}(A, 'U') -end - -@testset "fill[stored]!" begin - for uplo in (:U, :L) - # Hermitian - A = Hermitian(fill(1.0+0im, 2, 2), uplo) - @test fill!(A, 2) == fill(2, 2, 2) - @test A.data == (uplo === :U ? [2 2; 1.0+0im 2] : [2 1.0+0im; 2 2]) - @test_throws ArgumentError fill!(A, 2+im) - - # Symmetric - A = Symmetric(fill(1.0+im, 2, 2), uplo) - @test fill!(A, 2) == fill(2, 2, 2) - @test A.data == (uplo === :U ? [2 2; 1.0+im 2] : [2 1.0+im; 2 2]) - end -end - -@testset "#25625 recursive transposition" begin - A = Matrix{Matrix{Int}}(undef, 2, 2) - A[1,1] = [1 2; 2 3] - A[1,2] = [4 5 6; 7 8 9] - A[2,1] = [4 7; 5 8; 6 9] - A[2,2] = [1 2; 3 4] - for uplo in (:U, :L) - S = Symmetric(A, uplo) - @test S[1,1] == A[1,1] - @test S[1,2] == transpose(S[2,1]) == A[1,2] - @test S[2,2] == Symmetric(A[2,2], uplo) - @test S == transpose(S) == Matrix(S) == Matrix(transpose(S)) == transpose(Matrix(S)) - end - - B = Matrix{Matrix{Complex{Int}}}(undef, 2, 2) - B[1,1] = [1 2+im; 2-im 3] - B[1,2] = [4 5+1im 6-2im; 7+3im 8-4im 9+5im] - B[2,1] = [4 7-3im; 5-1im 8+4im; 6+2im 9-5im] - B[2,2] = [1+1im 2+2im; 3-3im 4-2im] - for uplo in (:U, :L) - H = Hermitian(B, uplo) - @test H[1,1] == Hermitian(B[1,1], uplo) - @test H[1,2] == adjoint(H[2,1]) == B[1,2] - @test H[2,1] == adjoint(H[1,2]) == B[2,1] - @test H[2,2] == Hermitian(B[2,2], uplo) - @test H == adjoint(H) == Matrix(H) == Matrix(adjoint(H)) == adjoint(Matrix(H)) - end -end - -@testset "getindex of diagonal element (#25972)" begin - A = rand(ComplexF64, 2, 2) - @test Hermitian(A, :U)[1,1] == Hermitian(A, :L)[1,1] == real(A[1,1]) -end - -@testset "issue #29392: SymOrHerm scaled with Number" begin - R = rand(Float64, 2, 2); C = rand(ComplexF64, 2, 2) - # Symmetric * Real, Real * Symmetric - A = Symmetric(R); x = 2.0 - @test (A * x)::Symmetric == (x * A)::Symmetric - A = Symmetric(C); x = 2.0 - @test (A * x)::Symmetric == (x * A)::Symmetric - # Symmetric * Complex, Complex * Symmetrics - A = Symmetric(R); x = 2.0im - @test (A * x)::Symmetric == (x * A)::Symmetric - A = Symmetric(C); x = 2.0im - @test (A * x)::Symmetric == (x * A)::Symmetric - # Hermitian * Real, Real * Hermitian - A = Hermitian(R); x = 2.0 - @test (A * x)::Hermitian == (x * A)::Hermitian - A = Hermitian(C); x = 2.0 - @test (A * x)::Hermitian == (x * A)::Hermitian - # Hermitian * Complex, Complex * Hermitian - A = Hermitian(R); x = 2.0im - @test (A * x)::Matrix == (x * A)::Matrix - A = Hermitian(C); x = 2.0im - @test (A * x)::Matrix == (x * A)::Matrix - # Symmetric / Real - A = Symmetric(R); x = 2.0 - @test (A / x)::Symmetric == Matrix(A) / x - A = Symmetric(C); x = 2.0 - @test (A / x)::Symmetric == Matrix(A) / x - # Symmetric / Complex - A = Symmetric(R); x = 2.0im - @test (A / x)::Symmetric == Matrix(A) / x - A = Symmetric(C); x = 2.0im - @test (A / x)::Symmetric == Matrix(A) / x - # Hermitian / Real - A = Hermitian(R); x = 2.0 - @test (A / x)::Hermitian == Matrix(A) / x - A = Hermitian(C); x = 2.0 - @test (A / x)::Hermitian == Matrix(A) / x - # Hermitian / Complex - A = Hermitian(R); x = 2.0im - @test (A / x)::Matrix == Matrix(A) / x - A = Hermitian(C); x = 2.0im - @test (A / x)::Matrix == Matrix(A) / x -end - -@testset "issue #30814: Symmetric of Hermitian if diag is not real" begin - A = [1 2; 3 4] * (1 + im) - B = Hermitian(A) - @test_throws ArgumentError Symmetric(B) == Symmetric(Matrix(B)) - A[1,1] = 1; A[2,2] = 4 - @test Symmetric(B) == Symmetric(Matrix(B)) -end - -@testset "issue #32079: det for singular Symmetric matrix" begin - A = ones(Float64, 3, 3) - @test det(Symmetric(A))::Float64 == det(A) == 0.0 - @test det(Hermitian(A))::Float64 == det(A) == 0.0 - A = ones(ComplexF64, 3, 3) - @test det(Symmetric(A))::ComplexF64 == det(A) == 0.0 - @test det(Hermitian(A))::Float64 == det(A) == 0.0 -end - -@testset "symmetric()/hermitian() for Numbers" begin - @test LinearAlgebra.symmetric(1) == LinearAlgebra.symmetric(1, :U) == 1 - @test LinearAlgebra.symmetric_type(Int) == Int - @test LinearAlgebra.hermitian(1) == LinearAlgebra.hermitian(1, :U) == 1 - @test LinearAlgebra.hermitian_type(Int) == Int -end - -@testset "sqrt(nearly semidefinite)" begin - let A = [0.9999999999999998 4.649058915617843e-16 -1.3149405273715513e-16 9.9959579317056e-17; -8.326672684688674e-16 1.0000000000000004 2.9280733590254494e-16 -2.9993900031619594e-16; 9.43689570931383e-16 -1.339206523454095e-15 1.0000000000000007 -8.550505126287743e-16; -6.245004513516506e-16 -2.0122792321330962e-16 1.183061278035052e-16 1.0000000000000002], - B = [0.09648289218436859 0.023497875751503007 0.0 0.0; 0.023497875751503007 0.045787575150300804 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0], - C = Symmetric(A*B*A'), # semidefinite up to roundoff - Csqrt = sqrt(C) - @test Csqrt isa Symmetric{Float64} - @test Csqrt*Csqrt ≈ C rtol=1e-14 - end - let D = Symmetric(Matrix(Diagonal([1 0; 0 -1e-14]))) - @test sqrt(D) ≈ [1 0; 0 1e-7im] rtol=1e-14 - @test sqrt(D, rtol=1e-13) ≈ [1 0; 0 0] rtol=1e-14 - @test sqrt(D, rtol=1e-13)^2 ≈ D rtol=1e-13 - end -end - -@testset "Multiplications symmetric/hermitian for $T and $S" for T in - (Float16, Float32, Float64, BigFloat), S in (ComplexF16, ComplexF32, ComplexF64) - let A = transpose(Symmetric(rand(S, 3, 3))), Bv = Vector(rand(T, 3)), Bm = Matrix(rand(T, 3,3)) - @test A * Bv ≈ Matrix(A) * Bv - @test A * Bm ≈ Matrix(A) * Bm - @test Bm * A ≈ Bm * Matrix(A) - end - let A = adjoint(Hermitian(rand(S, 3,3))), Bv = Vector(rand(T, 3)), Bm = Matrix(rand(T, 3,3)) - @test A * Bv ≈ Matrix(A) * Bv - @test A * Bm ≈ Matrix(A) * Bm - @test Bm * A ≈ Bm * Matrix(A) - end - let Ahrs = transpose(Hermitian(Symmetric(rand(T, 3, 3)))), - Acs = transpose(Symmetric(rand(S, 3, 3))), - Ahcs = transpose(Hermitian(Symmetric(rand(S, 3, 3)))) - - @test Ahrs * Ahrs ≈ Ahrs * Matrix(Ahrs) - @test Ahrs * Acs ≈ Ahrs * Matrix(Acs) - @test Acs * Acs ≈ Matrix(Acs) * Matrix(Acs) - @test Acs * Ahrs ≈ Matrix(Acs) * Ahrs - @test Ahrs * Ahcs ≈ Matrix(Ahrs) * Ahcs - @test Ahcs * Ahrs ≈ Ahcs * Matrix(Ahrs) - end - let Ahrs = adjoint(Hermitian(Symmetric(rand(T, 3, 3)))), - Acs = adjoint(Symmetric(rand(S, 3, 3))), - Ahcs = adjoint(Hermitian(Symmetric(rand(S, 3, 3)))) - - @test Ahrs * Ahrs ≈ Ahrs * Matrix(Ahrs) - @test Ahcs * Ahcs ≈ Matrix(Ahcs) * Matrix(Ahcs) - @test Ahrs * Ahcs ≈ Ahrs * Matrix(Ahcs) - @test Acs * Ahcs ≈ Acs * Matrix(Ahcs) - @test Ahcs * Ahrs ≈ Matrix(Ahcs) * Ahrs - @test Ahcs * Acs ≈ Matrix(Ahcs) * Acs - end -end - -@testset "Addition/subtraction with SymTridiagonal" begin - TR = SymTridiagonal(randn(Float64,5), randn(Float64,4)) - TC = SymTridiagonal(randn(ComplexF64,5), randn(ComplexF64,4)) - SR = Symmetric(randn(Float64,5,5)) - SC = Symmetric(randn(ComplexF64,5,5)) - HR = Hermitian(randn(Float64,5,5)) - HC = Hermitian(randn(ComplexF64,5,5)) - for op = (+,-) - for T = (TR, TC), S = (SR, SC) - @test op(T, S) == op(Array(T), S) - @test op(S, T) == op(S, Array(T)) - @test op(T, S) isa Symmetric - @test op(S, T) isa Symmetric - end - for H = (HR, HC) - for T = (TR, TC) - @test op(T, H) == op(Array(T), H) - @test op(H, T) == op(H, Array(T)) - end - @test op(TR, H) isa Hermitian - @test op(H, TR) isa Hermitian - end - end -end - -@testset "hermitian part" begin - for T in [Float32, Complex{Float32}, Int32, Rational{Int32}, - Complex{Int32}, Complex{Rational{Int32}}] - f, f!, t = hermitianpart, hermitianpart!, T <: Real ? transpose : adjoint - X = T[1 2 3; 4 5 6; 7 8 9] - T <: Complex && (X .+= im .* X) - Xc = copy(X) - Y = (X + t(X)) / 2 - U = f(X) - L = f(X, :L) - @test U isa Hermitian - @test L isa Hermitian - @test U.uplo == 'U' - @test L.uplo == 'L' - @test U == L == Y - if T <: AbstractFloat || real(T) <: AbstractFloat - HU = f!(X) - @test HU == Y - @test triu(X) == triu(Y) - HL = f!(Xc, :L) - @test HL == Y - @test tril(Xc) == tril(Y) - end - end - @test_throws DimensionMismatch hermitianpart(ones(1,2)) - for T in (Float64, ComplexF64), uplo in (:U, :L) - A = [randn(T, 2, 2) for _ in 1:2, _ in 1:2] - Aherm = hermitianpart(A, uplo) - @test Aherm == Aherm.data == (A + A')/2 - @test Aherm isa Hermitian - @test Aherm.uplo == LinearAlgebra.char_uplo(uplo) - end -end - -@testset "Structured display" begin - @testset "Diagonal" begin - d = 10:13 - D = Diagonal(d) - for uplo in (:L, :U), SymHerm in (Symmetric, Hermitian) - S = SymHerm(D, uplo) - @test sprint(Base.print_matrix, S) == sprint(Base.print_matrix, D) - end - - d = (10:13) .+ 2im - D = Diagonal(d) - DR = Diagonal(complex.(real.(d))) - for uplo in (:L, :U) - H = Hermitian(D, uplo) - @test sprint(Base.print_matrix, H) == sprint(Base.print_matrix, DR) - - S = Symmetric(D, uplo) - @test sprint(Base.print_matrix, S) == sprint(Base.print_matrix, D) - end - end - @testset "Bidiagonal" begin - dv, ev = 1:4, 1:3 - ST = SymTridiagonal(dv, ev) - D = Diagonal(dv) - for B_uplo in (:L, :U) - B = Bidiagonal(dv, ev, B_uplo) - for Sym_uplo in (:L, :U), SymHerm in (Symmetric, Hermitian) - SB = SymHerm(B, Sym_uplo) - teststr = sprint(Base.print_matrix, Sym_uplo == B_uplo ? ST : D) - @test sprint(Base.print_matrix, SB) == teststr - SB = SymHerm(Transpose(B), Sym_uplo) - teststr = sprint(Base.print_matrix, Sym_uplo == B_uplo ? D : ST) - @test sprint(Base.print_matrix, SB) == teststr - end - end - end - @testset "Tridiagonal" begin - superd, d, subd = 3:5, 10:13, 1:3 - for uplo in (:U, :L), SymHerm in (Symmetric, Hermitian) - S = SymHerm(Tridiagonal(subd, d, superd), uplo) - ST = SymTridiagonal(d, uplo == :U ? superd : subd) - @test sprint(Base.print_matrix, S) == sprint(Base.print_matrix, ST) - end - - superd, d, subd = collect((3:5)*im), collect(Complex{Int}, 10:13), collect((1:3)*im) - for uplo in (:U, :L) - S = Symmetric(Tridiagonal(subd, d, superd), uplo) - ST = SymTridiagonal(d, uplo == :U ? superd : subd) - @test sprint(Base.print_matrix, S) == sprint(Base.print_matrix, ST) - - H = Hermitian(Tridiagonal(subd, d, superd), uplo) - T = Tridiagonal(uplo == :L ? subd : conj(superd), d, uplo == :U ? superd : conj(subd)) - @test sprint(Base.print_matrix, H) == sprint(Base.print_matrix, T) - end - end -end - -@testset "symmetric/hermitian for matrices" begin - A = [1 2; 3 4] - @test LinearAlgebra.symmetric(A) === Symmetric(A) - @test LinearAlgebra.symmetric(A, :L) === Symmetric(A, :L) - @test LinearAlgebra.hermitian(A) === Hermitian(A) - @test LinearAlgebra.hermitian(A, :L) === Hermitian(A, :L) -end - -@testset "custom axes" begin - SZA = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - for T in (Symmetric, Hermitian) - S = T(SZA) - r = SizedArrays.SOneTo(2) - @test axes(S) === (r,r) - end -end - -@testset "Matrix elements" begin - M = [UpperTriangular([1 2; 3 4]) for i in 1:2, j in 1:2] - for T in (Symmetric, Hermitian) - H = T(M) - A = Array(H) - @test A isa Matrix - @test A == H - A = Array{Matrix{Int}}(H) - @test A isa Matrix{Matrix{Int}} - @test A == H - end -end - -@testset "conj for immutable" begin - S = Symmetric(reshape((1:16)*im, 4, 4)) - @test conj(S) == conj(Array(S)) - H = Hermitian(reshape((1:16)*im, 4, 4)) - @test conj(H) == conj(Array(H)) -end - -@testset "copyto! with aliasing (#39460)" begin - M = Matrix(reshape(1:36, 6, 6)) - @testset for T in (Symmetric, Hermitian), uploA in (:U, :L), uploB in (:U, :L) - A = T(view(M, 1:5, 1:5), uploA) - A2 = copy(A) - B = T(view(M, 2:6, 2:6), uploB) - @test copyto!(B, A) == A2 - - A = view(M, 2:4, 2:4) - B = T(view(M, 1:3, 1:3), uploB) - B2 = copy(B) - @test copyto!(A, B) == B2 - end -end - -@testset "copyto with incompatible sizes" begin - A = zeros(3,3); B = zeros(2,2) - @testset "copyto with incompatible sizes" begin - for T in (Symmetric, Hermitian) - @test_throws BoundsError copyto!(T(B), T(A)) - @test_throws "Cannot set a non-diagonal index" copyto!(T(A), T(B)) - end - end -end - -@testset "getindex with Integers" begin - M = reshape(1:4,2,2) - for ST in (Symmetric, Hermitian) - S = ST(M) - @test_throws "invalid index" S[true, true] - @test S[1,2] == S[Int8(1),UInt16(2)] == S[big(1), Int16(2)] - end -end - -@testset "tr for block matrices" begin - m = [1 2; 3 4] - for b in (m, m * (1 + im)) - M = fill(b, 3, 3) - for ST in (Symmetric, Hermitian) - S = ST(M) - @test tr(S) == sum(diag(S)) - end - end -end - -@testset "setindex! returns the destination" begin - M = rand(2,2) - for T in (Symmetric, Hermitian) - S = T(M) - @test setindex!(S, 0, 2, 2) === S - end -end - -@testset "partly iniitalized matrices" begin - a = Matrix{BigFloat}(undef, 2,2) - a[1] = 1; a[3] = 1; a[4] = 1 - h = Hermitian(a) - s = Symmetric(a) - d = Diagonal([1,1]) - symT = SymTridiagonal([1 1;1 1]) - @test h+d == Array(h) + Array(d) - @test h+symT == Array(h) + Array(symT) - @test s+d == Array(s) + Array(d) - @test s+symT == Array(s) + Array(symT) - @test h-d == Array(h) - Array(d) - @test h-symT == Array(h) - Array(symT) - @test s-d == Array(s) - Array(d) - @test s-symT == Array(s) - Array(symT) - @test d+h == Array(d) + Array(h) - @test symT+h == Array(symT) + Array(h) - @test d+s == Array(d) + Array(s) - @test symT+s == Array(symT) + Array(s) - @test d-h == Array(d) - Array(h) - @test symT-h == Array(symT) - Array(h) - @test d-s == Array(d) - Array(s) - @test symT-s == Array(symT) - Array(s) -end - -@testset "issue #56283" begin - a = 1.0 - D = Diagonal(randn(10)) - H = Hermitian(D*D') - @test a*H == H -end - -@testset "trigonometric functions for Integer matrices" begin - A = diagm(0=>1:4, 1=>1:3, -1=>1:3) - for B in (Symmetric(A), Symmetric(complex.(A))) - SC = @inferred(sincos(B)) - @test SC[1] ≈ sin(B) - @test SC[2] ≈ cos(B) - @test cos(A) ≈ real(exp(im*A)) - @test sin(A) ≈ imag(exp(im*A)) - end -end - -end # module TestSymmetric diff --git a/stdlib/LinearAlgebra/test/symmetriceigen.jl b/stdlib/LinearAlgebra/test/symmetriceigen.jl deleted file mode 100644 index 71087ae4d8d24..0000000000000 --- a/stdlib/LinearAlgebra/test/symmetriceigen.jl +++ /dev/null @@ -1,187 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestSymmetricEigen - -using Test, LinearAlgebra - -@testset "chol-eigen-eigvals" begin - ## Cholesky decomposition based - - # eigenvalue sorting - sf = x->(imag(x),real(x)) - - ## Real valued - A = Float64[1 1 0 0; 1 2 1 0; 0 1 3 1; 0 0 1 4] - H = (A+A')/2 - B = Float64[2 1 4 3; 0 3 1 3; 3 1 0 0; 0 1 3 1] - BH = (B+B')/2 - # PD matrix - BPD = B*B' - # eigen - C = cholesky(BPD) - e,v = eigen(A, C; sortby=sf) - @test A*v ≈ BPD*v*Diagonal(e) - # eigvals - @test eigvals(A, BPD; sortby=sf) ≈ eigvals(A, C; sortby=sf) - - ## Complex valued - A = [1.0+im 1.0+1.0im 0 0; 1.0+1.0im 2.0+3.0im 1.0+1.0im 0; 0 1.0+2.0im 3.0+4.0im 1.0+5.0im; 0 0 1.0+1.0im 4.0+4.0im] - AH = (A+A')/2 - B = [2.0+2.0im 1.0+1.0im 4.0+4.0im 3.0+3.0im; 0 3.0+2.0im 1.0+1.0im 3.0+4.0im; 3.0+3.0im 1.0+4.0im 0 0; 0 1.0+2.0im 3.0+1.0im 1.0+1.0im] - BH = (B+B')/2 - # PD matrix - BPD = B*B' - # eigen - C = cholesky(BPD) - e,v = eigen(A, C; sortby=sf) - @test A*v ≈ BPD*v*Diagonal(e) - # eigvals - @test eigvals(A, BPD; sortby=sf) ≈ eigvals(A, C; sortby=sf) -end - -@testset "issue #49533" begin - # eigenvalue sorting - sf = x->(imag(x),real(x)) - - ## Real valued - A = Float64[1 1 0 0; 1 2 1 0; 0 1 3 1; 0 0 1 4] - B = Matrix(Diagonal(Float64[1:4;])) - # eigen - e0,v0 = eigen(A, B) - e1,v1 = eigen(A, Symmetric(B)) - e2,v2 = eigen(Symmetric(A), B) - e3,v3 = eigen(Symmetric(A), Symmetric(B)) - @test e0 ≈ e1 && v0 ≈ v1 - @test e0 ≈ e2 && v0 ≈ v2 - @test e0 ≈ e3 && v0 ≈ v3 - # eigvals - @test eigvals(A, B) ≈ eigvals(A, Symmetric(B)) - @test eigvals(A, B) ≈ eigvals(Symmetric(A), B) - @test eigvals(A, B) ≈ eigvals(Symmetric(A), Symmetric(B)) - - ## Complex valued - A = [1.0+im 1.0+1.0im 0 0; 1.0+1.0im 2.0+3.0im 1.0+1.0im 0; 0 1.0+2.0im 3.0+4.0im 1.0+5.0im; 0 0 1.0+1.0im 4.0+4.0im] - AH = A'A - B = [2.0+2.0im 1.0+1.0im 4.0+4.0im 3.0+3.0im; 0 3.0+2.0im 1.0+1.0im 3.0+4.0im; 3.0+3.0im 1.0+4.0im 0 0; 0 1.0+2.0im 3.0+1.0im 1.0+1.0im] - BH = B'B - # eigen - e1,v1 = eigen(A, Hermitian(BH)) - @test A*v1 ≈ Hermitian(BH)*v1*Diagonal(e1) - e2,v2 = eigen(Hermitian(AH), B) - @test Hermitian(AH)*v2 ≈ B*v2*Diagonal(e2) - e3,v3 = eigen(Hermitian(AH), Hermitian(BH)) - @test Hermitian(AH)*v3 ≈ Hermitian(BH)*v3*Diagonal(e3) - # eigvals - @test eigvals(A, BH; sortby=sf) ≈ eigvals(A, Hermitian(BH); sortby=sf) - @test eigvals(AH, B; sortby=sf) ≈ eigvals(Hermitian(AH), B; sortby=sf) - @test eigvals(AH, BH; sortby=sf) ≈ eigvals(Hermitian(AH), Hermitian(BH); sortby=sf) -end - -@testset "bk-lu-eigen-eigvals" begin - # Bunchkaufman decomposition based - - # eigenvalue sorting - sf = x->(imag(x),real(x)) - - # Real-valued random matrix - N = 10 - A = randn(N,N) - B = randn(N,N) - BH = (B+B')/2 - # eigen - e0 = eigvals(A,BH; sortby=sf) - e,v = eigen(A,bunchkaufman(Hermitian(BH,:L)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,bunchkaufman(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,lu(Hermitian(BH,:L)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,lu(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - # eigvals - e0 = eigvals(A,BH; sortby=sf) - el = eigvals(A,bunchkaufman(Hermitian(BH,:L)); sortby=sf) - eu = eigvals(A,bunchkaufman(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ el - @test e0 ≈ eu - el = eigvals(A,lu(Hermitian(BH,:L)); sortby=sf) - eu = eigvals(A,lu(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ el - @test e0 ≈ eu - - # Complex-valued random matrix - N = 10 - A = complex.(randn(N,N),randn(N,N)) - B = complex.(randn(N,N),randn(N,N)) - BH = (B+B')/2 - # eigen - e0 = eigvals(A,BH; sortby=sf) - e,v = eigen(A,bunchkaufman(Hermitian(BH,:L)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,bunchkaufman(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,lu(Hermitian(BH,:L)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - e,v = eigen(A,lu(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ e - @test A*v ≈ BH*v*Diagonal(e) - # eigvals - e0 = eigvals(A,BH; sortby=sf) - el = eigvals(A,bunchkaufman(Hermitian(BH,:L)); sortby=sf) - eu = eigvals(A,bunchkaufman(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ el - @test e0 ≈ eu - el = eigvals(A,lu(Hermitian(BH,:L)); sortby=sf) - eu = eigvals(A,lu(Hermitian(BH,:U)); sortby=sf) - @test e0 ≈ el - @test e0 ≈ eu -end - -@testset "Hermitian tridiagonal eigen with Complex{Int} elements (#52801)" begin - dv, ev = fill(complex(2), 4), fill(3-4im, 3) - HT = Hermitian(Tridiagonal(ev, dv, ev)) - λ, V = eigen(HT) - @test HT * V ≈ V * Diagonal(λ) - HT = Hermitian(Tridiagonal(ComplexF16.(ev), ComplexF16.(dv), ComplexF16.(ev))) - F = eigen(HT) - @test F isa Eigen{ComplexF16, Float16, Matrix{ComplexF16}, Vector{Float16}} - λ, V = F - @test HT * V ≈ V * Diagonal(λ) -end - -@testset "Float16" begin - A = rand(Float16, 3, 3) - A = Symmetric(A*A') - B = eigen(A) - B32 = eigen(Symmetric(Float32.(A))) - @test B isa Eigen{Float16, Float16, Matrix{Float16}, Vector{Float16}} - @test B.values ≈ B32.values - @test B.vectors ≈ B32.vectors - C = randn(ComplexF16, 3, 3) - C = Hermitian(C*C') - D = eigen(C) - D32 = eigen(Hermitian(ComplexF32.(C))) - @test D isa Eigen{ComplexF16, Float16, Matrix{ComplexF16}, Vector{Float16}} - @test D.values ≈ D32.values - @test D.vectors ≈ D32.vectors - - # ensure that different algorithms dispatch correctly - λ, V = eigen(C, LinearAlgebra.QRIteration()) - @test λ isa Vector{Float16} - @test C * V ≈ V * Diagonal(λ) -end - -@testset "complex Symmetric" begin - S = Symmetric(rand(ComplexF64,2,2)) - λ, v = eigen(S) - @test S * v ≈ v * Diagonal(λ) -end - -end # module TestSymmetricEigen diff --git a/stdlib/LinearAlgebra/test/testgroups b/stdlib/LinearAlgebra/test/testgroups deleted file mode 100644 index 0f2f4f4af8708..0000000000000 --- a/stdlib/LinearAlgebra/test/testgroups +++ /dev/null @@ -1,30 +0,0 @@ -triangular -addmul -bidiag -matmul -dense -symmetric -diagonal -special -qr -cholesky -blas -lu -uniformscaling -structuredbroadcast -hessenberg -svd -eigen -tridiag -lapack -lq -adjtrans -generic -schur -bunchkaufman -givens -pinv -factorization -abstractq -ldlt -symmetriceigen diff --git a/stdlib/LinearAlgebra/test/testutils.jl b/stdlib/LinearAlgebra/test/testutils.jl deleted file mode 100644 index 33eff29765c70..0000000000000 --- a/stdlib/LinearAlgebra/test/testutils.jl +++ /dev/null @@ -1,27 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Test approximate equality of vectors or columns of matrices modulo floating -# point roundoff and phase (sign) differences. -# -# This function is designed to test for equality between vectors of floating point -# numbers when the vectors are defined only up to a global phase or sign, such as -# normalized eigenvectors or singular vectors. The global phase is usually -# defined consistently, but may occasionally change due to small differences in -# floating point rounding noise or rounding modes, or through the use of -# different conventions in different algorithms. As a result, most tests checking -# such vectors have to detect and discard such overall phase differences. -# -# Inputs: -# a, b:: StridedVecOrMat to be compared -# err :: Default: m^3*(eps(S)+eps(T)), where m is the number of rows -# -# Raises an error if any columnwise vector norm exceeds err. Otherwise, returns -# nothing. -function test_approx_eq_modphase(a::StridedVecOrMat{S}, b::StridedVecOrMat{T}, - err = length(axes(a,1))^3*(eps(S)+eps(T))) where {S<:Real,T<:Real} - @test axes(a,1) == axes(b,1) && axes(a,2) == axes(b,2) - for i in axes(a,2) - v1, v2 = a[:, i], b[:, i] - @test min(abs(norm(v1-v2)),abs(norm(v1+v2))) ≈ 0.0 atol=err - end -end diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl deleted file mode 100644 index e69c86cc93663..0000000000000 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ /dev/null @@ -1,1419 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestTriangular - -debug = false -using Test, LinearAlgebra, Random -using LinearAlgebra: BlasFloat, errorbounds, full!, transpose!, - UnitUpperTriangular, UnitLowerTriangular, - mul!, rdiv!, rmul!, lmul!, BandIndex - -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -using .Main.FillArrays - -debug && println("Triangular matrices") - -n = 9 -Random.seed!(123) - -debug && println("Test basic type functionality") -@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] - -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}} - - debug && println("elty1: $elty1, A1: $t1") - - # 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) - @testset for (t2, uplo2) in ((UpperTriangular, :U), - (UnitUpperTriangular, :U), - (LowerTriangular, :L), - (UnitLowerTriangular, :L)) - - debug && println("elty1: $elty1, A1: $t1, elty2: $elty2, A2: $t2") - - 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}) - B = convert(Matrix{eltyB}, (elty1 <: Complex ? real(A1) : A1)*fill(1., n, n)) - - debug && println("elty1: $elty1, A1: $t1, B: $eltyB") - - 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 - -@testset "non-strided arithmetic" begin - for (T,T1) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) - U = T(reshape(1:16, 4, 4)) - M = Matrix(U) - @test -U == -M - U1 = T1(reshape(1:16, 4, 4)) - M1 = Matrix(U1) - @test -U1 == -M1 - for op in (+, -) - for (A, MA) in ((U, M), (U1, M1)), (B, MB) in ((U, M), (U1, M1)) - @test op(A, B) == op(MA, MB) - end - end - @test imag(U) == zero(U) - 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 quasi-triangular blockwise" begin - @testset for T in (Float32, Float64, ComplexF32, ComplexF64) - A = schur(rand(T, 100, 100)^2).T - @test LinearAlgebra.sqrt_quasitriu(A; blockwidth=16)^2 ≈ A - end - n = 256 - A = rand(ComplexF64, n, n) - U = schur(A).T - Ubig = Complex{BigFloat}.(U) - @test LinearAlgebra.sqrt_quasitriu(U; blockwidth=64) ≈ LinearAlgebra.sqrt_quasitriu(Ubig; blockwidth=64) -end - -@testset "sylvester quasi-triangular blockwise" begin - @testset for T in (Float32, Float64, ComplexF32, ComplexF64), m in (15, 40), n in (15, 45) - A = schur(rand(T, m, m)).T - B = schur(rand(T, n, n)).T - C = randn(T, m, n) - Ccopy = copy(C) - X = LinearAlgebra._sylvester_quasitriu!(A, B, C; blockwidth=16) - @test X === C - @test A * X + X * B ≈ -Ccopy - - @testset "test raise=false does not break recursion" begin - Az = zero(A) - Bz = zero(B) - C2 = copy(Ccopy) - @test_throws LAPACKException LinearAlgebra._sylvester_quasitriu!(Az, Bz, C2; blockwidth=16) - m == n || @test any(C2 .== Ccopy) # recursion broken - C3 = copy(Ccopy) - X3 = LinearAlgebra._sylvester_quasitriu!(Az, Bz, C3; blockwidth=16, raise=false) - @test !any(X3 .== Ccopy) # recursion not broken - end - end -end - -@testset "check matrix logarithm type-inferable" for elty in (Float32,Float64,ComplexF32,ComplexF64) - A = UpperTriangular(exp(triu(randn(elty, n, n)))) - @inferred Union{typeof(A),typeof(complex(A))} log(A) - @test exp(Matrix(log(A))) ≈ A - if elty <: Real - @test typeof(log(A)) <: UpperTriangular{elty} - @test typeof(log(complex(A))) <: UpperTriangular{complex(elty)} - @test isreal(log(complex(A))) - @test log(complex(A)) ≈ log(A) - end - - Au = UnitUpperTriangular(exp(triu(randn(elty, n, n), 1))) - @inferred Union{typeof(A),typeof(complex(A))} log(Au) - @test exp(Matrix(log(Au))) ≈ Au - if elty <: Real - @test typeof(log(Au)) <: UpperTriangular{elty} - @test typeof(log(complex(Au))) <: UpperTriangular{complex(elty)} - @test isreal(log(complex(Au))) - @test log(complex(Au)) ≈ log(Au) - end -end - -Areal = randn(n, n)/2 -Aimg = randn(n, n)/2 -A2real = randn(n, n)/2 -A2img = randn(n, n)/2 - -for eltya in (Float32, Float64, ComplexF32, ComplexF64, BigFloat, Int) - A = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(Areal, Aimg) : Areal) - # a2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - εa = eps(abs(float(one(eltya)))) - - for eltyb in (Float32, Float64, ComplexF32, ComplexF64) - εb = eps(abs(float(one(eltyb)))) - ε = max(εa,εb) - - debug && println("\ntype of A: ", eltya, " type of b: ", eltyb, "\n") - - debug && println("Solve upper triangular system") - Atri = UpperTriangular(lu(A).U) |> t -> eltya <: Complex && eltyb <: Real ? real(t) : t # Here the triangular matrix can't be too badly conditioned - b = convert(Matrix{eltyb}, Matrix(Atri)*fill(1., n, 2)) - x = Matrix(Atri) \ b - - debug && println("Test error estimates") - if eltya != BigFloat && eltyb != BigFloat - for i = 1:2 - @test norm(x[:,1] .- 1) <= errorbounds(UpperTriangular(A), x, b)[1][i] - end - end - debug && println("Test forward error [JIN 5705] if this is not a BigFloat") - - x = Atri \ b - γ = n*ε/(1 - n*ε) - if eltya != BigFloat - bigA = big.(Atri) - x̂ = fill(1., n, 2) - for i = 1:size(b, 2) - @test norm(x̂[:,i] - x[:,i], Inf)/norm(x̂[:,i], Inf) <= condskeel(bigA, x̂[:,i])*γ/(1 - condskeel(bigA)*γ) - end - end - - debug && println("Test backward error [JIN 5705]") - for i = 1:size(b, 2) - @test norm(abs.(b[:,i] - Atri*x[:,i]), Inf) <= γ * norm(Atri, Inf) * norm(x[:,i], Inf) - end - - debug && println("Solve lower triangular system") - Atri = UpperTriangular(lu(A).U) |> t -> eltya <: Complex && eltyb <: Real ? real(t) : t # Here the triangular matrix can't be too badly conditioned - b = convert(Matrix{eltyb}, Matrix(Atri)*fill(1., n, 2)) - x = Matrix(Atri)\b - - debug && println("Test error estimates") - if eltya != BigFloat && eltyb != BigFloat - for i = 1:2 - @test norm(x[:,1] .- 1) <= errorbounds(UpperTriangular(A), x, b)[1][i] - end - end - - debug && println("Test forward error [JIN 5705] if this is not a BigFloat") - b = (b0 = Atri*fill(1, n, 2); convert(Matrix{eltyb}, eltyb == Int ? trunc.(b0) : b0)) - x = Atri \ b - γ = n*ε/(1 - n*ε) - if eltya != BigFloat - bigA = big.(Atri) - x̂ = fill(1., n, 2) - for i = 1:size(b, 2) - @test norm(x̂[:,i] - x[:,i], Inf)/norm(x̂[:,i], Inf) <= condskeel(bigA, x̂[:,i])*γ/(1 - condskeel(bigA)*γ) - end - end - - debug && println("Test backward error [JIN 5705]") - for i = 1:size(b, 2) - @test norm(abs.(b[:,i] - Atri*x[:,i]), Inf) <= γ * norm(Atri, Inf) * norm(x[:,i], Inf) - end - end -end - -# Issue 10742 and similar -@test istril(UpperTriangular(diagm(0 => [1,2,3,4]))) -@test istriu(LowerTriangular(diagm(0 => [1,2,3,4]))) -@test isdiag(UpperTriangular(diagm(0 => [1,2,3,4]))) -@test isdiag(LowerTriangular(diagm(0 => [1,2,3,4]))) -@test !isdiag(UpperTriangular(rand(4, 4))) -@test !isdiag(LowerTriangular(rand(4, 4))) - -# Test throwing in fallbacks for non BlasFloat/BlasComplex in A_rdiv_Bx! -let n = 5 - A = rand(Float16, n, n) - B = rand(Float16, n-1, n-1) - @test_throws DimensionMismatch rdiv!(A, LowerTriangular(B)) - @test_throws DimensionMismatch rdiv!(A, UpperTriangular(B)) - @test_throws DimensionMismatch rdiv!(A, UnitLowerTriangular(B)) - @test_throws DimensionMismatch rdiv!(A, UnitUpperTriangular(B)) - - @test_throws DimensionMismatch rdiv!(A, adjoint(LowerTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, adjoint(UpperTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, adjoint(UnitLowerTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, adjoint(UnitUpperTriangular(B))) - - @test_throws DimensionMismatch rdiv!(A, transpose(LowerTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, transpose(UpperTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, transpose(UnitLowerTriangular(B))) - @test_throws DimensionMismatch rdiv!(A, transpose(UnitUpperTriangular(B))) -end - -@test isdiag(LowerTriangular(UpperTriangular(randn(3,3)))) -@test isdiag(UpperTriangular(LowerTriangular(randn(3,3)))) - -# Issue 16196 -@test UpperTriangular(Matrix(1.0I, 3, 3)) \ view(fill(1., 3), [1,2,3]) == fill(1., 3) - -@testset "reverse" begin - A = randn(5, 5) - for (T, Trev) in ((UpperTriangular, LowerTriangular), - (UnitUpperTriangular, UnitLowerTriangular), - (LowerTriangular, UpperTriangular), - (UnitLowerTriangular, UnitUpperTriangular)) - A = T(randn(5, 5)) - AM = Matrix(A) - @test reverse(A, dims=1) == reverse(AM, dims=1) - @test reverse(A, dims=2) == reverse(AM, dims=2) - @test reverse(A)::Trev == reverse(AM) - end -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 - -@testset "AbstractArray constructor should preserve underlying storage type" begin - # tests corresponding to #34995 - local m = 4 - local T, S = Float32, Float64 - immutablemat = ImmutableArray(randn(T,m,m)) - for TriType in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - trimat = TriType(immutablemat) - @test convert(AbstractArray{S}, trimat).data isa ImmutableArray{S} - @test convert(AbstractMatrix{S}, trimat).data isa ImmutableArray{S} - @test AbstractArray{S}(trimat).data isa ImmutableArray{S} - @test AbstractMatrix{S}(trimat).data isa ImmutableArray{S} - @test convert(AbstractArray{S}, trimat) == trimat - @test convert(AbstractMatrix{S}, trimat) == trimat - end -end - -@testset "inplace mul of appropriate types should preserve triagular structure" begin - for elty1 in (Float64, ComplexF32), elty2 in (Float64, ComplexF32) - T = promote_type(elty1, elty2) - M1 = rand(elty1, 5, 5) - M2 = rand(elty2, 5, 5) - A = UpperTriangular(M1) - A2 = UpperTriangular(M2) - Au = UnitUpperTriangular(M1) - Au2 = UnitUpperTriangular(M2) - B = LowerTriangular(M1) - B2 = LowerTriangular(M2) - Bu = UnitLowerTriangular(M1) - Bu2 = UnitLowerTriangular(M2) - - @test mul!(similar(A), A, A)::typeof(A) == A*A - @test mul!(similar(A, T), A, A2) ≈ A*A2 - @test mul!(similar(A, T), A2, A) ≈ A2*A - @test mul!(typeof(similar(A, T))(A), A, A2, 2.0, 3.0) ≈ 2.0*A*A2 + 3.0*A - @test mul!(typeof(similar(A2, T))(A2), A2, A, 2.0, 3.0) ≈ 2.0*A2*A + 3.0*A2 - - @test mul!(similar(A), A, Au)::typeof(A) == A*Au - @test mul!(similar(A), Au, A)::typeof(A) == Au*A - @test mul!(similar(Au), Au, Au)::typeof(Au) == Au*Au - @test mul!(similar(A, T), A, Au2) ≈ A*Au2 - @test mul!(similar(A, T), Au2, A) ≈ Au2*A - @test mul!(similar(Au2), Au2, Au2) == Au2*Au2 - - @test mul!(similar(B), B, B)::typeof(B) == B*B - @test mul!(similar(B, T), B, B2) ≈ B*B2 - @test mul!(similar(B, T), B2, B) ≈ B2*B - @test mul!(typeof(similar(B, T))(B), B, B2, 2.0, 3.0) ≈ 2.0*B*B2 + 3.0*B - @test mul!(typeof(similar(B2, T))(B2), B2, B, 2.0, 3.0) ≈ 2.0*B2*B + 3.0*B2 - - @test mul!(similar(B), B, Bu)::typeof(B) == B*Bu - @test mul!(similar(B), Bu, B)::typeof(B) == Bu*B - @test mul!(similar(Bu), Bu, Bu)::typeof(Bu) == Bu*Bu - @test mul!(similar(B, T), B, Bu2) ≈ B*Bu2 - @test mul!(similar(B, T), Bu2, B) ≈ Bu2*B - end -end - -@testset "indexing partly initialized matrices" begin - M = Matrix{BigFloat}(undef, 2, 2) - U = UpperTriangular(M) - @test iszero(U[2,1]) - L = LowerTriangular(M) - @test iszero(L[1,2]) -end - -@testset "special printing of Lower/UpperTriangular" begin - @test occursin(r"3×3 (LinearAlgebra\.)?LowerTriangular{Int64, Matrix{Int64}}:\n 2 ⋅ ⋅\n 2 2 ⋅\n 2 2 2", - sprint(show, MIME"text/plain"(), LowerTriangular(2ones(Int64,3,3)))) - @test occursin(r"3×3 (LinearAlgebra\.)?UnitLowerTriangular{Int64, Matrix{Int64}}:\n 1 ⋅ ⋅\n 2 1 ⋅\n 2 2 1", - sprint(show, MIME"text/plain"(), UnitLowerTriangular(2ones(Int64,3,3)))) - @test occursin(r"3×3 (LinearAlgebra\.)?UpperTriangular{Int64, Matrix{Int64}}:\n 2 2 2\n ⋅ 2 2\n ⋅ ⋅ 2", - sprint(show, MIME"text/plain"(), UpperTriangular(2ones(Int64,3,3)))) - @test occursin(r"3×3 (LinearAlgebra\.)?UnitUpperTriangular{Int64, Matrix{Int64}}:\n 1 2 2\n ⋅ 1 2\n ⋅ ⋅ 1", - sprint(show, MIME"text/plain"(), UnitUpperTriangular(2ones(Int64,3,3)))) - - # don't access non-structural elements while displaying - M = Matrix{BigFloat}(undef, 2, 2) - @test sprint(show, UpperTriangular(M)) == "BigFloat[#undef #undef; 0.0 #undef]" - @test sprint(show, LowerTriangular(M)) == "BigFloat[#undef 0.0; #undef #undef]" -end - -@testset "adjoint/transpose triangular/vector multiplication" begin - for elty in (Float64, ComplexF64), trity in (UpperTriangular, LowerTriangular) - A1 = trity(rand(elty, 1, 1)) - b1 = rand(elty, 1) - A4 = trity(rand(elty, 4, 4)) - b4 = rand(elty, 4) - @test A1 * b1' ≈ Matrix(A1) * b1' - @test_throws DimensionMismatch A4 * b4' - @test A1 * transpose(b1) ≈ Matrix(A1) * transpose(b1) - @test_throws DimensionMismatch A4 * transpose(b4) - @test A1' * b1' ≈ Matrix(A1') * b1' - @test_throws DimensionMismatch A4' * b4' - @test A1' * transpose(b1) ≈ Matrix(A1') * transpose(b1) - @test_throws DimensionMismatch A4' * transpose(b4) - @test transpose(A1) * transpose(b1) ≈ Matrix(transpose(A1)) * transpose(b1) - @test_throws DimensionMismatch transpose(A4) * transpose(b4) - @test transpose(A1) * b1' ≈ Matrix(transpose(A1)) * b1' - @test_throws DimensionMismatch transpose(A4) * b4' - @test b1' * transpose(A1) ≈ b1' * Matrix(transpose(A1)) - @test b4' * transpose(A4) ≈ b4' * Matrix(transpose(A4)) - @test transpose(b1) * A1' ≈ transpose(b1) * Matrix(A1') - @test transpose(b4) * A4' ≈ transpose(b4) * Matrix(A4') - end -end - -@testset "Error condition for powm" begin - A = UpperTriangular(rand(ComplexF64, 10, 10)) - @test_throws ArgumentError LinearAlgebra.powm!(A, 2.2) - A = LowerTriangular(rand(ComplexF64, 10, 10)) - At = copy(transpose(A)) - p = rand() - @test LinearAlgebra.powm(A, p) == transpose(LinearAlgebra.powm!(At, p)) - @test_throws ArgumentError LinearAlgebra.powm(A, 2.2) -end - -# Issue 35058 -let A = [0.9999999999999998 4.649058915617843e-16 -1.3149405273715513e-16 9.9959579317056e-17; -8.326672684688674e-16 1.0000000000000004 2.9280733590254494e-16 -2.9993900031619594e-16; 9.43689570931383e-16 -1.339206523454095e-15 1.0000000000000007 -8.550505126287743e-16; -6.245004513516506e-16 -2.0122792321330962e-16 1.183061278035052e-16 1.0000000000000002], - B = [0.09648289218436859 0.023497875751503007 0.0 0.0; 0.023497875751503007 0.045787575150300804 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0] - @test sqrt(A*B*A')^2 ≈ A*B*A' -end - -@testset "one and oneunit for triangular" begin - m = rand(4,4) - function test_one_oneunit_triangular(a) - b = Matrix(a) - @test (@inferred a^1) == b^1 - @test (@inferred a^-1) ≈ b^-1 - @test one(a) == one(b) - @test one(a)*a == a - @test a*one(a) == a - @test oneunit(a) == oneunit(b) - @test oneunit(a) isa typeof(a) - end - for T in [UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular] - a = T(m) - test_one_oneunit_triangular(a) - end - # more complicated examples - b = UpperTriangular(LowerTriangular(m)) - test_one_oneunit_triangular(b) - c = UpperTriangular(Diagonal(rand(2))) - test_one_oneunit_triangular(c) -end - -@testset "LowerTriangular(Diagonal(...)) and friends (issue #28869)" begin - for elty in (Float32, Float64, BigFloat, ComplexF32, ComplexF64, Complex{BigFloat}, Int) - V = elty ≡ Int ? rand(1:10, 5) : elty.(randn(5)) - D = Diagonal(V) - for dty in (UpperTriangular, LowerTriangular) - A = dty(D) - @test A * A' == D * D' - end - end -end - -@testset "tril!/triu! for non-bitstype matrices" begin - @testset "numeric" begin - M = Matrix{BigFloat}(undef, 3, 3) - tril!(M) - L = LowerTriangular(ones(3,3)) - copytrito!(M, L, 'L') - @test M == L - - M = Matrix{BigFloat}(undef, 3, 3) - triu!(M) - U = UpperTriangular(ones(3,3)) - copytrito!(M, U, 'U') - @test M == U - end - @testset "array elements" begin - M = fill(ones(2,2), 4, 4) - tril!(M) - L = LowerTriangular(fill(fill(2,2,2),4,4)) - copytrito!(M, L, 'L') - @test M == L - - M = fill(ones(2,2), 4, 4) - triu!(M) - U = UpperTriangular(fill(fill(2,2,2),4,4)) - copytrito!(M, U, 'U') - @test M == U - end -end - -@testset "avoid matmul ambiguities with ::MyMatrix * ::AbstractMatrix" begin - A = [i+j for i in 1:2, j in 1:2] - S = SizedArrays.SizedArray{(2,2)}(A) - U = UpperTriangular(ones(2,2)) - @test S * U == A * U - @test U * S == U * A - C1, C2 = zeros(2,2), zeros(2,2) - @test mul!(C1, S, U) == mul!(C2, A, U) - @test mul!(C1, S, U, 1, 2) == mul!(C2, A, U, 1 ,2) - @test mul!(C1, U, S) == mul!(C2, U, A) - @test mul!(C1, U, S, 1, 2) == mul!(C2, U, A, 1 ,2) - - v = [i for i in 1:2] - sv = SizedArrays.SizedArray{(2,)}(v) - @test U * sv == U * v - C1, C2 = zeros(2), zeros(2) - @test mul!(C1, U, sv) == mul!(C2, U, v) - @test mul!(C1, U, sv, 1, 2) == mul!(C2, U, v, 1 ,2) -end - -@testset "custom axes" begin - SZA = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - S = T(SZA) - r = SizedArrays.SOneTo(2) - @test axes(S) === (r,r) - end -end - -@testset "immutable and non-strided parent" begin - F = FillArrays.Fill(2, (4,4)) - for UT in (UnitUpperTriangular, UnitLowerTriangular) - U = UT(F) - @test -U == -Array(U) - end - - F = FillArrays.Fill(3im, (4,4)) - for U in (UnitUpperTriangular(F), UnitLowerTriangular(F)) - @test imag(F) == imag(collect(F)) - end - - @testset "copyto!" begin - for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - @test Matrix(T(F)) == T(F) - end - @test copyto!(zeros(eltype(F), length(F)), UpperTriangular(F)) == vec(UpperTriangular(F)) - end -end - -@testset "error paths" begin - A = zeros(1,1); B = zeros(2,2) - @testset "inplace mul scaling with incompatible sizes" begin - for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - @test_throws DimensionMismatch mul!(T(A), T(B), 3) - @test_throws DimensionMismatch mul!(T(A), 3, T(B)) - end - end - @testset "copyto with incompatible sizes" begin - for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - @test_throws BoundsError copyto!(T(A), T(B)) - end - end -end - -@testset "uppertriangular/lowertriangular" begin - M = rand(2,2) - @test LinearAlgebra.uppertriangular(M) === UpperTriangular(M) - @test LinearAlgebra.lowertriangular(M) === LowerTriangular(M) - @test LinearAlgebra.uppertriangular(UnitUpperTriangular(M)) === UnitUpperTriangular(M) - @test LinearAlgebra.lowertriangular(UnitLowerTriangular(M)) === UnitLowerTriangular(M) -end - -@testset "arithmetic with partly uninitialized matrices" begin - @testset "$(typeof(A))" for A in (Matrix{BigFloat}(undef,2,2), Matrix{Complex{BigFloat}}(undef,2,2)') - A[2,1] = eltype(A) <: Complex ? 4 + 3im : 4 - B = Matrix{eltype(A)}(undef, size(A)) - for MT in (LowerTriangular, UnitLowerTriangular) - if MT == LowerTriangular - A[1,1] = A[2,2] = eltype(A) <: Complex ? 4 + 3im : 4 - end - L = MT(A) - B .= 0 - copyto!(B, L) - @test copy(L) == B - @test L * 2 == 2 * L == 2B - @test L/2 == B/2 - @test 2\L == 2\B - @test real(L) == real(B) - @test imag(L) == imag(B) - if MT == LowerTriangular - @test isa(kron(L,L), MT) - end - @test kron(L,L) == kron(B,B) - @test transpose!(MT(copy(A))) == transpose(L) broken=!(A isa Matrix) - @test adjoint!(MT(copy(A))) == adjoint(L) broken=!(A isa Matrix) - end - end - - @testset "$(typeof(A))" for A in (Matrix{BigFloat}(undef,2,2), Matrix{Complex{BigFloat}}(undef,2,2)') - A[1,2] = eltype(A) <: Complex ? 4 + 3im : 4 - B = Matrix{eltype(A)}(undef, size(A)) - for MT in (UpperTriangular, UnitUpperTriangular) - if MT == UpperTriangular - A[1,1] = A[2,2] = eltype(A) <: Complex ? 4 + 3im : 4 - end - U = MT(A) - B .= 0 - copyto!(B, U) - @test copy(U) == B - @test U * 2 == 2 * U == 2B - @test U/2 == B/2 - @test 2\U == 2\B - @test real(U) == real(B) - @test imag(U) == imag(B) - if MT == UpperTriangular - @test isa(kron(U,U), MT) - end - @test kron(U,U) == kron(B,B) - @test transpose!(MT(copy(A))) == transpose(U) broken=!(A isa Matrix) - @test adjoint!(MT(copy(A))) == adjoint(U) broken=!(A isa Matrix) - end - end -end - -@testset "kron with triangular matrices of matrices" begin - for T in (UpperTriangular, LowerTriangular) - t = T(fill(ones(2,2), 2, 2)) - m = Matrix(t) - @test isa(kron(t,t), T) - @test kron(t, t) ≈ kron(m, m) - end -end - -@testset "kron with triangular matrices of mixed eltypes" begin - for T in (UpperTriangular, LowerTriangular) - U = T(Matrix{Union{Missing,Int}}(fill(2, 2, 2))) - U[1, 1] = missing - @test kron(U, U)[2, 3] == 0 - @test kron(U, U)[3, 2] == 0 - end -end - -@testset "copyto! tests" begin - @testset "copyto! with aliasing (#39460)" begin - M = Matrix(reshape(1:36, 6, 6)) - @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - A = T(view(M, 1:5, 1:5)) - A2 = copy(A) - B = T(view(M, 2:6, 2:6)) - @test copyto!(B, A) == A2 - end - end - - @testset "copyto! with different matrix types" begin - M1 = Matrix(reshape(1:36, 6, 6)) - M2 = similar(M1) - # these copies always work - @testset for (Tdest, Tsrc) in ( - (UpperTriangular, UnitUpperTriangular), - (UpperTriangular, UpperTriangular), - (LowerTriangular, UnitLowerTriangular), - (LowerTriangular, LowerTriangular), - (UnitUpperTriangular, UnitUpperTriangular), - (UnitLowerTriangular, UnitLowerTriangular) - ) - - M2 .= 0 - copyto!(Tdest(M2), Tsrc(M1)) - @test Tdest(M2) == Tsrc(M1) - end - # these copies only work if the source has a unit diagonal - M3 = copy(M1) - M3[diagind(M3)] .= 1 - @testset for (Tdest, Tsrc) in ( - (UnitUpperTriangular, UpperTriangular), - (UnitLowerTriangular, LowerTriangular), - ) - - M2 .= 0 - copyto!(Tdest(M2), Tsrc(M3)) - @test Tdest(M2) == Tsrc(M3) - @test_throws ArgumentError copyto!(Tdest(M2), Tsrc(M1)) - end - # these copies work even when the parent of the source isn't initialized along the diagonal - @testset for (T, TU) in ((UpperTriangular, UnitUpperTriangular), - (LowerTriangular, UnitLowerTriangular)) - M1 = Matrix{BigFloat}(undef, 3, 3) - M2 = similar(M1) - if TU == UnitUpperTriangular - M1[1,2] = M1[1,3] = M1[2,3] = 2 - else - M1[2,1] = M1[3,1] = M1[3,2] = 2 - end - for TD in (T, TU) - M2 .= 0 - copyto!(T(M2), TU(M1)) - @test T(M2) == TU(M1) - end - end - end - - @testset "copyto! with different sizes" begin - Ap = zeros(3,3) - Bp = rand(2,2) - @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - A = T(Ap) - B = T(Bp) - @test_throws ArgumentError copyto!(A, B) - end - @testset "error message" begin - A = UpperTriangular(Ap) - B = UpperTriangular(Bp) - @test_throws "cannot set index in the lower triangular part" copyto!(A, B) - - A = LowerTriangular(Ap) - B = LowerTriangular(Bp) - @test_throws "cannot set index in the upper triangular part" copyto!(A, B) - end - end -end - -@testset "getindex with Integers" begin - M = reshape(1:4,2,2) - for Ttype in (UpperTriangular, UnitUpperTriangular) - T = Ttype(M) - @test_throws "invalid index" T[2, true] - @test T[1,2] == T[Int8(1),UInt16(2)] == T[big(1), Int16(2)] - end - for Ttype in (LowerTriangular, UnitLowerTriangular) - T = Ttype(M) - @test_throws "invalid index" T[true, 2] - @test T[2,1] == T[Int8(2),UInt16(1)] == T[big(2), Int16(1)] - end -end - -@testset "type-stable eigvecs" begin - D = Float64[1 0; 0 2] - V = @inferred eigvecs(UpperTriangular(D)) - @test V == Diagonal([1, 1]) -end - -@testset "preserve structure in scaling by NaN" begin - M = rand(Int8,2,2) - for (Ts, TD) in (((UpperTriangular, UnitUpperTriangular), UpperTriangular), - ((LowerTriangular, UnitLowerTriangular), LowerTriangular)) - for T in Ts - U = T(M) - for V in (U * NaN, NaN * U, U / NaN, NaN \ U) - @test V isa TD{Float64, Matrix{Float64}} - @test all(isnan, diag(V)) - end - end - end -end - -@testset "eigvecs for AbstractTriangular" begin - S = SizedArrays.SizedArray{(3,3)}(reshape(1:9,3,3)) - for T in (UpperTriangular, UnitUpperTriangular, - LowerTriangular, UnitLowerTriangular) - U = T(S) - V = eigvecs(U) - λ = eigvals(U) - @test U * V ≈ V * Diagonal(λ) - - MU = MyTriangular(U) - V = eigvecs(U) - λ = eigvals(U) - @test MU * V ≈ V * Diagonal(λ) - end -end - -@testset "(l/r)mul! and (l/r)div! for generic triangular" begin - @testset for T in (UpperTriangular, LowerTriangular, UnitUpperTriangular, UnitLowerTriangular) - M = MyTriangular(T(rand(4,4))) - A = rand(4,4) - Ac = similar(A) - @testset "lmul!" begin - Ac .= A - lmul!(M, Ac) - @test Ac ≈ M * A - end - @testset "rmul!" begin - Ac .= A - rmul!(Ac, M) - @test Ac ≈ A * M - end - @testset "ldiv!" begin - Ac .= A - ldiv!(M, Ac) - @test Ac ≈ M \ A - end - @testset "rdiv!" begin - Ac .= A - rdiv!(Ac, M) - @test Ac ≈ A / M - end - end -end - -@testset "istriu/istril forwards to parent" begin - @testset "$(nameof(typeof(M)))" for M in [Tridiagonal(rand(n-1), rand(n), rand(n-1)), - Tridiagonal(zeros(n-1), zeros(n), zeros(n-1)), - Diagonal(randn(n)), - Diagonal(zeros(n)), - ] - @testset for TriT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) - U = TriT(M) - A = Array(U) - for k in -n:n - @test istriu(U, k) == istriu(A, k) - @test istril(U, k) == istril(A, k) - end - end - end - z = zeros(n,n) - @testset for TriT in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular) - P = Matrix{BigFloat}(undef, n, n) - copytrito!(P, z, TriT <: Union{UpperTriangular, UnitUpperTriangular} ? 'U' : 'L') - U = TriT(P) - A = Array(U) - @testset for k in -n:n - @test istriu(U, k) == istriu(A, k) - @test istril(U, k) == istril(A, k) - end - end - - @testset "Union eltype" begin - M = Matrix{Union{Int,Missing}}(missing,2,2) - U = triu(M) - @test iszero(U[2,1]) - U = tril(M) - @test iszero(U[1,2]) - end -end - -@testset "indexing with a BandIndex" begin - # these tests should succeed even if the linear index along - # the band isn't a constant, or type-inferred at all - M = rand(Int,2,2) - f(A,j, v::Val{n}) where {n} = Val(A[BandIndex(n,j)]) - function common_tests(M, ind) - j = ind[] - @test @inferred(f(UpperTriangular(M), j, Val(-1))) == Val(0) - @test @inferred(f(UnitUpperTriangular(M), j, Val(-1))) == Val(0) - @test @inferred(f(UnitUpperTriangular(M), j, Val(0))) == Val(1) - @test @inferred(f(LowerTriangular(M), j, Val(1))) == Val(0) - @test @inferred(f(UnitLowerTriangular(M), j, Val(1))) == Val(0) - @test @inferred(f(UnitLowerTriangular(M), j, Val(0))) == Val(1) - end - common_tests(M, Any[1]) - - M = Diagonal([1,2]) - common_tests(M, Any[1]) - # extra tests for banded structure of the parent - for T in (UpperTriangular, UnitUpperTriangular) - @test @inferred(f(T(M), 1, Val(1))) == Val(0) - end - for T in (LowerTriangular, UnitLowerTriangular) - @test @inferred(f(T(M), 1, Val(-1))) == Val(0) - end - - M = Tridiagonal([1,2], [1,2,3], [1,2]) - common_tests(M, Any[1]) - for T in (UpperTriangular, UnitUpperTriangular) - @test @inferred(f(T(M), 1, Val(2))) == Val(0) - end - for T in (LowerTriangular, UnitLowerTriangular) - @test @inferred(f(T(M), 1, Val(-2))) == Val(0) - end -end - -@testset "indexing uses diagzero" begin - @testset "block matrix" begin - M = reshape([zeros(2,2), zeros(4,2), zeros(2,3), zeros(4,3)],2,2) - U = UpperTriangular(M) - @test [size(x) for x in U] == [size(x) for x in M] - end - @testset "Union eltype" begin - M = Matrix{Union{Int,Missing}}(missing,4,4) - U = UpperTriangular(M) - @test iszero(U[3,1]) - end -end - -@testset "addition/subtraction of mixed triangular" begin - for A in (Hermitian(rand(4, 4)), Diagonal(rand(5))) - for T in (UpperTriangular, LowerTriangular, - UnitUpperTriangular, UnitLowerTriangular) - B = T(A) - M = Matrix(B) - R = B - B' - if A isa Diagonal - @test R isa Diagonal - end - @test R == M - M' - R = B + B' - if A isa Diagonal - @test R isa Diagonal - end - @test R == M + M' - C = MyTriangular(B) - @test C - C' == M - M' - @test C + C' == M + M' - end - end - @testset "unfilled parent" begin - @testset for T in (UpperTriangular, LowerTriangular, - UnitUpperTriangular, UnitLowerTriangular) - F = Matrix{BigFloat}(undef, 2, 2) - B = T(F) - isupper = B isa Union{UpperTriangular, UnitUpperTriangular} - B[1+!isupper, 1+isupper] = 2 - if !(B isa Union{UnitUpperTriangular, UnitLowerTriangular}) - B[1,1] = B[2,2] = 3 - end - M = Matrix(B) - @test B - B' == M - M' - @test B + B' == M + M' - @test B - copy(B') == M - M' - @test B + copy(B') == M + M' - C = MyTriangular(B) - @test C - C' == M - M' - @test C + C' == M + M' - end - end -end - -@testset "log_quasitriu with internal scaling s=0 (issue #54833)" begin - M = [0.9949357359852791 -0.015567763143324862 -0.09091193493947397 -0.03994428739762443 0.07338356301650806; - 0.011813655598647289 0.9968988574699793 -0.06204555000202496 0.04694097614450692 0.09028834462782365; - 0.092737943594701 0.059546719185135925 0.9935850721633324 0.025348893985651405 -0.018530261590167685; - 0.0369187299165628 -0.04903571106913449 -0.025962938675946543 0.9977767446862031 0.12901494726320517; - 0.0 0.0 0.0 0.0 1.0] - - @test exp(log(M)) ≈ M -end - -@testset "copytrito!" begin - for T in (UpperTriangular, LowerTriangular) - M = Matrix{BigFloat}(undef, 2, 2) - M[1,1] = M[2,2] = 3 - U = T(M) - isupper = U isa UpperTriangular - M[1+!isupper, 1+isupper] = 4 - uplo, loup = U isa UpperTriangular ? ('U', 'L') : ('L', 'U' ) - @test copytrito!(similar(U), U, uplo) == U - @test copytrito!(zero(M), U, uplo) == U - @test copytrito!(similar(U), Array(U), uplo) == U - @test copytrito!(zero(U), U, loup) == Diagonal(U) - @test copytrito!(similar(U), MyTriangular(U), uplo) == U - @test copytrito!(zero(M), MyTriangular(U), uplo) == U - Ubig = T(similar(M, (3,3))) - copytrito!(Ubig, U, uplo) - @test Ubig[axes(U)...] == U - end -end - -end # module TestTriangular diff --git a/stdlib/LinearAlgebra/test/trickyarithmetic.jl b/stdlib/LinearAlgebra/test/trickyarithmetic.jl deleted file mode 100644 index ad04ac89c2761..0000000000000 --- a/stdlib/LinearAlgebra/test/trickyarithmetic.jl +++ /dev/null @@ -1,66 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TrickyArithmetic - struct A - x::Int - end - A(a::A) = a - Base.convert(::Type{A}, i::Int) = A(i) - Base.zero(::Union{A, Type{A}}) = A(0) - Base.one(::Union{A, Type{A}}) = A(1) - Base.isfinite(a::A) = isfinite(a.x) - struct B - x::Int - end - struct C - x::Int - end - Base.isfinite(b::B) = isfinite(b.x) - Base.isfinite(c::C) = isfinite(c.x) - C(a::A) = C(a.x) - Base.zero(::Union{C, Type{C}}) = C(0) - Base.one(::Union{C, Type{C}}) = C(1) - - Base.:(*)(x::Int, a::A) = B(x*a.x) - Base.:(*)(a::A, x::Int) = B(a.x*x) - Base.:(*)(a::Union{A,B}, b::Union{A,B}) = B(a.x*b.x) - Base.:(*)(a::Union{A,B,C}, b::Union{A,B,C}) = C(a.x*b.x) - Base.:(+)(a::Union{A,B,C}, b::Union{A,B,C}) = C(a.x+b.x) - Base.:(-)(a::Union{A,B,C}, b::Union{A,B,C}) = C(a.x-b.x) - - struct D{NT, DT} - n::NT - d::DT - end - D{NT, DT}(d::D{NT, DT}) where {NT, DT} = d # called by oneunit - Base.zero(::Union{D{NT, DT}, Type{D{NT, DT}}}) where {NT, DT} = zero(NT) / one(DT) - Base.one(::Union{D{NT, DT}, Type{D{NT, DT}}}) where {NT, DT} = one(NT) / one(DT) - Base.convert(::Type{D{NT, DT}}, a::Union{A, B, C}) where {NT, DT} = NT(a) / one(DT) - #Base.convert(::Type{D{NT, DT}}, a::D) where {NT, DT} = NT(a.n) / DT(a.d) - - Base.:(*)(a::D, b::D) = (a.n*b.n) / (a.d*b.d) - Base.:(*)(a::D, b::Union{A,B,C}) = (a.n * b) / a.d - Base.:(*)(a::Union{A,B,C}, b::D) = b * a - Base.inv(a::Union{A,B,C}) = A(1) / a - Base.inv(a::D) = a.d / a.n - Base.isfinite(a::D) = isfinite(a.n) && isfinite(a.d) - Base.:(/)(a::Union{A,B,C}, b::Union{A,B,C}) = D(a, b) - Base.:(/)(a::D, b::Union{A,B,C}) = a.n / (a.d*b) - Base.:(/)(a::Union{A,B,C,D}, b::D) = a * inv(b) - Base.:(+)(a::Union{A,B,C}, b::D) = (a*b.d+b.n) / b.d - Base.:(+)(a::D, b::Union{A,B,C}) = b + a - Base.:(+)(a::D, b::D) = (a.n*b.d+a.d*b.n) / (a.d*b.d) - Base.:(-)(a::Union{A,B,C}) = typeof(a)(a.x) - Base.:(-)(a::D) = (-a.n) / a.d - Base.:(-)(a::Union{A,B,C,D}, b::Union{A,B,C,D}) = a + (-b) - - Base.promote_rule(::Type{A}, ::Type{B}) = B - Base.promote_rule(::Type{B}, ::Type{A}) = B - Base.promote_rule(::Type{A}, ::Type{C}) = C - Base.promote_rule(::Type{C}, ::Type{A}) = C - Base.promote_rule(::Type{B}, ::Type{C}) = C - Base.promote_rule(::Type{C}, ::Type{B}) = C - Base.promote_rule(::Type{D{NT,DT}}, T::Type{<:Union{A,B,C}}) where {NT,DT} = D{promote_type(NT,T),DT} - Base.promote_rule(T::Type{<:Union{A,B,C}}, ::Type{D{NT,DT}}) where {NT,DT} = D{promote_type(NT,T),DT} - Base.promote_rule(::Type{D{NS,DS}}, ::Type{D{NT,DT}}) where {NS,DS,NT,DT} = D{promote_type(NS,NT),promote_type(DS,DT)} -end diff --git a/stdlib/LinearAlgebra/test/tridiag.jl b/stdlib/LinearAlgebra/test/tridiag.jl deleted file mode 100644 index dc14ddb1d1b27..0000000000000 --- a/stdlib/LinearAlgebra/test/tridiag.jl +++ /dev/null @@ -1,1078 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestTridiagonal - -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")) -using .Main.Quaternions - -isdefined(Main, :InfiniteArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "InfiniteArrays.jl")) -using .Main.InfiniteArrays - -isdefined(Main, :FillArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "FillArrays.jl")) -using .Main.FillArrays - -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays - -include("testutils.jl") # test_approx_eq_modphase - -#Test equivalence of eigenvectors/singular vectors taking into account possible phase (sign) differences -function test_approx_eq_vecs(a::StridedVecOrMat{S}, b::StridedVecOrMat{T}, error=nothing) where {S<:Real,T<:Real} - n = size(a, 1) - @test n==size(b,1) && size(a,2)==size(b,2) - error===nothing && (error=n^3*(eps(S)+eps(T))) - for i=1:n - ev1, ev2 = a[:,i], b[:,i] - deviation = min(abs(norm(ev1-ev2)),abs(norm(ev1+ev2))) - if !isnan(deviation) - @test deviation ≈ 0.0 atol=error - end - end -end - -@testset for elty in (Float32, Float64, ComplexF32, ComplexF64, Int) - n = 12 #Size of matrix problem to test - Random.seed!(123) - if elty == Int - Random.seed!(61516384) - d = rand(1:100, n) - dl = -rand(0:10, n-1) - du = -rand(0:10, n-1) - v = rand(1:100, n) - B = rand(1:100, n, 2) - a = rand(1:100, n-1) - b = rand(1:100, n) - c = rand(1:100, n-1) - else - d = convert(Vector{elty}, 1 .+ randn(n)) - dl = convert(Vector{elty}, randn(n - 1)) - du = convert(Vector{elty}, randn(n - 1)) - v = convert(Vector{elty}, randn(n)) - B = convert(Matrix{elty}, randn(n, 2)) - a = convert(Vector{elty}, randn(n - 1)) - b = convert(Vector{elty}, randn(n)) - c = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n - 1)) - b += im*convert(Vector{elty}, randn(n)) - c += im*convert(Vector{elty}, randn(n - 1)) - end - end - @test_throws DimensionMismatch SymTridiagonal(dl, fill(elty(1), n+1)) - @test_throws ArgumentError SymTridiagonal(rand(n, n)) - @test_throws ArgumentError Tridiagonal(dl, dl, dl) - @test_throws ArgumentError convert(SymTridiagonal{elty}, Tridiagonal(dl, d, du)) - - if elty != Int - @testset "issue #1490" begin - @test det(fill(elty(1),3,3)) ≈ zero(elty) atol=3*eps(real(one(elty))) - @test det(SymTridiagonal(elty[],elty[])) == one(elty) - end - end - - @testset "constructor" begin - for (x, y) in ((d, dl), (GenericArray(d), GenericArray(dl))) - ST = (SymTridiagonal(x, y))::SymTridiagonal{elty, typeof(x)} - @test ST == Matrix(ST) - @test ST.dv === x - @test ST.ev === y - @test typeof(ST)(ST) === ST - 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 - @test typeof(TT)(TT) === TT - end - ST = SymTridiagonal{elty}([1,2,3,4], [1,2,3]) - @test eltype(ST) == elty - @test SymTridiagonal{elty, Vector{elty}}(ST) === ST - @test SymTridiagonal{Int64, Vector{Int64}}(ST) isa SymTridiagonal{Int64, Vector{Int64}} - TT = Tridiagonal{elty}([1,2,3], [1,2,3,4], [1,2,3]) - @test eltype(TT) == elty - ST = SymTridiagonal{elty,Vector{elty}}(d, GenericArray(dl)) - @test isa(ST, SymTridiagonal{elty,Vector{elty}}) - TT = Tridiagonal{elty,Vector{elty}}(GenericArray(dl), d, GenericArray(dl)) - @test isa(TT, Tridiagonal{elty,Vector{elty}}) - @test_throws ArgumentError SymTridiagonal(d, GenericArray(dl)) - @test_throws ArgumentError SymTridiagonal(GenericArray(d), dl) - @test_throws ArgumentError Tridiagonal(GenericArray(dl), d, GenericArray(dl)) - @test_throws ArgumentError Tridiagonal(dl, GenericArray(d), dl) - @test_throws ArgumentError SymTridiagonal{elty}(d, GenericArray(dl)) - @test_throws ArgumentError Tridiagonal{elty}(GenericArray(dl), d,GenericArray(dl)) - STI = SymTridiagonal([1,2,3,4], [1,2,3]) - TTI = Tridiagonal([1,2,3], [1,2,3,4], [1,2,3]) - TTI2 = Tridiagonal([1,2,3], [1,2,3,4], [1,2,3], [1,2]) - @test SymTridiagonal(STI) === STI - @test Tridiagonal(TTI) === TTI - @test Tridiagonal(TTI2) === TTI2 - @test isa(SymTridiagonal{elty}(STI), SymTridiagonal{elty}) - @test isa(Tridiagonal{elty}(TTI), Tridiagonal{elty}) - TTI2y = Tridiagonal{elty}(TTI2) - @test isa(TTI2y, Tridiagonal{elty}) - @test TTI2y.du2 == convert(Vector{elty}, [1,2]) - end - @testset "interconversion of Tridiagonal and SymTridiagonal" begin - @test Tridiagonal(dl, d, dl) == SymTridiagonal(d, dl) - @test SymTridiagonal(d, dl) == Tridiagonal(dl, d, dl) - @test Tridiagonal(dl, d, du) + Tridiagonal(du, d, dl) == SymTridiagonal(2d, dl+du) - @test SymTridiagonal(d, dl) + Tridiagonal(dl, d, du) == Tridiagonal(dl + dl, d+d, dl+du) - @test convert(SymTridiagonal,Tridiagonal(SymTridiagonal(d, dl))) == SymTridiagonal(d, dl) - @test Array(convert(SymTridiagonal{ComplexF32},Tridiagonal(SymTridiagonal(d, dl)))) == convert(Matrix{ComplexF32}, SymTridiagonal(d, dl)) - end - @testset "tril/triu" begin - zerosd = fill!(similar(d), 0) - zerosdl = fill!(similar(dl), 0) - zerosdu = fill!(similar(du), 0) - @test_throws ArgumentError tril!(SymTridiagonal(d, dl), -n - 2) - @test_throws ArgumentError tril!(SymTridiagonal(d, dl), n) - @test_throws ArgumentError tril!(Tridiagonal(dl, d, du), -n - 2) - @test_throws ArgumentError tril!(Tridiagonal(dl, d, du), n) - @test @inferred(tril(SymTridiagonal(d,dl))) == Tridiagonal(dl,d,zerosdl) - @test @inferred(tril(SymTridiagonal(d,dl),1)) == Tridiagonal(dl,d,dl) - @test @inferred(tril(SymTridiagonal(d,dl),-1)) == Tridiagonal(dl,zerosd,zerosdl) - @test @inferred(tril(SymTridiagonal(d,dl),-2)) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test @inferred(tril(Tridiagonal(dl,d,du))) == Tridiagonal(dl,d,zerosdu) - @test @inferred(tril(Tridiagonal(dl,d,du),1)) == Tridiagonal(dl,d,du) - @test @inferred(tril(Tridiagonal(dl,d,du),-1)) == Tridiagonal(dl,zerosd,zerosdu) - @test @inferred(tril(Tridiagonal(dl,d,du),-2)) == Tridiagonal(zerosdl,zerosd,zerosdu) - @test @inferred(tril!(copy(SymTridiagonal(d,dl)))) == Tridiagonal(dl,d,zerosdl) - @test @inferred(tril!(copy(SymTridiagonal(d,dl)),1)) == Tridiagonal(dl,d,dl) - @test @inferred(tril!(copy(SymTridiagonal(d,dl)),-1)) == Tridiagonal(dl,zerosd,zerosdl) - @test @inferred(tril!(copy(SymTridiagonal(d,dl)),-2)) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test @inferred(tril!(copy(Tridiagonal(dl,d,du)))) == Tridiagonal(dl,d,zerosdu) - @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),1)) == Tridiagonal(dl,d,du) - @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),-1)) == Tridiagonal(dl,zerosd,zerosdu) - @test @inferred(tril!(copy(Tridiagonal(dl,d,du)),-2)) == Tridiagonal(zerosdl,zerosd,zerosdu) - - @test_throws ArgumentError triu!(SymTridiagonal(d, dl), -n) - @test_throws ArgumentError triu!(SymTridiagonal(d, dl), n + 2) - @test_throws ArgumentError triu!(Tridiagonal(dl, d, du), -n) - @test_throws ArgumentError triu!(Tridiagonal(dl, d, du), n + 2) - @test @inferred(triu(SymTridiagonal(d,dl))) == Tridiagonal(zerosdl,d,dl) - @test @inferred(triu(SymTridiagonal(d,dl),-1)) == Tridiagonal(dl,d,dl) - @test @inferred(triu(SymTridiagonal(d,dl),1)) == Tridiagonal(zerosdl,zerosd,dl) - @test @inferred(triu(SymTridiagonal(d,dl),2)) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test @inferred(triu(Tridiagonal(dl,d,du))) == Tridiagonal(zerosdl,d,du) - @test @inferred(triu(Tridiagonal(dl,d,du),-1)) == Tridiagonal(dl,d,du) - @test @inferred(triu(Tridiagonal(dl,d,du),1)) == Tridiagonal(zerosdl,zerosd,du) - @test @inferred(triu(Tridiagonal(dl,d,du),2)) == Tridiagonal(zerosdl,zerosd,zerosdu) - @test @inferred(triu!(copy(SymTridiagonal(d,dl)))) == Tridiagonal(zerosdl,d,dl) - @test @inferred(triu!(copy(SymTridiagonal(d,dl)),-1)) == Tridiagonal(dl,d,dl) - @test @inferred(triu!(copy(SymTridiagonal(d,dl)),1)) == Tridiagonal(zerosdl,zerosd,dl) - @test @inferred(triu!(copy(SymTridiagonal(d,dl)),2)) == Tridiagonal(zerosdl,zerosd,zerosdl) - @test @inferred(triu!(copy(Tridiagonal(dl,d,du)))) == Tridiagonal(zerosdl,d,du) - @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),-1)) == Tridiagonal(dl,d,du) - @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),1)) == Tridiagonal(zerosdl,zerosd,du) - @test @inferred(triu!(copy(Tridiagonal(dl,d,du)),2)) == Tridiagonal(zerosdl,zerosd,zerosdu) - - @test !istril(SymTridiagonal(d,dl)) - @test istril(SymTridiagonal(d,zerosdl)) - @test !istril(SymTridiagonal(d,dl),-2) - @test !istriu(SymTridiagonal(d,dl)) - @test istriu(SymTridiagonal(d,zerosdl)) - @test !istriu(SymTridiagonal(d,dl),2) - @test istriu(Tridiagonal(zerosdl,d,du)) - @test !istriu(Tridiagonal(dl,d,zerosdu)) - @test istriu(Tridiagonal(zerosdl,zerosd,du),1) - @test !istriu(Tridiagonal(dl,d,zerosdu),2) - @test istril(Tridiagonal(dl,d,zerosdu)) - @test !istril(Tridiagonal(zerosdl,d,du)) - @test istril(Tridiagonal(dl,zerosd,zerosdu),-1) - @test !istril(Tridiagonal(dl,d,zerosdu),-2) - - @test isdiag(SymTridiagonal(d,zerosdl)) - @test !isdiag(SymTridiagonal(d,dl)) - @test isdiag(Tridiagonal(zerosdl,d,zerosdu)) - @test !isdiag(Tridiagonal(dl,d,zerosdu)) - @test !isdiag(Tridiagonal(zerosdl,d,du)) - @test !isdiag(Tridiagonal(dl,d,du)) - - # Test methods that could fail due to dv and ev having the same length - # see #41089 - - badev = zero(d) - badev[end] = 1 - S = SymTridiagonal(d, badev) - - @test istriu(S, -2) - @test istriu(S, 0) - @test !istriu(S, 2) - - @test isdiag(S) - end - - @testset "iszero and isone" begin - Tzero = Tridiagonal(zeros(elty, 9), zeros(elty, 10), zeros(elty, 9)) - Tone = Tridiagonal(zeros(elty, 9), ones(elty, 10), zeros(elty, 9)) - Tmix = Tridiagonal(zeros(elty, 9), zeros(elty, 10), zeros(elty, 9)) - Tmix[end, end] = one(elty) - - Szero = SymTridiagonal(zeros(elty, 10), zeros(elty, 9)) - Sone = SymTridiagonal(ones(elty, 10), zeros(elty, 9)) - Smix = SymTridiagonal(zeros(elty, 10), zeros(elty, 9)) - Smix[end, end] = one(elty) - - @test iszero(Tzero) - @test !isone(Tzero) - @test !iszero(Tone) - @test isone(Tone) - @test !iszero(Tmix) - @test !isone(Tmix) - - @test iszero(Szero) - @test !isone(Szero) - @test !iszero(Sone) - @test isone(Sone) - @test !iszero(Smix) - @test !isone(Smix) - - badev = zeros(elty, 3) - badev[end] = 1 - - @test isone(SymTridiagonal(ones(elty, 3), badev)) - @test iszero(SymTridiagonal(zeros(elty, 3), badev)) - end - - @testset for mat_type in (Tridiagonal, SymTridiagonal) - A = mat_type == Tridiagonal ? mat_type(dl, d, du) : mat_type(d, dl) - fA = map(elty <: Complex ? ComplexF64 : Float64, Array(A)) - @testset "similar, size, and copyto!" begin - B = similar(A) - @test size(B) == size(A) - copyto!(B, A) - @test B == A - @test isa(similar(A), mat_type{elty}) - @test isa(similar(A, Int), mat_type{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) - @test_throws BoundsError size(A, 0) - end - @testset "getindex" begin - @test_throws BoundsError A[n + 1, 1] - @test_throws BoundsError A[1, n + 1] - @test A[1, n] == convert(elty, 0.0) - @test A[1, 1] == d[1] - end - @testset "setindex!" begin - @test_throws BoundsError A[n + 1, 1] = 0 # test bounds check - @test_throws BoundsError A[1, n + 1] = 0 # test bounds check - @test_throws ArgumentError A[1, 3] = 1 # test assignment off the main/sub/super diagonal - if mat_type == Tridiagonal - @test (A[3, 3] = A[3, 3]; A == fA) # test assignment on the main diagonal - @test (A[3, 2] = A[3, 2]; A == fA) # test assignment on the subdiagonal - @test (A[2, 3] = A[2, 3]; A == fA) # test assignment on the superdiagonal - @test ((A[1, 3] = 0) == 0; A == fA) # test zero assignment off the main/sub/super diagonal - else # mat_type is SymTridiagonal - @test ((A[3, 3] = A[3, 3]) == A[3, 3]; A == fA) # test assignment on the main diagonal - @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal - @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal - end - # setindex! should return the destination - @test setindex!(A, A[2,2], 2, 2) === A - end - @testset "diag" begin - @test (@inferred diag(A))::typeof(d) == d - @test (@inferred diag(A, 0))::typeof(d) == d - @test (@inferred diag(A, 1))::typeof(d) == (mat_type == Tridiagonal ? du : dl) - @test (@inferred diag(A, -1))::typeof(d) == dl - @test (@inferred diag(A, n-1))::typeof(d) == zeros(elty, 1) - @test isempty(@inferred diag(A, -n - 1)) - @test isempty(@inferred diag(A, n + 1)) - GA = mat_type == Tridiagonal ? mat_type(GenericArray.((dl, d, du))...) : mat_type(GenericArray.((d, dl))...) - @test (@inferred diag(GA))::typeof(GenericArray(d)) == GenericArray(d) - @test (@inferred diag(GA, -1))::typeof(GenericArray(d)) == GenericArray(dl) - end - @testset "trace" begin - if real(elty) <: Integer - @test tr(A) == tr(fA) - else - @test tr(A) ≈ tr(fA) rtol=2eps(real(elty)) - end - end - @testset "Idempotent tests" begin - for func in (conj, transpose, adjoint) - @test func(func(A)) == A - if func ∈ (transpose, adjoint) - @test func(func(A)) === A - end - end - end - @testset "permutedims(::[Sym]Tridiagonal)" begin - @test permutedims(permutedims(A)) === A - @test permutedims(A) == transpose.(transpose(A)) - @test permutedims(A, [1, 2]) === A - @test permutedims(A, (2, 1)) == permutedims(A) - end - if elty != Int - @testset "Simple unary functions" begin - for func in (det, inv) - @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(real(one(elty)))) - end - end - end - ds = mat_type == Tridiagonal ? (dl, d, du) : (d, dl) - for f in (real, imag) - @test f(A)::mat_type == mat_type(map(f, ds)...) - end - if elty <: Real - for f in (round, trunc, floor, ceil) - fds = [f.(d) for d in ds] - @test f.(A)::mat_type == mat_type(fds...) - @test f.(Int, A)::mat_type == f.(Int, fA) - end - end - fds = [abs.(d) for d in ds] - @test abs.(A)::mat_type == mat_type(fds...) - @testset "Multiplication with strided matrix/vector" begin - @test (x = fill(1.,n); A*x ≈ Array(A)*x) - @test (X = fill(1.,n,2); A*X ≈ Array(A)*X) - end - @testset "Binary operations" begin - B = mat_type == Tridiagonal ? mat_type(a, b, c) : mat_type(b, a) - fB = map(elty <: Complex ? ComplexF64 : Float64, Array(B)) - for op in (+, -, *) - @test Array(op(A, B)) ≈ op(fA, fB) - end - α = rand(elty) - @test Array(α*A) ≈ α*Array(A) - @test Array(A*α) ≈ Array(A)*α - @test Array(A/α) ≈ Array(A)/α - - @testset "Matmul with Triangular types" begin - @test A*LinearAlgebra.UnitUpperTriangular(Matrix(1.0I, n, n)) ≈ fA - @test A*LinearAlgebra.UnitLowerTriangular(Matrix(1.0I, n, n)) ≈ fA - @test A*UpperTriangular(Matrix(1.0I, n, n)) ≈ fA - @test A*LowerTriangular(Matrix(1.0I, n, n)) ≈ fA - end - @testset "mul! errors" begin - Cnn, Cnm, Cmn = Matrix{elty}.(undef, ((n,n), (n,n+1), (n+1,n))) - @test_throws DimensionMismatch LinearAlgebra.mul!(Cnn,A,Cnm) - @test_throws DimensionMismatch LinearAlgebra.mul!(Cnn,A,Cmn) - @test_throws DimensionMismatch LinearAlgebra.mul!(Cnn,B,Cmn) - @test_throws DimensionMismatch LinearAlgebra.mul!(Cmn,B,Cnn) - @test_throws DimensionMismatch LinearAlgebra.mul!(Cnm,B,Cnn) - end - end - @testset "Negation" begin - mA = -A - @test mA isa mat_type - @test -mA == A - end - if mat_type == SymTridiagonal - @testset "Tridiagonal/SymTridiagonal mixing ops" begin - B = convert(Tridiagonal{elty}, A) - @test B == A - @test B + A == A + B - @test B - A == A - B - end - if elty <: LinearAlgebra.BlasReal - @testset "Eigensystems" begin - zero, infinity = convert(elty, 0), convert(elty, Inf) - @testset "stebz! and stein!" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) - evecs = LAPACK.stein!(b, a, w) - - (e, v) = eigen(SymTridiagonal(b, a)) - @test e ≈ w - test_approx_eq_vecs(v, evecs) - end - @testset "stein! call using iblock and isplit" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) - evecs = LAPACK.stein!(b, a, w, iblock, isplit) - test_approx_eq_vecs(v, evecs) - end - @testset "stegr! call with index range" begin - F = eigen(SymTridiagonal(b, a),1:2) - fF = eigen(Symmetric(Array(SymTridiagonal(b, a))),1:2) - test_approx_eq_modphase(F.vectors, fF.vectors) - @test F.values ≈ fF.values - end - @testset "stegr! call with value range" begin - F = eigen(SymTridiagonal(b, a),0.0,1.0) - fF = eigen(Symmetric(Array(SymTridiagonal(b, a))),0.0,1.0) - test_approx_eq_modphase(F.vectors, fF.vectors) - @test F.values ≈ fF.values - end - @testset "eigenvalues/eigenvectors of symmetric tridiagonal" begin - if elty === Float32 || elty === Float64 - DT, VT = @inferred eigen(A) - @inferred eigen(A, 2:4) - @inferred eigen(A, 1.0, 2.0) - D, Vecs = eigen(fA) - @test DT ≈ D - @test abs.(VT'Vecs) ≈ Matrix(elty(1)I, n, n) - test_approx_eq_modphase(eigvecs(A), eigvecs(fA)) - #call to LAPACK.stein here - test_approx_eq_modphase(eigvecs(A,eigvals(A)),eigvecs(A)) - elseif elty != Int - # check that undef is determined accurately even if type inference - # bails out due to the number of try/catch blocks in this code. - @test_throws UndefVarError fA - end - end - end - end - if elty <: Real - Ts = SymTridiagonal(d, dl) - Fs = Array(Ts) - Tldlt = factorize(Ts) - @testset "symmetric tridiagonal" begin - @test_throws DimensionMismatch Tldlt\rand(elty,n+1) - @test size(Tldlt) == size(Ts) - if elty <: AbstractFloat - @test LinearAlgebra.LDLt{elty,SymTridiagonal{elty,Vector{elty}}}(Tldlt) === Tldlt - @test LinearAlgebra.LDLt{elty}(Tldlt) === Tldlt - @test typeof(convert(LinearAlgebra.LDLt{Float32,Matrix{Float32}},Tldlt)) == - LinearAlgebra.LDLt{Float32,Matrix{Float32}} - @test typeof(convert(LinearAlgebra.LDLt{Float32},Tldlt)) == - LinearAlgebra.LDLt{Float32,SymTridiagonal{Float32,Vector{Float32}}} - end - for vv in (copy(v), view(v, 1:n)) - invFsv = Fs\vv - x = Ts\vv - @test x ≈ invFsv - @test Array(Tldlt) ≈ Fs - end - - @testset "similar" begin - @test isa(similar(Ts), SymTridiagonal{elty}) - @test isa(similar(Ts, Int), SymTridiagonal{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)) - @test last(logabsdet(Tldlt)) ≈ last(logabsdet(Fs)) - # just test that the det method exists. The numerical value of the - # determinant is unreliable - det(Tldlt) - end - end - else # mat_type is Tridiagonal - @testset "tridiagonal linear algebra" begin - for vv in (copy(v), view(copy(v), 1:n)) - @test A*vv ≈ fA*vv - invFv = fA\vv - @test A\vv ≈ invFv - Tlu = factorize(A) - x = Tlu\vv - @test x ≈ invFv - end - elty != Int && @test A \ v ≈ ldiv!(copy(A), copy(v)) - end - F = lu(A) - L1, U1, p1 = F - G = lu!(F, 2A) - L2, U2, p2 = F - @test L1 ≈ L2 - @test 2U1 ≈ U2 - @test p1 == p2 - end - @testset "generalized dot" begin - x = fill(convert(elty, 1), n) - y = fill(convert(elty, 1), n) - @test dot(x, A, y) ≈ dot(A'x, y) ≈ dot(x, A*y) - @test dot([1], SymTridiagonal([1], Int[]), [1]) == 1 - @test dot([1], Tridiagonal(Int[], [1], Int[]), [1]) == 1 - @test dot(Int[], SymTridiagonal(Int[], Int[]), Int[]) === 0 - @test dot(Int[], Tridiagonal(Int[], Int[], Int[]), Int[]) === 0 - end - end -end - -@testset "SymTridiagonal/Tridiagonal block matrix" begin - M = [1 2; 3 4] - n = 5 - A = SymTridiagonal(fill(M, n), fill(M, n-1)) - @test @inferred A[1,1] == Symmetric(M) - @test @inferred A[1,2] == M - @test @inferred A[2,1] == transpose(M) - @test @inferred diag(A, 1) == fill(M, n-1) - @test @inferred diag(A, 0) == fill(Symmetric(M), n) - @test @inferred diag(A, -1) == fill(transpose(M), n-1) - @test_broken diag(A, -2) == fill(M, n-2) - @test_broken diag(A, 2) == fill(M, n-2) - @test isempty(@inferred diag(A, n+1)) - @test isempty(@inferred diag(A, -n-1)) - - A[1,1] = Symmetric(2M) - @test A[1,1] == Symmetric(2M) - @test_throws ArgumentError A[1,1] = M - - @test tr(A) == sum(diag(A)) - @test issymmetric(tr(A)) - - A = Tridiagonal(fill(M, n-1), fill(M, n), fill(M, n-1)) - @test @inferred A[1,1] == M - @test @inferred A[1,2] == M - @test @inferred A[2,1] == M - @test @inferred diag(A, 1) == fill(M, n-1) - @test @inferred diag(A, 0) == fill(M, n) - @test @inferred diag(A, -1) == fill(M, n-1) - @test_broken diag(A, -2) == fill(M, n-2) - @test_broken diag(A, 2) == fill(M, n-2) - @test isempty(@inferred diag(A, n+1)) - @test isempty(@inferred diag(A, -n-1)) - - for n in 0:2 - dv, ev = fill(M, n), fill(M, max(n-1,0)) - A = SymTridiagonal(dv, ev) - @test A == Matrix{eltype(A)}(A) - - A = Tridiagonal(ev, dv, ev) - @test A == Matrix{eltype(A)}(A) - end - - M = SizedArrays.SizedArray{(2,2)}([1 2; 3 4]) - S = SymTridiagonal(fill(M,4), fill(M,3)) - @test diag(S,2) == fill(zero(M), 2) - @test diag(S,-2) == fill(zero(M), 2) - @test isempty(diag(S,4)) - @test isempty(diag(S,-4)) -end - -@testset "Issue 12068" begin - @test SymTridiagonal([1, 2], [0])^3 == [1 0; 0 8] -end - -@testset "Issue #48505" begin - @test SymTridiagonal([1,2,3],[4,5.0]) == [1.0 4.0 0.0; 4.0 2.0 5.0; 0.0 5.0 3.0] - @test Tridiagonal([1, 2], [4, 5, 1], [6.0, 7]) == [4.0 6.0 0.0; 1.0 5.0 7.0; 0.0 2.0 1.0] -end - -@testset "convert for SymTridiagonal" begin - STF32 = SymTridiagonal{Float32}(fill(1f0, 5), fill(1f0, 4)) - @test convert(SymTridiagonal{Float64}, STF32)::SymTridiagonal{Float64} == STF32 - @test convert(AbstractMatrix{Float64}, STF32)::SymTridiagonal{Float64} == STF32 -end - -@testset "constructors from matrix" begin - @test SymTridiagonal([1 2 3; 2 5 6; 0 6 9]) == [1 2 0; 2 5 6; 0 6 9] - @test Tridiagonal([1 2 3; 4 5 6; 7 8 9]) == [1 2 0; 4 5 6; 0 8 9] -end - -@testset "constructors with range and other abstract vectors" begin - @test SymTridiagonal(1:3, 1:2) == [1 1 0; 1 2 2; 0 2 3] - @test Tridiagonal(4:5, 1:3, 1:2) == [1 1 0; 4 2 2; 0 5 3] -end - -@testset "Prevent off-diagonal aliasing in Tridiagonal" begin - e = ones(4) - f = e[1:end-1] - T = Tridiagonal(f, 2e, f) - T ./= 10 - @test all(==(0.1), f) -end - -@testset "Issue #26994 (and the empty case)" begin - T = SymTridiagonal([1.0],[3.0]) - x = ones(1) - @test T*x == ones(1) - @test SymTridiagonal(ones(0), ones(0)) * ones(0, 2) == ones(0, 2) -end - -@testset "Issue 29630" begin - function central_difference_discretization(N; dfunc = x -> 12x^2 - 2N^2, - dufunc = x -> N^2 + 4N*x, - dlfunc = x -> N^2 - 4N*x, - bfunc = x -> 114ℯ^-x * (1 + 3x), - b0 = 0, bf = 57/ℯ, - x0 = 0, xf = 1) - h = 1/N - d, du, dl, b = map(dfunc, (x0+h):h:(xf-h)), map(dufunc, (x0+h):h:(xf-2h)), - map(dlfunc, (x0+2h):h:(xf-h)), map(bfunc, (x0+h):h:(xf-h)) - b[1] -= dlfunc(x0)*b0 # subtract the boundary term - b[end] -= dufunc(xf)*bf # subtract the boundary term - Tridiagonal(dl, d, du), b - end - - A90, b90 = central_difference_discretization(90) - - @test A90\b90 ≈ inv(A90)*b90 -end - -@testset "singular values of SymTridiag" begin - @test svdvals(SymTridiagonal([-4,2,3], [0,0])) ≈ [4,3,2] - @test svdvals(SymTridiagonal(collect(0.:10.), zeros(10))) ≈ reverse(0:10) - @test svdvals(SymTridiagonal([1,2,1], [1,1])) ≈ [3,1,0] - # test that dependent methods such as `cond` also work - @test cond(SymTridiagonal([1,2,3], [0,0])) ≈ 3 -end - -@testset "sum, mapreduce" begin - T = Tridiagonal([1,2], [1,2,3], [7,8]) - Tdense = Matrix(T) - S = SymTridiagonal([1,2,3], [1,2]) - Sdense = Matrix(S) - @test sum(T) == 24 - @test sum(S) == 12 - @test_throws ArgumentError sum(T, dims=0) - @test sum(T, dims=1) == sum(Tdense, dims=1) - @test sum(T, dims=2) == sum(Tdense, dims=2) - @test sum(T, dims=3) == sum(Tdense, dims=3) - @test typeof(sum(T, dims=1)) == typeof(sum(Tdense, dims=1)) - @test mapreduce(one, min, T, dims=1) == mapreduce(one, min, Tdense, dims=1) - @test mapreduce(one, min, T, dims=2) == mapreduce(one, min, Tdense, dims=2) - @test mapreduce(one, min, T, dims=3) == mapreduce(one, min, Tdense, dims=3) - @test typeof(mapreduce(one, min, T, dims=1)) == typeof(mapreduce(one, min, Tdense, dims=1)) - @test mapreduce(zero, max, T, dims=1) == mapreduce(zero, max, Tdense, dims=1) - @test mapreduce(zero, max, T, dims=2) == mapreduce(zero, max, Tdense, dims=2) - @test mapreduce(zero, max, T, dims=3) == mapreduce(zero, max, Tdense, dims=3) - @test typeof(mapreduce(zero, max, T, dims=1)) == typeof(mapreduce(zero, max, Tdense, dims=1)) - @test_throws ArgumentError sum(S, dims=0) - @test sum(S, dims=1) == sum(Sdense, dims=1) - @test sum(S, dims=2) == sum(Sdense, dims=2) - @test sum(S, dims=3) == sum(Sdense, dims=3) - @test typeof(sum(S, dims=1)) == typeof(sum(Sdense, dims=1)) - @test mapreduce(one, min, S, dims=1) == mapreduce(one, min, Sdense, dims=1) - @test mapreduce(one, min, S, dims=2) == mapreduce(one, min, Sdense, dims=2) - @test mapreduce(one, min, S, dims=3) == mapreduce(one, min, Sdense, dims=3) - @test typeof(mapreduce(one, min, S, dims=1)) == typeof(mapreduce(one, min, Sdense, dims=1)) - @test mapreduce(zero, max, S, dims=1) == mapreduce(zero, max, Sdense, dims=1) - @test mapreduce(zero, max, S, dims=2) == mapreduce(zero, max, Sdense, dims=2) - @test mapreduce(zero, max, S, dims=3) == mapreduce(zero, max, Sdense, dims=3) - @test typeof(mapreduce(zero, max, S, dims=1)) == typeof(mapreduce(zero, max, Sdense, dims=1)) - - T = Tridiagonal(Int[], Int[], Int[]) - Tdense = Matrix(T) - S = SymTridiagonal(Int[], Int[]) - Sdense = Matrix(S) - @test sum(T) == 0 - @test sum(S) == 0 - @test_throws ArgumentError sum(T, dims=0) - @test sum(T, dims=1) == sum(Tdense, dims=1) - @test sum(T, dims=2) == sum(Tdense, dims=2) - @test sum(T, dims=3) == sum(Tdense, dims=3) - @test typeof(sum(T, dims=1)) == typeof(sum(Tdense, dims=1)) - @test_throws ArgumentError sum(S, dims=0) - @test sum(S, dims=1) == sum(Sdense, dims=1) - @test sum(S, dims=2) == sum(Sdense, dims=2) - @test sum(S, dims=3) == sum(Sdense, dims=3) - @test typeof(sum(S, dims=1)) == typeof(sum(Sdense, dims=1)) - - T = Tridiagonal(Int[], Int[2], Int[]) - Tdense = Matrix(T) - S = SymTridiagonal(Int[2], Int[]) - Sdense = Matrix(S) - @test sum(T) == 2 - @test sum(S) == 2 - @test_throws ArgumentError sum(T, dims=0) - @test sum(T, dims=1) == sum(Tdense, dims=1) - @test sum(T, dims=2) == sum(Tdense, dims=2) - @test sum(T, dims=3) == sum(Tdense, dims=3) - @test typeof(sum(T, dims=1)) == typeof(sum(Tdense, dims=1)) - @test_throws ArgumentError sum(S, dims=0) - @test sum(S, dims=1) == sum(Sdense, dims=1) - @test sum(S, dims=2) == sum(Sdense, dims=2) - @test sum(S, dims=3) == sum(Sdense, dims=3) - @test typeof(sum(S, dims=1)) == typeof(sum(Sdense, dims=1)) -end - -@testset "Issue #28994 (sum of Tridigonal and UniformScaling)" begin - dl = [1., 1.] - d = [-2., -2., -2.] - T = Tridiagonal(dl, d, dl) - S = SymTridiagonal(T) - - @test diag(T + 2I) == zero(d) - @test diag(S + 2I) == zero(d) -end - -@testset "convert Tridiagonal to SymTridiagonal error" begin - du = rand(Float64, 4) - d = rand(Float64, 5) - dl = rand(Float64, 4) - T = Tridiagonal(dl, d, du) - @test_throws ArgumentError SymTridiagonal{Float32}(T) -end - -# Issue #38765 -@testset "Eigendecomposition with different lengths" begin - # length(A.ev) can be either length(A.dv) or length(A.dv) - 1 - A = SymTridiagonal(fill(1.0, 3), fill(-1.0, 3)) - F = eigen(A) - A2 = SymTridiagonal(fill(1.0, 3), fill(-1.0, 2)) - F2 = eigen(A2) - test_approx_eq_modphase(F.vectors, F2.vectors) - @test F.values ≈ F2.values ≈ eigvals(A) ≈ eigvals(A2) - @test eigvecs(A) ≈ eigvecs(A2) - @test eigvecs(A, eigvals(A)[1:1]) ≈ eigvecs(A2, eigvals(A2)[1:1]) -end - -@testset "non-commutative algebra (#39701)" begin - for A in (SymTridiagonal(Quaternion.(randn(5), randn(5), randn(5), randn(5)), Quaternion.(randn(4), randn(4), randn(4), randn(4))), - Tridiagonal(Quaternion.(randn(4), randn(4), randn(4), randn(4)), Quaternion.(randn(5), randn(5), randn(5), randn(5)), Quaternion.(randn(4), randn(4), randn(4), randn(4)))) - c = Quaternion(1,2,3,4) - @test A * c ≈ Matrix(A) * c - @test A / c ≈ Matrix(A) / c - @test c * A ≈ c * Matrix(A) - @test c \ A ≈ c \ Matrix(A) - end -end - -@testset "adjoint of LDLt" begin - Sr = SymTridiagonal(randn(5), randn(4)) - Sc = SymTridiagonal(complex.(randn(5)) .+ 1im, complex.(randn(4), randn(4))) - b = ones(size(Sr, 1)) - - F = ldlt(Sr) - @test F\b == F'\b - - F = ldlt(Sc) - @test copy(Sc')\b == F'\b -end - -@testset "symmetric and hermitian tridiagonals" begin - A = [im 0; 0 -im] - @test issymmetric(A) - @test !ishermitian(A) - - # real - A = SymTridiagonal(randn(5), randn(4)) - @test issymmetric(A) - @test ishermitian(A) - - A = Tridiagonal(A.ev, A.dv, A.ev .+ 1) - @test !issymmetric(A) - @test !ishermitian(A) - - # complex - # https://github.com/JuliaLang/julia/pull/41037#discussion_r645524081 - S = SymTridiagonal(randn(5) .+ 0im, randn(5) .+ 0im) - S.ev[end] = im - @test issymmetric(S) - @test ishermitian(S) - - S = SymTridiagonal(randn(5) .+ 1im, randn(4) .+ 1im) - @test issymmetric(S) - @test !ishermitian(S) - - S = Tridiagonal(S.ev, S.dv, adjoint.(S.ev)) - @test !issymmetric(S) - @test !ishermitian(S) - - S = Tridiagonal(S.dl, real.(S.d) .+ 0im, S.du) - @test !issymmetric(S) - @test ishermitian(S) -end - -isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) -using .Main.ImmutableArrays - -@testset "Conversion to AbstractArray" begin - # tests corresponding to #34995 - v1 = ImmutableArray([1, 2]) - v2 = ImmutableArray([3, 4, 5]) - v3 = ImmutableArray([6, 7]) - T = Tridiagonal(v1, v2, v3) - Tsym = SymTridiagonal(v2, v1) - - @test convert(AbstractArray{Float64}, T)::Tridiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == T - @test convert(AbstractMatrix{Float64}, T)::Tridiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == T - @test convert(AbstractArray{Float64}, Tsym)::SymTridiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Tsym - @test convert(AbstractMatrix{Float64}, Tsym)::SymTridiagonal{Float64,ImmutableArray{Float64,1,Array{Float64,1}}} == Tsym -end - -@testset "dot(x,A,y) for A::Tridiagonal or SymTridiagonal" begin - for elty in (Float32, Float64, ComplexF32, ComplexF64, Int) - x = fill(convert(elty, 1), 0) - T = Tridiagonal(x, x, x) - Tsym = SymTridiagonal(x, x) - @test dot(x, T, x) == 0.0 - @test dot(x, Tsym, x) == 0.0 - end -end - -isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) -using .Main.SizedArrays -@testset "non-number eltype" begin - @testset "sum for SymTridiagonal" begin - dv = [SizedArray{(2,2)}(rand(1:2048,2,2)) for i in 1:10] - ev = [SizedArray{(2,2)}(rand(1:2048,2,2)) for i in 1:10] - S = SymTridiagonal(dv, ev) - Sdense = Matrix(S) - @test Sdense == collect(S) - @test sum(S) == sum(Sdense) - @test sum(S, dims = 1) == sum(Sdense, dims = 1) - @test sum(S, dims = 2) == sum(Sdense, dims = 2) - end - @testset "issymmetric/ishermitian for Tridiagonal" begin - @test !issymmetric(Tridiagonal([[1 2;3 4]], [[1 2;2 3], [1 2;2 3]], [[1 2;3 4]])) - @test !issymmetric(Tridiagonal([[1 3;2 4]], [[1 2;3 4], [1 2;3 4]], [[1 2;3 4]])) - @test issymmetric(Tridiagonal([[1 3;2 4]], [[1 2;2 3], [1 2;2 3]], [[1 2;3 4]])) - - @test ishermitian(Tridiagonal([[1 3;2 4].+im], [[1 2;2 3].+0im, [1 2;2 3].+0im], [[1 2;3 4].-im])) - @test !ishermitian(Tridiagonal([[1 3;2 4].+im], [[1 2;2 3].+0im, [1 2;2 3].+0im], [[1 2;3 4].+im])) - @test !ishermitian(Tridiagonal([[1 3;2 4].+im], [[1 2;2 3].+im, [1 2;2 3].+0im], [[1 2;3 4].-im])) - end - @testset "== between Tridiagonal and SymTridiagonal" begin - dv = [SizedArray{(2,2)}([1 2;3 4]) for i in 1:4] - ev = [SizedArray{(2,2)}([3 4;1 2]) for i in 1:4] - S = SymTridiagonal(dv, ev) - Sdense = Matrix(S) - @test S == Tridiagonal(diag(Sdense, -1), diag(Sdense), diag(Sdense, 1)) == S - @test S !== Tridiagonal(diag(Sdense, 1), diag(Sdense), diag(Sdense, 1)) !== S - end -end - -@testset "copyto! between SymTridiagonal and Tridiagonal" begin - ev, dv = [1:4;], [1:5;] - S = SymTridiagonal(dv, ev) - T = Tridiagonal(zero(ev), zero(dv), zero(ev)) - @test copyto!(T, S) == S - @test copyto!(zero(S), T) == T - - ev2 = [1:5;] - S = SymTridiagonal(dv, ev2) - T = Tridiagonal(zeros(length(ev2)-1), zero(dv), zeros(length(ev2)-1)) - @test copyto!(T, S) == S - @test copyto!(zero(S), T) == T - - T2 = Tridiagonal(ones(length(ev)), zero(dv), zero(ev)) - @test_throws "cannot copy an asymmetric Tridiagonal matrix to a SymTridiagonal" copyto!(zero(S), T2) - - @testset "mismatched sizes" begin - dv2 = [4; @view dv[2:end]] - @test copyto!(S, SymTridiagonal([4], Int[])) == SymTridiagonal(dv2, ev) - @test copyto!(T, SymTridiagonal([4], Int[])) == Tridiagonal(ev, dv2, ev) - @test copyto!(S, Tridiagonal(Int[], [4], Int[])) == SymTridiagonal(dv2, ev) - @test copyto!(T, Tridiagonal(Int[], [4], Int[])) == Tridiagonal(ev, dv2, ev) - @test copyto!(S, SymTridiagonal(Int[], Int[])) == SymTridiagonal(dv, ev) - @test copyto!(T, SymTridiagonal(Int[], Int[])) == Tridiagonal(ev, dv, ev) - @test copyto!(S, Tridiagonal(Int[], Int[], Int[])) == SymTridiagonal(dv, ev) - @test copyto!(T, Tridiagonal(Int[], Int[], Int[])) == Tridiagonal(ev, dv, ev) - end -end - -@testset "copyto! with UniformScaling" begin - @testset "Tridiagonal" begin - @testset "Fill" begin - for len in (4, InfiniteArrays.Infinity()) - d = FillArrays.Fill(1, len) - ud = FillArrays.Fill(0, len-1) - T = Tridiagonal(ud, d, ud) - @test copyto!(T, I) === T - end - end - T = Tridiagonal(fill(3, 3), fill(2, 4), fill(3, 3)) - copyto!(T, I) - @test all(isone, diag(T)) - @test all(iszero, diag(T, 1)) - @test all(iszero, diag(T, -1)) - end - @testset "SymTridiagonal" begin - @testset "Fill" begin - for len in (4, InfiniteArrays.Infinity()) - d = FillArrays.Fill(1, len) - ud = FillArrays.Fill(0, len-1) - ST = SymTridiagonal(d, ud) - @test copyto!(ST, I) === ST - end - end - ST = SymTridiagonal(fill(2, 4), fill(3, 3)) - copyto!(ST, I) - @test all(isone, diag(ST)) - @test all(iszero, diag(ST, 1)) - @test all(iszero, diag(ST, -1)) - end -end - -@testset "custom axes" begin - dv, uv = OffsetArray(1:4), OffsetArray(1:3) - B = Tridiagonal(uv, dv, uv) - ax = axes(dv, 1) - @test axes(B) === (ax, ax) - B = SymTridiagonal(dv, uv) - @test axes(B) === (ax, ax) -end - -@testset "Reverse operation on Tridiagonal" begin - for n in 5:6 - d = randn(n) - dl = randn(n - 1) - du = randn(n - 1) - T = Tridiagonal(dl, d, du) - @test reverse(T, dims=1) == reverse(Matrix(T), dims=1) - @test reverse(T, dims=2) == reverse(Matrix(T), dims=2) - @test reverse(T)::Tridiagonal == reverse(Matrix(T)) == reverse!(copy(T)) - end -end - -@testset "Reverse operation on SymTridiagonal" begin - n = 5 - d = randn(n) - dl = randn(n - 1) - ST = SymTridiagonal(d, dl) - @test reverse(ST, dims=1) == reverse(Matrix(ST), dims=1) - @test reverse(ST, dims=2) == reverse(Matrix(ST), dims=2) - @test reverse(ST)::SymTridiagonal == reverse(Matrix(ST)) -end - -@testset "getindex with Integers" begin - dv, ev = 1:4, 1:3 - for S in (Tridiagonal(ev, dv, ev), SymTridiagonal(dv, ev)) - @test_throws "invalid index" S[3, true] - @test S[1,2] == S[Int8(1),UInt16(2)] == S[big(1), Int16(2)] - end -end - -@testset "rmul!/lmul! with banded matrices" begin - dl, d, du = rand(3), rand(4), rand(3) - A = Tridiagonal(dl, d, du) - D = Diagonal(d) - @test rmul!(copy(A), D) ≈ A * D - @test lmul!(D, copy(A)) ≈ D * A - - @testset "non-commutative" begin - S32 = SizedArrays.SizedArray{(3,2)}(rand(3,2)) - S33 = SizedArrays.SizedArray{(3,3)}(rand(3,3)) - S22 = SizedArrays.SizedArray{(2,2)}(rand(2,2)) - T = Tridiagonal(fill(S32,3), fill(S32, 4), fill(S32, 3)) - D = Diagonal(fill(S22, size(T,2))) - @test rmul!(copy(T), D) ≈ T * D - D = Diagonal(fill(S33, size(T,1))) - @test lmul!(D, copy(T)) ≈ D * T - end -end - -@testset "rmul!/lmul! with numbers" begin - for T in (SymTridiagonal(rand(4), rand(3)), Tridiagonal(rand(3), rand(4), rand(3))) - @test rmul!(copy(T), 0.2) ≈ rmul!(Array(T), 0.2) - @test lmul!(0.2, copy(T)) ≈ lmul!(0.2, Array(T)) - @test_throws ArgumentError rmul!(T, NaN) - @test_throws ArgumentError lmul!(NaN, T) - end - for T in (SymTridiagonal(rand(2), rand(1)), Tridiagonal(rand(1), rand(2), rand(1))) - @test all(isnan, rmul!(copy(T), NaN)) - @test all(isnan, lmul!(NaN, copy(T))) - end -end - -@testset "mul with empty arrays" begin - A = zeros(5,0) - T = Tridiagonal(zeros(0), zeros(0), zeros(0)) - TL = Tridiagonal(zeros(4), zeros(5), zeros(4)) - @test size(A * T) == size(A) - @test size(TL * A) == size(A) - @test size(T * T) == size(T) - C = similar(A) - @test mul!(C, A, T) == A * T - @test mul!(C, TL, A) == TL * A - @test mul!(similar(T), T, T) == T * T - @test mul!(similar(T, size(T)), T, T) == T * T - - v = zeros(size(T,2)) - @test size(T * v) == size(v) - @test mul!(similar(v), T, v) == T * v - - D = Diagonal(zeros(size(T,2))) - @test size(T * D) == size(D * T) == size(D) - @test mul!(similar(D), T, D) == mul!(similar(D), D, T) == T * D -end - -@testset "show" begin - T = Tridiagonal(1:3, 1:4, 1:3) - @test sprint(show, T) == "Tridiagonal(1:3, 1:4, 1:3)" - S = SymTridiagonal(1:4, 1:3) - @test sprint(show, S) == "SymTridiagonal(1:4, 1:3)" - - m = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) - T = Tridiagonal(fill(m,2), fill(m,3), fill(m,2)) - @test sprint(show, T) == "Tridiagonal($(repr(diag(T,-1))), $(repr(diag(T))), $(repr(diag(T,1))))" - S = SymTridiagonal(fill(m,3), fill(m,2)) - @test sprint(show, S) == "SymTridiagonal($(repr(diag(S))), $(repr(diag(S,1))))" -end - -@testset "mul for small matrices" begin - @testset for n in 0:6 - for T in ( - Tridiagonal(rand(max(n-1,0)), rand(n), rand(max(n-1,0))), - SymTridiagonal(rand(n), rand(max(n-1,0))), - ) - M = Matrix(T) - @test T * T ≈ M * M - @test mul!(similar(T, size(T)), T, T) ≈ M * M - @test mul!(ones(size(T)), T, T, 2, 4) ≈ M * M * 2 .+ 4 - - for m in 0:6 - AR = rand(n,m) - AL = rand(m,n) - @test AL * T ≈ AL * M - @test T * AR ≈ M * AR - @test mul!(similar(AL), AL, T) ≈ AL * M - @test mul!(similar(AR), T, AR) ≈ M * AR - @test mul!(ones(size(AL)), AL, T, 2, 4) ≈ AL * M * 2 .+ 4 - @test mul!(ones(size(AR)), T, AR, 2, 4) ≈ M * AR * 2 .+ 4 - end - - v = rand(n) - @test T * v ≈ M * v - @test mul!(similar(v), T, v) ≈ M * v - - D = Diagonal(rand(n)) - @test T * D ≈ M * D - @test D * T ≈ D * M - @test mul!(Tridiagonal(similar(T)), D, T) ≈ D * M - @test mul!(Tridiagonal(similar(T)), T, D) ≈ M * D - @test mul!(similar(T, size(T)), D, T) ≈ D * M - @test mul!(similar(T, size(T)), T, D) ≈ M * D - @test mul!(ones(size(T)), D, T, 2, 4) ≈ D * M * 2 .+ 4 - @test mul!(ones(size(T)), T, D, 2, 4) ≈ M * D * 2 .+ 4 - - for uplo in (:U, :L) - B = Bidiagonal(rand(n), rand(max(0, n-1)), uplo) - @test T * B ≈ M * B - @test B * T ≈ B * M - if n <= 2 - @test mul!(Tridiagonal(similar(T)), B, T) ≈ B * M - @test mul!(Tridiagonal(similar(T)), T, B) ≈ M * B - end - @test mul!(similar(T, size(T)), B, T) ≈ B * M - @test mul!(similar(T, size(T)), T, B) ≈ M * B - @test mul!(ones(size(T)), B, T, 2, 4) ≈ B * M * 2 .+ 4 - @test mul!(ones(size(T)), T, B, 2, 4) ≈ M * B * 2 .+ 4 - end - end - end - - n = 4 - arr = SizedArrays.SizedArray{(2,2)}(reshape([1:4;],2,2)) - for T in ( - SymTridiagonal(fill(arr,n), fill(arr,n-1)), - Tridiagonal(fill(arr,n-1), fill(arr,n), fill(arr,n-1)), - ) - @test T * T ≈ Matrix(T) * Matrix(T) - BL = Bidiagonal(fill(arr,n), fill(arr,n-1), :L) - BU = Bidiagonal(fill(arr,n), fill(arr,n-1), :U) - @test BL * T ≈ Matrix(BL) * Matrix(T) - @test BU * T ≈ Matrix(BU) * Matrix(T) - @test T * BL ≈ Matrix(T) * Matrix(BL) - @test T * BU ≈ Matrix(T) * Matrix(BU) - D = Diagonal(fill(arr,n)) - @test D * T ≈ Matrix(D) * Matrix(T) - @test T * D ≈ Matrix(T) * Matrix(D) - end -end - -@testset "diagview" begin - A = Tridiagonal(rand(3), rand(4), rand(3)) - for k in -5:5 - @test diagview(A,k) == diag(A,k) - end - v = diagview(A,1) - v .= 0 - @test all(iszero, diag(A,1)) -end - -end # module TestTridiagonal diff --git a/stdlib/LinearAlgebra/test/uniformscaling.jl b/stdlib/LinearAlgebra/test/uniformscaling.jl deleted file mode 100644 index 10d427d1dc6c4..0000000000000 --- a/stdlib/LinearAlgebra/test/uniformscaling.jl +++ /dev/null @@ -1,577 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module TestUniformscaling - -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")) -using .Main.Quaternions -isdefined(Main, :OffsetArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "OffsetArrays.jl")) -using .Main.OffsetArrays - -Random.seed!(1234543) - -@testset "basic functions" begin - @test I === I' # transpose - @test ndims(I) == 2 - @test one(UniformScaling{Float32}) == UniformScaling(one(Float32)) - @test zero(UniformScaling{Float32}) == UniformScaling(zero(Float32)) - @test eltype(one(UniformScaling{Float32})) == Float32 - @test zero(UniformScaling(rand(ComplexF64))) == zero(UniformScaling{ComplexF64}) - @test one(UniformScaling(rand(ComplexF64))) == one(UniformScaling{ComplexF64}) - @test eltype(one(UniformScaling(rand(ComplexF64)))) == ComplexF64 - @test -one(UniformScaling(2)) == UniformScaling(-1) - @test opnorm(UniformScaling(1+im)) ≈ sqrt(2) - @test convert(UniformScaling{Float64}, 2I) === 2.0I - @test float(2I) === 2.0*I -end - -@testset "getindex" begin - @test I[1,1] == I[CartesianIndex(1,1)] == 1 - @test I[1,2] == I[CartesianIndex(1,2)] == 0 - - J = I(15) - for (a, b) in [ - # indexing that returns a Vector - (1:10, 1), - (4, 1:10), - (11, 1:10), - # indexing that returns a Matrix - (1:2, 1:2), - (1:2:3, 1:2:3), - (1:2:8, 2:2:9), - (1:2:8, 9:-4:1), - (9:-4:1, 1:2:8), - (2:3, 1:2), - (2:-1:1, 1:2), - (1:2:9, 5:2:13), - (1, [1,2,5]), - (1, [1,10,5,2]), - (10, [10]), - ([1], 1), - ([15,1,5,2], 6), - ([2], [2]), - ([2,9,8,2,1], [2,8,4,3,1]), - ([8,3,5,3], 2:9), - ] - @test I[a,b] == J[a,b] - ndims(a) == 1 && @test I[OffsetArray(a,-10),b] == J[OffsetArray(a,-10),b] - ndims(b) == 1 && @test I[a,OffsetArray(b,-9)] == J[a,OffsetArray(b,-9)] - ndims(a) == ndims(b) == 1 && @test I[OffsetArray(a,-7),OffsetArray(b,-8)] == J[OffsetArray(a,-7),OffsetArray(b,-8)] - end -end - -@testset "sqrt, exp, log, and trigonometric functions" begin - # convert to a dense matrix with random size - M(J) = (N = rand(1:10); Matrix(J, N, N)) - - # on complex plane - J = UniformScaling(randn(ComplexF64)) - for f in ( exp, log, cis, - sqrt, - sin, cos, tan, - asin, acos, atan, - csc, sec, cot, - acsc, asec, acot, - sinh, cosh, tanh, - asinh, acosh, atanh, - csch, sech, coth, - acsch, asech, acoth ) - @test f(J) ≈ f(M(J)) - end - - for f in (sincos, sincosd) - @test all(splat(≈), zip(f(J), f(M(J)))) - end - - # on real axis - for (λ, fs) in ( - # functions defined for x ∈ ℝ - (()->randn(), (exp, - sin, cos, tan, - csc, sec, cot, - atan, acot, - sinh, cosh, tanh, - csch, sech, coth, - asinh, acsch)), - # functions defined for x ≥ 0 - (()->abs(randn()), (log, sqrt)), - # functions defined for -1 ≤ x ≤ 1 - (()->2rand()-1, (asin, acos, atanh)), - # functions defined for x ≤ -1 or x ≥ 1 - (()->1/(2rand()-1), (acsc, asec, acoth)), - # functions defined for 0 ≤ x ≤ 1 - (()->rand(), (asech,)), - # functions defined for x ≥ 1 - (()->1/rand(), (acosh,)) - ) - for f in fs - J = UniformScaling(λ()) - @test f(J) ≈ f(M(J)) - end - end -end - -@testset "conjugation of UniformScaling" begin - @test conj(UniformScaling(1))::UniformScaling{Int} == UniformScaling(1) - @test conj(UniformScaling(1.0))::UniformScaling{Float64} == UniformScaling(1.0) - @test conj(UniformScaling(1+1im))::UniformScaling{Complex{Int}} == UniformScaling(1-1im) - @test conj(UniformScaling(1.0+1.0im))::UniformScaling{ComplexF64} == UniformScaling(1.0-1.0im) -end - -@testset "isdiag, istriu, istril, issymmetric, ishermitian, isposdef, isapprox" begin - @test isdiag(I) - @test istriu(I) - @test istril(I) - @test issymmetric(I) - @test issymmetric(UniformScaling(complex(1.0,1.0))) - @test ishermitian(I) - @test !ishermitian(UniformScaling(complex(1.0,1.0))) - @test isposdef(UniformScaling(rand())) - @test !isposdef(UniformScaling(-rand())) - @test !isposdef(UniformScaling(randn(ComplexF64))) - @test !isposdef(UniformScaling(NaN)) - @test isposdef(I) - @test !isposdef(-I) - @test isposdef(UniformScaling(complex(1.0, 0.0))) - @test !isposdef(UniformScaling(complex(1.0, 1.0))) - @test UniformScaling(4.00000000000001) ≈ UniformScaling(4.0) - @test UniformScaling(4.32) ≈ UniformScaling(4.3) rtol=0.1 atol=0.01 - @test UniformScaling(4.32) ≈ 4.3 * [1 0; 0 1] rtol=0.1 atol=0.01 - @test UniformScaling(4.32) ≈ 4.3 * [1 0; 0 1] rtol=0.1 atol=0.01 norm=norm - @test 4.3 * [1 0; 0 1] ≈ UniformScaling(4.32) rtol=0.1 atol=0.01 - @test [4.3201 0.002;0.001 4.32009] ≈ UniformScaling(4.32) rtol=0.1 atol=0. - @test UniformScaling(4.32) ≉ fill(4.3,2,2) rtol=0.1 atol=0.01 - @test UniformScaling(4.32) ≈ 4.32 * [1 0; 0 1] -end - -@testset "arithmetic with Number" begin - α = rand() - @test α + I == α + 1 - @test I + α == α + 1 - @test α - I == α - 1 - @test I - α == 1 - α - @test α .* UniformScaling(1.0) == UniformScaling(1.0) .* α - @test UniformScaling(α)./α == UniformScaling(1.0) - @test α.\UniformScaling(α) == UniformScaling(1.0) - @test α * UniformScaling(1.0) == UniformScaling(1.0) * α - @test UniformScaling(α)/α == UniformScaling(1.0) - @test 2I//3 == (2//3)*I - @test (2I)^α == (2I).^α == (2^α)I - - β = rand() - @test (α*I)^2 == UniformScaling(α^2) - @test (α*I)^(-2) == UniformScaling(α^(-2)) - @test (α*I)^(.5) == UniformScaling(α^(.5)) - @test (α*I)^β == UniformScaling(α^β) - - @test (α * I) .^ 2 == UniformScaling(α^2) - @test (α * I) .^ β == UniformScaling(α^β) -end - -@testset "unary" begin - @test +I === +1*I - @test -I === -1*I -end - -@testset "tr, det and logdet" begin - for T in (Int, Float64, ComplexF64, Bool) - @test tr(UniformScaling(zero(T))) === zero(T) - end - @test_throws ArgumentError tr(UniformScaling(1)) - @test det(I) === true - @test det(1.0I) === 1.0 - @test det(0I) === 0 - @test det(0.0I) === 0.0 - @test logdet(I) == 0 - @test_throws ArgumentError det(2I) -end - -@test copy(UniformScaling(one(Float64))) == UniformScaling(one(Float64)) -@test sprint(show,MIME"text/plain"(),UniformScaling(one(ComplexF64))) == "$(LinearAlgebra.UniformScaling){ComplexF64}\n(1.0 + 0.0im)*I" -@test sprint(show,MIME"text/plain"(),UniformScaling(one(Float32))) == "$(LinearAlgebra.UniformScaling){Float32}\n1.0*I" -@test sprint(show,UniformScaling(one(ComplexF64))) == "$(LinearAlgebra.UniformScaling){ComplexF64}(1.0 + 0.0im)" -@test sprint(show,UniformScaling(one(Float32))) == "$(LinearAlgebra.UniformScaling){Float32}(1.0f0)" - -let - λ = complex(randn(),randn()) - J = UniformScaling(λ) - @testset "transpose, conj, inv, pinv, cond" begin - @test ndims(J) == 2 - @test transpose(J) == J - @test J * [1 0; 0 1] == conj(*(adjoint(J), [1 0; 0 1])) # ctranpose (and A(c)_mul_B) - @test I + I === UniformScaling(2) # + - @test inv(I) == I - @test inv(J) == UniformScaling(inv(λ)) - @test pinv(J) == UniformScaling(inv(λ)) - @test @inferred(pinv(0.0I)) == 0.0I - @test @inferred(pinv(0I)) == 0.0I - @test @inferred(pinv(false*I)) == 0.0I - @test @inferred(pinv(0im*I)) == 0im*I - @test cond(I) == 1 - @test cond(J) == (λ ≠ zero(λ) ? one(real(λ)) : oftype(real(λ), Inf)) - end - - @testset "real, imag, reim" begin - @test real(J) == UniformScaling(real(λ)) - @test imag(J) == UniformScaling(imag(λ)) - @test reim(J) == (UniformScaling(real(λ)), UniformScaling(imag(λ))) - end - - @testset "copyto!" begin - A = Matrix{Int}(undef, (3,3)) - @test copyto!(A, I) == one(A) - B = Matrix{ComplexF64}(undef, (1,2)) - @test copyto!(B, J) == [λ zero(λ)] - end - - @testset "copy!" begin - A = Matrix{Int}(undef, (3,3)) - @test copy!(A, I) == one(A) - B = Matrix{ComplexF64}(undef, (1,2)) - @test copy!(B, J) == [λ zero(λ)] - end - - @testset "binary ops with vectors" begin - v = complex.(randn(3), randn(3)) - # As shown in #20423@GitHub, vector acts like x1 matrix when participating in linear algebra - @test v * J ≈ v * λ - @test v' * J ≈ v' * λ - @test J * v ≈ λ * v - @test J * v' ≈ λ * v' - @test v / J ≈ v / λ - @test v' / J ≈ v' / λ - @test J \ v ≈ λ \ v - @test J \ v' ≈ λ \ v' - end - - @testset "binary ops with matrices" begin - 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 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 = 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 - end -end - -@testset "hcat and vcat" 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 = 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 - I2_33 = [2 0 0; 0 2 0; 0 0 2] - I2_34 = [2 0 0 0; 0 2 0 0; 0 0 2 0] - I2_43 = [2 0 0; 0 2 0; 0 0 2; 0 0 0] - for ArrType in (Matrix, Array) - @test ArrType(2I, 3, 3)::Matrix{Int} == I2_33 - @test ArrType(2I, 3, 4)::Matrix{Int} == I2_34 - @test ArrType(2I, 4, 3)::Matrix{Int} == I2_43 - @test ArrType(2.0I, 3, 3)::Matrix{Float64} == I2_33 - @test ArrType{Real}(2I, 3, 3)::Matrix{Real} == I2_33 - @test ArrType{Float64}(2I, 3, 3)::Matrix{Float64} == I2_33 - end -end - -@testset "Diagonal construction from UniformScaling" begin - @test Diagonal(2I, 3)::Diagonal{Int} == Matrix(2I, 3, 3) - @test Diagonal(2.0I, 3)::Diagonal{Float64} == Matrix(2I, 3, 3) - @test Diagonal{Real}(2I, 3)::Diagonal{Real} == Matrix(2I, 3, 3) - @test Diagonal{Float64}(2I, 3)::Diagonal{Float64} == Matrix(2I, 3, 3) -end - -@testset "equality comparison of matrices with UniformScaling" begin - # AbstractMatrix methods - diagI = Diagonal(fill(1, 3)) - rdiagI = view(diagI, 1:2, 1:3) - bidiag = Bidiagonal(fill(2, 3), fill(2, 2), :U) - @test diagI == I == diagI # test isone(I) path / equality - @test 2diagI != I != 2diagI # test isone(I) path / inequality - @test 0diagI == 0I == 0diagI # test iszero(I) path / equality - @test 2diagI != 0I != 2diagI # test iszero(I) path / inequality - @test 2diagI == 2I == 2diagI # test generic path / equality - @test 0diagI != 2I != 0diagI # test generic path / inequality on diag - @test bidiag != 2I != bidiag # test generic path / inequality off diag - @test rdiagI != I != rdiagI # test square matrix check - # StridedMatrix specialization - denseI = [1 0 0; 0 1 0; 0 0 1] - rdenseI = [1 0 0 0; 0 1 0 0; 0 0 1 0] - alltwos = fill(2, (3, 3)) - @test denseI == I == denseI # test isone(I) path / equality - @test 2denseI != I != 2denseI # test isone(I) path / inequality - @test 0denseI == 0I == 0denseI # test iszero(I) path / equality - @test 2denseI != 0I != 2denseI # test iszero(I) path / inequality - @test 2denseI == 2I == 2denseI # test generic path / equality - @test 0denseI != 2I != 0denseI # test generic path / inequality on diag - @test alltwos != 2I != alltwos # test generic path / inequality off diag - @test rdenseI != I != rdenseI # test square matrix check - - # isequal - @test !isequal(I, I(3)) - @test !isequal(I(1), I) - @test !isequal([1], I) - @test isequal(I, 1I) - @test !isequal(2I, 3I) -end - -@testset "operations involving I should preserve eltype" begin - @test isa(Int8(1) + I, Int8) - @test isa(Float16(1) + I, Float16) - @test eltype(Int8(1)I) == Int8 - @test eltype(Float16(1)I) == Float16 - @test eltype(fill(Int8(1), 2, 2)I) == Int8 - @test eltype(fill(Float16(1), 2, 2)I) == Float16 - @test eltype(fill(Int8(1), 2, 2) + I) == Int8 - @test eltype(fill(Float16(1), 2, 2) + I) == Float16 -end - -@testset "test that UniformScaling is applied correctly for matrices of matrices" begin - LL = Bidiagonal(fill(0*I, 3), fill(1*I, 2), :L) - @test (I - LL')\[[0], [0], [1]] == (I - LL)'\[[0], [0], [1]] == fill([1], 3) -end - -# Ensure broadcasting of I is an error (could be made to work in the future) -@testset "broadcasting of I (#23197)" begin - @test_throws MethodError I .+ 1 - @test_throws MethodError I .+ [1 1; 1 1] -end - -@testset "in-place mul! and div! methods" begin - J = randn()*I - A = randn(4, 3) - C = similar(A) - target_mul = J * A - target_div = A / J - @test mul!(C, J, A) == target_mul - @test mul!(C, A, J) == target_mul - @test lmul!(J, copyto!(C, A)) == target_mul - @test rmul!(copyto!(C, A), J) == target_mul - @test ldiv!(J, copyto!(C, A)) == target_div - @test ldiv!(C, J, A) == target_div - @test rdiv!(copyto!(C, A), J) == target_div - - A = randn(4, 3) - C = randn!(similar(A)) - alpha = randn() - beta = randn() - target = J * A * alpha + C * beta - @test mul!(copy(C), J, A, alpha, beta) ≈ target - @test mul!(copy(C), A, J, alpha, beta) ≈ target - - a = randn() - C = randn(3, 3) - target_5mul = a*alpha*J + beta*C - @test mul!(copy(C), a, J, alpha, beta) ≈ target_5mul - @test mul!(copy(C), J, a, alpha, beta) ≈ target_5mul - target_5mul = beta*C # alpha = 0 - @test mul!(copy(C), a, J, 0, beta) ≈ target_5mul - target_5mul = a*alpha*Matrix(J, 3, 3) # beta = 0 - @test mul!(copy(C), a, J, alpha, 0) ≈ target_5mul - -end - -@testset "Construct Diagonal from UniformScaling" begin - @test size(I(3)) === (3,3) - @test I(3) isa Diagonal - @test I(3) == [1 0 0; 0 1 0; 0 0 1] -end - -@testset "dot" begin - A = randn(3, 3) - λ = randn() - J = UniformScaling(λ) - @test dot(A, J) ≈ dot(J, A) - @test dot(A, J) ≈ tr(A' * J) - - A = rand(ComplexF64, 3, 3) - λ = randn() + im * randn() - J = UniformScaling(λ) - @test dot(A, J) ≈ conj(dot(J, A)) - @test dot(A, J) ≈ tr(A' * J) -end - -@testset "generalized dot" begin - x = rand(-10:10, 3) - y = rand(-10:10, 3) - λ = rand(-10:10) - J = UniformScaling(λ) - @test dot(x, J, y) == λ*dot(x, y) - λ = Quaternion(0.44567, 0.755871, 0.882548, 0.423612) - x, y = Quaternion(rand(4)...), Quaternion(rand(4)...) - @test dot([x], λ*I, [y]) ≈ dot(x, λ, y) ≈ dot(x, λ*y) -end - -@testset "Factorization solutions" begin - J = complex(randn(),randn()) * I - qrp = A -> qr(A, ColumnNorm()) - - # thin matrices - X = randn(3,2) - Z = pinv(X) - for fac in (qr,qrp,svd) - F = fac(X) - @test @inferred(F \ I) ≈ Z - @test @inferred(F \ J) ≈ Z * J - end - - # square matrices - X = randn(3,3) - X = X'X + rand()I # make positive definite for cholesky - Z = pinv(X) - for fac in (bunchkaufman,cholesky,lu,qr,qrp,svd) - F = fac(X) - @test @inferred(F \ I) ≈ Z - @test @inferred(F \ J) ≈ Z * J - end - - # fat matrices - only rank-revealing variants - X = randn(2,3) - Z = pinv(X) - for fac in (qrp,svd) - F = fac(X) - @test @inferred(F \ I) ≈ Z - @test @inferred(F \ J) ≈ Z * J - end -end - -@testset "offset arrays" begin - A = OffsetArray(zeros(4,4), -1:2, 0:3) - @test sum(I + A) ≈ 3.0 - @test sum(A + I) ≈ 3.0 - @test sum(I - A) ≈ 3.0 - @test sum(A - I) ≈ -3.0 -end - -@testset "type promotion when dividing UniformScaling by matrix" begin - A = randn(5,5) - cA = complex(A) - J = (5+2im)*I - @test J/A ≈ J/cA - @test A\J ≈ cA\J -end - -end # module TestUniformscaling diff --git a/stdlib/Makefile b/stdlib/Makefile index aacf7ca30e146..a10503a3566c6 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -40,14 +40,14 @@ endef $(foreach jll,$(JLLS),$(eval $(call download-artifacts-toml,$(jll)))) STDLIBS = Artifacts Base64 CRC32c Dates FileWatching \ - Future InteractiveUtils Libdl LibGit2 LinearAlgebra Logging \ + Future InteractiveUtils Libdl LibGit2 Logging \ Markdown Mmap Printf Profile Random REPL Serialization \ SharedArrays Sockets Test TOML Unicode UUIDs \ $(JLL_NAMES) STDLIBS_EXT = Pkg Statistics LazyArtifacts LibCURL DelimitedFiles Downloads ArgTools \ Tar NetworkOptions SuiteSparse SparseArrays StyledStrings SHA Distributed \ - JuliaSyntaxHighlighting + JuliaSyntaxHighlighting LinearAlgebra $(foreach module, $(STDLIBS_EXT), $(eval $(call stdlib-external,$(module),$(shell echo $(module) | tr a-z A-Z))))