diff --git a/NEWS.md b/NEWS.md index b405b8f99..741b7be6b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,11 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.10.5] – unreleased +## [0.10.5] - 2024-10-24 + +### Added + +* the manifold `InvertibleMatrices` of invertible matrices ### Changed -* rewrote the `CONTRIBUTING.md` and adapt it to todays links and references. +* rewrote the `CONTRIBUTING.md` and adapt it to today's links and references. ## [0.10.4] - 2024-10-20 diff --git a/Project.toml b/Project.toml index 7aa155364..4c7f1e010 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Manifolds" uuid = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Antoine Levitt "] -version = "0.10.4" +version = "0.10.5" [deps] Einsum = "b7d42ee7-0b51-5a75-98ca-779d3107e4c0" diff --git a/docs/make.jl b/docs/make.jl index 8807884d8..6a5b69232 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -129,6 +129,7 @@ makedocs(; "Hamiltonian" => "manifolds/hamiltonian.md", "Hyperbolic space" => "manifolds/hyperbolic.md", "Hyperrectangle" => "manifolds/hyperrectangle.md", + "Invertible matrices" => "manifolds/invertible.md", "Lorentzian manifold" => "manifolds/lorentz.md", "Multinomial doubly stochastic matrices" => "manifolds/multinomialdoublystochastic.md", "Multinomial matrices" => "manifolds/multinomial.md", diff --git a/docs/src/index.md b/docs/src/index.md index 1a386ab19..59a44d220 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -44,7 +44,7 @@ If you use `Manifolds.jl` in your work, please cite the following } ``` -To refer to a certain version we recommend to also cite for example +To refer to a specific version, it is recommended to cite, for example, ```biblatex @software{manifoldsjl-zenodo-mostrecent, diff --git a/docs/src/manifolds/invertible.md b/docs/src/manifolds/invertible.md new file mode 100644 index 000000000..b7a1656a9 --- /dev/null +++ b/docs/src/manifolds/invertible.md @@ -0,0 +1,7 @@ +# Invertible matrices + +```@autodocs +Modules = [Manifolds] +Pages = ["manifolds/InvertibleMatrices.jl"] +Order = [:type, :function] +``` diff --git a/docs/styles/config/vocabularies/Manifolds/accept.txt b/docs/styles/config/vocabularies/Manifolds/accept.txt index a6ca4b1dd..8596c7e5b 100644 --- a/docs/styles/config/vocabularies/Manifolds/accept.txt +++ b/docs/styles/config/vocabularies/Manifolds/accept.txt @@ -1,2 +1,5 @@ +Grassmann Riemannian +Stiefel +[sS]ymplectic struct \ No newline at end of file diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 589cd9c5c..5c81913a5 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -397,7 +397,7 @@ include("manifolds/VectorFiber.jl") include("manifolds/VectorBundle.jl") include("groups/group.jl") -# Features I: Which are extended on Meta Manifolds +# Features I: Extending Meta Manifolds include("statistics.jl") # Meta Manifolds II: Products @@ -415,7 +415,7 @@ METAMANIFOLDS = [ VectorBundle, ] -# Features II: That require metas +# Features II: That require MetaManifolds include("atlases.jl") include("differentiation/ode_callback.jl") include("cotangent_space.jl") @@ -443,6 +443,7 @@ include("manifolds/GeneralizedGrassmann.jl") include("manifolds/GeneralizedStiefel.jl") include("manifolds/Hyperbolic.jl") include("manifolds/Hyperrectangle.jl") +include("manifolds/InvertibleMatrices.jl") include("manifolds/MultinomialDoublyStochastic.jl") include("manifolds/MultinomialSymmetric.jl") include("manifolds/MultinomialSymmetricPositiveDefinite.jl") @@ -681,6 +682,7 @@ export Euclidean, HeisenbergGroup, Hyperbolic, Hyperrectangle, + InvertibleMatrices, KendallsPreShapeSpace, KendallsShapeSpace, Lorentz, diff --git a/src/manifolds/InvertibleMatrices.jl b/src/manifolds/InvertibleMatrices.jl new file mode 100644 index 000000000..c898d3d36 --- /dev/null +++ b/src/manifolds/InvertibleMatrices.jl @@ -0,0 +1,106 @@ +@doc raw""" + InvertibleMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽} + +The [`AbstractManifold`](@extref `ManifoldsBase.AbstractManifold`) +consisting of the real- or complex-valued invertible matrices, that is the set + +```math +\bigl\{p ∈ 𝔽^{n×n}\ \big|\ \det(p) \neq 0 \bigr\}, +``` +where the field ``𝔽 ∈ \{ ℝ, ℂ\}``. + +# Constructor + + InvertibleMatrices(n::Int, field::AbstractNumbers=ℝ) + +Generate the manifold of ``n×n`` invertible matrices. +""" +struct InvertibleMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽} + size::T +end + +function InvertibleMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) + size = wrap_type_parameter(parameter, (n,)) + return InvertibleMatrices{field,typeof(size)}(size) +end + +function active_traits(f, ::InvertibleMatrices, args...) + return merge_traits(IsEmbeddedSubmanifold()) +end + +@doc raw""" + check_point(M::InvertibleMatrices{n,𝔽}, p; kwargs...) + +Check whether `p` is a valid manifold point on the [`InvertibleMatrices`](@ref) `M`, i.e. +whether `p` is an invertible matrix of size `(n,n)` with values from the corresponding +[`AbstractNumbers`](@extref ManifoldsBase number-system) `𝔽`. +""" +function check_point(M::InvertibleMatrices, p; kwargs...) + if det(p) == 0 + return DomainError( + det(p), + "The point $(p) does not lie on $(M), since its determinant is zero and hence it is not invertible.", + ) + end + return nothing +end + +""" + check_vector(M::InvertibleMatrices{n,𝔽}, p, X; kwargs... ) + +Check whether `X` is a tangent vector to manifold point `p` on the +[`InvertibleMatrices`](@ref) `M`, which are all matrices of size ``n×n`` +its values have to be from the correct [`AbstractNumbers`](@extref ManifoldsBase number-system). +""" +function check_vector(M::InvertibleMatrices, p, X; kwargs...) + return nothing +end + +embed(::InvertibleMatrices, p) = p +embed(::InvertibleMatrices, p, X) = X + +function get_embedding(::InvertibleMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽} + return Euclidean(n, n; field=𝔽) +end +function get_embedding(M::InvertibleMatrices{𝔽,Tuple{Int}}) where {𝔽} + n = get_parameter(M.size)[1] + return Euclidean(n, n; field=𝔽, parameter=:field) +end + +""" + is_flat(::InvertibleMatrices) + +Return true. [`InvertibleMatrices`](@ref) is a flat manifold. +""" +is_flat(M::InvertibleMatrices) = true + +@doc raw""" + manifold_dimension(M::InvertibleMatrices{n,𝔽}) + +Return the dimension of the [`InvertibleMatrices`](@ref) matrix `M` over the number system +`𝔽`, which is the same dimension as its embedding, the [`Euclidean`](@ref)`(n, n; field=𝔽)`. +""" +function manifold_dimension(M::InvertibleMatrices{<:Any,𝔽}) where {𝔽} + return manifold_dimension(get_embedding(M)) +end + +function Base.show(io::IO, ::InvertibleMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽} + return print(io, "InvertibleMatrices($(n), $(𝔽))") +end +function Base.show(io::IO, M::InvertibleMatrices{𝔽,Tuple{Int}}) where {𝔽} + n = get_parameter(M.size)[1] + return print(io, "InvertibleMatrices($(n), $(𝔽); parameter=:field)") +end + +@doc raw""" + Y = Weingarten(M::InvertibleMatrices, p, X, V) + Weingarten!(M::InvertibleMatrices, Y, p, X, V) + +Compute the Weingarten map ``\mathcal W_p`` at `p` on the [`InvertibleMatrices`](@ref) `M` with respect to the +tangent vector ``X \in T_p\mathcal M`` and the normal vector ``V \in N_p\mathcal M``. + +Since this a flat space by itself, the result is always the zero tangent vector. +""" +Weingarten(::InvertibleMatrices, p, X, V) + +Weingarten!(::InvertibleMatrices, Y, p, X, V) = fill!(Y, 0) diff --git a/test/manifolds/invertible_matrices.jl b/test/manifolds/invertible_matrices.jl new file mode 100644 index 000000000..cb45fc8b5 --- /dev/null +++ b/test/manifolds/invertible_matrices.jl @@ -0,0 +1,38 @@ +using LinearAlgebra, Manifolds, ManifoldsBase, Test + +@testset "Invertible matrices" begin + M = InvertibleMatrices(3, ℝ) + A = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] + B = [0.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] + Mc = InvertibleMatrices(3, ℂ) + Ac = [1.0im 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] + Bc = [0.0im 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] + @testset "Real invertible matrices" begin + @test repr(M) == "InvertibleMatrices(3, ℝ)" + M2 = InvertibleMatrices(3, ℝ; parameter=:field) + @test repr(M2) == "InvertibleMatrices(3, ℝ; parameter=:field)" + @test check_point(M, A) == nothing + @test_throws DomainError is_point(M, B; error=:error) + @test_throws ManifoldDomainError is_point(M, Ac; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, Ac; error=:error) + @test is_vector(M, A, A) + @test is_flat(M) + @test typeof(get_embedding(M)) === + Euclidean{ManifoldsBase.TypeParameter{Tuple{3,3}},ℝ} + @test typeof(get_embedding(M2)) === Euclidean{Tuple{Int64,Int64},ℝ} + @test embed(M, A) === A + @test embed(M, A, A) === A + @test manifold_dimension(M) == 9 + @test Weingarten(M, A, A, A) == zero(A) + end + @testset "Complex invertible matrices" begin + @test repr(Mc) == "InvertibleMatrices(3, ℂ)" + Mc2 = InvertibleMatrices(3, ℂ; parameter=:field) + @test repr(Mc2) == "InvertibleMatrices(3, ℂ; parameter=:field)" + @test manifold_dimension(Mc) == 2 * 3^2 + @test check_point(Mc, Ac) == nothing + @test_throws DomainError is_point(Mc, Bc; error=:error) + @test_throws DomainError is_point(Mc, B; error=:error) + @test is_point(Mc, A; error=:error) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index f6bd62c79..49b82cea6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -152,6 +152,7 @@ end include_test("manifolds/hamiltonian.jl") include_test("manifolds/hyperbolic.jl") include_test("manifolds/hyperrectangle.jl") + include_test("manifolds/invertible_matrices.jl") include_test("manifolds/lorentz.jl") include_test("manifolds/multinomial_doubly_stochastic.jl") include_test("manifolds/multinomial_symmetric.jl")