diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index 3d0ec1f0db..968830a64c 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -9,7 +9,7 @@ jobs: docs: name: Documentation runs-on: ubuntu-latest - if: "contains( github.event.pull_request.labels.*.name, 'preview docs') || github.ref == 'refs/heads/master' || contains(github.ref, 'refs/tags/')" + if: contains( github.event.pull_request.labels.*.name, 'preview docs') || github.ref == 'refs/heads/master' || contains(github.ref, 'refs/tags/') steps: - uses: actions/checkout@v4 - uses: quarto-dev/quarto-actions/setup@v2 diff --git a/NEWS.md b/NEWS.md index d038830cd8..d0e50a50f9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,21 @@ 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.9.13] – 2024-01-24 + +### Added + +* added the real symplectic Grassmann manifold `SymplecticGrassmann` +* Introduce the manifold of `HamiltonianMatrices` and a wrapper for `Hamiltonian` matrices +* introduce `rand(:HamiltonianMatrices)` +* extend `rand` to also `rand!` for `HamiltonianMatrices`, `SymplecticMatrices` and `SymplecticStiefel` +* implement `riemannian_gradient` conversion for `SymplecticMatrices` and `SymplecticGrassmann` + +### Deprecated + +* Rename `Symplectic` to `SimplecticMatrices` in order to have a `Symplectic` wrapper for such matrices as well in the future for the next breaking change. +* Rename `SymplecticMatrix` to `SymplecticElement` to clarify that it is the special matrix ``J_{2n}`` and not an arbitrary symplectic matrix. + ## [0.9.12] – 2024-01-21 ### Fixed @@ -43,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Improved distribution of random vector generation for rotation matrices and complex circle. -## [0.9.7] - 2023-11-14 +## [0.9.7] – 2023-11-14 ### Fixed diff --git a/Project.toml b/Project.toml index f0480b91b6..f70057a1d2 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.9.12" +version = "0.9.13" [deps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" diff --git a/docs/make.jl b/docs/make.jl index 0caf926f5a..c67ad99467 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -113,6 +113,7 @@ makedocs(; "Generalized Stiefel" => "manifolds/generalizedstiefel.md", "Generalized Grassmann" => "manifolds/generalizedgrassmann.md", "Grassmann" => "manifolds/grassmann.md", + "Hamiltonian" => "manifolds/hamiltonian.md", "Hyperbolic space" => "manifolds/hyperbolic.md", "Lorentzian manifold" => "manifolds/lorentz.md", "Multinomial doubly stochastic matrices" => "manifolds/multinomialdoublystochastic.md", @@ -133,7 +134,8 @@ makedocs(; "Symmetric positive definite" => "manifolds/symmetricpositivedefinite.md", "SPD, fixed determinant" => "manifolds/spdfixeddeterminant.md", "Symmetric positive semidefinite fixed rank" => "manifolds/symmetricpsdfixedrank.md", - "Symplectic" => "manifolds/symplectic.md", + "Symplectic Grassmann" => "manifolds/symplecticgrassmann.md", + "Symplectic matrices" => "manifolds/symplectic.md", "Symplectic Stiefel" => "manifolds/symplecticstiefel.md", "Torus" => "manifolds/torus.md", "Tucker" => "manifolds/tucker.md", diff --git a/docs/src/features/atlases.md b/docs/src/features/atlases.md index d7ca297e26..a0f4067af6 100644 --- a/docs/src/features/atlases.md +++ b/docs/src/features/atlases.md @@ -1,6 +1,6 @@ # [Atlases and charts](@id atlases_and_charts) -Atlases on an ``n``-dimensional manifold $\mathcal M$ are collections of charts ``\mathcal A = \{(U_i, φ_i) \colon i \in I\}``, where ``I`` is a (finite or infinte) index family, such that ``U_i \subseteq \mathcal M`` is an open set and each chart ``φ_i: U_i \to \mathbb{R}^n`` is a homeomorphism. This means, that ``φ_i`` is bijective – sometimes also called one-to-one and onto - and continuous, and its inverse ``φ_i^{-1}`` is continuous as well. +Atlases on an ``n``-dimensional manifold ``mathcal M``are collections of charts ``\mathcal A = \{(U_i, φ_i) \colon i \in I\}``, where ``I`` is a (finite or infinte) index family, such that ``U_i \subseteq \mathcal M`` is an open set and each chart ``φ_i: U_i → ℝ^n`` is a homeomorphism. This means, that ``φ_i`` is bijective – sometimes also called one-to-one and onto - and continuous, and its inverse ``φ_i^{-1}`` is continuous as well. The inverse ``φ_i^{-1}`` is called (local) parametrization. The resulting _parameters_ ``a=φ(p)`` of ``p`` (with respect to the chart ``φ``) are in the literature also called “(local) coordinates”. To distinguish the parameter ``a`` from [`get_coordinates`](@ref) in a basis, we use the terminology parameter in this package. @@ -11,9 +11,9 @@ For an atlas ``\mathcal A`` we further require that ``` We say that ``φ_i`` is a chart about ``p``, if ``p\in U_i``. -An atlas provides a connection between a manifold and the Euclidean space ``\mathbb{R}^n``, since +An atlas provides a connection between a manifold and the Euclidean space ``ℝ^n``, since locally, a chart about ``p`` can be used to identify its neighborhood (as long as you stay in ``U_i``) with a subset of a Euclidean space. -Most manifolds we consider are smooth, i.e. any change of charts ``φ_i \circ φ_j^{-1}: \mathbb{R}^n\to\mathbb{R}^n``, where ``i,j\in I``, is a smooth function. These changes of charts are also called transition maps. +Most manifolds we consider are smooth, i.e. any change of charts ``φ_i \circ φ_j^{-1}: ℝ^n → ℝ^n``, where ``i,j\in I``, is a smooth function. These changes of charts are also called transition maps. Most operations on manifolds in `Manifolds.jl` avoid operating in a chart through appropriate embeddings and formulas derived for particular manifolds, though atlases provide the most general way of working with manifolds. Compared to these approaches, using an atlas is often more technical and time-consuming. @@ -28,7 +28,7 @@ Operations using atlases and charts are available through the following function * [`get_parameters`](@ref Main.Manifolds.get_parameters) converts a point to its parameters with respect to the chart in a chart. * [`get_point`](@ref Main.Manifolds.get_point) converts parameters (local coordinates) in a chart to the point that corresponds to them. * [`induced_basis`](@ref Main.Manifolds.induced_basis) returns a basis of a given vector space at a point induced by a chart ``φ``. -* [`transition_map`](@ref Main.Manifolds.transition_map) converts coordinates of a point between two charts, e.g. computes ``φ_i\circ φ_j^{-1}: \mathbb{R}^n\to\mathbb{R}^n``, ``i,j\in I``. +* [`transition_map`](@ref Main.Manifolds.transition_map) converts coordinates of a point between two charts, e.g. computes ``φ_i\circ φ_j^{-1}: ℝ^n → ℝ^n``, ``i,j\in I``. While an atlas could store charts as explicit functions, it is favourable, that the [`get_parameters`] actually implements a chart ``φ``, [`get_point`](@ref) its inverse, the prametrization ``φ^{-1}``. diff --git a/docs/src/features/utilities.md b/docs/src/features/utilities.md index b1ecffe3c5..d0fa97ca44 100644 --- a/docs/src/features/utilities.md +++ b/docs/src/features/utilities.md @@ -2,7 +2,7 @@ ## Ease of notation -The following terms introduce a nicer notation for some operations, for example using the ∈ operator, $p ∈ \mathcal M$, to determine whether $p$ is a point on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $\mathcal M$. +The following terms introduce a nicer notation for some operations, for example using the ∈ operator, ``p ∈ \mathcal M`` to determine whether ``p`` is a point on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) ``\mathcal M``. ````@docs in diff --git a/docs/src/manifolds/connection.md b/docs/src/manifolds/connection.md index 9aa6b08e05..f9eb679fe6 100644 --- a/docs/src/manifolds/connection.md +++ b/docs/src/manifolds/connection.md @@ -1,6 +1,6 @@ # [Connection manifold](@id ConnectionSection) -A connection manifold always consists of a [topological manifold](https://en.wikipedia.org/wiki/Topological_manifold) together with a [connection](https://en.wikipedia.org/wiki/Connection_(mathematics)) $\Gamma$. +A connection manifold always consists of a [topological manifold](https://en.wikipedia.org/wiki/Topological_manifold) together with a [connection](https://en.wikipedia.org/wiki/Connection_(mathematics)) ``Γ``. However, often there is an implicitly assumed (default) connection, like the [`LeviCivitaConnection`](@ref) connection on a Riemannian manifold. It is not necessary to use this decorator if you implement just one (or the first) connection. diff --git a/docs/src/manifolds/essentialmanifold.md b/docs/src/manifolds/essentialmanifold.md index 7a0ef023a2..2aa07f3a1c 100644 --- a/docs/src/manifolds/essentialmanifold.md +++ b/docs/src/manifolds/essentialmanifold.md @@ -1,5 +1,5 @@ # Essential Manifold -The essential manifold is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the $3\times3$ [`Rotations`](@ref) and uses [`NestedPowerRepresentation`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.NestedPowerRepresentation). +The essential manifold is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the ``3×3`` [`Rotations`](@ref) and uses [`NestedPowerRepresentation`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.NestedPowerRepresentation). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/euclidean.md b/docs/src/manifolds/euclidean.md index 5cd8f6f498..a93aaddf1f 100644 --- a/docs/src/manifolds/euclidean.md +++ b/docs/src/manifolds/euclidean.md @@ -1,7 +1,7 @@ # [Euclidean space](@id EuclideanSection) -The Euclidean space $ℝ^n$ is a simple model space, since it has curvature constantly zero everywhere; hence, nearly all operations simplify. -The easiest way to generate an Euclidean space is to use a field, i.e. [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system), e.g. to create the $ℝ^n$ or $ℝ^{n\times n}$ you can simply type `M = ℝ^n` or `ℝ^(n,n)`, respectively. +The Euclidean space ``ℝ^n`` is a simple model space, since it has curvature constantly zero everywhere; hence, nearly all operations simplify. +The easiest way to generate an Euclidean space is to use a field, i.e. [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system), e.g. to create the ``ℝ^n`` or ``ℝ^{n×n}`` you can simply type `M = ℝ^n` or `ℝ^(n,n)`, respectively. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/fiber_bundle.md b/docs/src/manifolds/fiber_bundle.md index 9acbe33db4..9a25487e7d 100644 --- a/docs/src/manifolds/fiber_bundle.md +++ b/docs/src/manifolds/fiber_bundle.md @@ -1,10 +1,10 @@ # [Fiber bundles](@id FiberBundleSection) -Fiber bundle $E$ is a manifold that is built on top of another manifold $\mathcal M$ (base space). -It is characterized by a continuous function $Π : E → \mathcal M$. For each point $p ∈ \mathcal M$ the preimage of $p$ by $Π$, $Π^{-1}(\{p\})$ is called a fiber $F$. +Fiber bundle ``E`` is a manifold that is built on top of another manifold ``\mathcal M`` (base space). +It is characterized by a continuous function ``Π : E → \mathcal M``. For each point ``p ∈ \mathcal M`` the preimage of ``p`` by ``Π``, ``Π^{-1}(\{p\})`` is called a fiber ``F``. Bundle projection can be performed using function [`bundle_projection`](@ref). -`Manifolds.jl` primarily deals with the case of trivial bundles, where $E$ can be identified with a product $M \times F$. +`Manifolds.jl` primarily deals with the case of trivial bundles, where ``E`` can be topologically identified with a product ``M×F``. [Vector bundles](@ref VectorBundleSection) is a special case of a fiber bundle. Other examples include unit tangent bundle. Note that in general fiber bundles don't have a canonical Riemannian structure but can at least be equipped with an [Ehresmann connection](https://en.wikipedia.org/wiki/Ehresmann_connection), providing notions of parallel transport and curvature. diff --git a/docs/src/manifolds/graph.md b/docs/src/manifolds/graph.md index f744fdfb27..0dc2b0e229 100644 --- a/docs/src/manifolds/graph.md +++ b/docs/src/manifolds/graph.md @@ -1,11 +1,11 @@ # Graph manifold -For a given graph $G(V,E)$ implemented using [`Graphs.jl`](https://juliagraphs.github.io/Graphs.jl/latest/), the [`GraphManifold`](@ref) models a [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) either on the nodes or edges of the graph, depending on the [`GraphManifoldType`](@ref). -i.e., it's either a $\mathcal M^{\lvert V \rvert}$ for the case of a vertex manifold or a $\mathcal M^{\lvert E \rvert}$ for the case of a edge manifold. +For a given graph ``G(V,E)`` implemented using [`Graphs.jl`](https://juliagraphs.github.io/Graphs.jl/latest/), the [`GraphManifold`](@ref) models a [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) either on the nodes or edges of the graph, depending on the [`GraphManifoldType`](@ref). +i.e., it's either a ``\mathcal M^{\lvert V \rvert}`` for the case of a vertex manifold or a ``\mathcal M^{\lvert E \rvert}`` for the case of a edge manifold. ## Example -To make a graph manifold over $ℝ^2$ with three vertices and two edges, one can use +To make a graph manifold over ``ℝ^2`` with three vertices and two edges, one can use ```@example using Manifolds diff --git a/docs/src/manifolds/grassmann.md b/docs/src/manifolds/grassmann.md index 1fb337c887..c3dc8bfc28 100644 --- a/docs/src/manifolds/grassmann.md +++ b/docs/src/manifolds/grassmann.md @@ -10,7 +10,7 @@ Order = [:type,:function] ```@autodocs Modules = [Manifolds] -Pages = ["GrassmannStiefel.jl"] +Pages = ["manifolds/GrassmannStiefel.jl"] Order = [:type,:function] ``` diff --git a/docs/src/manifolds/hamiltonian.md b/docs/src/manifolds/hamiltonian.md new file mode 100644 index 0000000000..dc5dd48bbb --- /dev/null +++ b/docs/src/manifolds/hamiltonian.md @@ -0,0 +1,7 @@ +# Hamiltonian matrices + +```@autodocs +Modules = [Manifolds] +Pages = ["manifolds/Hamiltonian.jl"] +Order = [:type, :function] +``` diff --git a/docs/src/manifolds/metric.md b/docs/src/manifolds/metric.md index 96bde7a440..b57ea3d829 100644 --- a/docs/src/manifolds/metric.md +++ b/docs/src/manifolds/metric.md @@ -1,6 +1,6 @@ # Metric manifold -A Riemannian manifold always consists of a [topological manifold](https://en.wikipedia.org/wiki/Topological_manifold) together with a smoothly varying metric $g$. +A Riemannian manifold always consists of a [topological manifold](https://en.wikipedia.org/wiki/Topological_manifold) together with a smoothly varying metric ``g``. However, often there is an implicitly assumed (default) metric, like the usual inner product on [`Euclidean`](@ref) space. This decorator takes this into account. @@ -17,7 +17,7 @@ Pages = ["metric.md"] Depth = 2 ``` -Note that a metric manifold is has a [`IsConnectionManifold`](@ref) trait referring to the [`LeviCivitaConnection`](@ref) of the metric $g$, and thus a large part of metric manifold's functionality relies on this. +Note that a metric manifold is has a [`IsConnectionManifold`](@ref) trait referring to the [`LeviCivitaConnection`](@ref) of the metric ``g``, and thus a large part of metric manifold's functionality relies on this. Let's first look at the provided types. diff --git a/docs/src/manifolds/oblique.md b/docs/src/manifolds/oblique.md index 36f36233a4..7f0644ee28 100644 --- a/docs/src/manifolds/oblique.md +++ b/docs/src/manifolds/oblique.md @@ -1,7 +1,7 @@ # Oblique manifold -The oblique manifold $\mathcal{OB}(n,m)$ is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the (real-valued) [`Sphere`](@ref) and uses [`ArrayPowerRepresentation`](@ref). -Points on the torus are hence matrices, $x ∈ ℝ^{n,m}$. +The oblique manifold ``\mathcal{OB}(n,m)`` is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the (real-valued) [`Sphere`](@ref) and uses [`ArrayPowerRepresentation`](@ref). +Points on the torus are hence matrices, ``x ∈ ℝ^{n,m}``. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/power.md b/docs/src/manifolds/power.md index 3b9f70a3ee..c21b9faff4 100644 --- a/docs/src/manifolds/power.md +++ b/docs/src/manifolds/power.md @@ -1,8 +1,8 @@ # [Power manifold](@id PowerManifoldSection) -A power manifold is based on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $\mathcal M$ to build a $\mathcal M^{n_1 \times n_2 \times \cdots \times n_m}$. -In the case where $m=1$ we can represent a manifold-valued vector of data of length $n_1$, for example a time series. -The case where $m=2$ is useful for representing manifold-valued matrices of data of size $n_1 \times n_2$, for example certain types of images. +A power manifold is based on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) ``\mathcal M`` to build a ``\mathcal M^{n_1×n_2 ×⋯×n_m}``. +In the case where ``m=1`` we can represent a manifold-valued vector of data of length ``n_1``, for example a time series. +The case where ``m=2`` is useful for representing manifold-valued matrices of data of size ``n_1×n_2``, for example certain types of images. There are three available representations for points and vectors on a power manifold: @@ -17,7 +17,7 @@ Below are some examples of usage of these representations. There are two ways to store the data: in a multidimensional array or in a nested array. Let's look at an example for both. -Let $\mathcal M$ be `Sphere(2)` the 2-sphere and we want to look at vectors of length 4. +Let ``\mathcal M`` be `Sphere(2)` the 2-sphere and we want to look at vectors of length 4. ### `ArrayPowerRepresentation` diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index cd1e38aafb..550de31134 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # [Product manifold](@id ProductManifoldSection) -Product manifold $\mathcal M = \mathcal{M}_1 × \mathcal{M}_2 × … × \mathcal{M}_n$ of manifolds $\mathcal{M}_1, \mathcal{M}_2, …, \mathcal{M}_n$. -Points on the product manifold can be constructed using `ArrayPartition` (from `RecursiveArrayTools.jl`) with canonical projections $Π_i : \mathcal{M} → \mathcal{M}_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.submanifold_component-Tuple). +Product manifold ``\mathcal M = \mathcal{M}_1 × \mathcal{M}_2 × … × \mathcal{M}_n`` of manifolds ``\mathcal{M}_1, \mathcal{M}_2, …, \mathcal{M}_n``. +Points on the product manifold can be constructed using `ArrayPartition` (from `RecursiveArrayTools.jl`) with canonical projections ``Π_i : \mathcal{M} → \mathcal{M}_i`` for ``i ∈ 1, 2, …, n`` provided by [`submanifold_component`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.submanifold_component-Tuple). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index 0458a7e58c..fce5d8fde9 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -1,11 +1,11 @@ # Rotations -The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n × n}$, i.e. +The manifold ``\mathrm{SO}(n)`` of orthogonal matrices with determinant ``+1`` in ``ℝ^{n×n}``, i.e. -$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n × n} \big| R R^{\mathrm{T}} = +``\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n×n} \big| R R^{\mathrm{T}} = R^{\mathrm{T}}R = I_n, \det(R) = 1 \bigr\}$ -The Lie group $\mathrm{SO}(n)$ is a subgroup of the orthogonal group $\mathrm{O}(n)$ and also known as the special orthogonal group or the set of rotations group. +The Lie group ``\mathrm{SO}(n)`` is a subgroup of the orthogonal group ``\mathrm{O}(n)`` and also known as the special orthogonal group or the set of rotations group. See also [`SpecialOrthogonal`](@ref), which is this manifold equipped with the group operation. The tangent space to a point ``p ∈ \mathrm{SO}(n)`` is given by @@ -23,9 +23,9 @@ In the notation above, this means we just store the component ``Y`` of ``X``. This convention allows for more efficient operations on tangent vectors. Tangent spaces at different points are different vector spaces. -Let $L_R: \mathrm{SO}(n) → \mathrm{SO}(n)$ where $R ∈ \mathrm{SO}(n)$ be the left-multiplication by $R$, that is $L_R(S) = RS$. -The tangent space at rotation $R$, $T_R \mathrm{SO}(n)$, is related to the tangent space at the identity rotation $I_n$ by the differential of $L_R$ at identity, $(\mathrm{d}L_R)_{I_n} : T_{I_n} \mathrm{SO}(n) → T_R \mathrm{SO}(n)$. -To convert the tangent vector representation at the identity rotation $X ∈ T_{I_n} \mathrm{SO}(n)$ (i.e., the default) to the matrix representation of the corresponding tangent vector $Y$ at a rotation $R$ use the [`embed`](@ref embed(::Manifolds.Rotations, :Any...)) which implements the following multiplication: $Y = RX ∈ T_R \mathrm{SO}(n)$. +Let ``L_R: \mathrm{SO}(n) → \mathrm{SO}(n)`` where ``R ∈ \mathrm{SO}(n)`` be the left-multiplication by ``R``, that is ``L_R(S) = RS``. +The tangent space at rotation ``R``, ``T_R \mathrm{SO}(n)``, is related to the tangent space at the identity rotation ``I_n`` by the differential of ``L_R`` at identity, $(\mathrm{d}L_R)_{I_n} : T_{I_n} \mathrm{SO}(n) → T_R \mathrm{SO}(n)``. +To convert the tangent vector representation at the identity rotation ``X ∈ T_{I_n} \mathrm{SO}(n)`` (i.e., the default) to the matrix representation of the corresponding tangent vector ``Y`` at a rotation ``R`` use the [`embed`](@ref embed(::Manifolds.Rotations, :Any...)) which implements the following multiplication: ``Y = RX ∈ T_R \mathrm{SO}(n)``. You can compare the functions [`log`](@ref log(::Manifolds.Rotations, :Any...)) and [`exp`](@ref exp(::Manifolds.Rotations, ::Any...)) to see how it works in practice. Several common functions are also implemented together with [orthogonal and unitary matrices](@ref generalunitarymatrices). diff --git a/docs/src/manifolds/shapespace.md b/docs/src/manifolds/shapespace.md index 2867825624..e2df610aae 100644 --- a/docs/src/manifolds/shapespace.md +++ b/docs/src/manifolds/shapespace.md @@ -1,6 +1,6 @@ # Shape spaces -Shape spaces are spaces of ``k`` points in ``\mathbb{R}^n`` up to simultaneous action of a group on all points. +Shape spaces are spaces of ``k`` points in ``ℝ^n`` up to simultaneous action of a group on all points. The most commonly encountered are Kendall's pre-shape and shape spaces. In the case of the Kendall's pre-shape spaces the action is translation and scaling. In the case of the Kendall's shape spaces the action is translation, scaling and rotation. diff --git a/docs/src/manifolds/sphere.md b/docs/src/manifolds/sphere.md index c4ae99589e..47fa663cc1 100644 --- a/docs/src/manifolds/sphere.md +++ b/docs/src/manifolds/sphere.md @@ -4,14 +4,14 @@ AbstractSphere ``` -The classical sphere, i.e. unit norm (real- or complex-valued) vectors can be generated as usual: to create the 2-dimensional sphere (in $ℝ^3$), use `Sphere(2)` and `Sphere(2,ℂ)`, respectively. +The classical sphere, i.e. unit norm (real- or complex-valued) vectors can be generated as usual: to create the 2-dimensional sphere (in ``ℝ^3``), use `Sphere(2)` and `Sphere(2,ℂ)`, respectively. ```@docs Sphere ``` For the higher-dimensional arrays, for example unit (Frobenius) norm matrices, the manifold is generated using the size of the matrix. -To create the unit sphere of $3×2$ real-valued matrices, write `ArraySphere(3,2)` and the complex case is done – as for the [`Euclidean`](@ref) case – with an keyword argument `ArraySphere(3,2; field=ℂ)`. This case also covers the classical sphere as a special case, but you specify the size of the vectors/embedding instead: The 2-sphere can here be generated `ArraySphere(3)`. +To create the unit sphere of ``3×2`` real-valued matrices, write `ArraySphere(3,2)` and the complex case is done – as for the [`Euclidean`](@ref) case – with an keyword argument `ArraySphere(3,2; field=ℂ)`. This case also covers the classical sphere as a special case, but you specify the size of the vectors/embedding instead: The 2-sphere can here be generated `ArraySphere(3)`. ```@docs ArraySphere diff --git a/docs/src/manifolds/symplectic.md b/docs/src/manifolds/symplectic.md index 3673e6be2b..4a3e9a68de 100644 --- a/docs/src/manifolds/symplectic.md +++ b/docs/src/manifolds/symplectic.md @@ -1,12 +1,12 @@ -# Symplectic +# Symplectic matrices -The [`Symplectic`](@ref) manifold, denoted $\operatorname{Sp}(2n, \mathbb{F})$, is a closed, embedded, submanifold of -$\mathbb{F}^{2n \times 2n}$ that represents transformations into symplectic subspaces which keep the -canonical symplectic form over $\mathbb{F}^{2n \times 2n }$ invariant under the standard embedding inner product. +The [`SymplecticMatrices`](@ref) manifold, denoted ``\operatorname{Sp}(2n, 𝔽)``, is a closed, embedded, submanifold of +``𝔽^{2n×2n}`` that represents transformations into symplectic subspaces which keep the +canonical symplectic form over ``𝔽^{2n×2n}`` invariant under the standard embedding inner product. The canonical symplectic form is a non-degenerate bilinear and skew symmetric map -$\omega\colon \mathbb{F}^{2n} \times \mathbb{F}^{2n} -\rightarrow \mathbb{F}$, given by -$\omega(x, y) = x^T Q_{2n} y$ for elements $x, y \in \mathbb{F}^{2n}$, with +``\omega\colon 𝔽 𝔽^{2n}×𝔽^{2n} +→ 𝔽``, given by +``\omega(x, y) = x^T Q_{2n} y`` for elements ``x, y \in 𝔽^{2n}``, with ````math Q_{2n} = \begin{bmatrix} @@ -14,22 +14,22 @@ $\omega(x, y) = x^T Q_{2n} y$ for elements $x, y \in \mathbb{F}^{2n}$, with -I_n & 0_n \end{bmatrix}. ```` -That means that an element $p \in \operatorname{Sp}(2n)$ must fulfill the requirement that +That means that an element ``p \in \operatorname{Sp}(2n)`` must fulfill the requirement that ````math \omega (p x, p y) = x^T(p^TQp)y = x^TQy = \omega(x, y), ```` -leading to the requirement on $p$ that $p^TQp = Q$. +leading to the requirement on ``p`` that ``p^TQp = Q``. -The symplectic manifold also forms a group under matrix multiplication, called the $\textit{symplectic group}$. +The symplectic manifold also forms a group under matrix multiplication, called the ``\textit{symplectic group}``. Since all the symplectic matrices necessarily have determinant one, the [symplectic group](https://en.wikipedia.org/wiki/Symplectic_group) -$\operatorname{Sp}(2n, \mathbb{F})$ is a subgroup of the special linear group, $\operatorname{SL}(2n, \mathbb{F})$. When the underlying -field is either $\mathbb{R}$ or $\mathbb{C}$ the symplectic group with a manifold structure constitutes a Lie group, with the Lie +``\operatorname{Sp}(2n, 𝔽)`` is a subgroup of the special linear group, ``\operatorname{SL}(2n, 𝔽)``. When the underlying +field is either ``ℝ`` or ``ℂ`` the symplectic group with a manifold structure constitutes a Lie group, with the Lie Algebra ````math - \mathfrak{sp}(2n,F) = \{H \in \mathbb{F}^{2n \times 2n} \;|\; Q H + H^{T} Q = 0\}. + \mathfrak{sp}(2n,F) = \{H \in 𝔽^{2n×2n} \;|\; Q H + H^{T} Q = 0\}. ```` This set is also known as the [Hamiltonian matrices](https://en.wikipedia.org/wiki/Hamiltonian_matrix), which have the -property that $(QH)^T = QH$ and are commonly used in physics. +property that ``(QH)^T = QH`` and are commonly used in physics. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/symplecticgrassmann.md b/docs/src/manifolds/symplecticgrassmann.md new file mode 100644 index 0000000000..d8d061820f --- /dev/null +++ b/docs/src/manifolds/symplecticgrassmann.md @@ -0,0 +1,30 @@ +# (Real) Symplectic Grassmann + +```@autodocs +Modules = [Manifolds] +Pages = ["manifolds/SymplecticGrassmann.jl"] +Order = [:type, :function] +``` + +## The (default) symplectic Stiefel representation + +```@autodocs +Modules = [Manifolds] +Pages = ["manifolds/SymplecticGrassmannStiefel.jl"] +Order = [:type, :function] +``` + +## The symplectic projector representation + +```@autodocs +Modules = [Manifolds] +Pages = ["manifolds/SymplecticGrassmannProjector.jl"] +Order = [:type, :function] +``` + +## Literature + +```@bibliography +Pages = ["symplecticgrassmann.md"] +Canonical=false +``` \ No newline at end of file diff --git a/docs/src/manifolds/symplecticstiefel.md b/docs/src/manifolds/symplecticstiefel.md index 502f97e211..e2890dc566 100644 --- a/docs/src/manifolds/symplecticstiefel.md +++ b/docs/src/manifolds/symplecticstiefel.md @@ -1,13 +1,13 @@ -# Symplectic Stiefel +# (Real) Symplectic Stiefel -The [`SymplecticStiefel`](@ref) manifold, denoted $\operatorname{SpSt}(2n, 2k)$, -represents canonical symplectic bases of $2k$ dimensonal symplectic subspaces of $\mathbb{R}^{2n \times 2n}$. -This means that the columns of each element $p \in \operatorname{SpSt}(2n, 2k) \subset \mathbb{R}^{2n \times 2k}$ -constitute a canonical symplectic basis of $\operatorname{span}(p)$. +The [`SymplecticStiefel`](@ref) manifold, denoted ``\operatorname{SpSt}(2n, 2k)``, +represents canonical symplectic bases of ``2k`` dimensonal symplectic subspaces of ``ℝ^{2n×2n}``. +This means that the columns of each element ``p \in \operatorname{SpSt}(2n, 2k) \subset ℝ^{2n×2k}$ +constitute a canonical symplectic basis of ``\operatorname{span}(p)``. The canonical symplectic form is a non-degenerate, bilinear, and skew symmetric map -$\omega_{2k}\colon \mathbb{F}^{2k} \times \mathbb{F}^{2k} -\rightarrow \mathbb{F}$, given by -$\omega_{2k}(x, y) = x^T Q_{2k} y$ for elements $x, y \in \mathbb{F}^{2k}$, with +``\omega_{2k}\colon 𝔽^{2k}×𝔽^{2k} +→ 𝔽``, given by +``\omega_{2k}(x, y) = x^T Q_{2k} y`` for elements ``x, y \in 𝔽^{2k}``, with ````math Q_{2k} = \begin{bmatrix} @@ -15,12 +15,12 @@ $\omega_{2k}(x, y) = x^T Q_{2k} y$ for elements $x, y \in \mathbb{F}^{2k}$, with -I_k & 0_k \end{bmatrix}. ```` -Specifically given an element $p \in \operatorname{SpSt}(2n, 2k)$ we require that +Specifically given an element ``p \in \operatorname{SpSt}(2n, 2k)`` we require that ````math - \omega_{2n} (p x, p y) = x^T(p^TQ_{2n}p)y = x^TQ_{2k}y = \omega_{2k}(x, y) \;\forall\; x, y \in \mathbb{F}^{2k}, + \omega_{2n} (p x, p y) = x^T(p^TQ_{2n}p)y = x^TQ_{2k}y = \omega_{2k}(x, y) \;\forall\; x, y \in 𝔽^{2k}, ```` -leading to the requirement on $p$ that $p^TQ_{2n}p = Q_{2k}$. -In the case that $k = n$, this manifold reduces to the [`Symplectic`](@ref) manifold, which is also known as the symplectic group. +leading to the requirement on ``p`` that ``p^TQ_{2n}p = Q_{2k}``. +In the case that ``k = n``, this manifold reduces to the [`SymplecticMatrices`](@ref) manifold, which is also known as the symplectic group. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/torus.md b/docs/src/manifolds/torus.md index efd21ed246..17a695ca3a 100644 --- a/docs/src/manifolds/torus.md +++ b/docs/src/manifolds/torus.md @@ -1,11 +1,11 @@ # Torus -The torus $𝕋^d ≅ [-π,π)^d$ is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the (real-valued) [`Circle`](@ref) and uses [`ArrayPowerRepresentation`](@ref). -Points on the torus are hence row vectors, $x ∈ ℝ^{d}$. +The torus ``𝕋^d ≅ [-π,π)^d`` is modeled as an [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) of the (real-valued) [`Circle`](@ref) and uses [`ArrayPowerRepresentation`](@ref). +Points on the torus are hence row vectors, ``x ∈ ℝ^{d}``. ## Example -The following code can be used to make a three-dimensional torus $𝕋^3$ and compute a tangent vector: +The following code can be used to make a three-dimensional torus ``𝕋^3`` and compute a tangent vector: ```@example using Manifolds @@ -27,7 +27,7 @@ Order = [:type, :function] ## Embedded Torus -Two-dimensional torus embedded in $ℝ^3$. +Two-dimensional torus embedded in ``ℝ^3``. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 19d732ca6d..4f41be455e 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -1,8 +1,8 @@ # [Vector bundles](@id VectorBundleSection) -Vector bundle $E$ is a special case of a [fiber bundle](@ref FiberBundleSection) where each fiber is a vector space. +Vector bundle ``E`` is a special case of a [fiber bundle](@ref FiberBundleSection) where each fiber is a vector space. -Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point $p$. +Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point ``p``. An object representing a tangent bundle can be obtained using the constructor called `TangentBundle`. There is also another type, [`VectorSpaceFiber`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.VectorSpaceFiber), that represents a specific fiber at a given point. @@ -23,7 +23,7 @@ Order = [:constant, :type, :function] ## Example -The following code defines a point on the tangent bundle of the sphere $S^2$ and a tangent vector to that point. +The following code defines a point on the tangent bundle of the sphere ``S^2`` and a tangent vector to that point. ```@example tangent-bundle using Manifolds diff --git a/docs/src/misc/notation.md b/docs/src/misc/notation.md index ee7975861d..a87fd187a8 100644 --- a/docs/src/misc/notation.md +++ b/docs/src/misc/notation.md @@ -11,30 +11,30 @@ Within the documented functions, the utf8 symbols are used whenever possible, as |:--:|:--------------- |:--:|:-- | | ``\tau_p`` | action map by group element ``p`` | ``\mathrm{L}_p``, ``\mathrm{R}_p`` | either left or right | | ``\operatorname{Ad}_p(X)`` | adjoint action of element ``p`` of a Lie group on the element ``X`` of the corresponding Lie algebra | | | -| ``\times`` | Cartesian product of two manifolds | | see [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) | +| ``×`` | Cartesian product of two manifolds | | see [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) | | ``^{\wedge}`` | (n-ary) Cartesian power of a manifold | | see [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) | -| ``\cdot^\mathrm{H}`` | conjugate/Hermitian transpose | | +| ``⋅^\mathrm{H}`` | conjugate/Hermitian transpose | | | ``a`` | coordinates of a point in a chart | | see [`get_parameters`](@ref) | | ``\frac{\mathrm{D}}{\mathrm{d}t}`` | covariant derivative of a vector field ``X(t)`` | | | | ``T^*_p \mathcal M`` | the cotangent space at ``p`` | | | | ``ξ`` | a cotangent vector from ``T^*_p \mathcal M`` | ``ξ_1, ξ_2,… ,η,\zeta`` | sometimes written with base point ``ξ_p``. | -| ``\mathrm{d}\phi_p(q)`` | Differential of a map ``\phi: \mathcal M \to \mathcal N`` with respect to ``p`` at a point ``q``. For functions of multiple variables, for example ``\phi(p, p_1)`` where ``p \in \mathcal M`` and ``p_1 \in \mathcal M_1``, variable ``p`` is explicitly stated to specify with respect to which argument the differential is calculated. | ``\mathrm{d}\phi_q``, ``(\mathrm{d}\phi)_q``, ``(\phi_*)_q``, ``D_p\phi(q)`` | pushes tangent vectors ``X \in T_q \mathcal M`` forward to ``\mathrm{d}\phi_p(q)[X] \in T_{\phi(q)} \mathcal N`` | +| ``\mathrm{d}\phi_p(q)`` | Differential of a map ``\phi: \mathcal M → \mathcal N`` with respect to ``p`` at a point ``q``. For functions of multiple variables, for example ``\phi(p, p_1)`` where ``p \in \mathcal M`` and ``p_1 \in \mathcal M_1``, variable ``p`` is explicitly stated to specify with respect to which argument the differential is calculated. | ``\mathrm{d}\phi_q``, ``(\mathrm{d}\phi)_q``, ``(\phi_*)_q``, ``D_p\phi(q)`` | pushes tangent vectors ``X \in T_q \mathcal M`` forward to ``\mathrm{d}\phi_p(q)[X] \in T_{\phi(q)} \mathcal N`` | | ``n`` | dimension (of a manifold) | ``n_1,n_2,\ldots,m, \dim(\mathcal M)``| for the real dimension sometimes also ``\dim_{\mathbb R}(\mathcal M)``| -| ``d(\cdot,\cdot)`` | (Riemannian) distance | ``d_{\mathcal M}(\cdot,\cdot)`` | | +| ``d(⋅,⋅)`` | (Riemannian) distance | ``d_{\mathcal M}(⋅,⋅)`` | | | ``\exp_p X`` | exponential map at ``p \in \mathcal M`` of a vector ``X \in T_p \mathcal M`` | ``\exp_p(X)`` | | | ``F`` | a fiber | | see [`Fiber`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#Fiber) | | ``\mathbb F`` | a field, usually ``\mathbb F \in \{\mathbb R,\mathbb C, \mathbb H\}``, i.e. the real, complex, and quaternion numbers, respectively. | |field a manifold or a basis is based on | | ``\gamma`` | a geodesic | ``\gamma_{p;q}``, ``\gamma_{p,X}`` | connecting two points ``p,q`` or starting in ``p`` with velocity ``X``. | -| ``\operatorname{grad} f(p)`` | (Riemannian) gradient of function ``f \colon \mathcal{M} \to \mathbb{R}`` at ``p \in \mathcal{M}`` | | | -| ``\nabla f(p)`` | (Euclidean) gradient of function ``f \colon \mathcal{M} \to \mathbb{R}`` at ``p \in \mathcal{M}`` but thought of as evaluated in the embedding | `G` | | +| ``\operatorname{grad} f(p)`` | (Riemannian) gradient of function ``f \colon \mathcal{M} → ℝ`` at ``p \in \mathcal{M}`` | | | +| ``\nabla f(p)`` | (Euclidean) gradient of function ``f \colon \mathcal{M} → ℝ`` at ``p \in \mathcal{M}`` but thought of as evaluated in the embedding | `G` | | | ``\circ`` | a group operation | | -| ``\cdot^\mathrm{H}`` | Hermitian or conjugate transposed for both complex or quaternion matrices| | -| ``\operatorname{Hess} f(p)`` | (Riemannian) Hessian of function ``f \colon T_p\mathcal{M} \to T_p\mathcal M`` (i.e. the 1-1-tensor form) at ``p \in \mathcal{M}`` | | | +| ``⋅^\mathrm{H}`` | Hermitian or conjugate transposed for both complex or quaternion matrices| | +| ``\operatorname{Hess} f(p)`` | (Riemannian) Hessian of function ``f \colon T_p\mathcal{M} → T_p\mathcal M`` (i.e. the 1-1-tensor form) at ``p \in \mathcal{M}`` | | | | ``\nabla^2 f(p)`` | (Euclidean) Hessian of function ``f`` in the embedding | `H` | | | ``e`` | identity element of a group | | -| ``I_k`` | identity matrix of size ``k\times k`` | | +| ``I_k`` | identity matrix of size ``k×k`` | | | ``k`` | indices | ``i,j`` | | -| ``\langle\cdot,\cdot\rangle`` | inner product (in ``T_p \mathcal M``) | ``\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)`` | +| ``\langle⋅,⋅\rangle`` | inner product (in ``T_p \mathcal M``) | ``\langle⋅,⋅\rangle_p, g_p(⋅,⋅)`` | | ``\operatorname{retr}^{-1}_pq``| an inverse retraction | | | ``\mathfrak g`` | a Lie algebra | | | ``\mathcal{G}`` | a (Lie) group | | @@ -50,15 +50,16 @@ Within the documented functions, the utf8 symbols are used whenever possible, as | ``p`` | a point on ``\mathcal M`` | ``p_1, p_2, \ldots,q`` | for 3 points one might use ``x,y,z`` | | ``\operatorname{retr}_pX``| a retraction | | | ``ξ`` | a set of tangent vectors | ``\{X_1,\ldots,X_n\}`` | | +| ``J_{2n} \in ℝ^{2n×2n}`` | the [`SymplecticElement`](@ref) | | | | ``T_p \mathcal M`` | the tangent space at ``p`` | | | | ``X`` | a tangent vector from ``T_p \mathcal M`` | ``X_1,X_2,\ldots,Y,Z`` | sometimes written with base point ``X_p`` | | ``\operatorname{tr}`` | trace (of a matrix) | | -| ``\cdot^\mathrm{T}`` | transposed | | -| ``e_i \in \mathbb R^n`` | the ``i``th unit vector | ``e_i^n`` | the space dimension (``n``) is omited, when clear from context +| ``⋅^\mathrm{T}`` | transposed | | +| ``e_i \in \mathbb R^n`` | the ``i``th unit vector | ``e_i^n`` | the space dimension (``n``) is omitted, when clear from context | ``B`` | a vector bundle | | | ``\mathcal T_{q\gets p}X`` | vector transport | | of the vector ``X`` from ``T_p\mathcal M`` to ``T_q\mathcal M`` | ``\mathcal T_{p,Y}X`` | vector transport in direction ``Y`` | | of the vector ``X`` from ``T_p\mathcal M`` to ``T_q\mathcal M``, where ``q`` is deretmined by ``Y``, for example using the exponential map or some retraction. | | ``\operatorname{Vol}(\mathcal M)`` | volume of manifold ``\mathcal M`` | | | ``\theta_p(X)`` | volume density for vector ``X`` tangent at point ``p`` | | | ``\mathcal W`` | the Weingarten map ``\mathcal W: T_p\mathcal M × N_p\mathcal M → T_p\mathcal M`` | ``\mathcal W_p`` | the second notation to emphasize the dependency of the point ``p\in\mathcal M`` | -| ``0_k`` | the ``k\times k`` zero matrix. | | +| ``0_k`` | the ``k×k`` zero matrix. | | diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 89c466e728..0ab1a05787 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -4,6 +4,7 @@ module Manifolds import Base: + ^, angle, copyto!, convert, @@ -444,8 +445,6 @@ include("manifolds/SymmetricPositiveDefiniteAffineInvariant.jl") include("manifolds/SymmetricPositiveDefiniteLogCholesky.jl") include("manifolds/SymmetricPositiveDefiniteLogEuclidean.jl") include("manifolds/SymmetricPositiveSemidefiniteFixedRank.jl") -include("manifolds/Symplectic.jl") -include("manifolds/SymplecticStiefel.jl") include("manifolds/Tucker.jl") # include("manifolds/ProbabilitySimplex.jl") @@ -462,6 +461,13 @@ include("manifolds/KendallsShapeSpace.jl") # Introduce the quotient, Grassmann, only after Stiefel include("manifolds/Grassmann.jl") +# Introduce Symplectic and so on manifolds only after Grassmann +# Since that defines the StiefelPoint, StiefelTVector +include("manifolds/Symplectic.jl") +include("manifolds/Hamiltonian.jl") # Hamiltonian requires symplectic +include("manifolds/SymplecticStiefel.jl") +include("manifolds/SymplecticGrassmann.jl") # Requires SymplecticStiefel + # Product or power based manifolds include("manifolds/Torus.jl") include("manifolds/Multinomial.jl") @@ -520,7 +526,7 @@ Base.in(p, M::AbstractManifold; kwargs...) = is_point(M, p, false; kwargs...) Base.in(p, TpM::TangentSpace; kwargs...) X ∈ TangentSpace(M, p) -Check whether `X` is a tangent vector from (in) the tangent space $T_p\mathcal M$, i.e. +Check whether `X` is a tangent vector from (in) the tangent space ``T_p\mathcal M``, i.e. the [`TangentSpace`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.TangentSpace) at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. This method uses [`is_vector`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.is_vector) deactivating the error throw option. @@ -631,6 +637,7 @@ export Euclidean, GeneralizedGrassmann, GeneralizedStiefel, Grassmann, + HamiltonianMatrices, HeisenbergGroup, Hyperbolic, KendallsPreShapeSpace, @@ -659,8 +666,10 @@ export Euclidean, SPDFixedDeterminant, SymmetricPositiveSemidefiniteFixedRank, Symplectic, + SymplecticGrassmann, + SymplecticMatrices, SymplecticStiefel, - SymplecticMatrix, + SymplecticElement, Torus, Tucker, UnitaryMatrices @@ -682,7 +691,7 @@ export HyperboloidTVector, ProjectorTVector, StiefelTVector export AbstractNumbers, ℝ, ℂ, ℍ - +export Hamiltonian # decorator manifolds export AbstractDecoratorManifold export IsIsometricEmbeddedManifold, IsEmbeddedManifold, IsEmbeddedSubmanifold @@ -775,6 +784,7 @@ export CachedBasis, export ComponentManifoldError, CompositeManifoldError # Functions on Manifolds export ×, + ^, action_side, allocate, allocate_result, @@ -841,6 +851,7 @@ export ×, is_default_metric, is_flat, is_group_manifold, + is_hamiltonian, is_identity, is_point, is_vector, @@ -900,6 +911,8 @@ export ×, skewness, std, sym_rem, + symplectic_inverse, + symplectic_inverse!, symplectic_inverse_times, symplectic_inverse_times!, submanifold, diff --git a/src/cotangent_space.jl b/src/cotangent_space.jl index 7cdef27ec3..cfa271f52d 100644 --- a/src/cotangent_space.jl +++ b/src/cotangent_space.jl @@ -29,7 +29,7 @@ from the vector space of type `M` at point `p` from the underlying `AbstractMani The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$♭ : T\mathcal M → T^{*}\mathcal M$ +``♭ : T\mathcal M → T^{*}\mathcal M`` """ flat(M::AbstractManifold, p, X) = RieszRepresenterCotangentVector(M, p, X) function flat(M::AbstractManifold, p, X::TFVector{<:Any,<:AbstractBasis}) @@ -145,7 +145,7 @@ from the vector space `M` at point `p` from the underlying `AbstractManifold`. The function can be used for example to transform vectors from the cotangent bundle to vectors from the tangent bundle -$♯ : T^{*}\mathcal M → T\mathcal M$ +``♯ : T^{*}\mathcal M → T\mathcal M`` """ sharp(::AbstractManifold, p, ξ) diff --git a/src/deprecated.jl b/src/deprecated.jl index 03601238b0..eea4fe49ac 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -4,3 +4,6 @@ f, ) @deprecate ExtrinsicEstimation() ExtrinsicEstimation(EfficientEstimator()) + +Base.@deprecate_binding Symplectic SymplecticMatrices +Base.@deprecate_binding SymplecticMatrix SymplecticElement diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index 8f2f24d21b..da9abdc29e 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -268,11 +268,11 @@ function Random.rand!(rng::AbstractRNG, G::GeneralLinear, pX; kwargs...) end function Base.show(io::IO, ::GeneralLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "GeneralLinear($n, $𝔽)") + return print(io, "GeneralLinear($n, $(𝔽))") end function Base.show(io::IO, M::GeneralLinear{Tuple{Int},𝔽}) where {𝔽} n = get_parameter(M.size)[1] - return print(io, "GeneralLinear($n, $𝔽; parameter=:field)") + return print(io, "GeneralLinear($n, $(𝔽); parameter=:field)") end translate_diff(::GeneralLinear, p, q, X, ::LeftForwardAction) = X diff --git a/src/groups/group.jl b/src/groups/group.jl index 9d138b975e..45572d50f2 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -118,7 +118,7 @@ abstract type ActionDirection end @doc raw""" LeftAction() -Left action of a group on a manifold. For a forward action ``α: G × X → X`` it is characterized by +Left action of a group on a manifold. For a forward action ``α: G×X → X`` it is characterized by ```math α(g, α(h, x)) = α(gh, x) ``` @@ -129,7 +129,7 @@ struct LeftAction <: ActionDirection end """ RightAction() -Right action of a group on a manifold. For a forward action ``α: G × X → X`` it is characterized by +Right action of a group on a manifold. For a forward action ``α: G×X → X`` it is characterized by ```math α(g, α(h, x)) = α(hg, x) ``` @@ -149,14 +149,14 @@ abstract type GroupActionSide end """ LeftSide() -An action of a group on a manifold that acts from the left side, i.e. ``α: G × X → X``. +An action of a group on a manifold that acts from the left side, i.e. ``α: G×X → X``. """ struct LeftSide <: GroupActionSide end """ RightSide() -An action of a group on a manifold that acts from the right side, i.e. ``α: X × G → X``. +An action of a group on a manifold that acts from the right side, i.e. ``α: X×G → X``. """ struct RightSide <: GroupActionSide end @@ -1037,7 +1037,7 @@ Given an element ``q ∈ \mathcal{G}``, compute the right inverse of the group e ```` where ``e`` here is the [`Identity`](@ref) element, that is, ``1`` for numeric ``q`` or the -identity matrix ``I_m`` for matrix ``q ∈ ℝ^{m × m}``. +identity matrix ``I_m`` for matrix ``q ∈ ℝ^{m×m}``. Since this function also depends on the group operation, make sure to implement either diff --git a/src/groups/heisenberg.jl b/src/groups/heisenberg.jl index 2c5793cae3..2b9e815def 100644 --- a/src/groups/heisenberg.jl +++ b/src/groups/heisenberg.jl @@ -2,7 +2,7 @@ @doc raw""" HeisenbergGroup{T} <: AbstractDecoratorManifold{ℝ} -Heisenberg group `HeisenbergGroup(n)` is the group of ``(n+2) × (n+2)`` matrices [BinzPods:2008](@cite) +Heisenberg group `HeisenbergGroup(n)` is the group of ``(n+2)×(n+2)`` matrices [BinzPods:2008](@cite) ```math \begin{bmatrix} 1 & \mathbf{a} & c \\ diff --git a/src/groups/metric.jl b/src/groups/metric.jl index ed3552421b..1fcff4a045 100644 --- a/src/groups/metric.jl +++ b/src/groups/metric.jl @@ -10,7 +10,7 @@ ) -> Bool Check whether the metric on the group $\mathcal{G}$ is (approximately) invariant using a set of predefined -points. Namely, for $p ∈ \mathcal{G}$, $X,Y ∈ T_p \mathcal{G}$, a metric $g$, and a +points. Namely, for $p ∈ \mathcal{G}$, $X,Y ∈ T_p \mathcal{G}$, a metric ``g``, and a translation map $τ_q$ in the specified direction, check for each $q ∈ \mathcal{G}$ that the following condition holds: diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index fc803a5363..275f18cb61 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -121,7 +121,7 @@ The formula reads ````math p_{rot} = (\cos(θ))p + (k×p) \sin(θ) + k (k⋅p) (1-\cos(θ)), ```` -where $k$ is the vector `A.axis` and `⋅` is the dot product. +where ``k`` is the vector `A.axis` and `⋅` is the dot product. """ function apply(A::RotationAroundAxisAction, θ, p) sθ, cθ = sincos(θ) diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 337ffa424e..d814c85da3 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -10,7 +10,7 @@ Special Euclidean group $\mathrm{SE}(n)$, the group of rigid motions. \mathrm{SE}(n) ≐ \mathrm{T}(n) ⋊_θ \mathrm{SO}(n), ```` -where $θ$ is the canonical action of $\mathrm{SO}(n)$ on $\mathrm{T}(n)$ by vector rotation. +where ``θ`` is the canonical action of ``\mathrm{SO}(n)`` on $\mathrm{T}(n)$ by vector rotation. This constructor is equivalent to calling diff --git a/src/groups/special_linear.jl b/src/groups/special_linear.jl index e8f8eb05a9..c29907f080 100644 --- a/src/groups/special_linear.jl +++ b/src/groups/special_linear.jl @@ -126,7 +126,7 @@ end @doc raw""" project(G::SpecialLinear, p, X) -Orthogonally project ``X ∈ 𝔽^{n × n}`` onto the tangent space of ``p`` to the +Orthogonally project ``X ∈ 𝔽^{n×n}`` onto the tangent space of ``p`` to the [`SpecialLinear`](@ref) ``G = \mathrm{SL}(n, 𝔽)``. The formula reads ````math \operatorname{proj}_{p} @@ -146,11 +146,11 @@ function project!(G::SpecialLinear, Y, p, X) end function Base.show(io::IO, ::SpecialLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "SpecialLinear($n, $𝔽)") + return print(io, "SpecialLinear($n, $(𝔽))") end function Base.show(io::IO, M::SpecialLinear{Tuple{Int},𝔽}) where {𝔽} n = get_parameter(M.size)[1] - return print(io, "SpecialLinear($n, $𝔽; parameter=:field)") + return print(io, "SpecialLinear($n, $(𝔽); parameter=:field)") end translate_diff(::SpecialLinear, p, q, X, ::LeftForwardAction) = X diff --git a/src/groups/unitary.jl b/src/groups/unitary.jl index 7b7795d90b..3dd7255287 100644 --- a/src/groups/unitary.jl +++ b/src/groups/unitary.jl @@ -4,7 +4,7 @@ The group of unitary matrices ``\mathrm{U}(n, 𝔽)``, either complex (when 𝔽=ℂ) or quaternionic (when 𝔽=ℍ) -The group consists of all points ``p ∈ 𝔽^{n × n}`` where ``p^{\mathrm{H}}p = pp^{\mathrm{H}} = I``. +The group consists of all points ``p ∈ 𝔽^{n×n}`` where ``p^{\mathrm{H}}p = pp^{\mathrm{H}} = I``. The tangent spaces are if the form diff --git a/src/manifolds/CenteredMatrices.jl b/src/manifolds/CenteredMatrices.jl index e5f801797a..1cca7154e8 100644 --- a/src/manifolds/CenteredMatrices.jl +++ b/src/manifolds/CenteredMatrices.jl @@ -1,11 +1,11 @@ @doc raw""" CenteredMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The manifold of $m × n$ real-valued or complex-valued matrices whose columns sum to zero, i.e. +The manifold of ``m×n`` real-valued or complex-valued matrices whose columns sum to zero, i.e. ````math -\bigl\{ p ∈ 𝔽^{m × n}\ \big|\ [1 … 1] * p = [0 … 0] \bigr\}, +\bigl\{ p ∈ 𝔽^{m×n}\ \big|\ [1 … 1] * p = [0 … 0] \bigr\}, ```` -where $𝔽 ∈ \{ℝ,ℂ\}$. +where ``𝔽 ∈ \{ℝ,ℂ\}``. # Constructor CenteredMatrices(m, n[, field=ℝ]; parameter::Symbol=:type) @@ -105,7 +105,7 @@ Return the manifold dimension of the [`CenteredMatrices`](@ref) `m`-by-`n` matri ````math \dim(\mathcal M) = (m*n - n) \dim_ℝ 𝔽, ```` -where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. +where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::CenteredMatrices{<:Any,𝔽}) where {𝔽} m, n = get_parameter(M.size) @@ -124,7 +124,7 @@ Projects `p` from the embedding onto the [`CenteredMatrices`](@ref) `M`, i.e. 1 \end{bmatrix} * [c_1 \dots c_n], ```` -where $c_i = \frac{1}{m}\sum_{j=1}^m p_{j,i}$ for $i = 1, \dots, n$. +where ``c_i = \frac{1}{m}\sum_{j=1}^m p_{j,i}`` for ``i = 1, \dots, n``. """ project(::CenteredMatrices, ::Any) @@ -142,7 +142,7 @@ Project the matrix `X` onto the tangent space at `p` on the [`CenteredMatrices`] 1 \end{bmatrix} * [c_1 \dots c_n], ```` -where $c_i = \frac{1}{m}\sum_{j=1}^m x_{j,i}$ for $i = 1, \dots, n$. +where ``c_i = \frac{1}{m}\sum_{j=1}^m x_{j,i}`` for ``i = 1, \dots, n``. """ project(::CenteredMatrices, ::Any, ::Any) diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 2d2226c4b4..e5bf0eb31a 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -9,7 +9,7 @@ are for example summarized in Table 1 of [Lin:2019](@cite). CholeskySpace(n; parameter::Symbol=:type) -Generate the manifold of $n× n$ lower triangular matrices with positive diagonal. +Generate the manifold of ``n×n`` lower triangular matrices with positive diagonal. """ struct CholeskySpace{T} <: AbstractManifold{ℝ} size::T @@ -107,8 +107,8 @@ The formula reads \operatorname{diag}(p)\exp\bigl( \operatorname{diag}(X)\operatorname{diag}(p)^{-1}\bigr), ```` -where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, -and $\operatorname{diag}$ extracts the diagonal matrix. +where ``⌊⋅⌋`` denotes the strictly lower triangular matrix, +and ``\operatorname{diag}`` extracts the diagonal matrix. """ exp(::CholeskySpace, ::Any...) @@ -158,8 +158,8 @@ The formula reads \log_p q = ⌊ p ⌋ - ⌊ q ⌋ + \operatorname{diag}(p)\log\bigl(\operatorname{diag}(q)\operatorname{diag}(p)^{-1}\bigr), ```` -where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, -and $\operatorname{diag}$ extracts the diagonal matrix. +where ``⌊⋅⌋`` denotes the strictly lower triangular matrix, +and ``\operatorname{diag}`` extracts the diagonal matrix. """ log(::Cholesky, ::Any...) @@ -219,8 +219,8 @@ on the [`CholeskySpace`](@ref) manifold `M`. The formula reads + \operatorname{diag}(q)\operatorname{diag}(p)^{-1}\operatorname{diag}(X), ```` -where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, -and $\operatorname{diag}$ extracts the diagonal matrix. +where ``⌊⋅⌋`` denotes the strictly lower triangular matrix, +and ``\operatorname{diag}`` extracts the diagonal matrix. """ parallel_transport_to(::CholeskySpace, ::Any, ::Any, ::Any) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index a82cff544c..27d7df3807 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -148,7 +148,7 @@ Compute the exponential map on the [`Circle`](@ref). ````math \exp_p X = (p+X)_{2π}, ```` -where ``(\cdot)_{2π}`` is the (symmetric) remainder with respect to division by ``2π``, i.e. in ``[-π,π)``. +where ``(⋅)_{2π}`` is the (symmetric) remainder with respect to division by ``2π``, i.e. in ``[-π,π)``. For the complex-valued case, the same formula as for the [`Sphere`](@ref) ``𝕊^1`` is applied to values in the complex plane. @@ -299,7 +299,7 @@ Compute the logarithmic map on the [`Circle`](@ref) `M`. ````math \log_p q = (q-p)_{2π}, ```` -where ``(\cdot)_{2π}`` is the (symmetric) remainder with respect to division by ``2π``, i.e. in ``[-π,π)``. +where ``(⋅)_{2π}`` is the (symmetric) remainder with respect to division by ``2π``, i.e. in ``[-π,π)``. For the complex-valued case, the same formula as for the [`Sphere`](@ref) ``𝕊^1`` is applied to values in the complex plane. diff --git a/src/manifolds/Elliptope.jl b/src/manifolds/Elliptope.jl index e7d7d7432d..638bdbeaef 100644 --- a/src/manifolds/Elliptope.jl +++ b/src/manifolds/Elliptope.jl @@ -2,32 +2,32 @@ Elliptope{T} <: AbstractDecoratorManifold{ℝ} The Elliptope manifold, also known as the set of correlation matrices, consists of all -symmetric positive semidefinite matrices of rank $k$ with unit diagonal, i.e., +symmetric positive semidefinite matrices of rank ``k`` with unit diagonal, i.e., ````math \begin{aligned} \mathcal E(n,k) = -\bigl\{p ∈ ℝ^{n × n}\ \big|\ &a^\mathrm{T}pa \geq 0 \text{ for all } a ∈ ℝ^{n},\\ +\bigl\{p ∈ ℝ^{n×n}\ \big|\ &a^\mathrm{T}pa \geq 0 \text{ for all } a ∈ ℝ^{n},\\ &p_{ii} = 1 \text{ for all } i=1,\ldots,n,\\ -&\text{and } p = qq^{\mathrm{T}} \text{ for } q \in ℝ^{n × k} \text{ with } \operatorname{rank}(p) = \operatorname{rank}(q) = k +&\text{and } p = qq^{\mathrm{T}} \text{ for } q \in ℝ^{n×k} \text{ with } \operatorname{rank}(p) = \operatorname{rank}(q) = k \bigr\}. \end{aligned} ```` -And this manifold is working solely on the matrices $q$. Note that this $q$ is not unique, -indeed for any orthogonal matrix $A$ we have $(qA)(qA)^{\mathrm{T}} = qq^{\mathrm{T}} = p$, +And this manifold is working solely on the matrices ``q``. Note that this ``q`` is not unique, +indeed for any orthogonal matrix ``A`` we have ``(qA)(qA)^{\mathrm{T}} = qq^{\mathrm{T}} = p``, so the manifold implemented here is the quotient manifold. The unit diagonal translates to -unit norm columns of $q$. +unit norm columns of ``q``. -The tangent space at $p$, denoted $T_p\mathcal E(n,k)$, is also represented by matrices -$Y\in ℝ^{n × k}$ and reads as +The tangent space at ``p``, denoted ``T_p\mathcal E(n,k)``, is also represented by matrices +``Y\in ℝ^{n×k}`` and reads as ````math T_p\mathcal E(n,k) = \bigl\{ -X ∈ ℝ^{n × n}\,|\,X = qY^{\mathrm{T}} + Yq^{\mathrm{T}} \text{ with } X_{ii} = 0 \text{ for } i=1,\ldots,n +X ∈ ℝ^{n×n}\,|\,X = qY^{\mathrm{T}} + Yq^{\mathrm{T}} \text{ with } X_{ii} = 0 \text{ for } i=1,\ldots,n \bigr\} ```` -endowed with the [`Euclidean`](@ref) metric from the embedding, i.e. from the $ℝ^{n × k}$ +endowed with the [`Euclidean`](@ref) metric from the embedding, i.e. from the ``ℝ^{n×k}`` This manifold was for example @@ -37,7 +37,7 @@ investigated in[JourneeBachAbsilSepulchre:2010](@cite). Elliptope(n::Int, k::Int; parameter::Symbol=:type) -generates the manifold $\mathcal E(n,k) \subset ℝ^{n × n}$. +generates the manifold ``\mathcal E(n,k) \subset ℝ^{n×n}``. `parameter`: whether a type parameter should be used to store `n` and `k`. By default size is stored in type. Value can either be `:field` or `:type`. @@ -56,11 +56,11 @@ end @doc raw""" check_point(M::Elliptope, q; kwargs...) -checks, whether `q` is a valid reprsentation of a point $p=qq^{\mathrm{T}}$ on the +checks, whether `q` is a valid reprsentation of a point ``p=qq^{\mathrm{T}}`` on the [`Elliptope`](@ref) `M`, i.e. is a matrix -of size `(N,K)`, such that $p$ is symmetric positive semidefinite and has unit trace. -Since by construction $p$ is symmetric, this is not explicitly checked. -Since $p$ is by construction positive semidefinite, this is not checked. +of size `(N,K)`, such that ``p`` is symmetric positive semidefinite and has unit trace. +Since by construction ``p`` is symmetric, this is not explicitly checked. +Since ``p`` is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ function check_point(M::Elliptope, q; kwargs...) @@ -77,13 +77,13 @@ end @doc raw""" check_vector(M::Elliptope, q, Y; kwargs... ) -Check whether $X = qY^{\mathrm{T}} + Yq^{\mathrm{T}}$ is a tangent vector to -$p=qq^{\mathrm{T}}$ on the [`Elliptope`](@ref) `M`, -i.e. `Y` has to be of same dimension as `q` and a $X$ has to be a symmetric matrix with +Check whether ``X = qY^{\mathrm{T}} + Yq^{\mathrm{T}}`` is a tangent vector to +``p=qq^{\mathrm{T}}`` on the [`Elliptope`](@ref) `M`, +i.e. `Y` has to be of same dimension as `q` and a ``X`` has to be a symmetric matrix with zero diagonal. The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. -Note that symmetric of $X$ holds by construction an is not explicitly checked. +Note that symmetric of ``X`` holds by construction an is not explicitly checked. """ function check_vector( M::Elliptope, @@ -122,7 +122,7 @@ is_flat(M::Elliptope) = false manifold_dimension(M::Elliptope) returns the dimension of -[`Elliptope`](@ref) `M`$=\mathcal E(n,k), n,k ∈ ℕ$, i.e. +[`Elliptope`](@ref) `M` ``=\mathcal E(n,k), n,k ∈ ℕ``, i.e. ````math \dim \mathcal E(n,k) = n(k-1) - \frac{k(k-1)}{2}. ```` @@ -158,7 +158,7 @@ end @doc raw""" retract(M::Elliptope, q, Y, ::ProjectionRetraction) -compute a projection based retraction by projecting $q+Y$ back onto the manifold. +compute a projection based retraction by projecting ``q+Y`` back onto the manifold. """ retract(::Elliptope, ::Any, ::Any, ::ProjectionRetraction) @@ -172,8 +172,8 @@ end representation_size(M::Elliptope) Return the size of an array representing an element on the -[`Elliptope`](@ref) manifold `M`, i.e. $n × k$, the size of such factor of $p=qq^{\mathrm{T}}$ -on $\mathcal M = \mathcal E(n,k)$. +[`Elliptope`](@ref) manifold `M`, i.e. ``n×k``, the size of such factor of ``p=qq^{\mathrm{T}}`` +on ``\mathcal M = \mathcal E(n,k)``. """ representation_size(M::Elliptope) = get_parameter(M.size) diff --git a/src/manifolds/EssentialManifold.jl b/src/manifolds/EssentialManifold.jl index 6d585d4974..93102c00fa 100644 --- a/src/manifolds/EssentialManifold.jl +++ b/src/manifolds/EssentialManifold.jl @@ -4,7 +4,7 @@ The essential manifold is the space of the essential matrices which is represented as a quotient space of the [`Rotations`](@ref) manifold product ``\mathrm{SO}(3)^2``. -Let ``R_x(θ), R_y(θ), R_x(θ) \in ℝ^{x\times 3}`` denote the rotation around the ``z``, +Let ``R_x(θ), R_y(θ), R_x(θ) \in ℝ^{x×3}`` denote the rotation around the ``z``, ``y``, and ``x`` axis in ``ℝ^3``, respectively, and further the groups ```math @@ -39,7 +39,7 @@ E = (R'_1)^T [T'_2 - T'_1]_{×} R'_2, ```` where the poses of two cameras ``(R_i', T_i'), i=1,2``, are contained in the space of -rigid body transformations $SE(3)$ and the operator $[⋅]_{×}\colon ℝ^3 \to \operatorname{SkewSym}(3)$ +rigid body transformations $SE(3)$ and the operator $[⋅]_{×}\colon ℝ^3 → \operatorname{SkewSym}(3)$ denotes the matrix representation of the cross product operator. For more details see [TronDaniilidis:2017](@cite). # Constructor @@ -103,7 +103,7 @@ are equal (up to a sign flip). Using the logarithmic map, the distance is given \text{dist}([p],[q]) = \| \text{log}_{[p]} [q] \| = \| \log_p (S_z(t_{\text{opt}})q) \|, ```` where $S_z ∈ H_z = \{(R_z(θ),R_z(θ))\colon θ \in [-π,π) \}$ in which $R_z(θ)$ is the rotation around the z axis with angle -$θ$ and $t_{\text{opt}}$ is the minimizer of the cost function +``θ`` and $t_{\text{opt}}$ is the minimizer of the cost function ````math f(t) = f_1 + f_2, \quad f_i = \frac{1}{2} θ^2_i(t), \quad θ_i(t)=d(R_{p_i},R_z(t)R_{b_i}) \text{ for } i=1,2, ```` @@ -119,7 +119,7 @@ Compute the exponential map on the [`EssentialManifold`](@ref) from `p` into dir ````math \text{exp}_p(X) =\text{exp}_g( \tilde X), \quad g \in \text(SO)(3)^2, ```` -where $\tilde X$ is the horizontal lift of $X$[TronDaniilidis:2017](@cite). +where $\tilde X$ is the horizontal lift of ``X``[TronDaniilidis:2017](@cite). """ exp(::EssentialManifold, ::Any...) @@ -164,7 +164,7 @@ on the left on $SO(3)^2$ is defined as ````math H_z = \{(R_z(θ),R_z(θ))\colon θ \in [-π,π) \}, ```` -where $R_z(θ)$ is the rotation around the z axis with angle $θ$. Points in $H_z$ are denoted by +where $R_z(θ)$ is the rotation around the z axis with angle ``θ``. Points in $H_z$ are denoted by $S_z$. Then, the logarithm is defined as ````math @@ -223,7 +223,7 @@ f(t) = f_1 + f_2, \quad f_i = \frac{1}{2} θ^2_i(t), \quad θ_i(t)=d(R_{p_i},R_z ```` for the given values. This is done by finding the discontinuity points $t_{d_i}, i=1,2$ of its derivative and using Newton's method to minimize the function over the intervals $[t_{d_1},t_{d_2}]$ and $[t_{d_2},t_{d_1}+2π]$ -separately. Then, the minimizer for which $f$ is minimal is chosen and given back together with the minimal value. +separately. Then, the minimizer for which ``f`` is minimal is chosen and given back together with the minimal value. For more details see Algorithm 1 in [TronDaniilidis:2017](@cite). """ function dist_min_angle_pair(p, q) @@ -360,7 +360,7 @@ This function computes the minimizer of the function ````math f(t) = f_1 + f_2, \quad f_i = \frac{1}{2} θ^2_i(t), \quad θ_i(t)=d(R_{p_i},R_z(t)R_{b_i}) \text{ for } i=1,2, ```` -in the interval $[$`t_low`, `t_high`$]$ using Newton's method. For more details see [TronDaniilidis:2017](@cite). +in the interval ``[```t_low`, `t_high```]`` using Newton's method. For more details see [TronDaniilidis:2017](@cite). """ function dist_min_angle_pair_df_newton(m1, Φ1, c1, m2, Φ2, c2, t_min, t_low, t_high) tol_dist = sqrt(eps(eltype(t_min))) @@ -425,7 +425,7 @@ Then the orthogonal projection of `X` onto the horizontal space $T_{\text{hp}}\t ````math \Pi_h(X) = X - \frac{\text{vert\_proj}_p(X)}{2} \begin{bmatrix} R_1^T e_z \\ R_2^T e_z \end{bmatrix}, ```` -with $R_i = R_0 R'_i, i=1,2,$ where $R'_i$ is part of the pose of camera $i$ $g_i = (R'_i,T'_i) ∈ \text{SE}(3)$ +with $R_i = R_0 R'_i, i=1,2,$ where $R'_i$ is part of the pose of camera ``i`` $g_i = (R'_i,T'_i) ∈ \text{SE}(3)$ and $R_0 ∈ \text{SO}(3)$ such that $R_0(T'_2-T'_1) = e_z$. """ project(::EssentialManifold, ::Any, ::Any) @@ -486,7 +486,7 @@ Project `X` onto the vertical space $T_{\text{vp}}\text{SO}(3)^2$ with \text{vert\_proj}_p(X) = e_z^T(R_1 X_1 + R_2 X_2), ```` where $e_z$ is the third unit vector, $X_i ∈ T_{p}\text{SO}(3)$ for $i=1,2,$ and it holds $R_i = R_0 R'_i, i=1,2,$ where $R'_i$ is part of the -pose of camera $i$ $g_i = (R_i,T'_i) ∈ \text{SE}(3)$ and $R_0 ∈ \text{SO}(3)$ such that $R_0(T'_2-T'_1) = e_z$ [TronDaniilidis:2017](@cite). +pose of camera ``i`` $g_i = (R_i,T'_i) ∈ \text{SE}(3)$ and $R_0 ∈ \text{SO}(3)$ such that $R_0(T'_2-T'_1) = e_z$ [TronDaniilidis:2017](@cite). """ function vert_proj(M::EssentialManifold, p, X) return sum(vert_proj.(Ref(M.manifold), p, X)) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 4dc39a7da6..a0eacec744 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -12,7 +12,7 @@ Generate the ``n``-dimensional vector space ``ℝ^n``. Euclidean(n₁,n₂,...,nᵢ; field=ℝ, parameter::Symbol = :field) 𝔽^(n₁,n₂,...,nᵢ) = Euclidean(n₁,n₂,...,nᵢ; field=𝔽) -Generate the vector space of ``k = n_1 \cdot n_2 \cdot … \cdot n_i`` values, i.e. the +Generate the vector space of ``k = n_1 ⋅ n_2 ⋅ … ⋅ n_i`` values, i.e. the manifold ``𝔽^{n_1, n_2, …, n_i}``, ``𝔽\in\{ℝ,ℂ\}``, whose elements are interpreted as ``n_1 × n_2 × … × n_i`` arrays. For ``i=2`` we obtain a matrix space. @@ -402,7 +402,7 @@ g_p(X,Y) = \sum_{k ∈ I} \overline{X}_{k} Y_{k}, where ``I`` is the set of vectors ``k ∈ ℕ^i``, such that for all -``i ≤ j ≤ i`` it holds ``1 ≤ k_j ≤ n_j`` and ``\overline{\cdot}`` denotes the complex conjugate. +``i ≤ j ≤ i`` it holds ``1 ≤ k_j ≤ n_j`` and ``\overline{⋅}`` denotes the complex conjugate. For the special case of ``i ≤ 2``, i.e. matrices and vectors, this simplifies to @@ -410,7 +410,7 @@ For the special case of ``i ≤ 2``, i.e. matrices and vectors, this simplifies g_p(X,Y) = X^{\mathrm{H}}Y, ```` -where ``\cdot^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ inner(::Euclidean, ::Any...) @inline inner(::Euclidean, p, X, Y) = dot(X, Y) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index ddc145ea82..e84b17c6ca 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,16 +1,16 @@ @doc raw""" FixedRankMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The manifold of ``m × n`` real-valued or complex-valued matrices of fixed rank ``k``, i.e. +The manifold of ``m×n`` real-valued or complex-valued matrices of fixed rank ``k``, i.e. ````math -\bigl\{ p ∈ 𝔽^{m × n}\ \big|\ \operatorname{rank}(p) = k\bigr\}, +\bigl\{ p ∈ 𝔽^{m×n}\ \big|\ \operatorname{rank}(p) = k\bigr\}, ```` where ``𝔽 ∈ \{ℝ,ℂ\}`` and the rank is the number of linearly independent columns of a matrix. # Representation with 3 matrix factors -A point ``p ∈ \mathcal M`` can be stored using unitary matrices ``U ∈ 𝔽^{m × k}``, ``V ∈ 𝔽^{n × k}`` as well as the ``k`` -singular values of ``p = U_p S V_p^\mathrm{H}``, where ``\cdot^{\mathrm{H}}`` denotes the complex conjugate transpose or +A point ``p ∈ \mathcal M`` can be stored using unitary matrices ``U ∈ 𝔽^{m×k}``, ``V ∈ 𝔽^{n×k}`` as well as the ``k`` +singular values of ``p = U_p S V_p^\mathrm{H}``, where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian. In other words, ``U`` and ``V`` are from the manifolds [`Stiefel`](@ref)`(m,k,𝔽)` and [`Stiefel`](@ref)`(n,k,𝔽)`, respectively; see [`SVDMPoint`](@ref) for details. @@ -18,18 +18,18 @@ The tangent space ``T_p \mathcal M`` at a point ``p ∈ \mathcal M`` with ``p=U_ is given by ````math T_p\mathcal M = \bigl\{ U_p M V_p^\mathrm{H} + U_X V_p^\mathrm{H} + U_p V_X^\mathrm{H} : - M ∈ 𝔽^{k × k}, - U_X ∈ 𝔽^{m × k}, - V_X ∈ 𝔽^{n × k} + M ∈ 𝔽^{k×k}, + U_X ∈ 𝔽^{m×k}, + V_X ∈ 𝔽^{n×k} \text{ s.t. } U_p^\mathrm{H}U_X = 0_k, V_p^\mathrm{H}V_X = 0_k \bigr\}, ```` -where ``0_k`` is the ``k × k`` zero matrix. See [`UMVTVector`](@ref) for details. +where ``0_k`` is the ``k×k`` zero matrix. See [`UMVTVector`](@ref) for details. The (default) metric of this manifold is obtained by restricting the metric -on ``ℝ^{m × n}`` to the tangent bundle [Vandereycken:2013](@cite). +on ``ℝ^{m×n}`` to the tangent bundle [Vandereycken:2013](@cite). # Constructor FixedRankMatrices(m, n, k[, field=ℝ]) @@ -671,7 +671,7 @@ Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by comput q = U_kS_kV_k^\mathrm{H}, ```` where ``U_k S_k V_k^\mathrm{H}`` is the shortened singular value decomposition ``USV^\mathrm{H}=p+X``, -in the sense that ``S_k`` is the diagonal matrix of size ``k × k`` with the ``k`` largest +in the sense that ``S_k`` is the diagonal matrix of size ``k×k`` with the ``k`` largest singular values and ``U`` and ``V`` are shortened accordingly. """ retract(::FixedRankMatrices, ::Any, ::Any, ::PolarRetraction) diff --git a/src/manifolds/FlagOrthogonal.jl b/src/manifolds/FlagOrthogonal.jl index c6d897b70a..e30d034ac6 100644 --- a/src/manifolds/FlagOrthogonal.jl +++ b/src/manifolds/FlagOrthogonal.jl @@ -7,10 +7,10 @@ Check whether `X` is a tangent vector to point `p` on the [`Flag`](@ref) manifol i.e. that `X` is block-skew-symmetric with zero diagonal: ````math X = \begin{bmatrix} -0 & B_{1,2} & \cdots & B_{1,d+1} \\ --B_{1,2}^\mathrm{T} & 0 & \cdots & B_{2,d+1} \\ -\vdots & \vdots & \ddots & \vdots \\ --B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & \cdots & 0 +0 & B_{1,2} & ⋯ & B_{1,d+1} \\ +-B_{1,2}^\mathrm{T} & 0 & ⋯ & B_{2,d+1} \\ +\vdots & \vdots & ⋱ & \vdots \\ +-B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & ⋯ & 0 \end{bmatrix} ```` where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. @@ -105,10 +105,10 @@ It works by first projecting `X` to the space of [`SkewHermitianMatrices`](@ref) setting diagonal blocks to 0: ````math X = \begin{bmatrix} -0 & B_{1,2} & \cdots & B_{1,d+1} \\ --B_{1,2}^\mathrm{T} & 0 & \cdots & B_{2,d+1} \\ -\vdots & \vdots & \ddots & \vdots \\ --B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & \cdots & 0 +0 & B_{1,2} & ⋯ & B_{1,d+1} \\ +-B_{1,2}^\mathrm{T} & 0 & ⋯ & B_{2,d+1} \\ +\vdots & \vdots & ⋱ & \vdots \\ +-B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & ⋯ & 0 \end{bmatrix} ```` where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. diff --git a/src/manifolds/FlagStiefel.jl b/src/manifolds/FlagStiefel.jl index 11d2bee355..0338d21c6a 100644 --- a/src/manifolds/FlagStiefel.jl +++ b/src/manifolds/FlagStiefel.jl @@ -74,11 +74,11 @@ Check whether `X` is a tangent vector to point `p` on the [`Flag`](@ref) manifol i.e. that `X` is a matrix of the form ````math X = \begin{bmatrix} -0 & B_{1,2} & \cdots & B_{1,d} \\ --B_{1,2}^\mathrm{T} & 0 & \cdots & B_{2,d} \\ -\vdots & \vdots & \ddots & \vdots \\ --B_{1,d}^\mathrm{T} & -B_{2,d}^\mathrm{T} & \cdots & 0 \\ --B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & \cdots & -B_{d,d+1}^\mathrm{T} +0 & B_{1,2} & ⋯ & B_{1,d} \\ +-B_{1,2}^\mathrm{T} & 0 & ⋯ & B_{2,d} \\ +\vdots & \vdots & ⋱ & \vdots \\ +-B_{1,d}^\mathrm{T} & -B_{2,d}^\mathrm{T} & ⋯ & 0 \\ +-B_{1,d+1}^\mathrm{T} & -B_{2,d+1}^\mathrm{T} & ⋯ & -B_{d,d+1}^\mathrm{T} \end{bmatrix} ```` where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 6ff6f9068c..7adcc0c002 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -954,7 +954,7 @@ mean(::GeneralUnitaryMatrices{<:Any,ℝ}, ::Any) project(G::UnitaryMatrices, p) project(G::OrthogonalMatrices, p) -Project the point ``p ∈ 𝔽^{n × n}`` to the nearest point in +Project the point ``p ∈ 𝔽^{n×n}`` to the nearest point in ``\mathrm{U}(n,𝔽)=``[`Unitary(n,𝔽)`](@ref) under the Frobenius norm. If ``p = U S V^\mathrm{H}`` is the singular value decomposition of ``p``, then the projection is @@ -980,7 +980,7 @@ end project(M::Rotations, p, X) project(M::UnitaryMatrices, p, X) -Orthogonally project the tangent vector ``X ∈ 𝔽^{n × n}``, ``\mathbb F ∈ \{\mathbb R, \mathbb C\}`` +Orthogonally project the tangent vector ``X ∈ 𝔽^{n×n}``, ``\mathbb F ∈ \{\mathbb R, \mathbb C\}`` to the tangent space of `M` at `p`, and change the representer to use the corresponding Lie algebra, i.e. we compute diff --git a/src/manifolds/GeneralizedGrassmann.jl b/src/manifolds/GeneralizedGrassmann.jl index 7fd9c26dff..83e2fac35f 100644 --- a/src/manifolds/GeneralizedGrassmann.jl +++ b/src/manifolds/GeneralizedGrassmann.jl @@ -1,37 +1,37 @@ @doc raw""" GeneralizedGrassmann{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} -The generalized Grassmann manifold $\operatorname{Gr}(n,k,B)$ consists of all subspaces -spanned by $k$ linear independent vectors $𝔽^n$, where $𝔽 ∈ \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. -This yields all $k$-dimensional subspaces of $ℝ^n$ for the real-valued case and all $2k$-dimensional subspaces -of $ℂ^n$ for the second. +The generalized Grassmann manifold ``\operatorname{Gr}(n,k,B)`` consists of all subspaces +spanned by ``k`` linear independent vectors ``𝔽^n``, where ``𝔽 ∈ \{ℝ, ℂ\}`` is either the real- (or complex-) valued vectors. +This yields all ``k``-dimensional subspaces of ``ℝ^n`` for the real-valued case and all ``2k``-dimensional subspaces +of ``ℂ^n`` for the second. The manifold can be represented as ````math -\operatorname{Gr}(n, k, B) := \bigl\{ \operatorname{span}(p)\ \big|\ p ∈ 𝔽^{n × k}, p^\mathrm{H}Bp = I_k\}, +\operatorname{Gr}(n, k, B) := \bigl\{ \operatorname{span}(p)\ \big|\ p ∈ 𝔽^{n×k}, p^\mathrm{H}Bp = I_k\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate (or Hermitian) transpose and -$I_k$ is the $k × k$ identity matrix. This means, that the columns of $p$ +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate (or Hermitian) transpose and +``I_k`` is the ``k×k`` identity matrix. This means, that the columns of ``p`` form an unitary basis of the subspace with respect to the scaled inner product, that is a -point on $\operatorname{Gr}(n,k,B)$, and hence the subspace can actually be represented by -a whole equivalence class of representers. For $B=I_n$ this simplifies to the [`Grassmann`](@ref) manifold. +point on ``\operatorname{Gr}(n,k,B)``, and hence the subspace can actually be represented by +a whole equivalence class of representers. For ``B=I_n`` this simplifies to the [`Grassmann`](@ref) manifold. -The tangent space at a point (subspace) $p$ is given by +The tangent space at a point (subspace) ``p`` is given by ````math T_x\mathrm{Gr}(n,k,B) = \bigl\{ -X ∈ 𝔽^{n × k} : +X ∈ 𝔽^{n×k} : X^{\mathrm{H}}Bp + p^{\mathrm{H}}BX = 0_{k} \bigr\}, ```` -where $0_{k}$ denotes the $k × k$ zero matrix. +where ``0_{k}`` denotes the ``k×k`` zero matrix. -Note that a point $p ∈ \operatorname{Gr}(n,k,B)$ might be represented by -different matrices (i.e. matrices with $B$-unitary column vectors that span -the same subspace). Different representations of $p$ also lead to different -representation matrices for the tangent space $T_p\mathrm{Gr}(n,k,B)$ +Note that a point ``p ∈ \operatorname{Gr}(n,k,B)`` might be represented by +different matrices (i.e. matrices with ``B``-unitary column vectors that span +the same subspace). Different representations of ``p`` also lead to different +representation matrices for the tangent space ``T_p\mathrm{Gr}(n,k,B)`` The manifold is named after [Hermann G. Graßmann](https://en.wikipedia.org/wiki/Hermann_Grassmann) (1809-1877). @@ -40,7 +40,7 @@ The manifold is named after GeneralizedGrassmann(n, k, B=I_n, field=ℝ) -Generate the (real-valued) Generalized Grassmann manifold of $n\times k$ dimensional +Generate the (real-valued) Generalized Grassmann manifold of ``n×k`` dimensional orthonormal matrices with scalar product `B`. """ struct GeneralizedGrassmann{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} @@ -116,8 +116,8 @@ the [`GeneralizedGrassmann`](@ref) `M`, i.e. that `X` is of size and type as wel p^{\mathrm{H}}BX + \overline{X^{\mathrm{H}}Bp} = 0_k, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, -$\overline{\cdot}$ the (elementwise) complex conjugate, and $0_k$ denotes the $k × k$ zero natrix. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian, +``\overline{⋅}`` the (elementwise) complex conjugate, and ``0_k`` denotes the ``k×k`` zero natrix. """ function check_vector(M::GeneralizedGrassmann, p, X; kwargs...) return nothing # everything already checked in the embedding (generalized Stiefel) @@ -127,7 +127,7 @@ end distance(M::GeneralizedGrassmann, p, q) Compute the Riemannian distance on [`GeneralizedGrassmann`](@ref) -manifold `M`$= \mathrm{Gr}(n,k,B)$. +manifold `M```= \mathrm{Gr}(n,k,B)``. The distance is given by ````math @@ -149,16 +149,16 @@ embed(::GeneralizedGrassmann, p, X) = X @doc raw""" exp(M::GeneralizedGrassmann, p, X) -Compute the exponential map on the [`GeneralizedGrassmann`](@ref) `M`$= \mathrm{Gr}(n,k,B)$ -starting in `p` with tangent vector (direction) `X`. Let $X^{\mathrm{H}}BX = USV$ denote the -SVD decomposition of $X^{\mathrm{H}}BX$. Then the exponential map is written using +Compute the exponential map on the [`GeneralizedGrassmann`](@ref) `M` ``= \mathrm{Gr}(n,k,B)`` +starting in `p` with tangent vector (direction) `X`. Let ``X^{\mathrm{H}}BX = USV`` denote the +SVD decomposition of ``X^{\mathrm{H}}BX``. Then the exponential map is written using ````math \exp_p X = p V\cos(S)V^\mathrm{H} + U\sin(S)V^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian and the -cosine and sine are applied element wise to the diagonal entries of $S$. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian and the +cosine and sine are applied element wise to the diagonal entries of ``S``. """ exp(::GeneralizedGrassmann, ::Any...) @@ -177,7 +177,7 @@ end injectivity_radius(M::GeneralizedGrassmann, p) Return the injectivity radius on the [`GeneralizedGrassmann`](@ref) `M`, -which is $\frac{π}{2}$. +which is ``\frac{π}{2}``. """ injectivity_radius(::GeneralizedGrassmann) = π / 2 injectivity_radius(::GeneralizedGrassmann, p) = π / 2 @@ -209,7 +209,7 @@ of `p` on the [`GeneralizedGrassmann`](@ref) manifold `M`. The formula reads g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}BY), ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ inner(M::GeneralizedGrassmann, p, X, Y) = dot(X, M.B, Y) @@ -223,23 +223,23 @@ end @doc raw""" log(M::GeneralizedGrassmann, p, q) -Compute the logarithmic map on the [`GeneralizedGrassmann`](@ref) `M`$ = \mathcal M=\mathrm{Gr}(n,k,B)$, +Compute the logarithmic map on the [`GeneralizedGrassmann`](@ref) `M` `` = \mathcal M=\mathrm{Gr}(n,k,B)``, i.e. the tangent vector `X` whose corresponding [`geodesic`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.geodesic-Tuple{AbstractManifold,%20Any,%20Any}) starting from `p` reaches `q` after time 1 on `M`. The formula reads ````math -\log_p q = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, +\log_p q = V⋅ \operatorname{atan}(S) ⋅ U^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. -The matrices $U$ and $V$ are the unitary matrices, and $S$ is the diagonal matrix +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. +The matrices ``U`` and ``V`` are the unitary matrices, and ``S`` is the diagonal matrix containing the singular values of the SVD-decomposition ````math USV = (q^\mathrm{H}Bp)^{-1} ( q^\mathrm{H} - q^\mathrm{H}Bpp^\mathrm{H}). ```` -In this formula the $\operatorname{atan}$ is meant elementwise. +In this formula the ``\operatorname{atan}`` is meant elementwise. """ log(::GeneralizedGrassmann, ::Any...) @@ -259,7 +259,7 @@ Return the dimension of the [`GeneralizedGrassmann(n,k,𝔽)`](@ref) manifold `M \dim \operatorname{Gr}(n,k,B) = k(n-k) \dim_ℝ 𝔽, ```` -where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. +where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::GeneralizedGrassmann{<:Any,𝔽}) where {𝔽} n, k = get_parameter(M.size) @@ -288,8 +288,8 @@ end project(M::GeneralizedGrassmann, p) Project `p` from the embedding onto the [`GeneralizedGrassmann`](@ref) `M`, i.e. compute `q` -as the polar decomposition of $p$ such that $q^{\mathrm{H}}Bq$ is the identity, -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transpose. +as the polar decomposition of ``p`` such that ``q^{\mathrm{H}}Bq`` is the identity, +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transpose. """ project(::GeneralizedGrassmann, ::Any) @@ -311,8 +311,8 @@ Project the `n`-by-`k` `X` onto the tangent space of `p` on the \operatorname{proj_p}(X) = X - pp^{\mathrm{H}}B^\mathrm{T}X, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian -and $\cdot^{\mathrm{T}}$ the transpose. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian +and ``⋅^{\mathrm{T}}`` the transpose. """ project(::GeneralizedGrassmann, ::Any, ::Any) @@ -360,7 +360,7 @@ end representation_size(M::GeneralizedGrassmann) Return the represenation size or matrix dimension of a point on the [`GeneralizedGrassmann`](@ref) -`M`, i.e. $(n,k)$ for both the real-valued and the complex value case. +`M`, i.e. ``(n,k)`` for both the real-valued and the complex value case. """ representation_size(M::GeneralizedGrassmann) = get_parameter(M.size) @@ -369,7 +369,7 @@ representation_size(M::GeneralizedGrassmann) = get_parameter(M.size) Compute the SVD-based retraction [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction) on the [`GeneralizedGrassmann`](@ref) `M`, by -[`project`](@ref project(M::GeneralizedGrassmann, p))ing $p + X$ onto `M`. +[`project`](@ref project(M::GeneralizedGrassmann, p))ing ``p + X`` onto `M`. """ retract(::GeneralizedGrassmann, ::Any, ::Any, ::PolarRetraction) diff --git a/src/manifolds/GeneralizedStiefel.jl b/src/manifolds/GeneralizedStiefel.jl index 5510abe875..6ff117e86a 100644 --- a/src/manifolds/GeneralizedStiefel.jl +++ b/src/manifolds/GeneralizedStiefel.jl @@ -1,27 +1,27 @@ @doc raw""" GeneralizedStiefel{T,𝔽,B} <: AbstractDecoratorManifold{𝔽} -The Generalized Stiefel manifold consists of all $n\times k$, $n\geq k$ orthonormal +The Generalized Stiefel manifold consists of all ``n×k``, ``n\geq k`` orthonormal matrices w.r.t. an arbitrary scalar product with symmetric positive definite matrix -$B\in R^{n × n}$, i.e. +``B\in R^{n×n}``, i.e. ````math -\operatorname{St}(n,k,B) = \bigl\{ p \in \mathbb F^{n × k}\ \big|\ p^{\mathrm{H}} B p = I_k \bigr\}, +\operatorname{St}(n,k,B) = \bigl\{ p \in \mathbb F^{n×k}\ \big|\ p^{\mathrm{H}} B p = I_k \bigr\}, ```` -where $𝔽 ∈ \{ℝ, ℂ\}$, -$\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and -$I_k \in \mathbb R^{k × k}$ denotes the $k × k$ identity matrix. +where ``𝔽 ∈ \{ℝ, ℂ\}``, +``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian, and +``I_k \in \mathbb R^{k×k}`` denotes the ``k×k`` identity matrix. -In the case $B=I_k$ one gets the usual [`Stiefel`](@ref) manifold. +In the case ``B=I_k`` one gets the usual [`Stiefel`](@ref) manifold. -The tangent space at a point $p\in\mathcal M=\operatorname{St}(n,k,B)$ is given by +The tangent space at a point ``p\in\mathcal M=\operatorname{St}(n,k,B)`` is given by ````math -T_p\mathcal M = \{ X \in 𝔽^{n × k} : p^{\mathrm{H}}BX + X^{\mathrm{H}}Bp=0_n\}, +T_p\mathcal M = \{ X \in 𝔽^{n×k} : p^{\mathrm{H}}BX + X^{\mathrm{H}}Bp=0_n\}, ```` -where $0_k$ is the $k × k$ zero matrix. +where ``0_k`` is the ``k×k`` zero matrix. This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. several functions like the [`zero_vector`](@ref) are inherited from the embedding. @@ -32,7 +32,7 @@ The manifold is named after # Constructor GeneralizedStiefel(n, k, B=I_n, F=ℝ) -Generate the (real-valued) Generalized Stiefel manifold of $n\times k$ dimensional +Generate the (real-valued) Generalized Stiefel manifold of ``n×k`` dimensional orthonormal matrices with scalar product `B`. """ struct GeneralizedStiefel{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} @@ -56,9 +56,9 @@ active_traits(f, ::GeneralizedStiefel, args...) = merge_traits(IsEmbeddedManifol @doc raw""" check_point(M::GeneralizedStiefel, p; kwargs...) -Check whether `p` is a valid point on the [`GeneralizedStiefel`](@ref) `M`=$\operatorname{St}(n,k,B)$, -i.e. that it has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and $x^{\mathrm{H}}Bx$ -is (approximately) the identity, where $\cdot^{\mathrm{H}}$ is the complex conjugate +Check whether `p` is a valid point on the [`GeneralizedStiefel`](@ref) `M`=``\operatorname{St}(n,k,B)``, +i.e. that it has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and ``x^{\mathrm{H}}Bx`` +is (approximately) the identity, where ``⋅^{\mathrm{H}}`` is the complex conjugate transpose. The settings for approximately can be set with `kwargs...`. """ function check_point(M::GeneralizedStiefel, p; kwargs...) @@ -84,9 +84,9 @@ end check_vector(M::GeneralizedStiefel, p, X; kwargs...) Check whether `X` is a valid tangent vector at `p` on the [`GeneralizedStiefel`](@ref) -`M`=$\operatorname{St}(n,k,B)$, i.e. the [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) fits, +`M`=``\operatorname{St}(n,k,B)``, i.e. the [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) fits, `p` is a valid point on `M` and -it (approximately) holds that $p^{\mathrm{H}}BX + \overline{X^{\mathrm{H}}Bp} = 0$, where +it (approximately) holds that ``p^{\mathrm{H}}BX + \overline{X^{\mathrm{H}}Bp} = 0``, where `kwargs...` is passed to the `isapprox`. """ function check_vector(M::GeneralizedStiefel, p, X; kwargs...) @@ -131,7 +131,7 @@ is_flat(M::GeneralizedStiefel) = manifold_dimension(M) == 1 @doc raw""" manifold_dimension(M::GeneralizedStiefel) -Return the dimension of the [`GeneralizedStiefel`](@ref) manifold `M`=$\operatorname{St}(n,k,B,𝔽)$. +Return the dimension of the [`GeneralizedStiefel`](@ref) manifold `M`=``\operatorname{St}(n,k,B,𝔽)``. The dimension is given by ````math @@ -159,8 +159,8 @@ end project(M::GeneralizedStiefel, p) Project `p` from the embedding onto the [`GeneralizedStiefel`](@ref) `M`, i.e. compute `q` -as the polar decomposition of $p$ such that $q^{\mathrm{H}}Bq$ is the identity, -where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +as the polar decomposition of ``p`` such that ``q^{\mathrm{H}}Bq`` is the identity, +where ``⋅^{\mathrm{H}}`` denotes the hermitian, i.e. complex conjugate transposed. """ project(::GeneralizedStiefel, ::Any) @@ -182,8 +182,8 @@ The formula reads \operatorname{proj}_{\operatorname{St}(n,k)}(p,X) = X - p\operatorname{Sym}(p^{\mathrm{H}}BX), ```` -where $\operatorname{Sym}(y)$ is the symmetrization of $y$, e.g. by -$\operatorname{Sym}(y) = \frac{y^{\mathrm{H}}+y}{2}$. +where ``\operatorname{Sym}(y)`` is the symmetrization of ``y``, e.g. by +``\operatorname{Sym}(y) = \frac{y^{\mathrm{H}}+y}{2}``. """ project(::GeneralizedStiefel, ::Any, ::Any) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index a7adb80612..cda76c1efc 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -1,47 +1,47 @@ @doc raw""" Grassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned by $k$ linear independent -vectors $𝔽^n$, where $𝔽 ∈ \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. -This yields all $k$-dimensional subspaces of $ℝ^n$ for the real-valued case and all $2k$-dimensional subspaces -of $ℂ^n$ for the second. +The Grassmann manifold ``\mathrm{Gr}(n,k)`` consists of all subspaces spanned by ``k`` linear independent +vectors ``𝔽^n``, where ``𝔽 ∈ \{ℝ, ℂ\}`` is either the real- (or complex-) valued vectors. +This yields all ``k``-dimensional subspaces of ``ℝ^n`` for the real-valued case and all ``2k``-dimensional subspaces +of ``ℂ^n`` for the second. The manifold can be represented as ````math -\operatorname{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n × k}, p^\mathrm{H}p = I_k\}, +\mathrm{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n×k}, p^\mathrm{H}p = I_k\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and -$I_k$ is the $k × k$ identity matrix. This means, that the columns of $p$ +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian and +``I_k`` is the ``k×k`` identity matrix. This means, that the columns of ``p`` form an unitary basis of the subspace, that is a point on -$\operatorname{Gr}(n,k)$, and hence the subspace can actually be represented by +``\operatorname{Gr}(n,k)``, and hence the subspace can actually be represented by a whole equivalence class of representers. Another interpretation is, that ````math -\operatorname{Gr}(n,k) = \operatorname{St}(n,k) / \operatorname{O}(k), +\mathrm{Gr}(n,k) = \mathrm{St}(n,k) / \operatorname{O}(k), ```` i.e the Grassmann manifold is the quotient of the [`Stiefel`](@ref) manifold and -the orthogonal group $\operatorname{O}(k)$ of orthogonal $k × k$ matrices. +the orthogonal group ``\operatorname{O}(k)`` of orthogonal ``k×k`` matrices. Note that it doesn't matter whether we start from the Euclidean or canonical metric on the Stiefel manifold, the resulting quotient metric on Grassmann is the same. -The tangent space at a point (subspace) $p$ is given by +The tangent space at a point (subspace) ``p`` is given by ````math T_p\mathrm{Gr}(n,k) = \bigl\{ -X ∈ 𝔽^{n × k} : +X ∈ 𝔽^{n×k} : X^{\mathrm{H}}p + p^{\mathrm{H}}X = 0_{k} \bigr\}, ```` -where $0_k$ is the $k × k$ zero matrix. +where ``0_k`` is the ``k×k`` zero matrix. -Note that a point $p ∈ \operatorname{Gr}(n,k)$ might be represented by +Note that a point ``p ∈ \operatorname{Gr}(n,k)`` might be represented by different matrices (i.e. matrices with unitary column vectors that span -the same subspace). Different representations of $p$ also lead to different -representation matrices for the tangent space $T_p\mathrm{Gr}(n,k)$ +the same subspace). Different representations of ``p`` also lead to different +representation matrices for the tangent space ``T_p\mathrm{Gr}(n,k)`` For a representation of points as orthogonal projectors. Here @@ -53,7 +53,7 @@ with tangent space ```math T_p\mathrm{Gr}(n,k) = \bigl\{ -X ∈ \mathbb R^{n × n} : X=X^{\mathrm{T}} \text{ and } X = pX+Xp \bigr\}, +X ∈ \mathbb R^{n×n} : X=X^{\mathrm{T}} \text{ and } X = pX+Xp \bigr\}, ``` see also [`ProjectorPoint`](@ref) and [`ProjectorTVector`](@ref). @@ -67,7 +67,7 @@ A good overview can be found in[BendokatZimmermannAbsil:2020](@cite). Grassmann(n, k, field=ℝ, parameter::Symbol=:type) -Generate the Grassmann manifold $\operatorname{Gr}(n,k)$, where the real-valued +Generate the Grassmann manifold ``\operatorname{Gr}(n,k)``, where the real-valued case `field=ℝ` is the default. """ struct Grassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} @@ -120,7 +120,7 @@ end injectivity_radius(M::Grassmann) injectivity_radius(M::Grassmann, p) -Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is $\frac{π}{2}$. +Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is ``\frac{π}{2}``. """ injectivity_radius(::Grassmann) = π / 2 injectivity_radius(::Grassmann, p) = π / 2 @@ -144,13 +144,13 @@ is_flat(M::Grassmann) = manifold_dimension(M) == 1 @doc raw""" manifold_dimension(M::Grassmann) -Return the dimension of the [`Grassmann(n,k,𝔽)`](@ref) manifold `M`, i.e. +Return the dimension of the [`Grassmann`](@ref)`(n,k,𝔽)` manifold `M`, i.e. ````math \dim \operatorname{Gr}(n,k) = k(n-k) \dim_ℝ 𝔽, ```` -where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. +where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::Grassmann{<:Any,𝔽}) where {𝔽} n, k = get_parameter(M.size) @@ -195,7 +195,7 @@ function get_total_space(M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} end # -# Reprenter specific implementations in their corresponding subfiles +# Representer specific implementations in their corresponding subfiles # include("GrassmannStiefel.jl") include("GrassmannProjector.jl") diff --git a/src/manifolds/GrassmannStiefel.jl b/src/manifolds/GrassmannStiefel.jl index 5fa2382b61..1db4205144 100644 --- a/src/manifolds/GrassmannStiefel.jl +++ b/src/manifolds/GrassmannStiefel.jl @@ -39,7 +39,7 @@ default_vector_transport_method(::Grassmann, ::Type{<:StiefelPoint}) = Projectio @doc raw""" distance(M::Grassmann, p, q) -Compute the Riemannian distance on [`Grassmann`](@ref) manifold `M`$= \mathrm{Gr}(n,k)$. +Compute the Riemannian distance on [`Grassmann`](@ref) manifold `M```= \mathrm{Gr}(n,k)``. The distance is given by ````math @@ -68,17 +68,17 @@ embed(::Stiefel, p::StiefelPoint, X::StiefelTVector) = X.value @doc raw""" exp(M::Grassmann, p, X) -Compute the exponential map on the [`Grassmann`](@ref) `M`$= \mathrm{Gr}(n,k)$ starting in -`p` with tangent vector (direction) `X`. Let $X = USV$ denote the SVD decomposition of $X$. +Compute the exponential map on the [`Grassmann`](@ref) `M` ``= \mathrm{Gr}(n,k)`` starting in +`p` with tangent vector (direction) `X`. Let ``X = USV`` denote the SVD decomposition of ``X``. Then the exponential map is written using ````math z = p V\cos(S)V^\mathrm{H} + U\sin(S)V^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian and the -cosine and sine are applied element wise to the diagonal entries of $S$. A final QR -decomposition $z=QR$ is performed for numerical stability reasons, yielding the result as +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian and the +cosine and sine are applied element wise to the diagonal entries of ``S``. A final QR +decomposition ``z=QR`` is performed for numerical stability reasons, yielding the result as ````math \exp_p X = Q. @@ -111,7 +111,7 @@ of `p` on the [`Grassmann`](@ref) manifold `M`. The formula reads g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}Y), ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ inner(::Grassmann, p, X, Y) = dot(X, Y) @@ -125,7 +125,7 @@ Compute the inverse retraction for the [`PolarRetraction`](https://juliamanifold \operatorname{retr}_p^{-1}q = q*(p^\mathrm{H}q)^{-1} - p, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ inverse_retract(::Grassmann, ::Any, ::Any, ::PolarInverseRetraction) @@ -143,7 +143,7 @@ Compute the inverse retraction for the [`QRRetraction`](https://juliamanifolds.g ````math \operatorname{retr}_p^{-1}q = q(p^\mathrm{H}q)^{-1} - p, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ inverse_retract(::Grassmann, ::Any, ::Any, ::QRInverseRetraction) @@ -155,23 +155,23 @@ end @doc raw""" log(M::Grassmann, p, q) -Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = \mathcal M=\mathrm{Gr}(n,k)$, +Compute the logarithmic map on the [`Grassmann`](@ref) `M` `` = \mathcal M=\mathrm{Gr}(n,k)``, i.e. the tangent vector `X` whose corresponding [`geodesic`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.geodesic-Tuple{AbstractManifold,%20Any,%20Any}) starting from `p` reaches `q` after time 1 on `M`. The formula reads ````math -\log_p q = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, +\log_p q = V⋅ \operatorname{atan}(S) ⋅ U^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. -The matrices $U$ and $V$ are the unitary matrices, and $S$ is the diagonal matrix +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. +The matrices ``U`` and ``V`` are the unitary matrices, and ``S`` is the diagonal matrix containing the singular values of the SVD-decomposition ````math USV = (q^\mathrm{H}p)^{-1} ( q^\mathrm{H} - q^\mathrm{H}pp^\mathrm{H}). ```` -In this formula the $\operatorname{atan}$ is meant elementwise. +In this formula the ``\operatorname{atan}`` is meant elementwise. """ log(::Grassmann, ::Any...) @@ -186,8 +186,8 @@ end project(M::Grassmann, p) Project `p` from the embedding onto the [`Grassmann`](@ref) `M`, i.e. compute `q` -as the polar decomposition of $p$ such that $q^{\mathrm{H}}q$ is the identity, -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +as the polar decomposition of ``p`` such that ``q^{\mathrm{H}}q`` is the identity, +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::Grassmann, ::Any) @@ -207,7 +207,7 @@ which is computed by \operatorname{proj_p}(X) = X - pp^{\mathrm{H}}X, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ project(::Grassmann, ::Any...) @@ -253,7 +253,7 @@ end representation_size(M::Grassmann) Return the representation size or matrix dimension of a point on the [`Grassmann`](@ref) -`M`, i.e. $(n,k)$ for both the real-valued and the complex value case. +`M`, i.e. ``(n,k)`` for both the real-valued and the complex value case. """ representation_size(M::Grassmann) = get_parameter(M.size) @@ -261,12 +261,12 @@ representation_size(M::Grassmann) = get_parameter(M.size) retract(M::Grassmann, p, X, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction) on the -[`Grassmann`](@ref) `M`. With $USV = p + X$ the retraction reads +[`Grassmann`](@ref) `M`. With ``USV = p + X`` the retraction reads ````math \operatorname{retr}_p X = UV^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where ``⋅^{\mathrm{H}}`` denotes the complex conjugate transposed or Hermitian. """ retract(::Grassmann, ::Any, ::Any, ::PolarRetraction) @@ -280,11 +280,11 @@ end retract(M::Grassmann, p, X, ::QRRetraction ) Compute the QR-based retraction [`QRRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRRetraction) on the -[`Grassmann`](@ref) `M`. With $QR = p + X$ the retraction reads +[`Grassmann`](@ref) `M`. With ``QR = p + X`` the retraction reads ````math \operatorname{retr}_p X = QD, ```` -where D is a $m × n$ matrix with +where D is a ``m×n`` matrix with ````math D = \operatorname{diag}\left( \operatorname{sgn}\left(R_{ii}+\frac{1}{2}\right)_{i=1}^n \right). ```` diff --git a/src/manifolds/Hamiltonian.jl b/src/manifolds/Hamiltonian.jl new file mode 100644 index 0000000000..be0f33539d --- /dev/null +++ b/src/manifolds/Hamiltonian.jl @@ -0,0 +1,212 @@ +# +# This file requires Symplectic to be defined, since it need the symplectic inverse A^+ +# This type is used in the Symplectic Grassmann +@doc raw""" + Hamiltonian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} + +A type to store a Hamiltonian matrix, that is a square matrix for which ``A^+ = -A`` where + +```math +A^+ = J_{2n}A^{\mathrm{T}}J_{2n}, \qquad J_{2n} \begin{pmatrix} 0 & I_n\\-I_n & 0 \end{pmatrix}, +``` + +and ``I_n`` denotes the ``n×n`` +""" +struct Hamiltonian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} + value::S + function Hamiltonian(A::S) where {T,S<:AbstractMatrix{<:T}} + n = div(size(A, 1), 2) + @assert size(A, 1) == 2 * n "The first dimension of A ($(size(A,1))) is not even" + @assert size(A, 2) == 2 * n "The matrix A is of size ($(size(A))), which is not square." + return new{T,S}(A) + end +end +# Avoid double wrapping / unwrap if that happened +Hamiltonian(A::Hamiltonian) = Hamiltonian(A.value) +# Conversion +function Matrix(A::Hamiltonian) + return Matrix(A.value) +end + +Base.:*(H::Hamiltonian, K::Hamiltonian) = Hamiltonian(H.value * K.value) +Base.:+(H::Hamiltonian, K::Hamiltonian) = Hamiltonian(H.value .+ K.value) +Base.:-(H::Hamiltonian, K::Hamiltonian) = Hamiltonian(H.value .- K.value) + +function show(io::IO, A::Hamiltonian) + return print(io, "Hamiltonian($(A.value))") +end +size(A::Hamiltonian) = size(A.value) + +@doc raw""" + HamiltonianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + +The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) +consisting of (real-valued) hamiltonian matrices of size ``n×n``, i.e. the set + +````math +\mathfrak{sp}(2n,𝔽) = \bigl\{p ∈ 𝔽^{2n×2n}\ \big|\ p^+ = p \bigr\}, +```` +where ``⋅^{+}`` denotes the [`symplectic_inverse`](@ref), and ``𝔽 ∈ \{ ℝ, ℂ\}``. + +Though it is slightly redundant, usually the matrices are stored as ``2n×2n`` arrays. + +The symbol ``\mathfak{sp}`` refers to the main usage within `Manifolds.jl` that is the +Lie algebra to the [`SymplecticMatrices`](@ref) interpreted as a Lie group with the +matrix multiplication as group operation. + +# Constructor + + HamiltonianMatrices(2n::Int, field::AbstractNumbers=ℝ) + +Generate the manifold of ``2n×2n`` Hamiltonian matrices. +""" +struct HamiltonianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end + +function HamiltonianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) + n % 2 == 0 || throw(ArgumentError("The dimension of the symplectic manifold + embedding space must be even. Was odd, n % 2 == $(n % 2).")) + size = wrap_type_parameter(parameter, (div(n, 2),)) + return HamiltonianMatrices{typeof(size),field}(size) +end + +function active_traits(f, ::HamiltonianMatrices, args...) + return merge_traits(IsEmbeddedSubmanifold()) +end + +ManifoldsBase.@default_manifold_fallbacks HamiltonianMatrices Hamiltonian Hamiltonian value value + +@doc raw""" + ^(A::Hamilonian, ::typeof(+)) + +Compute the [`symplectic_inverse`](@ref) of a Hamiltonian (A) +""" +function ^(A::Hamiltonian, ::typeof(+)) + return Hamiltonian(symplectic_inverse(A.value)) +end +function symplectic_inverse(A::Hamiltonian) + return Hamiltonian(symplectic_inverse(A.value)) +end + +@doc raw""" + check_point(M::HamiltonianMatrices{n,𝔽}, p; kwargs...) + +Check whether `p` is a valid manifold point on the [`HamiltonianMatrices`](@ref) `M`, i.e. +whether `p` [`is_hamiltonian`](@ref). + +The tolerance for the test of `p` can be set using `kwargs...`. +""" +function check_point(M::HamiltonianMatrices, p; kwargs...) + if !is_hamiltonian(p; kwargs...) + return DomainError( + norm((Hamiltonian(p)^+).value + p), + "The point $(p) does not lie on $M, since it is not hamiltonian.", + ) + end + return nothing +end + +""" + check_vector(M::HamiltonianMatrices{n,𝔽}, p, X; kwargs... ) + +Check whether `X` is a tangent vector to manifold point `p` on the +[`HamiltonianMatrices`](@ref) `M`, i.e. `X` has to be a Hamiltonian matrix +The tolerance for [`is_hamiltonian`](@ref) `X` can be set using `kwargs...`. +""" +function check_vector(M::HamiltonianMatrices, p, X; kwargs...) + if !is_hamiltonian(X; kwargs...) + return DomainError( + norm((Hamiltonian(X)^+).value + X), + "The vector $(X) is not a tangent vector to $(p) on $(M), since it is not hamiltonian.", + ) + end + return nothing +end + +embed(::HamiltonianMatrices, p) = p +embed(::HamiltonianMatrices, p, X) = X + +function get_embedding(::HamiltonianMatrices{TypeParameter{Tuple{N}},𝔽}) where {N,𝔽} + return Euclidean(2 * N, 2 * N; field=𝔽) +end +function get_embedding(M::HamiltonianMatrices{Tuple{Int},𝔽}) where {𝔽} + N = get_parameter(M.size)[1] + return Euclidean(2 * N, 2 * N; field=𝔽, parameter=:field) +end + +""" + is_flat(::HamiltonianMatrices) + +Return true. [`HamiltonianMatrices`](@ref) is a flat manifold. +""" +is_flat(M::HamiltonianMatrices) = true + +@doc raw""" + is_hamiltonian(A::AbstractMatrix; kwargs...) + +Test whether a matrix `A` is hamiltonian. +The test consists of verifying whether + +```math +A^+ = -A +``` +where ``A^+`` denotes the [`symplectic_inverse`](@ref) of `A`. + +The passed keyword arguments are passed on to `isapprox` +check within +""" +function is_hamiltonian(A::AbstractMatrix; kwargs...) + return isapprox(symplectic_inverse(A), -A; kwargs...) +end +function is_hamiltonian(A::Hamiltonian; kwargs...) + return isapprox((A^+).value, -A.value; kwargs...) +end + +function Base.show(io::IO, ::HamiltonianMatrices{TypeParameter{Tuple{n}},F}) where {n,F} + return print(io, "HamiltonianMatrices($(2n), $(F))") +end +function Base.show(io::IO, M::HamiltonianMatrices{Tuple{Int},F}) where {F} + n = get_parameter(M.size)[1] + return print(io, "HamiltonianMatrices($(2n), $(F); parameter=:field)") +end + +@doc raw""" + pX = rand(M::HamiltonianMatrices; σ::Real=1.0, vector_at=nothing) + rand!(M::HamiltonianMatrices, pX; σ::Real=1.0, vector_at=nothing) + +Generate a random Hamiltonian matrix. Since these are a submanifold of ``ℝ^{2n×2n}``, +the same method applies for points and tangent vectors. +This can also be done in-place of `pX`. + +The construction is based on generating one normally-distributed +``n×n`` matrix ``A`` and two symmetric ``n×n`` matrices ``B, C`` which are then stacked: + +```math +p = \begin{pmatrix} A & B\\ C & -A^{\mathrm{T}} \end{pmatrix} +``` + +""" +rand(M::HamiltonianMatrices; σ::Real=1.0) + +function rand!( + rng::AbstractRNG, + M::HamiltonianMatrices{<:Any,ℝ}, + pX; + σ::Real=one(real(eltype(pX))), + vector_at=nothing, +) + n = get_parameter(M.size)[1] + p1 = @view(pX[1:n, 1:n]) + p2 = @view(pX[1:n, (n + 1):(2n)]) + p3 = @view(pX[(n + 1):(2n), 1:n]) + p4 = @view(pX[(n + 1):(2n), (n + 1):(2n)]) + randn!(rng, p1) + p4 .= -p1' + randn!(rng, p2) + randn!(rng, p3) + p2 .= (1 / 2) .* (p2 .+ p2') + p3 .= (1 / 2) .* (p2 .+ p2') + pX .*= σ + return pX +end diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 3d40bbcd4c..4ff055eff0 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -3,7 +3,7 @@ The hyperbolic space $\mathcal H^n$ represented by $n+1$-Tuples, i.e. embedded in the [`Lorentz`](@ref)ian manifold equipped with the [`MinkowskiMetric`](@ref) -$⟨\cdot,\cdot⟩_{\mathrm{M}}$. The space is defined as +$⟨⋅,⋅⟩_{\mathrm{M}}$. The space is defined as ```math \mathcal H^n = \Bigl\{p ∈ ℝ^{n+1}\ \Big|\ ⟨p,p⟩_{\mathrm{M}}= -p_{n+1}^2 @@ -63,7 +63,7 @@ end In the Hyperboloid model of the [`Hyperbolic`](@ref) $\mathcal H^n$ tangent vctors are represented as vectors in $ℝ^{n+1}$ with [`MinkowskiMetric`](@ref) $⟨p,X⟩_{\mathrm{M}}=0$ to their base -point $p$. +point ``p``. This representation is the default, i.e. vectors are assumed to have this repesentation. """ @@ -223,7 +223,7 @@ from `p` towards `X`. The formula reads + \sinh(\sqrt{⟨X,X⟩_{\mathrm{M}}})\frac{X}{\sqrt{⟨X,X⟩_{\mathrm{M}}}}, ```` -where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, +where $⟨⋅,⋅⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, the [`Lorentz`](@ref)ian manifold. """ exp(::Hyperbolic, ::Any...) @@ -253,7 +253,7 @@ end injectivity_radius(M::Hyperbolic) injectivity_radius(M::Hyperbolic, p) -Return the injectivity radius on the [`Hyperbolic`](@ref), which is $∞$. +Return the injectivity radius on the [`Hyperbolic`](@ref), which is ``∞``. """ injectivity_radius(::Hyperbolic) = Inf @@ -287,7 +287,7 @@ reaches `q` after time 1. The formula reads for $p ≠ q$ \frac{q-⟨p,q⟩_{\mathrm{M}} p}{\lVert q-⟨p,q⟩_{\mathrm{M}} p \rVert_2}, ``` -where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, +where $⟨⋅,⋅⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, the [`Lorentz`](@ref)ian manifold. For $p=q$ the logarihmic map is equal to the zero vector. """ log(::Hyperbolic, ::Any...) @@ -344,7 +344,7 @@ The formula reads ````math Y = X + ⟨p,X⟩_{\mathrm{M}} p, ```` -where $⟨\cdot, \cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, +where $⟨⋅, ⋅⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, the [`Lorentz`](@ref)ian manifold. !!! note @@ -376,7 +376,7 @@ connecting `p` and `q`. The formula reads \mathcal P_{q←p}X = X - \frac{⟨\log_p q,X⟩_p}{d^2_{\mathcal H^n}(p,q)} \bigl(\log_p q + \log_qp \bigr), ```` -where $⟨\cdot,\cdot⟩_p$ denotes the inner product in the tangent space at `p`. +where $⟨⋅,⋅⟩_p$ denotes the inner product in the tangent space at `p`. """ parallel_transport_to(::Hyperbolic, ::Any, ::Any, ::Any) diff --git a/src/manifolds/HyperbolicHyperboloid.jl b/src/manifolds/HyperbolicHyperboloid.jl index b5df6543bb..e5070232e5 100644 --- a/src/manifolds/HyperbolicHyperboloid.jl +++ b/src/manifolds/HyperbolicHyperboloid.jl @@ -229,7 +229,7 @@ Compute the distance on the [`Hyperbolic`](@ref) `M`, which reads d_{\mathcal H^n}(p,q) = \operatorname{acosh}( - ⟨p, q⟩_{\mathrm{M}}), ```` -where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, +where $⟨⋅,⋅⟩_{\mathrm{M}}$ denotes the [`MinkowskiMetric`](@ref) on the embedding, the [`Lorentz`](@ref)ian manifold. """ function distance(::Hyperbolic, p, q) @@ -308,7 +308,7 @@ end get_coordinates(M::Hyperbolic, p, X, ::DefaultOrthonormalBasis) Compute the coordinates of the vector `X` with respect to the orthogonalized version of -the unit vectors from $ℝ^n$, where $n$ is the manifold dimension of the [`Hyperbolic`](@ref) +the unit vectors from $ℝ^n$, where ``n`` is the manifold dimension of the [`Hyperbolic`](@ref) `M`, utting them intop the tangent space at `p` and orthonormalizing them. """ get_coordinates(M::Hyperbolic, p, X, ::DefaultOrthonormalBasis) @@ -335,7 +335,7 @@ end get_vector(M::Hyperbolic, p, c, ::DefaultOrthonormalBasis) Compute the vector from the coordinates with respect to the orthogonalized version of -the unit vectors from $ℝ^n$, where $n$ is the manifold dimension of the [`Hyperbolic`](@ref) +the unit vectors from $ℝ^n$, where ``n`` is the manifold dimension of the [`Hyperbolic`](@ref) `M`, utting them intop the tangent space at `p` and orthonormalizing them. """ get_vector(M::Hyperbolic, p, c, ::DefaultOrthonormalBasis) diff --git a/src/manifolds/HyperbolicPoincareBall.jl b/src/manifolds/HyperbolicPoincareBall.jl index 81726e55da..fd1c96ffd2 100644 --- a/src/manifolds/HyperbolicPoincareBall.jl +++ b/src/manifolds/HyperbolicPoincareBall.jl @@ -120,7 +120,7 @@ end convert a [`HyperboloidTVector`](@ref) `X` at `p` to a [`PoincareBallTVector`](@ref) on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ by computing the push forward $π_*(p)[X]$ of -the isometry $π$ that maps from the Hyperboloid to the Poincaré ball, +the isometry ``π`` that maps from the Hyperboloid to the Poincaré ball, cf. [`convert(::Type{PoincareBallPoint}, ::HyperboloidPoint)`](@ref). The formula reads @@ -184,7 +184,7 @@ end convert a [`PoincareHalfSpaceTVector`](@ref) `X` at `p` to a [`PoincareBallTVector`](@ref) on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ by computing the push forward $π_*(p)[X]$ of -the isometry $π$ that maps from the Poincaré half space to the Poincaré ball, +the isometry ``π`` that maps from the Poincaré half space to the Poincaré ball, cf. [`convert(::Type{PoincareBallPoint}, ::PoincareHalfSpacePoint)`](@ref). The formula reads diff --git a/src/manifolds/HyperbolicPoincareHalfspace.jl b/src/manifolds/HyperbolicPoincareHalfspace.jl index 0bc0c14457..8ecc74d2e2 100644 --- a/src/manifolds/HyperbolicPoincareHalfspace.jl +++ b/src/manifolds/HyperbolicPoincareHalfspace.jl @@ -74,7 +74,7 @@ end convert a [`PoincareBallTVector`](@ref) `X` at `p` to a [`PoincareHalfSpacePoint`](@ref) on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ by computing the push forward $π_*(p)[X]$ of -the isometry $π$ that maps from the Poincaré ball to the Poincaré half space, +the isometry ``π`` that maps from the Poincaré ball to the Poincaré half space, cf. [`convert(::Type{PoincareHalfSpacePoint}, ::PoincareBallPoint)`](@ref). The formula reads @@ -119,7 +119,7 @@ end convert a [`HyperboloidTVector`](@ref) `X` at `p` to a [`PoincareHalfSpaceTVector`](@ref) on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ by computing the push forward $π_*(p)[X]$ of -the isometry $π$ that maps from the Hyperboloid to the Poincaré half space, +the isometry ``π`` that maps from the Hyperboloid to the Poincaré half space, cf. [`convert(::Type{PoincareHalfSpacePoint}, ::HyperboloidPoint)`](@ref). This is done similarly to the approach there, i.e. by using the Poincaré ball model as diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 87ba053734..62a028ddbf 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -449,7 +449,7 @@ end Return the local matrix representation at the point `p` of the metric tensor ``g`` with respect to the [`AbstractBasis`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.AbstractBasis) `B` on the [`AbstractManifold`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.AbstractManifold) `M`. Let ``d``denote the dimension of the manifold and $b_1,\ldots,b_d$ the basis vectors. -Then the local matrix representation is a matrix ``G\in 𝔽^{n\times n}`` whose entries are +Then the local matrix representation is a matrix ``G\in 𝔽^{n×n}`` whose entries are given by ``g_{ij} = g_p(b_i,b_j), i,j\in\{1,…,d\}``. This yields the property for two tangent vectors (using Einstein summation convention) diff --git a/src/manifolds/Multinomial.jl b/src/manifolds/Multinomial.jl index e01f2b763d..5d0dbc42b4 100644 --- a/src/manifolds/Multinomial.jl +++ b/src/manifolds/Multinomial.jl @@ -7,11 +7,11 @@ The multinomial manifold consists of `m` column vectors, where each column is of ````math \mathcal{MN}(n,m) \coloneqq \bigl\{ p ∈ ℝ^{n×m}\ \big|\ p_{i,j} > 0 \text{ for all } i=1,…,n, j=1,…,m \text{ and } p^{\mathrm{T}}\mathbb{1}_m = \mathbb{1}_n\bigr\}, ```` -where $\mathbb{1}_k$ is the vector of length $k$ containing ones. +where ``\mathbb{1}_k`` is the vector of length ``k`` containing ones. This yields exactly the same metric as considering the product metric of the probablity vectors, i.e. [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) of the -$(n-1)$-dimensional [`ProbabilitySimplex`](@ref). +``(n-1)``-dimensional [`ProbabilitySimplex`](@ref). The [`ProbabilitySimplex`](@ref) is stored internally within `M.manifold`, such that all functions of [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) can be used directly. @@ -20,7 +20,7 @@ The [`ProbabilitySimplex`](@ref) is stored internally within `M.manifold`, such MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:type) -Generate the manifold of matrices $\mathbb R^{n×m}$ such that the $m$ columns are +Generate the manifold of matrices ``ℝ^{n×m}`` such that the ``m`` columns are discrete probability distributions, i.e. sum up to one. `parameter`: whether a type parameter should be used to store `n` and `m`. By default size @@ -50,7 +50,7 @@ end check_point(M::MultinomialMatrices, p) Checks whether `p` is a valid point on the [`MultinomialMatrices`](@ref)`(m,n)` `M`, i.e. is a matrix -of `m` discrete probability distributions as columns from $\mathbb R^{n}$, i.e. each column is a point from +of `m` discrete probability distributions as columns from ``ℝ^n``, i.e. each column is a point from [`ProbabilitySimplex`](@ref)`(n-1)`. """ check_point(::MultinomialMatrices, ::Any) diff --git a/src/manifolds/MultinomialDoublyStochastic.jl b/src/manifolds/MultinomialDoublyStochastic.jl index 40a4ef3e03..6ff2769a94 100644 --- a/src/manifolds/MultinomialDoublyStochastic.jl +++ b/src/manifolds/MultinomialDoublyStochastic.jl @@ -14,7 +14,7 @@ end @doc raw""" MultinomialDoublyStochastic{T} <: AbstractMultinomialDoublyStochastic -The set of doubly stochastic multinomial matrices consists of all $n×n$ matrices with +The set of doubly stochastic multinomial matrices consists of all ``n×n`` matrices with stochastic columns and rows, i.e. ````math \begin{aligned} @@ -24,7 +24,7 @@ stochastic columns and rows, i.e. \end{aligned} ```` -where $\mathbf{1}_n$ is the vector of length $n$ containing ones. +where ``\mathbf{1}_n`` is the vector of length ``n`` containing ones. The tangent space can be written as @@ -35,7 +35,7 @@ X\mathbf{1}_n = X^{\mathrm{T}}\mathbf{1}_n = \mathbf{0}_n \bigr\}, ```` -where $\mathbf{0}_n$ is the vector of length $n$ containing zeros. +where ``\mathbf{0}_n`` is the vector of length ``n`` containing zeros. More details can be found in Section III [DouikHassibi:2019](@cite). @@ -43,7 +43,7 @@ More details can be found in Section III [DouikHassibi:2019](@cite). MultinomialDoubleStochastic(n::Int; parameter::Symbol=:type) -Generate the manifold of matrices $\mathbb R^{n×n}$ that are doubly stochastic and symmetric. +Generate the manifold of matrices ``ℝ^{n×n}`` that are doubly stochastic and symmetric. """ struct MultinomialDoubleStochastic{T} <: AbstractMultinomialDoublyStochastic size::T @@ -137,15 +137,15 @@ The formula reads ````math \operatorname{proj}_p(Y) = Y - (α\mathbf{1}_n^{\mathrm{T}} + \mathbf{1}_nβ^{\mathrm{T}}) ⊙ p, ```` -where $⊙$ denotes the Hadamard or elementwise product and $\mathbb{1}_n$ is the vector of length $n$ containing ones. -The two vectors $α,β ∈ ℝ^{n×n}$ are computed as a solution (typically using the left pseudo inverse) of +where ``⊙`` denotes the Hadamard or elementwise product and ``\mathbb{1}_n`` is the vector of length ``n`` containing ones. +The two vectors ``α,β ∈ ℝ^{n×n}`` are computed as a solution (typically using the left pseudo inverse) of ````math \begin{pmatrix} I_n & p\\p^{\mathrm{T}} & I_n \end{pmatrix} \begin{pmatrix} α\\ β\end{pmatrix} = \begin{pmatrix} Y\mathbf{1}\\Y^{\mathrm{T}}\mathbf{1}\end{pmatrix}, ```` -where $I_n$ is the $n×n$ unit matrix and $\mathbf{1}_n$ is the vector of length $n$ containing ones. +where ``I_n`` is the ``n×n`` unit matrix and ``\mathbf{1}_n`` is the vector of length ``n`` containing ones. """ project(::MultinomialDoubleStochastic, ::Any, ::Any) @@ -210,7 +210,7 @@ end retract(M::MultinomialDoubleStochastic, p, X, ::ProjectionRetraction) compute a projection based retraction by projecting $p\odot\exp(X⨸p)$ back onto the manifold, -where $⊙,⨸$ are elementwise multiplication and division, respectively. Similarly, $\exp$ +where ``⊙,⨸`` are elementwise multiplication and division, respectively. Similarly, ``\exp`` refers to the elementwise exponentiation. """ retract(::MultinomialDoubleStochastic, ::Any, ::Any, ::ProjectionRetraction) diff --git a/src/manifolds/MultinomialSymmetric.jl b/src/manifolds/MultinomialSymmetric.jl index 23b178b368..470f6bb779 100644 --- a/src/manifolds/MultinomialSymmetric.jl +++ b/src/manifolds/MultinomialSymmetric.jl @@ -1,7 +1,7 @@ @doc raw""" MultinomialSymmetric{T} <: AbstractMultinomialDoublyStochastic{N} -The multinomial symmetric matrices manifold consists of all symmetric $n×n$ matrices with +The multinomial symmetric matrices manifold consists of all symmetric ``n×n`` matrices with positive entries such that each column sums to one, i.e. ````math @@ -13,7 +13,7 @@ positive entries such that each column sums to one, i.e. \end{aligned} ```` -where $\mathbf{1}_n$ is the vector of length $n$ containing ones. +where ``\mathbf{1}_n`` is the vector of length ``n`` containing ones. It is modeled as [`IsIsometricEmbeddedManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.IsIsometricEmbeddedManifold). via the [`AbstractMultinomialDoublyStochastic`](@ref) type, since it shares a few functions @@ -29,7 +29,7 @@ X\mathbf{1}_n = \mathbf{0}_n \bigr\}, ```` -where $\mathbf{0}_n$ is the vector of length $n$ containing zeros. +where ``\mathbf{0}_n`` is the vector of length ``n`` containing zeros. More details can be found in Section IV [DouikHassibi:2019](@cite). @@ -37,7 +37,7 @@ More details can be found in Section IV [DouikHassibi:2019](@cite). MultinomialSymmetric(n) -Generate the manifold of matrices $\mathbb R^{n×n}$ that are doubly stochastic and symmetric. +Generate the manifold of matrices ``ℝ^{n×n}`` that are doubly stochastic and symmetric. """ struct MultinomialSymmetric{T} <: AbstractMultinomialDoublyStochastic size::T @@ -110,12 +110,12 @@ The formula reads ````math \operatorname{proj}_p(Y) = Y - (α\mathbf{1}_n^{\mathrm{T}} + \mathbf{1}_n α^{\mathrm{T}}) ⊙ p, ```` -where $⊙$ denotes the Hadamard or elementwise product and $\mathbb{1}_n$ is the vector of length $n$ containing ones. -The two vector $α ∈ ℝ^{n×n}$ is given by solving +where ``⊙`` denotes the Hadamard or elementwise product and ``\mathbb{1}_n`` is the vector of length ``n`` containing ones. +The two vector ``α ∈ ℝ^{n×n}`` is given by solving ````math (I_n+p)α = Y\mathbf{1}, ```` -where $I_n$ is teh $n×n$ unit matrix and $\mathbf{1}_n$ is the vector of length $n$ containing ones. +where ``I_n`` is teh ``n×n`` unit matrix and ``\mathbf{1}_n`` is the vector of length ``n`` containing ones. """ project(::MultinomialSymmetric, ::Any, ::Any) @@ -133,8 +133,8 @@ end @doc raw""" retract(M::MultinomialSymmetric, p, X, ::ProjectionRetraction) -compute a projection based retraction by projecting $p\odot\exp(X⨸p)$ back onto the manifold, -where $⊙,⨸$ are elementwise multiplication and division, respectively. Similarly, $\exp$ +compute a projection based retraction by projecting ``p⊙\exp(X⨸p)`` back onto the manifold, +where ``⊙,⨸`` are elementwise multiplication and division, respectively. Similarly, ``\exp`` refers to the elementwise exponentiation. """ retract(::MultinomialSymmetric, ::Any, ::Any, ::ProjectionRetraction) diff --git a/src/manifolds/Oblique.jl b/src/manifolds/Oblique.jl index 9663f92633..94407b7564 100644 --- a/src/manifolds/Oblique.jl +++ b/src/manifolds/Oblique.jl @@ -1,10 +1,10 @@ @doc raw""" Oblique{T,𝔽,S} <: AbstractPowerManifold{𝔽} -The oblique manifold $\mathcal{OB}(n,m)$ is the set of 𝔽-valued matrices with unit norm +The oblique manifold ``\mathcal{OB}(n,m)`` is the set of 𝔽-valued matrices with unit norm column endowed with the metric from the embedding. This yields exactly the same metric as considering the product metric of the unit norm vectors, i.e. [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) of the -$(n-1)$-dimensional [`Sphere`](@ref). +``(n-1)``-dimensional [`Sphere`](@ref). The [`Sphere`](@ref) is stored internally within `M.manifold`, such that all functions of [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) can be used directly. @@ -13,7 +13,7 @@ The [`Sphere`](@ref) is stored internally within `M.manifold`, such that all fun Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) -Generate the manifold of matrices $\mathbb R^{n × m}$ such that the $m$ columns are unit +Generate the manifold of matrices ``\mathbb R^{n×m}`` such that the ``m`` columns are unit vectors, i.e. from the [`Sphere`](@ref)`(n-1)`. """ struct Oblique{T,𝔽,S} <: AbstractPowerManifold{𝔽,Sphere{S,𝔽},ArrayPowerRepresentation} @@ -39,7 +39,7 @@ end check_point(M::Oblique, p) Checks whether `p` is a valid point on the [`Oblique`](@ref)`{m,n}` `M`, i.e. is a matrix -of `m` unit columns from $\mathbb R^{n}$, i.e. each column is a point from +of `m` unit columns from ``\mathbb R^{n}``, i.e. each column is a point from [`Sphere`](@ref)`(n-1)`. """ check_point(::Oblique, ::Any) diff --git a/src/manifolds/PositiveNumbers.jl b/src/manifolds/PositiveNumbers.jl index 84313f329c..05abf09b57 100644 --- a/src/manifolds/PositiveNumbers.jl +++ b/src/manifolds/PositiveNumbers.jl @@ -304,7 +304,7 @@ Compute the parallel transport of `X` from the tangent space at `p` to the tange `q` on the [`PositiveNumbers`](@ref) `M`. ````math -\mathcal P_{q\gets p}(X) = X\cdot\frac{q}{p}. +\mathcal P_{q\gets p}(X) = X⋅\frac{q}{p}. ```` """ parallel_transport_to(::PositiveNumbers, ::Any, ::Any, ::Any) diff --git a/src/manifolds/ProbabilitySimplex.jl b/src/manifolds/ProbabilitySimplex.jl index 7a7983314d..74da5388bc 100644 --- a/src/manifolds/ProbabilitySimplex.jl +++ b/src/manifolds/ProbabilitySimplex.jl @@ -24,7 +24,7 @@ T_pΔ^n = \biggl\{ X ∈ ℝ^{n+1}\ \big|\ ⟨\mathbb{1},X⟩ = \sum_{i=1}^{n+1} The manifold is implemented assuming the Fisher-Rao metric for the multinomial distribution, which is equivalent to the induced metric from isometrically embedding the probability -simplex in the $n$-sphere of radius 2. +simplex in the ``n``-sphere of radius 2. The corresponding diffeomorphism $\varphi: \mathbb Δ^n → \mathcal N$, where $\mathcal N \subset 2𝕊^n$ is given by $\varphi(p) = 2\sqrt{p}$. @@ -82,7 +82,7 @@ end @doc raw""" change_metric(M::ProbabilitySimplex, ::EuclideanMetric, p, X) -To change the metric, we are looking for a function ``c\colon T_pΔ^n \to T_pΔ^n`` such that for all ``X,Y ∈ T_pΔ^n`` +To change the metric, we are looking for a function ``c\colon T_pΔ^n → T_pΔ^n`` such that for all ``X,Y ∈ T_pΔ^n`` This can be achieved by rewriting representer change in matrix form as `(Diagonal(p) - p * p') * X` and taking square root of the matrix """ @@ -441,7 +441,7 @@ end @doc raw""" representation_size(::ProbabilitySimplex) -Return the representation size of points in the $n$-dimensional probability simplex, +Return the representation size of points in the ``n``-dimensional probability simplex, i.e. an array size of `(n+1,)`. """ function representation_size(M::ProbabilitySimplex) diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 7135eb4df2..008a5d138e 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -9,32 +9,32 @@ abstract type AbstractProjectiveSpace{𝔽} <: AbstractDecoratorManifold{𝔽} e @doc raw""" ProjectiveSpace{n,𝔽} <: AbstractProjectiveSpace{𝔽} -The projective space $𝔽ℙ^n$ is the manifold of all lines in $𝔽^{n+1}$. +The projective space ``𝔽ℙ^n`` is the manifold of all lines in ``𝔽^{n+1}``. The default representation is in the embedding, i.e. as unit norm vectors in -$𝔽^{n+1}$: +``𝔽^{n+1}``: ````math 𝔽ℙ^n := \bigl\{ [p] ⊂ 𝔽^{n+1} \ \big|\ \lVert p \rVert = 1, λ ∈ 𝔽, |λ| = 1, p ∼ p λ \bigr\}, ```` -where $[p]$ is an equivalence class of points $p$, and $∼$ indicates equivalence. -For example, the real projective space $ℝℙ^n$ is represented as the unit sphere $𝕊^n$, where +where ``[p]`` is an equivalence class of points ``p``, and ``∼`` indicates equivalence. +For example, the real projective space ``ℝℙ^n`` is represented as the unit sphere ``𝕊^n``, where antipodal points are considered equivalent. -The tangent space at point $p$ is given by +The tangent space at point ``p`` is given by ````math T_p 𝔽ℙ^{n} := \bigl\{ X ∈ 𝔽^{n+1}\ \big|\ ⟨p,X⟩ = 0 \bigr \}, ```` -where $⟨⋅,⋅⟩$ denotes the inner product in the embedding $𝔽^{n+1}$. +where ``⟨⋅,⋅⟩`` denotes the inner product in the embedding ``𝔽^{n+1}``. -When $𝔽 = ℍ$, this implementation of $ℍℙ^n$ is the right-quaternionic projective +When ``𝔽 = ℍ``, this implementation of ``ℍℙ^n`` is the right-quaternionic projective space. # Constructor ProjectiveSpace(n[, field=ℝ]) -Generate the projective space $𝔽ℙ^{n} ⊂ 𝔽^{n+1}$, defaulting to the real projective space -$ℝℙ^n$, where `field` can also be used to generate the complex- and right-quaternionic +Generate the projective space ``𝔽ℙ^{n} ⊂ 𝔽^{n+1}``, defaulting to the real projective space +``ℝℙ^n``, where `field` can also be used to generate the complex- and right-quaternionic projective spaces. """ struct ProjectiveSpace{T,𝔽} <: AbstractProjectiveSpace{𝔽} @@ -52,15 +52,15 @@ end @doc raw""" ArrayProjectiveSpace{T<:Tuple,𝔽} <: AbstractProjectiveSpace{𝔽} -The projective space $𝔽ℙ^{n₁,n₂,…,nᵢ}$ is the manifold of all lines in $𝔽^{n₁,n₂,…,nᵢ}$. +The projective space ``𝔽ℙ^{n₁,n₂,…,nᵢ}`` is the manifold of all lines in ``𝔽^{n₁,n₂,…,nᵢ}``. The default representation is in the embedding, i.e. as unit (Frobenius) norm matrices in -$𝔽^{n₁,n₂,…,nᵢ}$: +``𝔽^{n₁,n₂,…,nᵢ}``: ````math 𝔽ℙ^{n_1, n_2, …, n_i} := \bigl\{ [p] ⊂ 𝔽^{n_1, n_2, …, n_i} \ \big|\ \lVert p \rVert_{\mathrm{F}} = 1, λ ∈ 𝔽, |λ| = 1, p ∼ p λ \bigr\}. ```` -where $[p]$ is an equivalence class of points $p$, $\sim$ indicates equivalence, and -$\lVert ⋅ \rVert_{\mathrm{F}}$ is the Frobenius norm. +where ``[p]`` is an equivalence class of points ``p``, ``∼`` indicates equivalence, and +``\lVert ⋅ \rVert_{\mathrm{F}}`` is the Frobenius norm. Note that unlike [`ProjectiveSpace`](@ref), the argument for `ArrayProjectiveSpace` is given by the size of the embedding. This means that [`ProjectiveSpace(2)`](@ref) and `ArrayProjectiveSpace(3)` are the same @@ -68,20 +68,20 @@ manifold. Additionally, `ArrayProjectiveSpace(n,1;field=𝔽)` and [`Grassmann(n,1;field=𝔽)`](@ref) are the same. -The tangent space at point $p$ is given by +The tangent space at point ``p`` is given by ````math T_p 𝔽ℙ^{n_1, n_2, …, n_i} := \bigl\{ X ∈ 𝔽^{n_1, n_2, …, n_i}\ |\ ⟨p,X⟩_{\mathrm{F}} = 0 \bigr \}, ```` -where $⟨⋅,⋅⟩_{\mathrm{F}}$ denotes the (Frobenius) inner product in the embedding -$𝔽^{n_1, n_2, …, n_i}$. +where ``⟨⋅,⋅⟩_{\mathrm{F}}`` denotes the (Frobenius) inner product in the embedding +``𝔽^{n_1, n_2, …, n_i}``. # Constructor ArrayProjectiveSpace(n₁,n₂,...,nᵢ; field=ℝ) -Generate the projective space $𝔽ℙ^{n_1, n_2, …, n_i}$, defaulting to the real projective +Generate the projective space ``𝔽ℙ^{n_1, n_2, …, n_i}``, defaulting to the real projective space, where `field` can also be used to generate the complex- and right-quaternionic projective spaces. """ @@ -124,7 +124,7 @@ end Check whether `X` is a tangent vector in the tangent space of `p` on the [`AbstractProjectiveSpace`](@ref) `M`, i.e. that `X` has the same size as elements of the tangent space of the embedding and that the Frobenius inner product -$⟨p, X⟩_{\mathrm{F}} = 0$. +``⟨p, X⟩_{\mathrm{F}} = 0``. """ function check_vector( M::AbstractProjectiveSpace, @@ -158,7 +158,7 @@ embed(::AbstractProjectiveSpace, p, X) = X @doc raw""" distance(M::AbstractProjectiveSpace, p, q) -Compute the Riemannian distance on [`AbstractProjectiveSpace`](@ref) `M`$=𝔽ℙ^n$ between +Compute the Riemannian distance on [`AbstractProjectiveSpace`](@ref) `M```=𝔽ℙ^n`` between points `p` and `q`, i.e. ````math d_{𝔽ℙ^n}(p, q) = \arccos\bigl| ⟨p, q⟩_{\mathrm{F}} \bigr|. @@ -193,14 +193,14 @@ end @doc raw""" get_coordinates(M::AbstractProjectiveSpace, p, X, B::DefaultOrthonormalBasis{ℝ}) -Represent the tangent vector $X$ at point $p$ from the [`AbstractProjectiveSpace`](@ref) -$M = 𝔽ℙ^n$ in an orthonormal basis by unitarily transforming the hyperplane containing $X$, -whose normal is $p$, to the hyperplane whose normal is the $x$-axis. +Represent the tangent vector ``X`` at point ``p`` from the [`AbstractProjectiveSpace`](@ref) +``M = 𝔽ℙ^n`` in an orthonormal basis by unitarily transforming the hyperplane containing ``X``, +whose normal is ``p``, to the hyperplane whose normal is the ``x``-axis. -Given $q = p \overline{λ} + x$, where -$λ = \frac{⟨x, p⟩_{\mathrm{F}}}{|⟨x, p⟩_{\mathrm{F}}|}$, $⟨⋅, ⋅⟩_{\mathrm{F}}$ denotes the -Frobenius inner product, and $\overline{⋅}$ denotes complex or quaternionic conjugation, the -formula for $Y$ is +Given ``q = p \overline{λ} + x``, where +``λ = \frac{⟨x, p⟩_{\mathrm{F}}}{|⟨x, p⟩_{\mathrm{F}}|}``, ``⟨⋅, ⋅⟩_{\mathrm{F}}`` denotes the +Frobenius inner product, and ``\overline{⋅}`` denotes complex or quaternionic conjugation, the +formula for ``Y`` is ````math \begin{pmatrix}0 \\ Y\end{pmatrix} = \left(X - q\frac{2 ⟨q, X⟩_{\mathrm{F}}}{⟨q, q⟩_{\mathrm{F}}}\right)\overline{λ}. ```` @@ -227,15 +227,15 @@ end @doc raw""" get_vector(M::AbstractProjectiveSpace, p, X, B::DefaultOrthonormalBasis{ℝ}) -Convert a one-dimensional vector of coefficients $X$ in the basis `B` of the tangent space -at $p$ on the [`AbstractProjectiveSpace`](@ref) $M=𝔽ℙ^n$ to a tangent vector $Y$ at $p$ by -unitarily transforming the hyperplane containing $X$, whose normal is the $x$-axis, to the -hyperplane whose normal is $p$. +Convert a one-dimensional vector of coefficients ``X`` in the basis `B` of the tangent space +at ``p`` on the [`AbstractProjectiveSpace`](@ref) ``M=𝔽ℙ^n`` to a tangent vector ``Y`` at ``p`` by +unitarily transforming the hyperplane containing ``X``, whose normal is the ``x``-axis, to the +hyperplane whose normal is ``p``. -Given $q = p \overline{λ} + x$, where -$λ = \frac{⟨x, p⟩_{\mathrm{F}}}{|⟨x, p⟩_{\mathrm{F}}|}$, $⟨⋅, ⋅⟩_{\mathrm{F}}$ denotes the -Frobenius inner product, and $\overline{⋅}$ denotes complex or quaternionic conjugation, the -formula for $Y$ is +Given ``q = p \overline{λ} + x``, where +``λ = \frac{⟨x, p⟩_{\mathrm{F}}}{|⟨x, p⟩_{\mathrm{F}}|}``, ``⟨⋅, ⋅⟩_{\mathrm{F}}`` denotes the +Frobenius inner product, and ``\overline{⋅}`` denotes complex or quaternionic conjugation, the +formula for ``Y`` is ````math Y = \left(X - q\frac{2 \left\langle q, \begin{pmatrix}0 \\ X\end{pmatrix}\right\rangle_{\mathrm{F}}}{⟨q, q⟩_{\mathrm{F}}}\right) λ. ```` @@ -272,16 +272,16 @@ injectivity_radius(::AbstractProjectiveSpace, p, ::AbstractRetractionMethod) = Compute the equivalent inverse retraction [`ProjectionInverseRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.ProjectionInverseRetraction), [`PolarInverseRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarInverseRetraction), and [`QRInverseRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRInverseRetraction) on the -[`AbstractProjectiveSpace`](@ref) manifold `M`$=𝔽ℙ^n$, i.e. +[`AbstractProjectiveSpace`](@ref) manifold `M```=𝔽ℙ^n``, i.e. ````math \operatorname{retr}_p^{-1} q = q \frac{1}{⟨p, q⟩_{\mathrm{F}}} - p, ```` -where $⟨⋅, ⋅⟩_{\mathrm{F}}$ is the Frobenius inner product. +where ``⟨⋅, ⋅⟩_{\mathrm{F}}`` is the Frobenius inner product. Note that this inverse retraction is equivalent to the three corresponding inverse retractions on [`Grassmann(n+1,1,𝔽)`](@ref), where the three inverse retractions in this case coincide. -For $ℝℙ^n$, it is the same as the `ProjectionInverseRetraction` on the real +For ``ℝℙ^n``, it is the same as the `ProjectionInverseRetraction` on the real [`Sphere`](@ref). """ inverse_retract( @@ -307,9 +307,9 @@ end @doc raw""" isapprox(M::AbstractProjectiveSpace, p, q; kwargs...) -Check that points `p` and `q` on the [`AbstractProjectiveSpace`](@ref) `M`$=𝔽ℙ^n$ are -members of the same equivalence class, i.e. that $p = q λ$ for some element $λ ∈ 𝔽$ with -unit absolute value, that is, $|λ| = 1$. +Check that points `p` and `q` on the [`AbstractProjectiveSpace`](@ref) `M```=𝔽ℙ^n`` are +members of the same equivalence class, i.e. that ``p = q λ`` for some element ``λ ∈ 𝔽`` with +unit absolute value, that is, ``|λ| = 1``. This is equivalent to the Riemannian [`distance`](@ref distance(::AbstractProjectiveSpace, p, q)) being 0. """ @@ -327,24 +327,24 @@ is_flat(M::AbstractProjectiveSpace) = manifold_dimension(M) == 1 @doc raw""" log(M::AbstractProjectiveSpace, p, q) -Compute the logarithmic map on [`AbstractProjectiveSpace`](@ref) `M`$ = 𝔽ℙ^n$, +Compute the logarithmic map on [`AbstractProjectiveSpace`](@ref) `M``` = 𝔽ℙ^n``, i.e. the tangent vector whose corresponding [`geodesic`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.geodesic-Tuple{AbstractManifold,%20Any,%20Any}) starting from `p` reaches `q` after time 1 on `M`. The formula reads ````math \log_p q = (q λ - \cos θ p) \frac{θ}{\sin θ}, ```` -where $θ = \arccos|⟨q, p⟩_{\mathrm{F}}|$ is the -[`distance`](@ref distance(::AbstractProjectiveSpace, p, q)) between $p$ and $q$, -$⟨⋅, ⋅⟩_{\mathrm{F}}$ is the Frobenius inner product, and -$λ = \frac{⟨q, p⟩_{\mathrm{F}}}{|⟨q, p⟩_{\mathrm{F}}|} ∈ 𝔽$ is the unit scalar that -minimizes $d_{𝔽^{n+1}}(p - q λ)$. -That is, $q λ$ is the member of the equivalence class $[q]$ that is closest to $p$ in the +where ``θ = \arccos|⟨q, p⟩_{\mathrm{F}}|`` is the +[`distance`](@ref distance(::AbstractProjectiveSpace, p, q)) between ``p`` and ``q``, +``⟨⋅, ⋅⟩_{\mathrm{F}}`` is the Frobenius inner product, and +``λ = \frac{⟨q, p⟩_{\mathrm{F}}}{|⟨q, p⟩_{\mathrm{F}}|} ∈ 𝔽`` is the unit scalar that +minimizes ``d_{𝔽^{n+1}}(p - q λ)``. +That is, ``q λ`` is the member of the equivalence class ``[q]`` that is closest to ``p`` in the embedding. -As a result, $\exp_p \circ \log_p \colon q ↦ q λ$. +As a result, ``\exp_p \circ \log_p \colon q ↦ q λ``. -The logarithmic maps for the real [`AbstractSphere`](@ref) $𝕊^n$ and the real projective -space $ℝℙ^n$ are identical when $p$ and $q$ are in the same hemisphere. +The logarithmic maps for the real [`AbstractSphere`](@ref) ``𝕊^n`` and the real projective +space ``ℝℙ^n`` are identical when ``p`` and ``q`` are in the same hemisphere. """ log(::AbstractProjectiveSpace, p, q) @@ -417,7 +417,7 @@ Orthogonally project the point `p` from the embedding onto the ````math \operatorname{proj}(p) = \frac{p}{\lVert p \rVert}_{\mathrm{F}}, ```` -where $\lVert ⋅ \rVert_{\mathrm{F}}$ denotes the Frobenius norm. +where ``\lVert ⋅ \rVert_{\mathrm{F}}`` denotes the Frobenius norm. This is identical to projection onto the [`AbstractSphere`](@ref). """ project(::AbstractProjectiveSpace, ::Any) @@ -433,7 +433,7 @@ Orthogonally project the point `X` onto the tangent space at `p` on the ````math \operatorname{proj}_p (X) = X - p⟨p, X⟩_{\mathrm{F}}, ```` -where $⟨⋅, ⋅⟩_{\mathrm{F}}$ denotes the Frobenius inner product. +where ``⟨⋅, ⋅⟩_{\mathrm{F}}`` denotes the Frobenius inner product. For the real [`AbstractSphere`](@ref) and `AbstractProjectiveSpace`, this projection is the same. """ @@ -461,7 +461,7 @@ end retract(M::AbstractProjectiveSpace, p, X, method::QRRetraction) Compute the equivalent retraction [`ProjectionRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.ProjectionRetraction), [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction), -and [`QRRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRRetraction) on the [`AbstractProjectiveSpace`](@ref) manifold `M`$=𝔽ℙ^n$, +and [`QRRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRRetraction) on the [`AbstractProjectiveSpace`](@ref) manifold `M```=𝔽ℙ^n``, i.e. ````math \operatorname{retr}_p X = \operatorname{proj}_p(p + X). @@ -469,7 +469,7 @@ i.e. Note that this retraction is equivalent to the three corresponding retractions on [`Grassmann(n+1,1,𝔽)`](@ref), where in this case they coincide. -For $ℝℙ^n$, it is the same as the `ProjectionRetraction` on the real [`Sphere`](@ref). +For ``ℝℙ^n``, it is the same as the `ProjectionRetraction` on the real [`Sphere`](@ref). """ retract( ::AbstractProjectiveSpace, @@ -521,21 +521,21 @@ end parallel_transport_to(M::AbstractProjectiveSpace, p, X, q) Parallel transport a vector `X` from the tangent space at a point `p` on the -[`AbstractProjectiveSpace`](@ref) `M`$=𝔽ℙ^n$ to the tangent space at another point `q`. +[`AbstractProjectiveSpace`](@ref) `M```=𝔽ℙ^n`` to the tangent space at another point `q`. -This implementation proceeds by transporting $X$ to $T_{q λ} M$ using the same approach as +This implementation proceeds by transporting ``X`` to ``T_{q λ} M`` using the same approach as [`parallel_transport_direction`](@ref parallel_transport_direction(::AbstractProjectiveSpace, p, X, d)), -where $λ = \frac{⟨q, p⟩_{\mathrm{F}}}{|⟨q, p⟩_{\mathrm{F}}|} ∈ 𝔽$ is the unit scalar that -takes $q$ to the member $q λ$ of its equivalence class $[q]$ closest to $p$ in the +where ``λ = \frac{⟨q, p⟩_{\mathrm{F}}}{|⟨q, p⟩_{\mathrm{F}}|} ∈ 𝔽`` is the unit scalar that +takes ``q`` to the member ``q λ`` of its equivalence class ``[q]`` closest to ``p`` in the embedding. -It then maps the transported vector from $T_{q λ} M$ to $T_{q} M$. -The resulting transport to $T_{q} M$ is +It then maps the transported vector from ``T_{q λ} M`` to ``T_{q} M``. +The resulting transport to ``T_{q} M`` is ````math \mathcal{P}_{q ← p}(X) = \left(X - \left(p \frac{\sin θ}{θ} + d \frac{1 - \cos θ}{θ^2}\right) ⟨d, X⟩_p\right) \overline{λ}, ```` -where $d = \log_p q$ is the direction of the transport, $θ = \lVert d \rVert_p$ is the -[`distance`](@ref distance(::AbstractProjectiveSpace, p, q)) between $p$ and $q$, and -$\overline{⋅}$ denotes complex or quaternionic conjugation. +where ``d = \log_p q`` is the direction of the transport, ``θ = \lVert d \rVert_p`` is the +[`distance`](@ref distance(::AbstractProjectiveSpace, p, q)) between ``p`` and ``q``, and +``\overline{⋅}`` denotes complex or quaternionic conjugation. """ parallel_transport_to(::AbstractProjectiveSpace, ::Any, ::Any, ::Any) @@ -564,7 +564,7 @@ indicated by the tangent vector `d`, i.e. ````math \mathcal{P}_{\exp_p (d) ← p}(X) = X - \left(p \frac{\sin θ}{θ} + d \frac{1 - \cos θ}{θ^2}\right) ⟨d, X⟩_p, ```` -where $θ = \lVert d \rVert$, and $⟨⋅, ⋅⟩_p$ is the [`inner`](@ref) product at the point $p$. +where ``θ = \lVert d \rVert``, and ``⟨⋅, ⋅⟩_p`` is the [`inner`](@ref) product at the point ``p``. For the real projective space, this is equivalent to the same vector transport on the real [`AbstractSphere`](@ref). """ diff --git a/src/manifolds/QuotientManifold.jl b/src/manifolds/QuotientManifold.jl index 3c806f8b09..c7e5467df5 100644 --- a/src/manifolds/QuotientManifold.jl +++ b/src/manifolds/QuotientManifold.jl @@ -136,7 +136,7 @@ get_orbit_action(::AbstractManifold) @doc raw""" horizontal_lift(N::AbstractManifold, q, X) - horizontal_lift(::QuotientManifold{𝔽,MT<:AbstractManifold{𝔽},NT<:AbstractManifold}, p, X) where {𝔽} + horizontal_lift(::QuotientManifold{𝔽,M,N}, p, X) Given a point `q` in total space of quotient manifold `N` such that ``p=π(q)`` is a point on a quotient manifold `M` (implicitly given for the first case) and a tangent vector `X` this @@ -150,18 +150,21 @@ end @doc raw""" horizontal_lift!(N, Y, q, X) - horizontal_lift!(QuotientManifold{M,N}, Y, p, X) + horizontal_lift!(QuotientManifold{𝔽,M,N}, Y, p, X) -Compute the [`horizontal_lift`](@ref) of `X` from ``T_p\mathcal M``, ``p=π(q)``. +Compute the horizontal lift of `X` from ``T_p\mathcal M``, ``p=π(q)``. to ``T_q\mathcal N` in place of `Y`. """ horizontal_lift!(N::AbstractManifold, Y, q, X) -""" +@doc raw""" horizontal_component(N::AbstractManifold, p, X) + horizontal_compontent(QuotientManifold{𝔽,M,N}, p, X) Compute the horizontal component of tangent vector `X` at point `p` in the total space of quotient manifold `N`. + +This is often written as the space ``\mathrm{Hor}_p^π\mathcal N``. """ function horizontal_component(N::AbstractManifold, p, X) Y = allocate_result(N, horizontal_component, X, p) @@ -172,11 +175,14 @@ function Base.show(io::IO, M::QuotientManifold) return print(io, "QuotientManifold($(M.manifold), $(M.total_space))") end -""" +@doc raw""" vertical_component(N::AbstractManifold, p, X) + vertical_component(QuotientManifold{𝔽,M,N}, p, X) Compute the vertical component of tangent vector `X` at point `p` in the total space of quotient manifold `N`. + +This is often written as the space ``\mathrm{ver}_p^π\mathcal N``. """ function vertical_component(N::AbstractManifold, p, X) return X - horizontal_component(N, p, X) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 1a1aef5776..dc66b52b7b 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,14 +1,14 @@ @doc raw""" Rotations{T} <: AbstractManifold{ℝ} -The manifold of rotation matrices of size ``n × n``, i.e. +The manifold of rotation matrices of size ``n×n``, i.e. real-valued orthogonal matrices with determinant ``+1``. # Constructor Rotations(n::Int; parameter::Symbol=:type) -Generate the manifold of ``n × n`` rotation matrices. +Generate the manifold of ``n×n`` rotation matrices. """ const Rotations{T} = GeneralUnitaryMatrices{T,ℝ,DeterminantOneMatrices} @@ -43,7 +43,7 @@ end @doc raw""" angles_4d_skew_sym_matrix(A) -The Lie algebra of [`Rotations(4)`](@ref) in ``ℝ^{4 × 4}``, ``𝔰𝔬(4)``, consists of ``4 × 4`` +The Lie algebra of [`Rotations(4)`](@ref) in ``ℝ^{4×4}``, ``𝔰𝔬(4)``, consists of ``4×4`` skew-symmetric matrices. The unique imaginary components of their eigenvalues are the angles of the two plane rotations. This function computes these more efficiently than `eigvals`. diff --git a/src/manifolds/SPDFixedDeterminant.jl b/src/manifolds/SPDFixedDeterminant.jl index d72d3f1f8d..4b4f467e6e 100644 --- a/src/manifolds/SPDFixedDeterminant.jl +++ b/src/manifolds/SPDFixedDeterminant.jl @@ -6,7 +6,7 @@ The manifold of symmetric positive definite matrices of fixed determinant ``d > ````math \mathcal P_d(n) = \bigl\{ -p ∈ ℝ^{n × n} \ \big|\ a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} +p ∈ ℝ^{n×n} \ \big|\ a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \text{ and } \det(p) = d \bigr\}. ```` diff --git a/src/manifolds/SkewHermitian.jl b/src/manifolds/SkewHermitian.jl index 0427cf8c8a..a8641512ca 100644 --- a/src/manifolds/SkewHermitian.jl +++ b/src/manifolds/SkewHermitian.jl @@ -1,16 +1,16 @@ @doc raw""" SkewHermitianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $ \operatorname{SkewHerm}(n)$ consisting of the real- or -complex-valued skew-hermitian matrices of size ``n × n``, i.e. the set +The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) ``\operatorname{SkewHerm}(n)`` consisting of the real- or +complex-valued skew-hermitian matrices of size ``n×n``, i.e. the set ````math -\operatorname{SkewHerm}(n) = \bigl\{p ∈ 𝔽^{n × n}\ \big|\ p^{\mathrm{H}} = -p \bigr\}, +\operatorname{SkewHerm}(n) = \bigl\{p ∈ 𝔽^{n×n}\ \big|\ p^{\mathrm{H}} = -p \bigr\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transpose, -and the field $𝔽 ∈ \{ ℝ, ℂ, ℍ\}$. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transpose, +and the field ``𝔽 ∈ \{ ℝ, ℂ, ℍ\}``. -Though it is slightly redundant, usually the matrices are stored as ``n × n`` arrays. +Though it is slightly redundant, usually the matrices are stored as ``n×n`` arrays. Note that in this representation, the real-valued part of the diagonal must be zero, which is also reflected in the @@ -20,7 +20,7 @@ which is also reflected in the SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) -Generate the manifold of ``n × n`` skew-hermitian matrices. +Generate the manifold of ``n×n`` skew-hermitian matrices. """ struct SkewHermitianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T @@ -34,7 +34,7 @@ end @doc raw""" SkewSymmetricMatrices{T} -Generate the manifold of ``n × n`` real skew-symmetric matrices. +Generate the manifold of ``n×n`` real skew-symmetric matrices. This is equivalent to [`SkewHermitianMatrices(n, ℝ)`](@ref). # Constructor @@ -223,7 +223,7 @@ Projects `p` from the embedding onto the [`SkewHermitianMatrices`](@ref) `M`, i. \operatorname{proj}_{\operatorname{SkewHerm}(n)}(p) = \frac{1}{2} \bigl( p - p^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SkewHermitianMatrices, ::Any) @@ -241,7 +241,7 @@ Project the matrix `X` onto the tangent space at `p` on the [`SkewHermitianMatri \operatorname{proj}_p(X) = \frac{1}{2} \bigl( X - X^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SkewHermitianMatrices, ::Any, ::Any) diff --git a/src/manifolds/Spectrahedron.jl b/src/manifolds/Spectrahedron.jl index 082b6f8fa0..b1f626ac72 100644 --- a/src/manifolds/Spectrahedron.jl +++ b/src/manifolds/Spectrahedron.jl @@ -2,35 +2,35 @@ Spectrahedron{T} <: AbstractDecoratorManifold{ℝ} The Spectrahedron manifold, also known as the set of correlation matrices (symmetric -positive semidefinite matrices) of rank $k$ with unit trace. +positive semidefinite matrices) of rank ``k`` with unit trace. ````math \begin{aligned} \mathcal S(n,k) = -\bigl\{p ∈ ℝ^{n × n}\ \big|\ &a^\mathrm{T}pa \geq 0 \text{ for all } a ∈ ℝ^{n},\\ +\bigl\{p ∈ ℝ^{n×n}\ \big|\ &a^\mathrm{T}pa \geq 0 \text{ for all } a ∈ ℝ^{n},\\ &\operatorname{tr}(p) = \sum_{i=1}^n p_{ii} = 1,\\ -&\text{and } p = qq^{\mathrm{T}} \text{ for } q \in ℝ^{n × k} +&\text{and } p = qq^{\mathrm{T}} \text{ for } q \in ℝ^{n×k} \text{ with } \operatorname{rank}(p) = \operatorname{rank}(q) = k \bigr\}. \end{aligned} ```` -This manifold is working solely on the matrices $q$. Note that this $q$ is not unique, -indeed for any orthogonal matrix $A$ we have $(qA)(qA)^{\mathrm{T}} = qq^{\mathrm{T}} = p$, +This manifold is working solely on the matrices ``q``. Note that this ``q`` is not unique, +indeed for any orthogonal matrix ``A`` we have $(qA)(qA)^{\mathrm{T}} = qq^{\mathrm{T}} = p$, so the manifold implemented here is the quotient manifold. The unit trace translates to -unit frobenius norm of $q$. +unit frobenius norm of ``q``. -The tangent space at $p$, denoted $T_p\mathcal E(n,k)$, is also represented by matrices -$Y\in ℝ^{n × k}$ and reads as +The tangent space at ``p``, denoted $T_p\mathcal E(n,k)$, is also represented by matrices +$Y\in ℝ^{n×k}$ and reads as ````math T_p\mathcal S(n,k) = \bigl\{ -X ∈ ℝ^{n × n}\,|\,X = qY^{\mathrm{T}} + Yq^{\mathrm{T}} +X ∈ ℝ^{n×n}\,|\,X = qY^{\mathrm{T}} + Yq^{\mathrm{T}} \text{ with } \operatorname{tr}(X) = \sum_{i=1}^{n}X_{ii} = 0 \bigr\} ```` -endowed with the [`Euclidean`](@ref) metric from the embedding, i.e. from the $ℝ^{n × k}$ +endowed with the [`Euclidean`](@ref) metric from the embedding, i.e. from the $ℝ^{n×k}$ This manifold was for example @@ -40,7 +40,7 @@ investigated in [JourneeBachAbsilSepulchre:2010](@cite). Spectrahedron(n::Int, k::Int; parameter::Symbol=:type) -generates the manifold $\mathcal S(n,k) \subset ℝ^{n × n}$. +generates the manifold $\mathcal S(n,k) \subset ℝ^{n×n}$. """ struct Spectrahedron{T} <: AbstractDecoratorManifold{ℝ} size::T @@ -58,10 +58,10 @@ active_traits(f, ::Spectrahedron, args...) = merge_traits(IsIsometricEmbeddedMan checks, whether `q` is a valid reprsentation of a point $p=qq^{\mathrm{T}}$ on the [`Spectrahedron`](@ref) `M`, i.e. is a matrix -of size `(N,K)`, such that $p$ is symmetric positive semidefinite and has unit trace, -i.e. $q$ has to have unit frobenius norm. -Since by construction $p$ is symmetric, this is not explicitly checked. -Since $p$ is by construction positive semidefinite, this is not checked. +of size `(N,K)`, such that ``p`` is symmetric positive semidefinite and has unit trace, +i.e. ``q`` has to have unit frobenius norm. +Since by construction ``p`` is symmetric, this is not explicitly checked. +Since ``p`` is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ function check_point(M::Spectrahedron, q; kwargs...) @@ -81,9 +81,9 @@ end Check whether $X = qY^{\mathrm{T}} + Yq^{\mathrm{T}}$ is a tangent vector to $p=qq^{\mathrm{T}}$ on the [`Spectrahedron`](@ref) `M`, i.e. atfer [`check_point`](@ref) of `q`, `Y` has to be of same dimension as `q` -and a $X$ has to be a symmetric matrix with trace. +and a ``X`` has to be a symmetric matrix with trace. The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. -Note that symmetry of $X$ holds by construction and is not explicitly checked. +Note that symmetry of ``X`` holds by construction and is not explicitly checked. """ function check_vector( M::Spectrahedron, @@ -168,7 +168,7 @@ retract_project!(M::Spectrahedron, r, q, Y, t::Number) = project!(M, r, q .+ t . representation_size(M::Spectrahedron) Return the size of an array representing an element on the -[`Spectrahedron`](@ref) manifold `M`, i.e. $n × k$, the size of such factor of $p=qq^{\mathrm{T}}$ +[`Spectrahedron`](@ref) manifold `M`, i.e. $n×k$, the size of such factor of $p=qq^{\mathrm{T}}$ on $\mathcal M = \mathcal S(n,k)$. """ representation_size(M::Spectrahedron) = get_parameter(M.size) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index b39be325f1..8ed626caaf 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -12,31 +12,31 @@ end @doc raw""" Sphere{T,𝔽} <: AbstractSphere{𝔽} -The (unit) sphere manifold $𝕊^{n}$ is the set of all unit norm vectors in $𝔽^{n+1}$. +The (unit) sphere manifold ``𝕊^{n}`` is the set of all unit norm vectors in ``𝔽^{n+1}``. The sphere is represented in the embedding, i.e. ````math 𝕊^{n} := \bigl\{ p \in 𝔽^{n+1}\ \big|\ \lVert p \rVert = 1 \bigr\} ```` -where $𝔽\in\{ℝ,ℂ,ℍ\}$. Note that compared to the [`ArraySphere`](@ref), here the -argument `n` of the manifold is the dimension of the manifold, i.e. $𝕊^{n} ⊂ 𝔽^{n+1}$, $n\in ℕ$. +where ``𝔽\in\{ℝ,ℂ,ℍ\}``. Note that compared to the [`ArraySphere`](@ref), here the +argument `n` of the manifold is the dimension of the manifold, i.e. ``𝕊^{n} ⊂ 𝔽^{n+1}``, ``n\in ℕ``. -The tangent space at point $p$ is given by +The tangent space at point ``p`` is given by ````math T_p𝕊^{n} := \bigl\{ X ∈ 𝔽^{n+1}\ |\ \Re(⟨p,X⟩) = 0 \bigr \}, ```` -where $𝔽\in\{ℝ,ℂ,ℍ\}$ and $⟨\cdot,\cdot⟩$ denotes the inner product in the -embedding $𝔽^{n+1}$. +where ``𝔽\in\{ℝ,ℂ,ℍ\}`` and ``⟨⋅,⋅⟩`` denotes the inner product in the +embedding ``𝔽^{n+1}``. -For $𝔽=ℂ$, the manifold is the complex sphere, written $ℂ𝕊^n$, embedded in $ℂ^{n+1}$. -$ℂ𝕊^n$ is the complexification of the real sphere $𝕊^{2n+1}$. -Likewise, the quaternionic sphere $ℍ𝕊^n$ is the quaternionification of the real sphere -$𝕊^{4n+3}$. -Consequently, $ℂ𝕊^0$ is equivalent to $𝕊^1$ and [`Circle`](@ref), while $ℂ𝕊^1$ and $ℍ𝕊^0$ -are equivalent to $𝕊^3$, though with different default representations. +For ``𝔽=ℂ``, the manifold is the complex sphere, written ``ℂ𝕊^n``, embedded in ``ℂ^{n+1}``. +``ℂ𝕊^n`` is the complexification of the real sphere ``𝕊^{2n+1}``. +Likewise, the quaternionic sphere ``ℍ𝕊^n`` is the quaternionification of the real sphere +``𝕊^{4n+3}``. +Consequently, ``ℂ𝕊^0`` is equivalent to ``𝕊^1`` and [`Circle`](@ref), while ``ℂ𝕊^1`` and ``ℍ𝕊^0`` +are equivalent to ``𝕊^3``, though with different default representations. This manifold is modeled as a special case of the more general case, i.e. as an embedded manifold to the [`Euclidean`](@ref), and several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product @@ -46,7 +46,7 @@ and the [`zero_vector`](@ref zero_vector(::Euclidean, ::Any...)) are inherited f Sphere(n[, field=ℝ]) -Generate the (real-valued) sphere $𝕊^{n} ⊂ ℝ^{n+1}$, where `field` can also be used to +Generate the (real-valued) sphere ``𝕊^{n} ⊂ ℝ^{n+1}``, where `field` can also be used to generate the complex- and quaternionic-valued sphere. """ struct Sphere{T,𝔽} <: AbstractSphere{𝔽} @@ -60,8 +60,8 @@ end @doc raw""" ArraySphere{T<:Tuple,𝔽} <: AbstractSphere{𝔽} -The (unit) sphere manifold $𝕊^{n₁,n₂,...,nᵢ}$ is the set of all unit (Frobenius) norm elements of -$𝔽^{n₁,n₂,...,nᵢ}$, where $𝔽\in\{ℝ,ℂ,ℍ\}. The generalized sphere is +The (unit) sphere manifold ``𝕊^{n₁,n₂,...,nᵢ}`` is the set of all unit (Frobenius) norm elements of +``𝔽^{n₁,n₂,...,nᵢ}``, where ``𝔽\in\{ℝ,ℂ,ℍ\}. The generalized sphere is represented in the embedding, and supports arbitrary sized arrays or in other words arbitrary tensors of unit norm. The set formally reads @@ -69,19 +69,19 @@ tensors of unit norm. The set formally reads 𝕊^{n_1, n_2, …, n_i} := \bigl\{ p \in 𝔽^{n_1, n_2, …, n_i}\ \big|\ \lVert p \rVert = 1 \bigr\} ```` -where $𝔽\in\{ℝ,ℂ,ℍ\}$. Setting $i=1$ and $𝔽=ℝ$ this simplifies to unit vectors in $ℝ^n$, see +where ``𝔽\in\{ℝ,ℂ,ℍ\}``. Setting ``i=1`` and ``𝔽=ℝ`` this simplifies to unit vectors in ``ℝ^n``, see [`Sphere`](@ref) for this special case. Note that compared to this classical case, the argument for the generalized case here is given by the dimension of the embedding. This means that `Sphere(2)` and `ArraySphere(3)` are the same manifold. -The tangent space at point $p$ is given by +The tangent space at point ``p`` is given by ````math T_p 𝕊^{n_1, n_2, …, n_i} := \bigl\{ X ∈ 𝔽^{n_1, n_2, …, n_i}\ |\ \Re(⟨p,X⟩) = 0 \bigr \}, ```` -where $𝔽\in\{ℝ,ℂ,ℍ\}$ and $⟨\cdot,\cdot⟩$ denotes the (Frobenius) inner product in the -embedding $𝔽^{n_1, n_2, …, n_i}$. +where ``𝔽\in\{ℝ,ℂ,ℍ\}`` and ``⟨⋅,⋅⟩`` denotes the (Frobenius) inner product in the +embedding ``𝔽^{n_1, n_2, …, n_i}``. This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product and the @@ -91,7 +91,7 @@ several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product ArraySphere(n₁,n₂,...,nᵢ; field=ℝ, parameter::Symbol=:type) -Generate sphere in $𝔽^{n_1, n_2, …, n_i}$, where $𝔽$ defaults to the real-valued case $ℝ$. +Generate sphere in ``𝔽^{n_1, n_2, …, n_i}``, where ``𝔽`` defaults to the real-valued case ``ℝ``. """ struct ArraySphere{T,𝔽} <: AbstractSphere{𝔽} size::T @@ -187,7 +187,7 @@ Compute the exponential map from `p` in the tangent direction `X` on the [`Abstr ````math \exp_p X = \cos(\lVert X \rVert_p)p + \sin(\lVert X \rVert_p)\frac{X}{\lVert X \rVert_p}, ```` -where $\lVert X \rVert_p$ is the [`norm`](@ref norm(::AbstractSphere,p,X)) on the +where ``\lVert X \rVert_p`` is the [`norm`](@ref norm(::AbstractSphere,p,X)) on the tangent space at `p` of the [`AbstractSphere`](@ref) `M`. """ exp(::AbstractSphere, ::Any...) @@ -225,10 +225,10 @@ end Represent the tangent vector `X` at point `p` from the [`AbstractSphere`](@ref) `M` in an orthonormal basis by rotating the hyperplane containing `X` to a hyperplane whose -normal is the $x$-axis. +normal is the ``x``-axis. -Given $q = p λ + x$, where $λ = \operatorname{sgn}(⟨x, p⟩)$, and $⟨⋅, ⋅⟩_{\mathrm{F}}$ -denotes the Frobenius inner product, the formula for $Y$ is +Given ``q = p λ + x``, where ``λ = \operatorname{sgn}(⟨x, p⟩)``, and ``⟨⋅, ⋅⟩_{\mathrm{F}}`` +denotes the Frobenius inner product, the formula for ``Y`` is ````math \begin{pmatrix}0 \\ Y\end{pmatrix} = X - q\frac{2 ⟨q, X⟩_{\mathrm{F}}}{⟨q, q⟩_{\mathrm{F}}}. ```` @@ -258,11 +258,11 @@ end Convert a one-dimensional vector of coefficients `X` in the basis `B` of the tangent space at `p` on the [`AbstractSphere`](@ref) `M` to a tangent vector `Y` at `p` by rotating the -hyperplane containing `X`, whose normal is the $x$-axis, to the hyperplane whose normal is +hyperplane containing `X`, whose normal is the ``x``-axis, to the hyperplane whose normal is `p`. -Given $q = p λ + x$, where $λ = \operatorname{sgn}(⟨x, p⟩)$, and $⟨⋅, ⋅⟩_{\mathrm{F}}$ -denotes the Frobenius inner product, the formula for $Y$ is +Given ``q = p λ + x``, where ``λ = \operatorname{sgn}(⟨x, p⟩)``, and ``⟨⋅, ⋅⟩_{\mathrm{F}}`` +denotes the Frobenius inner product, the formula for ``Y`` is ````math Y = X - q\frac{2 \left\langle q, \begin{pmatrix}0 \\ X\end{pmatrix}\right\rangle_{\mathrm{F}}}{⟨q, q⟩_{\mathrm{F}}}. ```` @@ -285,12 +285,12 @@ end @doc raw""" injectivity_radius(M::AbstractSphere[, p]) -Return the injectivity radius for the [`AbstractSphere`](@ref) `M`, which is globally $π$. +Return the injectivity radius for the [`AbstractSphere`](@ref) `M`, which is globally ``π``. injectivity_radius(M::Sphere, x, ::ProjectionRetraction) Return the injectivity radius for the [`ProjectionRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.ProjectionRetraction) on the -[`AbstractSphere`](@ref), which is globally $\frac{π}{2}$. +[`AbstractSphere`](@ref), which is globally ``\frac{π}{2}``. """ injectivity_radius(::AbstractSphere) = π injectivity_radius(::AbstractSphere, p) = π @@ -308,8 +308,8 @@ _injectivity_radius(::AbstractSphere, ::ProjectionRetraction) = π / 2 inverse_retract(M::AbstractSphere, p, q, ::ProjectionInverseRetraction) Compute the inverse of the projection based retraction on the [`AbstractSphere`](@ref) `M`, -i.e. rearranging $p+X = q\lVert p+X\rVert_2$ yields -since $\Re(⟨p,X⟩) = 0$ and when $d_{𝕊^2}(p,q) ≤ \frac{π}{2}$ that +i.e. rearranging ``p+X = q\lVert p+X\rVert_2`` yields +since ``\Re(⟨p,X⟩) = 0`` and when ``d_{𝕊^2}(p,q) ≤ \frac{π}{2}`` that ````math \operatorname{retr}_p^{-1}(q) = \frac{q}{\Re(⟨p, q⟩)} - p. @@ -352,13 +352,13 @@ end Compute the logarithmic map on the [`AbstractSphere`](@ref) `M`, i.e. the tangent vector, whose geodesic starting from `p` reaches `q` after time 1. -The formula reads for $x ≠ -y$ +The formula reads for ``x ≠ -y`` ````math \log_p q = d_{𝕊}(p,q) \frac{q-\Re(⟨p,q⟩) p}{\lVert q-\Re(⟨p,q⟩) p \rVert_2}, ```` -and a deterministic choice from the set of tangent vectors is returned if $x=-y$, i.e. for +and a deterministic choice from the set of tangent vectors is returned if ``x=-y``, i.e. for opposite points. """ log(::AbstractSphere, ::Any...) @@ -437,8 +437,8 @@ Project the point `p` from the embedding onto the [`Sphere`](@ref) `M`. ````math \operatorname{proj}(p) = \frac{p}{\lVert p \rVert}, ```` -where $\lVert\cdot\rVert$ denotes the usual 2-norm for vectors if $m=1$ and the Frobenius -norm for the case $m>1$. +where ``\lVert⋅\rVert`` denotes the usual 2-norm for vectors if ``m=1`` and the Frobenius +norm for the case ``m>1``. """ project(::AbstractSphere, ::Any) diff --git a/src/manifolds/SphereSymmetricMatrices.jl b/src/manifolds/SphereSymmetricMatrices.jl index 1c9eecf0f0..d023e881c7 100644 --- a/src/manifolds/SphereSymmetricMatrices.jl +++ b/src/manifolds/SphereSymmetricMatrices.jl @@ -1,13 +1,13 @@ @doc raw""" SphereSymmetricMatrices{T,𝔽} <: AbstractEmbeddedManifold{ℝ,TransparentIsometricEmbedding} -The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) consisting of the $n × n$ symmetric matrices +The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) consisting of the ``n×n`` symmetric matrices of unit Frobenius norm, i.e. ````math -\mathcal{S}_{\text{sym}} :=\bigl\{p ∈ 𝔽^{n × n}\ \big|\ p^{\mathrm{H}} = p, \lVert p \rVert = 1 \bigr\}, +\mathcal{S}_{\text{sym}} :=\bigl\{p ∈ 𝔽^{n×n}\ \big|\ p^{\mathrm{H}} = p, \lVert p \rVert = 1 \bigr\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transpose, -and the field $𝔽 ∈ \{ ℝ, ℂ\}$. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transpose, +and the field ``𝔽 ∈ \{ ℝ, ℂ\}``. # Constructor SphereSymmetricMatrices(n[, field=ℝ]) @@ -119,7 +119,7 @@ Projects `p` from the embedding onto the [`SphereSymmetricMatrices`](@ref) `M`, ````math \operatorname{proj}_{\mathcal{S}_{\text{sym}}}(p) = \frac{1}{2} \bigl( p + p^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SphereSymmetricMatrices, ::Any) @@ -135,7 +135,7 @@ Project the matrix `X` onto the tangent space at `p` on the [`SphereSymmetricMat ````math \operatorname{proj}_p(X) = \frac{X + X^{\mathrm{H}}}{2} - ⟨p, \frac{X + X^{\mathrm{H}}}{2}⟩p, ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SphereSymmetricMatrices, ::Any, ::Any) diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index e17da0546a..f4ca7f7bf0 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,23 +1,23 @@ @doc raw""" Stiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The Stiefel manifold consists of all $n × k$, $n ≥ k$ unitary matrices, i.e. +The Stiefel manifold consists of all ``n×k``, ``n ≥ k`` unitary matrices, i.e. ````math -\operatorname{St}(n,k) = \bigl\{ p ∈ 𝔽^{n × k}\ \big|\ p^{\mathrm{H}}p = I_k \bigr\}, +\operatorname{St}(n,k) = \bigl\{ p ∈ 𝔽^{n×k}\ \big|\ p^{\mathrm{H}}p = I_k \bigr\}, ```` -where $𝔽 ∈ \{ℝ, ℂ\}$, -$\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and -$I_k ∈ ℝ^{k × k}$ denotes the $k × k$ identity matrix. +where ``𝔽 ∈ \{ℝ, ℂ\}``, +``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian, and +``I_k ∈ ℝ^{k×k}`` denotes the ``k×k`` identity matrix. -The tangent space at a point $p ∈ \mathcal M$ is given by +The tangent space at a point ``p ∈ \mathcal M`` is given by ````math -T_p \mathcal M = \{ X ∈ 𝔽^{n × k} : p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0_k\}, +T_p \mathcal M = \{ X ∈ 𝔽^{n×k} : p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0_k\}, ```` -where $0_k$ is the $k × k$ zero matrix and $\overline{\cdot}$ the (elementwise) complex conjugate. +where ``0_k`` is the ``k×k`` zero matrix and ``\overline{⋅}`` the (elementwise) complex conjugate. This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product and the @@ -29,7 +29,7 @@ The manifold is named after # Constructor Stiefel(n, k, field=ℝ; parameter::Symbol=:type) -Generate the (real-valued) Stiefel manifold of $n × k$ dimensional orthonormal matrices. +Generate the (real-valued) Stiefel manifold of ``n×k`` dimensional orthonormal matrices. """ struct Stiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T @@ -77,8 +77,8 @@ end @doc raw""" check_point(M::Stiefel, p; kwargs...) -Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. that it has the right -[`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and $p^{\mathrm{H}}p$ is (approximately) the identity, where $\cdot^{\mathrm{H}}$ is the +Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=``\operatorname{St}(n,k)``, i.e. that it has the right +[`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and ``p^{\mathrm{H}}p`` is (approximately) the identity, where ``⋅^{\mathrm{H}}`` is the complex conjugate transpose. The settings for approximately can be set with `kwargs...`. """ function check_point(M::Stiefel, p; kwargs...) @@ -98,9 +98,9 @@ end check_vector(M::Stiefel, p, X; kwargs...) Checks whether `X` is a valid tangent vector at `p` on the [`Stiefel`](@ref) -`M`=$\operatorname{St}(n,k)$, i.e. the [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) fits and -it (approximately) holds that $p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0$, -where $\cdot^{\mathrm{H}}$ denotes the Hermitian and $\overline{\cdot}$ the (elementwise) complex conjugate. +`M`=``\operatorname{St}(n,k)``, i.e. the [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) fits and +it (approximately) holds that ``p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0``, +where ``⋅^{\mathrm{H}}`` denotes the Hermitian and ``\overline{⋅}`` the (elementwise) complex conjugate. The settings for approximately can be set with `kwargs...`. """ function check_vector(M::Stiefel, p, X; kwargs...) @@ -163,8 +163,8 @@ This follows the folloing approach: From the Polar retraction we know that \operatorname{retr}_p^{-1}q = qs - t ```` -if such a symmetric positive definite $k × k$ matrix exists. Since $qs - t$ -is also a tangent vector at $p$ we obtain +if such a symmetric positive definite ``k×k`` matrix exists. Since ``qs - t`` +is also a tangent vector at ``p`` we obtain ````math p^{\mathrm{H}}qs + s(p^{\mathrm{H}}q)^{\mathrm{H}} + 2I_k = 0, @@ -318,7 +318,7 @@ is_flat(M::Stiefel) = manifold_dimension(M) == 1 @doc raw""" manifold_dimension(M::Stiefel) -Return the dimension of the [`Stiefel`](@ref) manifold `M`=$\operatorname{St}(n,k,𝔽)$. +Return the dimension of the [`Stiefel`](@ref) manifold `M`=``\operatorname{St}(n,k,𝔽)``. The dimension is given by ````math @@ -390,15 +390,15 @@ the formula reads \operatorname{retr}_pX = \Bigl(I - \frac{1}{2}W_{p,X}\Bigr)^{-1}\Bigl(I + \frac{1}{2}W_{p,X}\Bigr)p. ```` -It is implemented as the case $m=1$ of the `PadeRetraction`. +It is implemented as the case ``m=1`` of the `PadeRetraction`. """ retract(::Stiefel, ::Any, ::Any, ::CayleyRetraction) @doc raw""" retract(M::Stiefel, p, X, ::PadeRetraction{m}) -Compute the retraction on the [`Stiefel`](@ref) manifold `M` based on the Padé approximation of order $m$ [ZhuDuan:2018](@cite). -Let $p_m$ and $q_m$ be defined for any matrix $A ∈ ℝ^{n×x}$ as +Compute the retraction on the [`Stiefel`](@ref) manifold `M` based on the Padé approximation of order ``m`` [ZhuDuan:2018](@cite). +Let ``p_m`` and ``q_m`` be defined for any matrix ``A ∈ ℝ^{n×x}`` as ````math p_m(A) = \sum_{k=0}^m \frac{(2m-k)!m!}{(2m)!(m-k)!}\frac{A^k}{k!} @@ -410,7 +410,7 @@ and q_m(A) = \sum_{k=0}^m \frac{(2m-k)!m!}{(2m)!(m-k)!}\frac{(-A)^k}{k!} ```` -respectively. Then the Padé approximation (of the matrix exponential $\exp(A)$) reads +respectively. Then the Padé approximation (of the matrix exponential ``\exp(A)``) reads ````math r_m(A) = q_m(A)^{-1}p_m(A) @@ -436,7 +436,7 @@ retract(::Stiefel, ::Any, ::Any, ::PadeRetraction) retract(M::Stiefel, p, X, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction) on the -[`Stiefel`](@ref) manifold `M`. With $USV = p + X$ the retraction reads +[`Stiefel`](@ref) manifold `M`. With ``USV = p + X`` the retraction reads ````math \operatorname{retr}_p X = U\bar{V}^\mathrm{H}. @@ -448,23 +448,23 @@ retract(::Stiefel, ::Any, ::Any, ::PolarRetraction) retract(M::Stiefel, p, X, ::QRRetraction) Compute the QR-based retraction [`QRRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRRetraction) on the -[`Stiefel`](@ref) manifold `M`. With $QR = p + X$ the retraction reads +[`Stiefel`](@ref) manifold `M`. With ``QR = p + X`` the retraction reads ````math \operatorname{retr}_p X = QD, ```` -where $D$ is a $n × k$ matrix with +where ``D`` is a ``n×k`` matrix with ````math D = \operatorname{diag}\bigl(\operatorname{sgn}(R_{ii}+0,5)_{i=1}^k \bigr), ```` -where $\operatorname{sgn}(p) = \begin{cases} +where ``\operatorname{sgn}(p) = \begin{cases} 1 & \text{ for } p > 0,\\ 0 & \text{ for } p = 0,\\ -1& \text{ for } p < 0. -\end{cases}$ +\end{cases}`` """ retract(::Stiefel, ::Any, ::Any, ::QRRetraction) @@ -509,7 +509,7 @@ end @doc raw""" representation_size(M::Stiefel) -Returns the representation size of the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, +Returns the representation size of the [`Stiefel`](@ref) `M`=``\operatorname{St}(n,k)``, i.e. `(n,k)`, which is the matrix dimensions. """ representation_size(M::Stiefel) = get_parameter(M.size) @@ -561,7 +561,7 @@ with ```` Since this is the differentiated retraction as a vector transport, the result will be in the -tangent space at $q=\operatorname{retr}_p(d)$ using the [`CayleyRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.CayleyRetraction). +tangent space at ``q=\operatorname{retr}_p(d)`` using the [`CayleyRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.CayleyRetraction). """ vector_transport_direction( M::Stiefel, @@ -581,7 +581,7 @@ Compute the vector transport by computing the push forward of T_{p,d}^{\text{Pol}}(X) = q*Λ + (I-qq^{\mathrm{T}})X(1+d^\mathrm{T}d)^{-\frac{1}{2}}, ``` -where $q = \operatorname{retr}^{\mathrm{Pol}}_p(d)$, and $Λ$ is the unique solution of the Sylvester equation +where ``q = \operatorname{retr}^{\mathrm{Pol}}_p(d)``, and ``Λ`` is the unique solution of the Sylvester equation ```math Λ(I+d^\mathrm{T}d)^{\frac{1}{2}} + (I + d^\mathrm{T}d)^{\frac{1}{2}} = q^\mathrm{T}X - X^\mathrm{T}q @@ -604,8 +604,8 @@ See [AbsilMahonySepulchre:2008](@cite), p. 173, or Section 3.5 of [Zhu:2016](@c ```math T_{p,d}^{\text{QR}}(X) = q*\rho_{\mathrm{s}}(q^\mathrm{T}XR^{-1}) + (I-qq^{\mathrm{T}})XR^{-1}, ``` -where $q = \operatorname{retr}^{\mathrm{QR}}_p(d)$, $R$ is the $R$ factor of the QR -decomposition of $p + d$, and +where ``q = \operatorname{retr}^{\mathrm{QR}}_p(d)``, ``R`` is the ``R`` factor of the QR +decomposition of ``p + d``, and ```math \bigl( \rho_{\mathrm{s}}(A) \bigr)_{ij} = \begin{cases} @@ -665,8 +665,8 @@ Section 4 of [HuangGallivanAbsil:2015](@cite) or Section 3.5 of [Zhu:2016](@cit T_{q\gets p}^{\text{Pol}}(X) = q*Λ + (I-qq^{\mathrm{T}})X(1+d^\mathrm{T}d)^{-\frac{1}{2}}, ``` -where $d = \bigl( \operatorname{retr}^{\mathrm{Pol}}_p\bigr)^{-1}(q)$, -and $Λ$ is the unique solution of the Sylvester equation +where ``d = \bigl( \operatorname{retr}^{\mathrm{Pol}}_p\bigr)^{-1}(q)``, +and ``Λ`` is the unique solution of the Sylvester equation ```math Λ(I+d^\mathrm{T}d)^{\frac{1}{2}} + (I + d^\mathrm{T}d)^{\frac{1}{2}} = q^\mathrm{T}X - X^\mathrm{T}q @@ -690,8 +690,8 @@ see [AbsilMahonySepulchre:2008](@cite), p. 173, or Section 3.5 of [Zhu:2016](@c ```math T_{q \gets p}^{\text{QR}}(X) = q*\rho_{\mathrm{s}}(q^\mathrm{T}XR^{-1}) + (I-qq^{\mathrm{T}})XR^{-1}, ``` -where $d = \bigl(\operatorname{retr}^{\mathrm{QR}}\bigr)^{-1}_p(q)$, $R$ is the $R$ factor of the QR -decomposition of $p+X$, and +where ``d = \bigl(\operatorname{retr}^{\mathrm{QR}}\bigr)^{-1}_p(q)``, ``R`` is the ``R`` factor of the QR +decomposition of ``p+X``, and ```math \bigl( \rho_{\mathrm{s}}(A) \bigr)_{ij} = \begin{cases} diff --git a/src/manifolds/StiefelCanonicalMetric.jl b/src/manifolds/StiefelCanonicalMetric.jl index 7e750c3f9d..9306988fd8 100644 --- a/src/manifolds/StiefelCanonicalMetric.jl +++ b/src/manifolds/StiefelCanonicalMetric.jl @@ -40,7 +40,7 @@ respect to ``p``, i.e. ```math X = pp^{\mathrm{T}}X + (I_n-pp^{\mathrm{T}})X, ``` -where ``I_n`` is the ``n\times n`` identity matrix. +where ``I_n`` is the ``n×n`` identity matrix. We introduce ``A=p^{\mathrm{T}}X`` and ``QR = (I_n-pp^{\mathrm{T}})X`` the `qr` decomposition of the vertical component. Then using the matrix exponential ``\operatorname{Exp}`` we introduce ``B`` and ``C`` as diff --git a/src/manifolds/StiefelEuclideanMetric.jl b/src/manifolds/StiefelEuclideanMetric.jl index fa2e64518c..9edc20aba3 100644 --- a/src/manifolds/StiefelEuclideanMetric.jl +++ b/src/manifolds/StiefelEuclideanMetric.jl @@ -17,9 +17,9 @@ emanating from `p` in tangent direction `X`. \begin{pmatrix} \exp( -p^{\mathrm{H}}X) \\ 0_n\end{pmatrix}, ```` -where $\operatorname{Exp}$ denotes matrix exponential, -$\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_k$ and -$0_k$ are the identity matrix and the zero matrix of dimension $k × k$, respectively. +where ``\operatorname{Exp}`` denotes matrix exponential, +``⋅^{\mathrm{H}}`` denotes the complex conjugate transpose or Hermitian, and ``I_k`` and +``0_k`` are the identity matrix and the zero matrix of dimension ``k×k``, respectively. """ exp(::Stiefel, ::Any...) @@ -38,25 +38,25 @@ end @doc raw""" get_basis(M::Stiefel{<:Any,ℝ}, p, B::DefaultOrthonormalBasis) -Create the default basis using the parametrization for any $X ∈ T_p\mathcal M$. -Set $p_\bot \in ℝ^{n\times(n-k)}$ the matrix such that the $n\times n$ matrix of the common -columns $[p\ p_\bot]$ is an ONB. -For any skew symmetric matrix $a ∈ ℝ^{k\times k}$ and any $b ∈ ℝ^{(n-k)\times k}$ the matrix +Create the default basis using the parametrization for any ``X ∈ T_p\mathcal M``. +Set ``p_\bot \in ℝ^{n×(n-k)}`` the matrix such that the ``n×n`` matrix of the common +columns ``[p\ p_\bot]`` is an ONB. +For any skew symmetric matrix ``a ∈ ℝ^{k×k}`` and any ``b ∈ ℝ^{(n-k)×k}`` the matrix ````math X = pa + p_\bot b ∈ T_p\mathcal M ```` -and we can use the $\frac{1}{2}k(k-1) + (n-k)k = nk-\frac{1}{2}k(k+1)$ entries -of $a$ and $b$ to specify a basis for the tangent space. +and we can use the ``\frac{1}{2}k(k-1) + (n-k)k = nk-\frac{1}{2}k(k+1)`` entries +of ``a`` and ``b`` to specify a basis for the tangent space. using unit vectors for constructing both -the upper matrix of $a$ to build a skew symmetric matrix and the matrix b, the default +the upper matrix of ``a`` to build a skew symmetric matrix and the matrix b, the default basis is constructed. -Since $[p\ p_\bot]$ is an automorphism on $ℝ^{n\times p}$ the elements of $a$ and $b$ are +Since ``[p\ p_⊥]`` is an automorphism on ``ℝ^{n×p}`` the elements of ``a`` and ``b`` are orthonormal coordinates for the tangent space. To be precise exactly one element in the upper -trangular entries of $a$ is set to $1$ its symmetric entry to $-1$ and we normalize with -the factor $\frac{1}{\sqrt{2}}$ and for $b$ one can just use unit vectors reshaped to a matrix +trangular entries of ``a`` is set to ``1`` its symmetric entry to ``-1`` and we normalize with +the factor ``\frac{1}{\sqrt{2}}`` and for ``b`` one can just use unit vectors reshaped to a matrix to obtain orthonormal set of parameters. """ get_basis(M::Stiefel{<:Any,ℝ}, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) @@ -128,8 +128,8 @@ end project(M::Stiefel,p) Projects `p` from the embedding onto the [`Stiefel`](@ref) `M`, i.e. compute `q` -as the polar decomposition of $p$ such that ``q^{\mathrm{H}}q`` is the identity, -where ``\cdot^{\mathrm{H}}`` denotes the hermitian, i.e. complex conjugate transposed. +as the polar decomposition of ``p`` such that ``q^{\mathrm{H}}q`` is the identity, +where ``⋅^{\mathrm{H}}`` denotes the hermitian, i.e. complex conjugate transposed. """ project(::Stiefel, ::Any, ::Any) @@ -149,8 +149,8 @@ The formula reads \operatorname{proj}_{T_p\mathcal M}(X) = X - p \operatorname{Sym}(p^{\mathrm{H}}X), ```` -where $\operatorname{Sym}(q)$ is the symmetrization of $q$, e.g. by -$\operatorname{Sym}(q) = \frac{q^{\mathrm{H}}+q}{2}$. +where ``\operatorname{Sym}(q)`` is the symmetrization of ``q``, e.g. by +``\operatorname{Sym}(q) = \frac{q^{\mathrm{H}}+q}{2}``. """ project(::Stiefel, ::Any...) diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index 601db4d684..52d2b7d746 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -292,15 +292,15 @@ end @doc raw""" StiefelFactorization{UT,XT} <: AbstractManifoldPoint -Represent points (and vectors) on `Stiefel(n, k)` with ``2k × k`` factors [ZimmermannHueper:2022](@cite). +Represent points (and vectors) on `Stiefel(n, k)` with ``2k×k`` factors [ZimmermannHueper:2022](@cite). -Given a point ``p ∈ \mathrm{St}(n, k)`` and another matrix ``B ∈ ℝ^{n × k}`` for +Given a point ``p ∈ \mathrm{St}(n, k)`` and another matrix ``B ∈ ℝ^{n×k}`` for ``k ≤ \lfloor\frac{n}{2}\rfloor`` the factorization is ````math \begin{aligned} B &= UZ\\ U &= \begin{bmatrix}p & Q\end{bmatrix} ∈ \mathrm{St}(n, 2k)\\ -Z &= \begin{bmatrix}Z_1 \\ Z_2\end{bmatrix}, \quad Z_1,Z_2 ∈ ℝ^{k × k}. +Z &= \begin{bmatrix}Z_1 \\ Z_2\end{bmatrix}, \quad Z_1,Z_2 ∈ ℝ^{k×k}. \end{aligned} ```` If ``B ∈ \mathrm{St}(n, k)``, then ``Z ∈ \mathrm{St}(2k, k)``. @@ -310,7 +310,7 @@ For a fixed ``U``, if ``r ∈ \mathrm{St}(n, k)`` has the factor ``Z_r ∈ \math then ``X_r ∈ T_r \mathrm{St}(n, k)`` has the factor ``Z_{X_r} ∈ T_{Z_r} \mathrm{St}(2k, k)``. -``Q`` is determined by choice of a second matrix ``A ∈ ℝ^{n × k}`` with the decomposition +``Q`` is determined by choice of a second matrix ``A ∈ ℝ^{n×k}`` with the decomposition ````math \begin{aligned} A &= UZ\\ @@ -325,7 +325,7 @@ This factorization is useful because it is closed under addition, subtraction, s projection, and the Riemannian exponential and logarithm under the [`StiefelSubmersionMetric`](@ref). That is, if all matrices involved are factorized to have the same ``U``, then all of these operations and any algorithm that depends only on them can -be performed in terms of the ``2k × k`` matrices ``Z``. For ``n ≫ k``, this can be much more +be performed in terms of the ``2k×k`` matrices ``Z``. For ``n ≫ k``, this can be much more efficient than working with the full matrices. !!! warning diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 989d3dba29..5f0a982e24 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -1,16 +1,16 @@ @doc raw""" SymmetricMatrices{n,𝔽} <: AbstractDecoratorManifold{𝔽} -The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $ \operatorname{Sym}(n)$ consisting of the real- or complex-valued -symmetric matrices of size $n × n$, i.e. the set +The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) ``\operatorname{Sym}(n)`` consisting of the real- or complex-valued +symmetric matrices of size ``n×n``, i.e. the set ````math -\operatorname{Sym}(n) = \bigl\{p ∈ 𝔽^{n × n}\ \big|\ p^{\mathrm{H}} = p \bigr\}, +\operatorname{Sym}(n) = \bigl\{p ∈ 𝔽^{n×n}\ \big|\ p^{\mathrm{H}} = p \bigr\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transpose, -and the field $𝔽 ∈ \{ ℝ, ℂ\}$. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transpose, +and the field ``𝔽 ∈ \{ ℝ, ℂ\}``. -Though it is slightly redundant, usually the matrices are stored as $n × n$ arrays. +Though it is slightly redundant, usually the matrices are stored as ``n×n`` arrays. Note that in this representation, the complex valued case has to have a real-valued diagonal, which is also reflected in the [`manifold_dimension`](@ref manifold_dimension(::SymmetricMatrices)). @@ -19,7 +19,7 @@ which is also reflected in the [`manifold_dimension`](@ref manifold_dimension(:: SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ) -Generate the manifold of $n × n$ symmetric matrices. +Generate the manifold of ``n×n`` symmetric matrices. """ struct SymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T @@ -186,7 +186,7 @@ Return the dimension of the [`SymmetricMatrices`](@ref) matrix `M` over the numb \end{aligned} ```` -where the last $-n$ is due to the zero imaginary part for Hermitian matrices +where the last ``-n`` is due to the zero imaginary part for Hermitian matrices """ function manifold_dimension(M::SymmetricMatrices{<:Any,𝔽}) where {𝔽} N = get_parameter(M.size)[1] @@ -202,7 +202,7 @@ Projects `p` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. \operatorname{proj}_{\operatorname{Sym}(n)}(p) = \frac{1}{2} \bigl( p + p^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SymmetricMatrices, ::Any) @@ -220,7 +220,7 @@ Project the matrix `X` onto the tangent space at `p` on the [`SymmetricMatrices` \operatorname{proj}_p(X) = \frac{1}{2} \bigl( X + X^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transposed. +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transposed. """ project(::SymmetricMatrices, ::Any, ::Any) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 13b58b171b..95d051b9ec 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -6,7 +6,7 @@ The manifold of symmetric positive definite matrices, i.e. ````math \mathcal P(n) = \bigl\{ -p ∈ ℝ^{n × n}\ \big|\ a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} +p ∈ ℝ^{n×n}\ \big|\ a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \bigr\} ```` @@ -24,7 +24,7 @@ i.e. the set of symmetric matrices, SymmetricPositiveDefinite(n; parameter::Symbol=:type) -generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$ +generates the manifold ``\mathcal P(n) \subset ℝ^{n×n}`` """ struct SymmetricPositiveDefinite{T} <: AbstractDecoratorManifold{ℝ} size::T @@ -243,7 +243,7 @@ end Return the injectivity radius of the [`SymmetricPositiveDefinite`](@ref). Since `M` is a Hadamard manifold with respect to the [`AffineInvariantMetric`](@ref) and the -[`LogCholeskyMetric`](@ref), the injectivity radius is globally $∞$. +[`LogCholeskyMetric`](@ref), the injectivity radius is globally ``∞``. """ injectivity_radius(::SymmetricPositiveDefinite) = Inf injectivity_radius(::SymmetricPositiveDefinite, p) = Inf @@ -265,7 +265,7 @@ is_flat(M::SymmetricPositiveDefinite) = false manifold_dimension(M::SymmetricPositiveDefinite) returns the dimension of -[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n ∈ ℕ$, i.e. +[`SymmetricPositiveDefinite`](@ref) `M` ``=\mathcal P(n), n ∈ ℕ``, i.e. ````math \dim \mathcal P(n) = \frac{n(n+1)}{2}. ```` @@ -466,8 +466,8 @@ end representation_size(M::SymmetricPositiveDefinite) Return the size of an array representing an element on the -[`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. $n × n$, the size of such a -symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. +[`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. ``n×n``, the size of such a +symmetric positive definite matrix on ``\mathcal M = \mathcal P(n)``. """ function representation_size(M::SymmetricPositiveDefinite) N = get_parameter(M.size)[1] diff --git a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl index 126b40c1e2..54ffe9169c 100644 --- a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl +++ b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl @@ -37,7 +37,7 @@ Given a tangent vector ``X ∈ T_p\mathcal P(n)`` with respect to the [`Euclidea `g_E`, this function changes into the [`AffineInvariantMetric`](@ref) (default) metric on the [`SymmetricPositiveDefinite`](@ref) `M`. -To be precise we are looking for ``c\colon T_p\mathcal P(n) \to T_p\mathcal P(n) `` +To be precise we are looking for ``c\colon T_p\mathcal P(n) → T_p\mathcal P(n) `` such that for all ``Y,Z ∈ T_p\mathcal P(n)``` it holds ```math @@ -65,7 +65,7 @@ d_{\mathcal P(n)}(p,q) = \lVert \operatorname{Log}(p^{-\frac{1}{2}}qp^{-\frac{1}{2}})\rVert_{\mathrm{F}}., ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert⋅\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance(::SymmetricPositiveDefinite, p, q) # avoid numerical instabilities in cholesky @@ -234,7 +234,7 @@ the coordinates with respect to this ONB can be simplified to ```math c_k = \mathrm{tr}(p^{-\frac{1}{2}}\Delta_{i,j} X) ``` -where $k$ is trhe linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. +where ``k`` is trhe linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. """ get_coordinates(::SymmetricPositiveDefinite, c, p, X, ::DefaultOrthonormalBasis) @@ -272,7 +272,7 @@ the vector reconstruction with respect to this ONB can be simplified to ```math X = p^{\frac{1}{2}} \Biggl( \sum_{i=1,j=i}^n c_k \Delta_{i,j} \Biggr) p^{\frac{1}{2}} ``` -where $k$ is the linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. +where ``k`` is the linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. """ get_vector(::SymmetricPositiveDefinite, X, p, c, ::DefaultOrthonormalBasis) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 47c43ea723..b137bc495c 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -31,9 +31,9 @@ d_{\mathcal P(n)}(p,q) = \sqrt{ + \lVert \log(\operatorname{diag}(x)) - \log(\operatorname{diag}(y))\rVert_{\mathrm{F}}^2 }\ \ , ```` -where $x$ and $y$ are the cholesky factors of $p$ and $q$, respectively, -$⌊\cdot⌋$ denbotes the strictly lower triangular matrix of its argument, -and $\lVert\cdot\rVert_{\mathrm{F}}$ the Frobenius norm. +where ``x`` and ``y`` are the cholesky factors of ``p`` and ``q``, respectively, +$⌊⋅⌋$ denbotes the strictly lower triangular matrix of its argument, +and $\lVert⋅\rVert_{\mathrm{F}}$ the Frobenius norm. """ function distance(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) N = get_parameter(M.manifold.size)[1] @@ -50,9 +50,9 @@ Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with \exp_p X = (\exp_y W)(\exp_y W)^\mathrm{T} ```` -where $\exp_xW$ is the exponential map on [`CholeskySpace`](@ref), $y$ is the cholesky -decomposition of $p$, $W = y(y^{-1}Xy^{-\mathrm{T}})_\frac{1}{2}$, -and $(\cdot)_\frac{1}{2}$ +where $\exp_xW$ is the exponential map on [`CholeskySpace`](@ref), ``y`` is the cholesky +decomposition of ``p``, $W = y(y^{-1}Xy^{-\mathrm{T}})_\frac{1}{2}$, +and $(⋅)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$. """ exp(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) @@ -84,9 +84,9 @@ a [`MetricManifold`](@ref) with [`LogCholeskyMetric`](@ref). The formula reads g_p(X,Y) = ⟨a_z(X),a_z(Y)⟩_z, ```` -where $⟨\cdot,\cdot⟩_x$ denotes inner product on the [`CholeskySpace`](@ref), -$z$ is the cholesky factor of $p$, -$a_z(W) = z (z^{-1}Wz^{-\mathrm{T}})_{\frac{1}{2}}$, and $(\cdot)_\frac{1}{2}$ +where $⟨⋅,⋅⟩_x$ denotes inner product on the [`CholeskySpace`](@ref), +``z`` is the cholesky factor of ``p``, +$a_z(W) = z (z^{-1}Wz^{-\mathrm{T}})_{\frac{1}{2}}$, and $(⋅)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$ """ function inner(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, Y) @@ -113,8 +113,8 @@ The formula can be adapted from the [`CholeskySpace`](@ref) as ````math \log_p q = xW^{\mathrm{T}} + Wx^{\mathrm{T}}, ```` -where $x$ is the cholesky factor of $p$ and $W=\log_x y$ for $y$ the cholesky factor -of $q$ and the just mentioned logarithmic map is the one on [`CholeskySpace`](@ref). +where ``x`` is the cholesky factor of ``p`` and $W=\log_x y$ for ``y`` the cholesky factor +of ``q`` and the just mentioned logarithmic map is the one on [`CholeskySpace`](@ref). """ log(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) @@ -138,10 +138,10 @@ end Parallel transport the tangent vector `X` at `p` along the geodesic to `q` with respect to the [`SymmetricPositiveDefinite`](@ref) manifold `M` and [`LogCholeskyMetric`](@ref). The parallel transport is based on the parallel transport on [`CholeskySpace`](@ref): -Let $x$ and $y$ denote the cholesky factors of `p` and `q`, respectively and -$W = x(x^{-1}Xx^{-\mathrm{T}})_\frac{1}{2}$, where $(\cdot)_\frac{1}{2}$ denotes the lower -triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With $V$ the parallel -transport on [`CholeskySpace`](@ref) from $x$ to $y$. The formula hear reads +Let ``x`` and ``y`` denote the cholesky factors of `p` and `q`, respectively and +$W = x(x^{-1}Xx^{-\mathrm{T}})_\frac{1}{2}$, where $(⋅)_\frac{1}{2}$ denotes the lower +triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With ``V`` the parallel +transport on [`CholeskySpace`](@ref) from ``x`` to ``y``. The formula hear reads ````math \mathcal P_{q←p}X = yV^{\mathrm{T}} + Vy^{\mathrm{T}}. diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index ad579dffc1..4bd85955f1 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -18,7 +18,7 @@ The formula reads ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert⋅\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance(::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogEuclideanMetric}, p, q) return norm(log(Symmetric(p)) - log(Symmetric(q))) diff --git a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl index 65c774f07c..46d3d867d7 100644 --- a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl +++ b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl @@ -1,31 +1,31 @@ @doc raw""" SymmetricPositiveSemidefiniteFixedRank{T,𝔽} <: AbstractDecoratorManifold{𝔽} -The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $ \operatorname{SPS}_k(n)$ consisting of the real- or complex-valued -symmetric positive semidefinite matrices of size $n × n$ and rank $k$, i.e. the set +The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `` \operatorname{SPS}_k(n)`` consisting of the real- or complex-valued +symmetric positive semidefinite matrices of size ``n×n`` and rank ``k``, i.e. the set ````math \operatorname{SPS}_k(n) = \bigl\{ -p ∈ 𝔽^{n × n}\ \big|\ p^{\mathrm{H}} = p, +p ∈ 𝔽^{n×n}\ \big|\ p^{\mathrm{H}} = p, apa^{\mathrm{H}} \geq 0 \text{ for all } a ∈ 𝔽 \text{ and } \operatorname{rank}(p) = k\bigr\}, ```` -where $\cdot^{\mathrm{H}}$ denotes the Hermitian, i.e. complex conjugate transpose, -and the field $𝔽 ∈ \{ ℝ, ℂ\}$. -We sometimes $\operatorname{SPS}_{k,𝔽}(n)$, when distinguishing the real- and complex-valued +where ``⋅^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate transpose, +and the field ``𝔽 ∈ \{ ℝ, ℂ\}``. +We sometimes ``\operatorname{SPS}_{k,𝔽}(n)``, when distinguishing the real- and complex-valued manifold is important. -An element is represented by $q ∈ 𝔽^{n × k}$ from the factorization $p = qq^{\mathrm{H}}$. -Note that since for any unitary (orthogonal) $A ∈ 𝔽^{n × n}$ we have -$(Aq)(Aq)^{\mathrm{H}} = qq^{\mathrm{H}} = p$, the representation is not unique, or in -other words, the manifold is a quotient manifold of $𝔽^{n × k}$. +An element is represented by ``q ∈ 𝔽^{n×k}`` from the factorization ``p = qq^{\mathrm{H}}``. +Note that since for any unitary (orthogonal) ``A ∈ 𝔽^{n×n}`` we have +``(Aq)(Aq)^{\mathrm{H}} = qq^{\mathrm{H}} = p``, the representation is not unique, or in +other words, the manifold is a quotient manifold of ``𝔽^{n×k}``. -The tangent space at $p$, $T_p\operatorname{SPS}_k(n)$, is also represented -by matrices $Y ∈ 𝔽^{n × k}$ and reads as +The tangent space at ``p``, ``T_p\operatorname{SPS}_k(n)``, is also represented +by matrices ``Y ∈ 𝔽^{n×k}`` and reads as ````math T_p\operatorname{SPS}_k(n) = \bigl\{ -X ∈ 𝔽^{n × n}\,|\,X = qY^{\mathrm{H}} + Yq^{\mathrm{H}} +X ∈ 𝔽^{n×n}\,|\,X = qY^{\mathrm{H}} + Yq^{\mathrm{H}} \text{ i.e. } X = X^{\mathrm{H}} \bigr\}. ```` @@ -37,7 +37,7 @@ The metric was used in [JourneeBachAbsilSepulchre:2010](@cite)[MassartAbsil:2020 SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) -Generate the manifold of $n × n$ symmetric positive semidefinite matrices of rank $k$ +Generate the manifold of ``n×n`` symmetric positive semidefinite matrices of rank ``k`` over the `field` of real numbers `ℝ` or complex numbers `ℂ`. """ struct SymmetricPositiveSemidefiniteFixedRank{T,𝔽} <: AbstractDecoratorManifold{𝔽} @@ -107,8 +107,8 @@ end distance(M::SymmetricPositiveSemidefiniteFixedRank, p, q) Compute the distance between two points `p`, `q` on the -[`SymmetricPositiveSemidefiniteFixedRank`](@ref), which is the Frobenius norm of $Y$ which -minimizes $\lVert p - qY\rVert$ with respect to $Y$. +[`SymmetricPositiveSemidefiniteFixedRank`](@ref), which is the Frobenius norm of ``Y`` which +minimizes ``\lVert p - qY\rVert`` with respect to ``Y``. """ distance(M::SymmetricPositiveSemidefiniteFixedRank, p, q) = norm(M, p, log(M, p, q)) @@ -129,8 +129,8 @@ which just reads ````math q_2 = \exp_p(\log_pq) ```` - might yield a matrix $q_2\neq q$, but they represent the same point on the quotient - manifold, i.e. $d_{\operatorname{SPS}_k(n)}(q_2,q) = 0$. + might yield a matrix ``q_2\neq q``, but they represent the same point on the quotient + manifold, i.e. ``d_{\operatorname{SPS}_k(n)}(q_2,q) = 0``. """ exp(::SymmetricPositiveSemidefiniteFixedRank, ::Any, ::Any) @@ -144,7 +144,7 @@ end test, whether two points `p`, `q` are (approximately) nearly the same. Since this is a quotient manifold in the embedding, the test is performed by checking -their distance, if they are not the same, i.e. that $d_{\mathcal M}(p,q) \approx 0$, where +their distance, if they are not the same, i.e. that ``d_{\mathcal M}(p,q) \approx 0``, where the comparison is performed with the classical `isapprox`. The `kwargs...` are passed on to this accordingly. """ @@ -170,7 +170,7 @@ is_flat(M::SymmetricPositiveSemidefiniteFixedRank) = false log(M::SymmetricPositiveSemidefiniteFixedRank, q, p) Compute the logarithmic map on the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) manifold -by minimizing $\lVert p - qY\rVert$ with respect to $Y$. +by minimizing ``\lVert p - qY\rVert`` with respect to ``Y``. !!! note @@ -180,8 +180,8 @@ by minimizing $\lVert p - qY\rVert$ with respect to $Y$. ````math q_2 = \exp_p(\log_pq) ```` - might yield a matrix $q_2\neq q$, but they represent the same point on the quotient - manifold, i.e. $d_{\operatorname{SPS}_k(n)}(q_2,q) = 0$. + might yield a matrix ``q_2≠q``, but they represent the same point on the quotient + manifold, i.e. ``d_{\operatorname{SPS}_k(n)}(q_2,q) = 0``. """ log(::SymmetricPositiveSemidefiniteFixedRank, q, p) @@ -203,7 +203,7 @@ Return the dimension of the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) mat \end{aligned} ```` -where the last $k^2$ is due to the zero imaginary part for Hermitian matrices diagonal +where the last ``k^2`` is due to the zero imaginary part for Hermitian matrices diagonal """ manifold_dimension(::SymmetricPositiveSemidefiniteFixedRank) diff --git a/src/manifolds/Symplectic.jl b/src/manifolds/Symplectic.jl index 67b3492b3f..bb630dc391 100644 --- a/src/manifolds/Symplectic.jl +++ b/src/manifolds/Symplectic.jl @@ -1,153 +1,169 @@ @doc raw""" - Symplectic{T, 𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} + SymplecticMatricesMatrices{T, 𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} -The symplectic manifold consists of all ``2n \times 2n`` matrices which preserve -the canonical symplectic form over ``𝔽^{2n × 2n} \times 𝔽^{2n × 2n}``, -````math - \omega\colon 𝔽^{2n × 2n} \times 𝔽^{2n × 2n} \rightarrow 𝔽, - \quad \omega(x, y) = p^{\mathrm{T}} Q_{2n} q, \; x, y \in 𝔽^{2n × 2n}, -```` -where -````math -Q_{2n} = -\begin{bmatrix} - 0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -That is, the symplectic manifold consists of -````math -\operatorname{Sp}(2n, ℝ) = \bigl\{ p ∈ ℝ^{2n × 2n} \, \big| \, p^{\mathrm{T}}Q_{2n}p = Q_{2n} \bigr\}, -```` -with ``0_n`` and ``I_n`` denoting the ``n × n`` zero-matrix -and indentity matrix in ``ℝ^{n \times n}`` respectively. +The symplectic manifold consists of all ``2n×2n`` matrices which preserve +the canonical symplectic form over ``𝔽^{2n×2n}×𝔽^{2n×2n}``, +```math + \omega\colon 𝔽^{2n×2n}×𝔽^{2n×2n} → 𝔽, + \quad \omega(x, y) = p^{\mathrm{T}} J_{2n} q, \ x, y \in 𝔽^{2n×2n}, +``` + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + +The symplectic manifold consists of + +```math +\mathrm{Sp}(2n, ℝ) = \bigl\{ p ∈ ℝ^{2n×2n} \, \big| \, p^{\mathrm{T}}J_{2n}p = J_{2n} \bigr\}, +``` The tangent space at a point ``p`` is given by [BendokatZimmermann:2021](@cite) -````math + +```math \begin{align*} - T_p\operatorname{Sp}(2n) - &= \{X \in \mathbb{R}^{2n \times 2n} \;|\; p^{T}Q_{2n}X + X^{T}Q_{2n}p = 0 \}, \\ - &= \{X = pQS \;|\; S ∈ R^{2n × 2n}, S^{\mathrm{T}} = S \}. + T_p\mathrm{Sp}(2n) + &= \{X \in ℝ^{2n×2n} \ |\ p^{T}J_{2n}X + X^{T}J_{2n}p = 0 \}, \\ + &= \{X = pJ_{2n}S \ \mid\ S ∈ R^{2n×2n}, S^{\mathrm{T}} = S \}. \end{align*} -```` +``` # Constructor - Symplectic(2n, field=ℝ; parameter::Symbol=:type) + SymplecticMatrices(2n, field=ℝ; parameter::Symbol=:type) -Generate the (real-valued) symplectic manifold of ``2n \times 2n`` symplectic matrices. -The constructor for the [`Symplectic`](@ref) manifold accepts the even column/row embedding -dimension ``2n`` for the real symplectic manifold, ``ℝ^{2n × 2n}``. +Generate the (real-valued) symplectic manifold of ``2n×2n`` symplectic matrices. +The constructor for the [`SymplecticMatrices`](@ref) manifold accepts the even column/row embedding +dimension ``2n`` for the real symplectic manifold, ``ℝ^{2n×2n}``. """ -struct Symplectic{T,𝔽} <: AbstractDecoratorManifold{𝔽} +struct SymplecticMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function active_traits(f, ::Symplectic, args...) +function active_traits(f, ::SymplecticMatrices, args...) return merge_traits(IsEmbeddedManifold(), IsDefaultMetric(RealSymplecticMetric())) end -function Symplectic(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) - n % 2 == 0 || throw(ArgumentError("The dimension of the symplectic manifold - embedding space must be even. Was odd, n % 2 == $(n % 2).")) - size = wrap_type_parameter(parameter, (div(n, 2),)) - return Symplectic{typeof(size),field}(size) +function SymplecticMatrices(two_n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) + two_n % 2 == 0 || throw( + ArgumentError( + "The matrix size `2n` of the symplectic manifold must be even, but was $(two_n).", + ), + ) + size = wrap_type_parameter(parameter, (div(two_n, 2),)) + return SymplecticMatrices{typeof(size),field}(size) end @doc raw""" RealSymplecticMetric <: RiemannianMetric The canonical Riemannian metric on the symplectic manifold, -defined pointwise for ``p \in \operatorname{Sp}(2n)`` by [Fiori:2011](@cite)] -````math +defined pointwise for ``p \in \mathrm{Sp}(2n)`` by [Fiori:2011](@cite)] + +```math \begin{align*} - & g_p \colon T_p\operatorname{Sp}(2n) \times T_p\operatorname{Sp}(2n) \rightarrow ℝ, \\ - & g_p(Z_1, Z_2) = \operatorname{tr}((p^{-1}Z_1)^{\mathrm{T}} (p^{-1}Z_2)). + & g_p \colon T_p\mathrm{Sp}(2n)×T_p\mathrm{Sp}(2n) → ℝ, \\ + & g_p(Z_1, Z_2) = \operatorname{tr}((p^{-1}Z_1)^{\mathrm{T}} (p^{-1}Z_2)). \end{align*} -```` -This metric is also the default metric for the [`Symplectic`](@ref) manifold. +``` + +This metric is also the default metric for the [`SymplecticMatrices`](@ref) manifold. """ struct RealSymplecticMetric <: RiemannianMetric end @doc raw""" ExtendedSymplecticMetric <: AbstractMetric -The extension of the [`RealSymplecticMetric`](@ref) at a point `p \in \operatorname{Sp}(2n)` -as an inner product over the embedding space ``ℝ^{2n \times 2n}``, i.e. -````math - \langle x, y \rangle_{p} = \langle p^{-1}x, p^{-1}\rangle_{\operatorname{Fr}} - = \operatorname{tr}(x^{\mathrm{T}}(pp^{\mathrm{T}})^{-1}y), \;\forall\; x, y \in ℝ^{2n \times 2n}. -```` +The extension of the [`RealSymplecticMetric`](@ref) at a point `p \in \mathrm{Sp}(2n)` +as an inner product over the embedding space ``ℝ^{2n×2n}``, i.e. + +```math + ⟨x, y⟩_p = ⟨p^{-1}x, p^{-1}⟩_{\mathrm{Fr}} + = \operatorname{tr}(x^{\mathrm{T}}(pp^{\mathrm{T}})^{-1}y), \text{ for all } x, y \in ℝ^{2n×2n}. +``` """ struct ExtendedSymplecticMetric <: AbstractMetric end @doc raw""" - SymplecticMatrix{T} + SymplecticElement{T} A lightweight structure to represent the action of the matrix representation of the canonical symplectic form, -````math -Q_{2n}(λ) = λ -\begin{bmatrix} + +```math +J_{2n}(λ) = λ\begin{bmatrix} 0_n & I_n \\ -I_n & 0_n -\end{bmatrix} \quad \in ℝ^{2n \times 2n}, -```` -such that the canonical symplectic form is represented by -````math -\omega_{2n}(x, y) = x^{\mathrm{T}}Q_{2n}(1)y, \quad x, y \in ℝ^{2n}. -```` +\end{bmatrix} ∈ ℝ^{2n×2n}, +``` + +where we write ``J_{2n} = J_{2n}(1)`` for short. +The canonical symplectic form is represented by + +```math +\omega_{2n}(x, y) = x^{\mathrm{T}}J_{2n}y, \quad x, y ∈ ℝ^{2n}. +``` The entire matrix is however not instantiated in memory, instead a scalar ``λ`` of type `T` is stored, which is used to keep track of scaling and transpose operations -applied to each `SymplecticMatrix`. -For example, given `Q = SymplecticMatrix(1.0)` represented as `1.0*[0 I; -I 0]`, -the adjoint `Q'` returns `SymplecticMatrix(-1.0) = (-1.0)*[0 I; -I 0]`. +applied to each `SymplecticElement`. +This type acts similar to `I` from `LinearAlgeba`. + +# Constructor + + SymplecticElement(λ=1) + +Generate the sumplectic matrix with scaling ``1``. """ -struct SymplecticMatrix{T} +struct SymplecticElement{T} λ::T end -SymplecticMatrix() = SymplecticMatrix(1) -SymplecticMatrix(λ::T) where {T<:Number} = SymplecticMatrix{T}(λ) +SymplecticElement() = SymplecticElement(1) +SymplecticElement(λ::T) where {T<:Number} = SymplecticElement{T}(λ) -function SymplecticMatrix(arrays::Vararg{AbstractArray}) +function SymplecticElement(arrays::Vararg{AbstractArray}) TS = Base.promote_type(map(eltype, arrays)...) - return SymplecticMatrix(one(TS)) + return SymplecticElement(one(TS)) end @doc raw""" - change_representer(::Symplectic, ::EuclideanMetric, p, X) - change_representer!(::Symplectic, Y, ::EuclideanMetric, p, X) + change_representer(::SymplecticMatrices, ::EuclideanMetric, p, X) + change_representer!(::SymplecticMatrices, Y, ::EuclideanMetric, p, X) -Compute the representation of a tangent vector ``ξ ∈ T_p\operatorname{Sp}(2n, ℝ)`` s.t. -````math - g_p(c_p(ξ), η) = ⟨ξ, η⟩^{\text{Euc}} \;∀\; η ∈ T_p\operatorname{Sp}(2n, ℝ). -```` +Compute the representation of a tangent vector ``ξ ∈ T_p\mathrm{Sp}(2n, ℝ)`` s.t. +```math + g_p(c_p(ξ), η) = ⟨ξ, η⟩^{\text{Euc}} \text{for all } η ∈ T_p\mathrm{Sp}(2n, ℝ). +``` with the conversion function -````math - c_p : T_p\operatorname{Sp}(2n, ℝ) \rightarrow T_p\operatorname{Sp}(2n, ℝ), \quad - c_p(ξ) = \frac{1}{2} pp^{\mathrm{T}} ξ + \frac{1}{2} pQ ξ^{\mathrm{T}} pQ. -```` -Each of the terms ``c_p^1(ξ) = p p^{\mathrm{T}} ξ`` and ``c_p^2(ξ) = pQ ξ^{\mathrm{T}} pQ`` from the +```math + c_p : T_p\mathrm{Sp}(2n, ℝ) → T_p\mathrm{Sp}(2n, ℝ), \quad + c_p(ξ) = \frac{1}{2} pp^{\mathrm{T}} ξ + \frac{1}{2} pJ_{2n} ξ^{\mathrm{T}} pJ_{2n}, +``` + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + +Each of the terms ``c_p^1(ξ) = p p^{\mathrm{T}} ξ`` and ``c_p^2(ξ) = pJ_{2n} ξ^{\mathrm{T}} pJ_{2n}`` from the above definition of ``c_p(η)`` are themselves metric compatible in the sense that -````math - c_p^i : T_p\operatorname{Sp}(2n, ℝ) \rightarrow \mathbb{R}^{2n \times 2n}\quad - g_p^i(c_p(ξ), η) = ⟨ξ, η⟩^{\text{Euc}} \;∀\; η ∈ T_p\operatorname{Sp}(2n, ℝ), -```` + +```math + c_p^i : T_p\mathrm{Sp}(2n, ℝ) → ℝ^{2n×2n}\quad + g_p^i(c_p(ξ), η) = ⟨ξ, η⟩^{\text{Euc}} \;∀\; η ∈ T_p\mathrm{Sp}(2n, ℝ), +``` + for ``i \in {1, 2}``. However the range of each function alone is not confined to -``T_p\operatorname{Sp}(2n, ℝ)``, but the convex combination -````math + ``T_p\mathrm{Sp}(2n, ℝ)``, but the convex combination + +```math c_p(ξ) = \frac{1}{2}c_p^1(ξ) + \frac{1}{2}c_p^2(ξ) -```` -does have the correct range ``T_p\operatorname{Sp}(2n, ℝ)``. +``` + +does have the correct range ``T_p\mathrm{Sp}(2n, ℝ)``. """ -change_representer(::Symplectic, ::EuclideanMetric, p, X) +change_representer(::SymplecticMatrices, ::EuclideanMetric, p, X) -function change_representer!(::Symplectic, Y, ::EuclideanMetric, p, X) - Q = SymplecticMatrix(p, X) +function change_representer!(::SymplecticMatrices, Y, ::EuclideanMetric, p, X) + J = SymplecticElement(p, X) # J_{2n} pT_X = p' * X - Y .= (1 / 2) .* p * (pT_X .+ Q * pT_X' * Q) + Y .= (1 / 2) .* p * (pT_X .+ J * pT_X' * J) return Y end @@ -157,20 +173,20 @@ end change_representer!(MetMan::MetricManifold{<:Any, <:Euclidean, ExtendedSymplecticMetric}, Y, EucMet::EuclideanMetric, p, X) -Change the representation of a matrix ``ξ ∈ \mathbb{R}^{2n \times 2n}`` -into the inner product space ``(ℝ^{2n \times 2n}, g_p)`` where the inner product +Change the representation of a matrix ``ξ ∈ ℝ^{2n×2n}`` +into the inner product space ``(ℝ^{2n×2n}, g_p)`` where the inner product is given by ``g_p(ξ, η) = \langle p^{-1}ξ, p^{-1}η \rangle = \operatorname{tr}(ξ^{\mathrm{T}}(pp^{\mathrm{T}})^{-1}η)``, as the extension of the [`RealSymplecticMetric`](@ref) onto the entire embedding space. By changing the representation we mean to apply a mapping ````math - c_p : \mathbb{R}^{2n \times 2n} \rightarrow \mathbb{R}^{2n \times 2n}, + c_p : ℝ^{2n×2n} → ℝ^{2n×2n}, ```` defined by requiring that it satisfy the metric compatibility condition ````math g_p(c_p(ξ), η) = ⟨p^{-1}c_p(ξ), p^{-1}η⟩ = ⟨ξ, η⟩^{\text{Euc}} - \;∀\; η ∈ T_p\operatorname{Sp}(2n, ℝ). + \;∀\; η ∈ T_p\mathrm{Sp}(2n, ℝ). ```` In this case, we compute the mapping ````math @@ -198,31 +214,24 @@ function change_representer!( end @doc raw""" - check_point(M::Symplectic, p; kwargs...) + check_point(M::SymplecticMatrices, p; kwargs...) -Check whether `p` is a valid point on the [`Symplectic`](@ref) `M`=$\operatorname{Sp}(2n)$, -i.e. that it has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and $p^{+}p$ is (approximately) -the identity, where $A^{+} = Q_{2n}^{\mathrm{T}}A^{\mathrm{T}}Q_{2n}$ is the symplectic inverse, with -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). +Check whether `p` is a valid point on the [`SymplecticMatrices`](@ref) `M`=$\mathrm{Sp}(2n)$, +i.e. that it has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and ``p^{+}p`` is (approximately) +the identity, where ``A^+`` denotes the [`symplectic_inverse`]/@ref). + +The tolerance can be set with `kwargs...`. """ function check_point( - M::Symplectic, + M::SymplecticMatrices, p::T; atol::Real=sqrt(prod(representation_size(M))) * eps(real(float(number_eltype(T)))), kwargs..., ) where {T} # Perform check that the matrix lives on the real symplectic manifold: - expected_zero = norm(inv(M, p) * p - LinearAlgebra.I) - if !isapprox(expected_zero, 0; atol=atol, kwargs...) + if !isapprox(inv(M, p) * p, LinearAlgebra.I; atol=atol, kwargs...) return DomainError( - expected_zero, + norm(inv(M, p) * p - LinearAlgebra.I), ( "The point p does not lie on $(M) because its symplectic" * " inverse composed with itself is not the identity." @@ -233,58 +242,51 @@ function check_point( end @doc raw""" - check_vector(M::Symplectic, p, X; kwargs...) + check_vector(M::SymplecticMatrices, p, X; kwargs...) -Checks whether `X` is a valid tangent vector at `p` on the [`Symplectic`](@ref) -`M`=``\operatorname{Sp}(2n)``, i.e. the [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) fits and -it (approximately) holds that ``p^{T}Q_{2n}X + X^{T}Q_{2n}p = 0``, -where -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). +Checks whether `X` is a valid tangent vector at `p` on the [`SymplecticMatrices`](@ref) +`M`=``\mathrm{Sp}(2n)``, which requires that + +```math +p^{T}J_{2n}X + X^{T}J_{2n}p = 0 +``` +holds (approximately), where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + +The tolerance can be set with `kwargs...` """ -check_vector(::Symplectic, ::Any...) +check_vector(::SymplecticMatrices, ::Any...) -function check_vector( - M::Symplectic, - p, - X::T; - atol::Real=sqrt(prod(representation_size(M))) * eps(real(float(number_eltype(T)))), - kwargs..., -) where {T} - Q = SymplecticMatrix(p, X) - tangent_requirement_norm = norm(X' * Q * p + p' * Q * X, 2) - if !isapprox(tangent_requirement_norm, 0; atol=atol, kwargs...) +function check_vector(M::SymplecticMatrices, p, X::T; kwargs...) where {T} + J = SymplecticElement(p, X) + if !isapprox(X' * J * p, -p' * J * X; kwargs...) return DomainError( - tangent_requirement_norm, + norm(X' * J * p + p' * J * X, 2), ( "The matrix X is not in the tangent space at point p of the" * - " manifold $(M), as X'Qp + p'QX is not the zero matrix." + " manifold $(M), as X'Jp + p'JX is not the zero matrix." ), ) end return nothing end -ManifoldsBase.default_inverse_retraction_method(::Symplectic) = CayleyInverseRetraction() +function ManifoldsBase.default_inverse_retraction_method(::SymplecticMatrices) + return CayleyInverseRetraction() +end -ManifoldsBase.default_retraction_method(::Symplectic) = CayleyRetraction() +ManifoldsBase.default_retraction_method(::SymplecticMatrices) = CayleyRetraction() @doc raw""" - distance(M::Symplectic, p, q) + distance(M::SymplecticMatrices, p, q) Compute an approximate geodesic distance between two Symplectic matrices -``p, q \in \operatorname{Sp}(2n)``, as done in [WangSunFiori:2018](@cite). +``p, q \in \mathrm{Sp}(2n)``, as done in [WangSunFiori:2018](@cite). + ````math - \operatorname{dist}(p, q) - ≈ ||\operatorname{Log}(p^+q)||_{\operatorname{Fr}}, + \operatorname{dist}(p, q) + ≈ \lVert\operatorname{Log}(p^+q)\rVert_{\\mathrm{Fr}}, ```` -where the ``\operatorname{Log}(\cdot)`` operator is the matrix logarithm. +where the ``\operatorname{Log}(⋅)`` operator is the matrix logarithm. This approximation is justified by first recalling the Baker-Campbell-Hausdorf formula, ````math @@ -299,97 +301,99 @@ Then we write the expression for the exponential map from ``p`` to ``q`` as = p \operatorname{Exp}((p^{+}X)^{\mathrm{T}}) \operatorname{Exp}([p^{+}X - (p^{+}X)^{\mathrm{T}}]), - X \in T_p\operatorname{Sp}, + X \in T_p\mathrm{Sp}, ```` and with the geodesic distance between ``p`` and ``q`` given by -``\operatorname{dist}(p, q) = ||X||_p = ||p^+X||_{\operatorname{Fr}}`` +``\operatorname{dist}(p, q) = \lVertX\rVert_p = \lVertp^+X\rVert_{\\mathrm{Fr}}`` we see that ````math \begin{align*} - ||\operatorname{Log}(p^+q)||_{\operatorname{Fr}} - &= ||\operatorname{Log}\left( - \operatorname{Exp}((p^{+}X)^{\mathrm{T}}) - \operatorname{Exp}(p^{+}X - (p^{+}X)^{\mathrm{T}}) - \right)||_{\operatorname{Fr}} \\ - &= ||p^{+}X + \frac{1}{2}[(p^{+}X)^{\mathrm{T}}, p^{+}X - (p^{+}X)^{\mathrm{T}}] - + \ldots ||_{\operatorname{Fr}} \\ - &≈ ||p^{+}X||_{\operatorname{Fr}} = \operatorname{dist}(p, q). + \lVert\operatorname{Log}(p^+q)\rVert_{\\mathrm{Fr}} + &=\Bigl\lVert + \operatorname{Log}\bigl( + \operatorname{Exp}((p^{+}X)^{\mathrm{T}}) + \operatorname{Exp}(p^{+}X - (p^{+}X)^{\mathrm{T}}) + \bigr) + \Bigr\rVert_{\\mathrm{Fr}} \\ + &=\lVertp^{+}X + \frac{1}{2}[(p^{+}X)^{\mathrm{T}}, p^{+}X - (p^{+}X)^{\mathrm{T}}] + + \ldots\lVert_{\\mathrm{Fr}} \\ + &≈\lVertp^{+}X\rVert_{\\mathrm{Fr}} = \operatorname{dist}(p, q). \end{align*} ```` """ -function distance(M::Symplectic, p, q) +function distance(M::SymplecticMatrices, p, q) return norm(log(symplectic_inverse_times(M, p, q))) end -embed(::Symplectic, p) = p -embed(::Symplectic, p, X) = X +embed(::SymplecticMatrices, p) = p +embed(::SymplecticMatrices, p, X) = X @doc raw""" - exp(M::Symplectic, p, X) - exp!(M::Symplectic, q, p, X) + exp(M::SymplecticMatrices, p, X) + exp!(M::SymplecticMatrices, q, p, X) The Exponential mapping on the Symplectic manifold with the [`RealSymplecticMetric`](@ref) Riemannian metric. -For the point ``p \in \operatorname{Sp}(2n)`` the exponential mapping along the tangent -vector ``X \in T_p\operatorname{Sp}(2n)`` is computed as [WangSunFiori:2018](@cite) +For the point ``p \in \mathrm{Sp}(2n)`` the exponential mapping along the tangent +vector ``X \in T_p\mathrm{Sp}(2n)`` is computed as [WangSunFiori:2018](@cite) ````math \operatorname{exp}_p(X) = p \operatorname{Exp}((p^{-1}X)^{\mathrm{T}}) \operatorname{Exp}(p^{-1}X - (p^{-1}X)^{\mathrm{T}}), ```` -where ``\operatorname{Exp}(\cdot)`` denotes the matrix exponential. +where ``\operatorname{Exp}(⋅)`` denotes the matrix exponential. """ -exp(::Symplectic, ::Any...) +exp(::SymplecticMatrices, ::Any...) -function exp!(M::Symplectic, q, p, X) +function exp!(M::SymplecticMatrices, q, p, X) p_star_X = symplectic_inverse_times(M, p, X) q .= p * exp(Array(p_star_X')) * exp(p_star_X - p_star_X') return q end -function get_embedding(::Symplectic{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} +function get_embedding(::SymplecticMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} return Euclidean(2 * n, 2 * n; field=𝔽) end -function get_embedding(M::Symplectic{Tuple{Int},𝔽}) where {𝔽} +function get_embedding(M::SymplecticMatrices{Tuple{Int},𝔽}) where {𝔽} n = get_parameter(M.size)[1] return Euclidean(2 * n, 2 * n; field=𝔽, parameter=:field) end @doc raw""" - gradient(M::Symplectic, f, p, backend::RiemannianProjectionBackend; + gradient(M::SymplecticMatrices, f, p, backend::RiemannianProjectionBackend; extended_metric=true) - gradient!(M::Symplectic, f, p, backend::RiemannianProjectionBackend; + gradient!(M::SymplecticMatrices, f, p, backend::RiemannianProjectionBackend; extended_metric=true) Compute the manifold gradient ``\text{grad}f(p)`` of a scalar function -``f \colon \operatorname{Sp}(2n) \rightarrow ℝ`` at -``p \in \operatorname{Sp}(2n)``. +``f \colon \mathrm{Sp}(2n) → ℝ`` at +``p \in \mathrm{Sp}(2n)``. The element ``\text{grad}f(p)`` is found as the Riesz representer of the differential -``\text{D}f(p) \colon T_p\operatorname{Sp}(2n) \rightarrow ℝ`` w.r.t. +``\text{D}f(p) \colon T_p\mathrm{Sp}(2n) → ℝ`` with respect to the Riemannian metric inner product at ``p`` [Fiori:2011](@cite)]. -That is, ``\text{grad}f(p) \in T_p\operatorname{Sp}(2n)`` solves the relation +That is, ``\text{grad}f(p) \in T_p\mathrm{Sp}(2n)`` solves the relation ````math - g_p(\text{grad}f(p), X) = \text{D}f(p) \quad\forall\; X \in T_p\operatorname{Sp}(2n). + g_p(\text{grad}f(p), X) = \text{D}f(p) \quad\forall\; X \in T_p\mathrm{Sp}(2n). ```` The default behaviour is to first change the representation of the Euclidean gradient from the Euclidean metric to the [`RealSymplecticMetric`](@ref) at ``p``, and then we projecting -the result onto the correct tangent tangent space ``T_p\operatorname{Sp}(2n, ℝ)`` +the result onto the correct tangent tangent space ``T_p\mathrm{Sp}(2n, ℝ)`` w.r.t the Riemannian metric ``g_p`` extended to the entire embedding space. # Arguments: - `extended_metric = true`: If `true`, compute the gradient ``\text{grad}f(p)`` by first changing the representer of the Euclidean gradient of a smooth extension - of ``f``, ``∇f(p)``, w.r.t. the [`RealSymplecticMetric`](@ref) at ``p`` + of ``f``, ``∇f(p)``, with respect to the [`RealSymplecticMetric`](@ref) at ``p`` extended to the entire embedding space, before projecting onto the correct - tangent vector space w.r.t. the same extended metric ``g_p``. + tangent vector space with respect to the same extended metric ``g_p``. If `false`, compute the gradient by first projecting ``∇f(p)`` onto the tangent vector space, before changing the representer in the tangent vector space to comply with the [`RealSymplecticMetric`](@ref). """ function ManifoldDiff.gradient( - M::Symplectic, + M::SymplecticMatrices, f, p, backend::RiemannianProjectionBackend; @@ -400,7 +404,7 @@ function ManifoldDiff.gradient( end function ManifoldDiff.gradient!( - M::Symplectic, + M::SymplecticMatrices, f, X, p, @@ -419,74 +423,87 @@ function ManifoldDiff.gradient!( end @doc raw""" - inner(::Symplectic{<:Any,ℝ}, p, X, Y) + inner(::SymplecticMatrices{<:Any,ℝ}, p, X, Y) Compute the canonical Riemannian inner product [`RealSymplecticMetric`](@ref) ````math g_p(X, Y) = \operatorname{tr}((p^{-1}X)^{\mathrm{T}} (p^{-1}Y)) ```` -between the two tangent vectors ``X, Y \in T_p\operatorname{Sp}(2n)``. +between the two tangent vectors ``X, Y \in T_p\mathrm{Sp}(2n)``. """ -function inner(M::Symplectic{<:Any,ℝ}, p, X, Y) +function inner(M::SymplecticMatrices{<:Any,ℝ}, p, X, Y) p_star = inv(M, p) return dot((p_star * X), (p_star * Y)) end @doc raw""" - inv(::Symplectic, A) - inv!(::Symplectic, A) + symplectic_inverse(A) + +Given a matrix +```math + A ∈ ℝ^{2n×2k},\quad + A = + \begin{bmatrix} + A_{1,1} & A_{1,2} \\ + A_{2,1} & A_{2, 2} + \end{bmatrix} +``` -Compute the symplectic inverse ``A^+`` of matrix ``A ∈ ℝ^{2n × 2n}``. Given a matrix -````math -A ∈ ℝ^{2n × 2n},\quad -A = -\begin{bmatrix} -A_{1,1} & A_{1,2} \\ -A_{2,1} & A_{2, 2} -\end{bmatrix} -```` the symplectic inverse is defined as: -````math -A^{+} := Q_{2n}^{\mathrm{T}} A^{\mathrm{T}} Q_{2n}, -```` -where -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` + +```math +A^{+} := J_{2k}^{\mathrm{T}} A^{\mathrm{T}} J_{2n}, +``` + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + The symplectic inverse of A can be expressed explicitly as: -````math + +```math A^{+} = -\begin{bmatrix} - A_{2, 2}^{\mathrm{T}} & -A_{1, 2}^{\mathrm{T}} \\[1.2mm] - -A_{2, 1}^{\mathrm{T}} & A_{1, 1}^{\mathrm{T}} -\end{bmatrix}. -```` + \begin{bmatrix} + A_{2, 2}^{\mathrm{T}} & -A_{1, 2}^{\mathrm{T}} \\[1.2mm] + -A_{2, 1}^{\mathrm{T}} & A_{1, 1}^{\mathrm{T}} + \end{bmatrix}. +``` """ -function Base.inv(M::Symplectic{<:Any,ℝ}, A) - n = get_parameter(M.size)[1] - Ai = similar(A) - checkbounds(A, 1:(2n), 1:(2n)) - @inbounds for i in 1:n, j in 1:n - Ai[i, j] = A[j + n, i + n] +function symplectic_inverse(A::AbstractMatrix) + N, K = size(A) + @assert iseven(N) "The first matrix dimension of A ($N) has to be even" + @assert iseven(K) "The second matrix dimension of A ($K) has to be even" + n = div(N, 2) + k = div(K, 2) + Ai = similar(A') + checkbounds(A, 1:(2n), 1:(2k)) + @inbounds for i in 1:k, j in 1:n + Ai[i, j] = A[j + n, i + k] end - @inbounds for i in 1:n, j in 1:n - Ai[i + n, j] = -A[j + n, i] + @inbounds for i in 1:k, j in 1:n + Ai[i + k, j] = -A[j + n, i] end - @inbounds for i in 1:n, j in 1:n - Ai[i, j + n] = -A[j, i + n] + @inbounds for i in 1:k, j in 1:n + Ai[i, j + n] = -A[j, i + k] end - @inbounds for i in 1:n, j in 1:n - Ai[i + n, j + n] = A[j, i] + @inbounds for i in 1:k, j in 1:n + Ai[i + k, j + n] = A[j, i] end return Ai end -function inv!(M::Symplectic{<:Any,ℝ}, A) - n = get_parameter(M.size)[1] +@doc raw""" + inv(::SymplecticMatrices, A) + inv!(::SymplecticMatrices, A) + +Compute the symplectic inverse ``A^+`` of matrix ``A ∈ ℝ^{2n×2n}``. +See [`symplectic_inverse`](@ref) for details. + +""" +function Base.inv(M::SymplecticMatrices{<:Any,ℝ}, A) + return symplectic_inverse(A) +end + +function symplectic_inverse!(A) + n = div(size(A, 1), 2) checkbounds(A, 1:(2n), 1:(2n)) @inbounds for i in 1:n, j in 1:n A[i, j], A[j + n, i + n] = A[j + n, i + n], A[i, j] @@ -509,42 +526,38 @@ function inv!(M::Symplectic{<:Any,ℝ}, A) end @doc raw""" - inverse_retract(M::Symplectic, p, q, ::CayleyInverseRetraction) + inv!(M::SymplecticMatrices, A) -Compute the Cayley Inverse Retraction ``X = \mathcal{L}_p^{\operatorname{Sp}}(q)`` +Compute the [`symplectic_inverse`](@ref) of a suqare matrix A inplace of A +""" +function inv!(M::SymplecticMatrices{<:Any,ℝ}, A) + return symplectic_inverse!(A) +end + +@doc raw""" + inverse_retract(M::SymplecticMatrices, p, q, ::CayleyInverseRetraction) + +Compute the Cayley Inverse Retraction ``X = \mathcal{L}_p^{\mathrm{Sp}}(q)`` such that the Cayley Retraction from ``p`` along ``X`` lands at ``q``, i.e. ``\mathcal{R}_p(X) = q`` [BendokatZimmermann:2021](@cite). -First, recall the definition the standard symplectic matrix -````math -Q = -\begin{bmatrix} - 0 & I \\ --I & 0 -\end{bmatrix} -```` -as well as the symplectic inverse of a matrix ``A``, ``A^{+} = Q^{\mathrm{T}} A^{\mathrm{T}} Q``. - -For ``p, q ∈ \operatorname{Sp}(2n, ℝ)`` then, we can then define the +For ``p, q ∈ \mathrm{Sp}(2n, ℝ)`` then, we can define the inverse cayley retraction as long as the following matrices exist. ````math - U = (I + p^+ q)^{-1}, \quad V = (I + q^+ p)^{-1}. + U = (I + p^+ q)^{-1}, \quad V = (I + q^+ p)^{-1}, ```` -If that is the case, the inverse cayley retration at ``p`` applied to ``q`` is -````math -\mathcal{L}_p^{\operatorname{Sp}}(q) = 2p\bigl(V - U\bigr) + 2\bigl((p + q)U - p\bigr) - ∈ T_p\operatorname{Sp}(2n). -```` +where ``(⋅)^+`` denotes the [`symplectic_inverse`](@ref). -[BendokatZimmermann:2021](@cite): - > Bendokat, Thomas and Zimmermann, Ralf: - > The real symplectic Stiefel and Grassmann manifolds: metrics, geodesics and applications - > arXiv preprint arXiv:[2108.12447](https://arxiv.org/abs/2108.12447), 2021. +Then inverse cayley retration at ``p`` applied to ``q`` is +```math +\mathcal{L}_p^{\mathrm{Sp}}(q) + = 2p\bigl(V - U\bigr) + 2\bigl((p + q)U - p\bigr) ∈ T_p\mathrm{Sp}(2n). +``` """ -inverse_retract(::Symplectic, p, q, ::CayleyInverseRetraction) +inverse_retract(::SymplecticMatrices, p, q, ::CayleyInverseRetraction) -function inverse_retract_cayley!(M::Symplectic, X, p, q) +function inverse_retract_cayley!(M::SymplecticMatrices, X, p, q) U_inv = lu(add_scaled_I!(symplectic_inverse_times(M, p, q), 1)) V_inv = lu(add_scaled_I!(symplectic_inverse_times(M, q, p), 1)) @@ -553,204 +566,195 @@ function inverse_retract_cayley!(M::Symplectic, X, p, q) end """ - is_flat(::Symplectic) + is_flat(::SymplecticMatrices) -Return false. [`Symplectic`](@ref) is not a flat manifold. +Return false. [`SymplecticMatrices`](@ref) is not a flat manifold. """ -is_flat(M::Symplectic) = false +is_flat(M::SymplecticMatrices) = false @doc raw""" - manifold_dimension(::Symplectic) + manifold_dimension(::SymplecticMatrices) Returns the dimension of the symplectic manifold -embedded in ``ℝ^{2n \times 2n}``, i.e. -````math - \operatorname{dim}(\operatorname{Sp}(2n)) = (2n + 1)n. -```` +embedded in ``ℝ^{2n×2n}``, i.e. +```math + \operatorname{dim}(\mathrm{Sp}(2n)) = (2n + 1)n. +``` """ -function manifold_dimension(M::Symplectic) +function manifold_dimension(M::SymplecticMatrices) n = get_parameter(M.size)[1] return (2n + 1) * n end @doc raw""" - project(::Symplectic, p, A) - project!(::Symplectic, Y, p, A) + project(::SymplecticMatrices, p, A) + project!(::SymplecticMatrices, Y, p, A) -Given a point ``p \in \operatorname{Sp}(2n)``, -project an element ``A \in \mathbb{R}^{2n \times 2n}`` onto -the tangent space ``T_p\operatorname{Sp}(2n)`` relative to -the euclidean metric of the embedding ``\mathbb{R}^{2n \times 2n}``. +Given a point ``p \in \mathrm{Sp}(2n)``, +project an element ``A \in ℝ^{2n×2n}`` onto +the tangent space ``T_p\mathrm{Sp}(2n)`` relative to +the euclidean metric of the embedding ``ℝ^{2n×2n}``. -That is, we find the element ``X \in T_p\operatorname{SpSt}(2n, 2k)`` +That is, we find the element ``X \in T_p\operatorname{Sp}(2n)`` which solves the constrained optimization problem ````math - \operatorname{min}_{X \in \mathbb{R}^{2n \times 2n}} \frac{1}{2}||X - A||^2, \quad - \text{s.t.}\; - h(X) \colon= X^{\mathrm{T}} Q p + p^{\mathrm{T}} Q X = 0, + \operatorname{min}_{X \in ℝ^{2n×2n}} \frac{1}{2}\lVert X - A\rVert^2, \quad + \text{such that}\; + h(X) := X^{\mathrm{T}} J_{2n} p + p^{\mathrm{T}} J_{2n} X = 0, ```` -where ``h\colon\mathbb{R}^{2n \times 2n} \rightarrow \operatorname{skew}(2n)`` defines -the restriction of ``X`` onto the tangent space ``T_p\operatorname{SpSt}(2n, 2k)``. +where ``h: ℝ^{2n×2n} → \operatorname{skew}(2n)`` denotes +the restriction of ``X`` onto the tangent space ``T_p\operatorname{SpSt}(2n, 2k)`` +and ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). """ -project(::Symplectic, p, A) +project(::SymplecticMatrices, p, A) -function project!(::Symplectic, Y, p, A) - Q = SymplecticMatrix(Y, p, A) - Q_p = Q * p +function project!(::SymplecticMatrices, Y, p, A) + J = SymplecticElement(Y, p, A) + Jp = J * p function h(X) - XT_Q_p = X' * Q_p - return XT_Q_p .- XT_Q_p' + XtJp = X' * Jp + return XtJp .- XtJp' end # Solve for Λ (Lagrange mutliplier): - pT_p = p' * p # (2k × 2k) + pT_p = p' * p # (2k×2k) Λ = sylvester(pT_p, pT_p, h(A) ./ 2) - Y[:, :] = A .- Q_p * (Λ .- Λ') + Y[:, :] = A .- Jp * (Λ .- Λ') return Y end @doc raw""" project!(::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) where {𝔽} -Compute the projection of ``X ∈ R^{2n × 2n}`` onto ``T_p\operatorname{Sp}(2n, ℝ)`` w.r.t. -the Riemannian metric ``g`` [`RealSymplecticMetric`](@ref). +Compute the projection of ``X ∈ R^{2n×2n}`` onto ``T_p\mathrm{Sp}(2n, ℝ)`` with respect to +the [`RealSymplecticMetric`](@ref) ``g``. + The closed form projection mapping is given by [GaoSonAbsilStykel:2021](@cite) ````math - \operatorname{P}^{T_p\operatorname{Sp}(2n)}_{g_p}(X) = pQ\operatorname{sym}(p^{\mathrm{T}}Q^{\mathrm{T}}X), + \operatorname{P}^{T_p\mathrm{Sp}(2n)}_{g_p}(X) = pJ_{2n}\operatorname{sym}(p^{\mathrm{T}}J_{2n}^{\mathrm{T}}X), ```` -where ``\operatorname{sym}(A) = \frac{1}{2}(A + A^{\mathrm{T}})``. -This function is not exported. +where ``\operatorname{sym}(A) = \frac{1}{2}(A + A^{\mathrm{T}})`` and and ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). """ function project!(::MetricManifold{<:Any,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) - Q = SymplecticMatrix(p, X) - - pT_QT_X = p' * Q' * X - symmetrized_pT_QT_X = (1 / 2) .* (pT_QT_X + pT_QT_X') + J = SymplecticElement(p, X) - Y .= p * Q * (symmetrized_pT_QT_X) + pTJTX = p' * J' * X + sym_pTJTX = (1 / 2) .* (pTJTX + pTJTX') + Y .= p * J * (sym_pTJTX) return Y end @doc raw""" project_normal!(::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) -Project onto the normal of the tangent space ``(T_p\operatorname{Sp}(2n))^{\perp_g}`` at -a point ``p ∈ \operatorname{Sp}(2n)``, relative to the riemannian metric +Project onto the normal of the tangent space ``(T_p\mathrm{Sp}(2n))^{\perp_g}`` at +a point ``p ∈ \mathrm{Sp}(2n)``, relative to the riemannian metric ``g`` [`RealSymplecticMetric`](@ref). + That is, ````math -(T_p\operatorname{Sp}(2n))^{\perp_g} = \{Y \in \mathbb{R}^{2n \times 2n} : - g_p(Y, X) = 0 \;\forall\; X \in T_p\operatorname{Sp}(2n)\}. +(T_p\mathrm{Sp}(2n))^{\perp_g} + = \{Y ∈ ℝ^{2n×2n} : g_p(Y, X) = 0 \test{ for all } X \in T_p\mathrm{Sp}(2n)\}. ```` The closed form projection operator onto the normal space is given by [GaoSonAbsilStykel:2021](@cite) ````math -\operatorname{P}^{(T_p\operatorname{Sp}(2n))\perp}_{g_p}(X) = pQ\operatorname{skew}(p^{\mathrm{T}}Q^{\mathrm{T}}X), +\operatorname{P}^{(T_p\mathrm{Sp}(2n))\perp}_{g_p}(X) = pJ_{2n}\operatorname{skew}(p^{\mathrm{T}}J_{2n}^{\mathrm{T}}X), ```` -where ``\operatorname{skew}(A) = \frac{1}{2}(A - A^{\mathrm{T}})``. +where ``\operatorname{skew}(A) = \frac{1}{2}(A - A^{\mathrm{T}})`` +and ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + This function is not exported. """ function project_normal!( - M::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, + ::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X, ) where {𝔽} - Q = SymplecticMatrix(p, X) - - pT_QT_X = p' * Q' * X - skew_pT_QT_X = (1 / 2) .* (pT_QT_X .- pT_QT_X') - - Y .= p * Q * skew_pT_QT_X + J = SymplecticElement(p, X) + pTJTX = p' * J' * X + skew_pTJTX = (1 / 2) .* (pTJTX .- pTJTX') + Y .= p * J * skew_pTJTX return Y end @doc raw""" - rand(::SymplecticStiefel; vector_at=nothing, - hamiltonian_norm = (vector_at === nothing ? 1/2 : 1.0)) + rand(::SymplecticStiefel; vector_at=nothing, σ=1.0) -Generate a random point on ``\operatorname{Sp}(2n)`` or a random -tangent vector ``X \in T_p\operatorname{Sp}(2n)`` if `vector_at` is set to -a point ``p \in \operatorname{Sp}(2n)``. +Generate a random point on ``\mathrm{Sp}(2n)`` or a random +tangent vector ``X \in T_p\mathrm{Sp}(2n)`` if `vector_at` is set to +a point ``p \in \mathrm{Sp}(2n)``. -A random point on ``\operatorname{Sp}(2n)`` is constructed by generating a -random Hamiltonian matrix ``Ω \in \mathfrak{sp}(2n,F)`` with norm `hamiltonian_norm`, +A random point on ``\mathrm{Sp}(2n)`` is constructed by generating a +random Hamiltonian matrix ``Ω \in \mathfrak{sp}(2n,F)`` with norm `σ`, and then transforming it to a symplectic matrix by applying the Cayley transform -````math - \operatorname{cay}\colon \mathfrak{sp}(2n,F) \rightarrow \operatorname{Sp}(2n), - \; \Omega \mapsto (I - \Omega)^{-1}(I + \Omega). -```` -To generate a random tangent vector in ``T_p\operatorname{Sp}(2n)``, this code employs the -second tangent vector space parametrization of [Symplectic](@ref). + +```math + \operatorname{cay}: \mathfrak{sp}(2n,F) → \mathrm{Sp}(2n), + \ \Omega \mapsto (I - \Omega)^{-1}(I + \Omega). +``` + +To generate a random tangent vector in ``T_p\mathrm{Sp}(2n)``, this code employs the +second tangent vector space parametrization of [`SymplecticMatrices`](@ref). It first generates a random symmetric matrix ``S`` by `S = randn(2n, 2n)` and then symmetrizes it as `S = S + S'`. -Then ``S`` is normalized to have Frobenius norm of `hamiltonian_norm` -and `X = pQS` is returned, where `Q` is the [`SymplecticMatrix`](@ref). +Then ``S`` is normalized to have Frobenius norm of `σ` +and `X = pJS` is returned, where `J` is the [`SymplecticElement`](@ref). """ -function Random.rand( - M::Symplectic; +rand(SymplecticMatrices; σ::Rieal=1.0, kwargs...) + +function Random.rand!( + rng::AbstractRNG, + M::SymplecticMatrices, + pX; vector_at=nothing, - hamiltonian_norm=(vector_at === nothing ? 1 / 2 : 1.0), + hamiltonian_norm=nothing, + σ=hamiltonian_norm === nothing ? 1.0 : hamiltonian_norm, ) + !(hamiltonian_norm === nothing) && Base.depwarn( + Random.rand!, + "hamiltonian_norm is deprecated as a keyword, please use the default σ.", + ) + n = get_parameter(M.size)[1] if vector_at === nothing - Ω = rand_hamiltonian(M; frobenius_norm=hamiltonian_norm) - return (I - Ω) \ (I + Ω) + rand!(rng, HamiltonianMatrices(2n), pX; σ=σ) + pX .= (I - pX) \ (I + pX) + return pX else - random_vector(M, vector_at; symmetric_norm=hamiltonian_norm) + random_vector!(M, pX, vector_at; σ=σ) + return pX end end -function random_vector(M::Symplectic, p::AbstractMatrix; symmetric_norm=1.0) +function random_vector!(M::SymplecticMatrices, X, p; σ=1.0) n = get_parameter(M.size)[1] # Generate random symmetric matrix: - S = randn(2n, 2n) - S .= (S + S') - S *= symmetric_norm / norm(S) - Q = SymplecticMatrix(p) - lmul!(Q, S) - return p * S -end - -function rand_hamiltonian(M::Symplectic; frobenius_norm=1.0) - n = get_parameter(M.size)[1] - A = randn(n, n) - B = randn(n, n) - C = randn(n, n) - B = (1 / 2) .* (B .+ B') - C = (1 / 2) .* (C .+ C') - Ω = [A B; C -A'] - return frobenius_norm * Ω / norm(Ω, 2) + randn!(X) + X .= 0.5 * (X + X') + X .*= σ / norm(X) + lmul!(SymplecticElement(p), X) + X .= p * X + return X end @doc raw""" - retract(::Symplectic, p, X, ::CayleyRetraction) - retract!(::Symplectic, q, p, X, ::CayleyRetraction) + retract(::SymplecticMatrices, p, X, ::CayleyRetraction) + retract!(::SymplecticMatrices, q, p, X, ::CayleyRetraction) -Compute the Cayley retraction on ``p ∈ \operatorname{Sp}(2n, ℝ)`` in -the direction of tangent vector ``X ∈ T_p\operatorname{Sp}(2n, ℝ)``, +Compute the Cayley retraction on ``p ∈ \mathrm{Sp}(2n, ℝ)`` in +the direction of tangent vector ``X ∈ T_p\mathrm{Sp}(2n, ℝ)``, as defined in by Birtea et al in proposition 2 [BirteaCaşuComănescu:2020](@cite). -Using the symplectic inverse of a matrix ``A \in ℝ^{2n \times 2n}``, -`` -A^{+} := Q_{2n}^{\mathrm{T}} A^{\mathrm{T}} Q_{2n} -`` -where -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}, -```` -the retraction -``\mathcal{R}\colon T\operatorname{Sp}(2n) \rightarrow \operatorname{Sp}(2n)`` +Using the [`symplectic_inverse`](@ref) ``A^+`` of a matrix ``A \in ℝ^{2n×2n}`` +the retraction ``\mathcal{R}: T\mathrm{Sp}(2n) → \mathrm{Sp}(2n)`` is defined pointwise as ````math \begin{align*} @@ -763,9 +767,9 @@ Here ``\operatorname{exp}_{1/1}(z) = (2 - z)^{-1}(2 + z)`` denotes the Padé (1, 1) approximation to ``\operatorname{exp}(z)``. """ -retract(M::Symplectic, p, X) +retract(M::SymplecticMatrices, p, X) -function retract_cayley!(M::Symplectic, q, p, X, t::Number) +function retract_cayley!(M::SymplecticMatrices, q, p, X, t::Number) p_star_X = symplectic_inverse_times(M, p, t * X) divisor = lu(2 * I - p_star_X) @@ -773,31 +777,56 @@ function retract_cayley!(M::Symplectic, q, p, X, t::Number) return q end -function Base.show(io::IO, ::Symplectic{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "Symplectic($(2n), $(𝔽))") +@doc raw""" + riemannian_gradient(M::SymplecticMatrices, p, Y) + +Given a gradient ``Y = \operatorname{grad} \tilde f(p)`` in the embedding ``ℝ^{2n×2n}`` or at +least around the [`SymplecticMatrices`](@ref) `M` where `p` (the embedding of) a point on `M`, +we restrict ``\tilde f`` to the manifold and denote that by ``f``. +Then the Riemannian gradient ``X = \operatorname{grad} f(p)`` is given by + +```math + X = Yp^{\mathrm{T}}p + J_{2n}pY^{\mathrm{T}}J_{2n}p, +``` + +where ``J_{2n}`` denotes the [`SymplecticElement`](@ref). +""" +function riemannian_gradient(::SymplecticMatrices, p, Y; kwargs...) + J = SymplecticElement(p) + return Y * p' * p .+ (J * p) * Y' * (J * p) +end + +function riemannian_gradient!(M::SymplecticMatrices, X, p, Y; kwargs...) + J = SymplecticElement(p, X) + X .= Y * p'p .+ (J * p) * Y' * (J * p) + return X +end + +function Base.show(io::IO, ::SymplecticMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "SymplecticMatrices($(2n), $(𝔽))") end -function Base.show(io::IO, M::Symplectic{Tuple{Int},𝔽}) where {𝔽} +function Base.show(io::IO, M::SymplecticMatrices{Tuple{Int},𝔽}) where {𝔽} n = get_parameter(M.size)[1] - return print(io, "Symplectic($(2n), $(𝔽); parameter=:field)") + return print(io, "SymplecticMatrices($(2n), $(𝔽); parameter=:field)") end @doc raw""" - symplectic_inverse_times(::Symplectic, p, q) - symplectic_inverse_times!(::Symplectic, A, p, q) + symplectic_inverse_times(::SymplecticMatrices, p, q) + symplectic_inverse_times!(::SymplecticMatrices, A, p, q) -Directly compute the symplectic inverse of ``p \in \operatorname{Sp}(2n)``, -multiplied with ``q \in \operatorname{Sp}(2n)``. +Directly compute the symplectic inverse of ``p \in \mathrm{Sp}(2n)``, +multiplied with ``q \in \mathrm{Sp}(2n)``. That is, this function efficiently computes -``p^+q = (Q_{2n}p^{\mathrm{T}}Q_{2n})q \in ℝ^{2n \times 2n}``, -where ``Q_{2n}`` is the [`SymplecticMatrix`](@ref) -of size ``2n \times 2n``. +``p^+q = (J_{2n}p^{\mathrm{T}}J_{2n})q ∈ ℝ^{2n×2n}``, +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + """ -function symplectic_inverse_times(M::Symplectic, p, q) +function symplectic_inverse_times(M::SymplecticMatrices, p, q) A = similar(p) return symplectic_inverse_times!(M, A, p, q) end -function symplectic_inverse_times!(M::Symplectic, A, p, q) +function symplectic_inverse_times!(M::SymplecticMatrices, A, p, q) n = get_parameter(M.size)[1] # we write p = [p1 p2; p3 p4] (and q, too), then p1 = @view(p[1:n, 1:n]) @@ -823,46 +852,46 @@ function symplectic_inverse_times!(M::Symplectic, A, p, q) return A end -ndims(Q::SymplecticMatrix) = 2 -copy(Q::SymplecticMatrix) = SymplecticMatrix(copy(Q.λ)) -Base.eltype(::SymplecticMatrix{T}) where {T} = T -function Base.convert(::Type{SymplecticMatrix{T}}, Q::SymplecticMatrix) where {T} - return SymplecticMatrix(convert(T, Q.λ)) +ndims(J::SymplecticElement) = 2 +copy(J::SymplecticElement) = SymplecticElement(copy(J.λ)) +Base.eltype(::SymplecticElement{T}) where {T} = T +function Base.convert(::Type{SymplecticElement{T}}, J::SymplecticElement) where {T} + return SymplecticElement(convert(T, J.λ)) end -function Base.show(io::IO, Q::SymplecticMatrix) - s = "$(Q.λ)" +function Base.show(io::IO, J::SymplecticElement) + s = "$(J.λ)" if occursin(r"\w+\s*[\+\-]\s*\w+", s) s = "($s)" end - return print(io, typeof(Q), "(): $(s)*[0 I; -I 0]") + return print(io, typeof(J), "(): $(s)*[0 I; -I 0]") end -(Base.:-)(Q::SymplecticMatrix) = SymplecticMatrix(-Q.λ) +(Base.:-)(J::SymplecticElement) = SymplecticElement(-J.λ) -function (Base.:^)(Q::SymplecticMatrix, n::Integer) +function (Base.:^)(J::SymplecticElement, n::Integer) return ifelse( n % 2 == 0, - UniformScaling((-1)^(div(n, 2)) * (Q.λ)^n), - SymplecticMatrix((-1)^(div(n - 1, 2)) * (Q.λ)^n), + UniformScaling((-1)^(div(n, 2)) * (J.λ)^n), + SymplecticElement((-1)^(div(n - 1, 2)) * (J.λ)^n), ) end -(Base.:*)(x::Number, Q::SymplecticMatrix) = SymplecticMatrix(x * Q.λ) -(Base.:*)(Q::SymplecticMatrix, x::Number) = SymplecticMatrix(x * Q.λ) -function (Base.:*)(Q1::SymplecticMatrix, Q2::SymplecticMatrix) - return LinearAlgebra.UniformScaling(-Q1.λ * Q2.λ) +(Base.:*)(x::Number, J::SymplecticElement) = SymplecticElement(x * J.λ) +(Base.:*)(J::SymplecticElement, x::Number) = SymplecticElement(x * J.λ) +function (Base.:*)(J::SymplecticElement, K::SymplecticElement) + return LinearAlgebra.UniformScaling(-J.λ * K.λ) end -Base.transpose(Q::SymplecticMatrix) = -Q -Base.adjoint(Q::SymplecticMatrix) = SymplecticMatrix(-conj(Q.λ)) -Base.inv(Q::SymplecticMatrix) = SymplecticMatrix(-(1 / Q.λ)) +Base.transpose(J::SymplecticElement) = -J +Base.adjoint(J::SymplecticElement) = SymplecticElement(-conj(J.λ)) +Base.inv(J::SymplecticElement) = SymplecticElement(-(1 / J.λ)) -(Base.:+)(Q1::SymplecticMatrix, Q2::SymplecticMatrix) = SymplecticMatrix(Q1.λ + Q2.λ) -(Base.:-)(Q1::SymplecticMatrix, Q2::SymplecticMatrix) = SymplecticMatrix(Q1.λ - Q2.λ) +(Base.:+)(J::SymplecticElement, K::SymplecticElement) = SymplecticElement(J.λ + K.λ) +(Base.:-)(J::SymplecticElement, K::SymplecticElement) = SymplecticElement(J.λ - K.λ) -(Base.:+)(Q::SymplecticMatrix, p::AbstractMatrix) = p + Q -function (Base.:+)(p::AbstractMatrix, Q::SymplecticMatrix) +(Base.:+)(J::SymplecticElement, p::AbstractMatrix) = p + J +function (Base.:+)(p::AbstractMatrix, J::SymplecticElement) # When we are adding, the Matrices must match in size: two_n, two_k = size(p) if (two_n % 2 != 0) || (two_n != two_k) @@ -876,19 +905,19 @@ function (Base.:+)(p::AbstractMatrix, Q::SymplecticMatrix) n = div(two_n, 2) # Allocate new memory: - TS = typeof(one(eltype(p)) + one(eltype(Q))) + TS = typeof(one(eltype(p)) + one(eltype(J))) out = copyto!(similar(p, TS), p) - add_scaled_I!(view(out, 1:n, (n + 1):(2n)), Q.λ) - add_scaled_I!(view(out, (n + 1):(2n), 1:n), -Q.λ) + add_scaled_I!(view(out, 1:n, (n + 1):(2n)), J.λ) + add_scaled_I!(view(out, (n + 1):(2n), 1:n), -J.λ) return out end # Binary minus: -(Base.:-)(Q::SymplecticMatrix, p::AbstractMatrix) = Q + (-p) -(Base.:-)(p::AbstractMatrix, Q::SymplecticMatrix) = p + (-Q) +(Base.:-)(J::SymplecticElement, p::AbstractMatrix) = J + (-p) +(Base.:-)(p::AbstractMatrix, J::SymplecticElement) = p + (-J) -function (Base.:*)(Q::SymplecticMatrix, p::AbstractVecOrMat) +function (Base.:*)(J::SymplecticElement, p::AbstractVecOrMat) two_n = size(p)[1] if two_n % 2 != 0 throw(ArgumentError("'p' must have even row dimension, was: $(two_n) != 2n.")) @@ -896,17 +925,17 @@ function (Base.:*)(Q::SymplecticMatrix, p::AbstractVecOrMat) n = div(two_n, 2) # Allocate new memory: - TS = typeof(one(eltype(p)) + one(eltype(Q))) - Qp = similar(p, TS) + TS = typeof(one(eltype(p)) + one(eltype(J))) + Jp = similar(p, TS) - # Perform left mulitply by λ*Q: - mul!((@inbounds view(Qp, 1:n, :)), Q.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) - mul!((@inbounds view(Qp, (n + 1):lastindex(Qp, 1), :)), -Q.λ, @inbounds view(p, 1:n, :)) + # Perform left mulitply by λ*J: + mul!((@inbounds view(Jp, 1:n, :)), J.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) + mul!((@inbounds view(Jp, (n + 1):lastindex(Jp, 1), :)), -J.λ, @inbounds view(p, 1:n, :)) - return Qp + return Jp end -function (Base.:*)(p::AbstractMatrix, Q::SymplecticMatrix) +function (Base.:*)(p::AbstractMatrix, J::SymplecticElement) two_k = size(p)[2] if two_k % 2 != 0 throw(ArgumentError("'p' must have even column dimension, was: $(two_k) != 2k.")) @@ -914,16 +943,16 @@ function (Base.:*)(p::AbstractMatrix, Q::SymplecticMatrix) k = div(two_k, 2) # Allocate new memory: - TS = typeof(one(eltype(p)) + one(eltype(Q))) - pQ = similar(p, TS) + TS = typeof(one(eltype(p)) + one(eltype(J))) + pJ = similar(p, TS) - # Perform right mulitply by λ*Q: - mul!((@inbounds view(pQ, :, 1:k)), -Q.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) - mul!((@inbounds view(pQ, :, (k + 1):lastindex(pQ, 2))), Q.λ, @inbounds view(p, :, 1:k)) - return pQ + # Perform right mulitply by λ*J: + mul!((@inbounds view(pJ, :, 1:k)), -J.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) + mul!((@inbounds view(pJ, :, (k + 1):lastindex(pJ, 2))), J.λ, @inbounds view(p, :, 1:k)) + return pJ end -function LinearAlgebra.lmul!(Q::SymplecticMatrix, p::AbstractVecOrMat) +function LinearAlgebra.lmul!(J::SymplecticElement, p::AbstractVecOrMat) # Perform left multiplication by a symplectic matrix, # overwriting the matrix p in place: two_n = size(p)[1] @@ -934,17 +963,17 @@ function LinearAlgebra.lmul!(Q::SymplecticMatrix, p::AbstractVecOrMat) half_row_p = copy(@inbounds view(p, 1:n, :)) - mul!((@inbounds view(p, 1:n, :)), Q.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) + mul!((@inbounds view(p, 1:n, :)), J.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) mul!( (@inbounds view(p, (n + 1):lastindex(p, 1), :)), - -Q.λ, + -J.λ, @inbounds view(half_row_p, :, :) ) return p end -function LinearAlgebra.rmul!(p::AbstractMatrix, Q::SymplecticMatrix) +function LinearAlgebra.rmul!(p::AbstractMatrix, J::SymplecticElement) # Perform right multiplication by a symplectic matrix, # overwriting the matrix p in place: two_k = size(p)[2] @@ -955,18 +984,18 @@ function LinearAlgebra.rmul!(p::AbstractMatrix, Q::SymplecticMatrix) half_col_p = copy(@inbounds view(p, :, 1:k)) - mul!((@inbounds view(p, :, 1:k)), -Q.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) + mul!((@inbounds view(p, :, 1:k)), -J.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) mul!( (@inbounds view(p, :, (k + 1):lastindex(p, 2))), - Q.λ, + J.λ, @inbounds view(half_col_p, :, :) ) return p end -function LinearAlgebra.mul!(A::AbstractVecOrMat, Q::SymplecticMatrix, p::AbstractVecOrMat) +function LinearAlgebra.mul!(A::AbstractVecOrMat, J::SymplecticElement, p::AbstractVecOrMat) size_p = size(p) two_n = size_p[1] if two_n % 2 != 0 @@ -978,13 +1007,13 @@ function LinearAlgebra.mul!(A::AbstractVecOrMat, Q::SymplecticMatrix, p::Abstrac # k == 0 means we're multiplying with a vector: @boundscheck k == 0 ? checkbounds(A, 1:(2n), 1) : checkbounds(A, 1:(2n), 1:(2k)) - # Perform left multiply by λ*Q: - mul!((@inbounds view(A, 1:n, :)), Q.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) - mul!((@inbounds view(A, (n + 1):lastindex(A, 1), :)), -Q.λ, @inbounds view(p, 1:n, :)) + # Perform left multiply by λ*J: + mul!((@inbounds view(A, 1:n, :)), J.λ, @inbounds view(p, (n + 1):lastindex(p, 1), :)) + mul!((@inbounds view(A, (n + 1):lastindex(A, 1), :)), -J.λ, @inbounds view(p, 1:n, :)) return A end -function LinearAlgebra.mul!(A::AbstractVecOrMat, p::AbstractMatrix, Q::SymplecticMatrix) +function LinearAlgebra.mul!(A::AbstractVecOrMat, p::AbstractMatrix, J::SymplecticElement) two_n, two_k = size(p) if two_k % 2 != 0 throw(ArgumentError("'p' must have even col dimension, was: $(two_k) != 2k.")) @@ -995,9 +1024,9 @@ function LinearAlgebra.mul!(A::AbstractVecOrMat, p::AbstractMatrix, Q::Symplecti # n == 0 means we're multiplying with a vector: @boundscheck n == 0 ? checkbounds(A, 1, 1:(2k)) : checkbounds(A, 1:(2n), 1:(2k)) - # Perform right multiply by λ*Q: - mul!((@inbounds view(A, :, 1:k)), -Q.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) - mul!((@inbounds view(A, :, (k + 1):lastindex(A, 2))), Q.λ, @inbounds view(p, :, 1:k)) + # Perform right multiply by λ*J: + mul!((@inbounds view(A, :, 1:k)), -J.λ, @inbounds view(p, :, (k + 1):lastindex(p, 2))) + mul!((@inbounds view(A, :, (k + 1):lastindex(A, 2))), J.λ, @inbounds view(p, :, 1:k)) return A end diff --git a/src/manifolds/SymplecticGrassmann.jl b/src/manifolds/SymplecticGrassmann.jl new file mode 100644 index 0000000000..2438a616cc --- /dev/null +++ b/src/manifolds/SymplecticGrassmann.jl @@ -0,0 +1,120 @@ +@doc raw""" + SymplecticGrassmann{T,𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} + +The symplectic Grassmann manifold consists of all symplectic subspaces of +``ℝ^{2n}`` of dimension ``2k``, ``n ≥ k``. + +Points on this manifold can be represented as corresponding representers on the [`SymplecticStiefel`](@ref) + +```math +\operatorname{SpGr}(2n,2k) = \bigl\{ \operatorname{span}(p)\ \big| \ p ∈ \operatorname{SpSt}(2n, 2k, ℝ)\}, +``` + +or as projectors + +```math +\operatorname{SpGr}(2n, 2k, ℝ) = \bigl\{ p ∈ ℝ^{2n×2n} \ \big| \ p^2 = p, \operatorname{rank}(p) = 2k, p^+=p \bigr\}, +``` + +where ``⋅^+`` is the [`symplectic_inverse`](@ref). +See also [`ProjectorPoint`](@ref) and [`StiefelPoint`](@ref) for these two representations, +where arrays are interpreted as those on the Stiefel manifold. + +With respect to the quotient structure, the canonical projection ``π = π_{\mathrm{SpSt},\mathrm{SpGr}}`` is given by + +```math +π: \mathrm{SpSt}(2n2k) → \mathrm{SpGr}(2n,2k), p ↦ π(p) = pp^+. +``` + +The tangent space is either the tangent space from the symplectic Stiefel manifold, where +tangent vectors are representers of their corresponding congruence classes, or for the +representation as projectors, using a [`ProjectorTVector`](@ref) as + +```math + T_p\operatorname{SpGr}(2n, 2k, ℝ) = + \bigl\{ [X,p] \ \mid\ X ∈ \mathfrak{sp}(2n,ℝ), Xp+pX = X \bigr\}, +``` + +where ``[X,p] = Xp-pX`` denotes the matrix commutator and +``\mathfrak{sp}(2n,ℝ)`` is the Lie algebra of the symplectic group consisting of [`HamiltonianMatrices`](@ref). + +The first repesentation is in [`StiefelPoint`](@ref)s and [`StiefelTVector`](@ref)s, +which both represent their symplectic Grassmann equivalence class. Arrays are interpreted +in this representation as well + +For the representation in [`ProjectorPoint`](@ref) and [`ProjectorTVector`](@ref)s, +we use the representation from the surjective submersion + +```math +ρ: \mathrm{SpSt}(2n,2k) → \mathrm{SpGr}(2n,2k), +\qquad +ρ(p) = pp^+ +``` + +and its differential + +```math +\mathrm{d}ρ(p,X) = Xp^+ + pX^+, +``` + +respectively. +The manifold was first introduced in [BendokatZimmermann:2021](@cite) + +# Constructor + + SymplecticGrassmann(2n::Int, 2k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) + +Generate the (real-valued) symplectic Grassmann manifold. +of ``2k`` dimensional symplectic subspace of ``ℝ^{2n}``. +Note that both dimensions passed to this constructor have to be even. +""" +struct SymplecticGrassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end + +function SymplecticGrassmann( + two_n::Int, + two_k::Int, + field::AbstractNumbers=ℝ; + parameter::Symbol=:type, +) + size = wrap_type_parameter(parameter, (div(two_n, 2), div(two_k, 2))) + return SymplecticGrassmann{typeof(size),field}(size) +end + +function active_traits(f, ::SymplecticGrassmann, args...) + return merge_traits(IsEmbeddedManifold(), IsQuotientManifold()) +end + +# Define Stiefel as the array fallback +ManifoldsBase.@default_manifold_fallbacks SymplecticGrassmann StiefelPoint StiefelTVector value value + +@doc raw""" + manifold_dimension(::SymplecticGrassmann) + +Return the dimension of the [`SymplecticGrassmann`](@ref)`(2n,2k)`, which is + +````math +\operatorname{dim}\operatorname{SpGr}(2n, 2k) = 4(n-k)k, +```` + +see [BendokatZimmermann:2021](@cite), Section 4. +""" +function manifold_dimension(M::SymplecticGrassmann{<:Any,ℝ}) + n, k = get_parameter(M.size) + return 4 * (n - k) * k +end + +function Base.show(io::IO, ::SymplecticGrassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return print(io, "SymplecticGrassmann($(2n), $(2k); field=$(𝔽))") +end +function Base.show(io::IO, M::SymplecticGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_parameter(M.size) + return print(io, "SymplecticGrassmann($(2n), $(2k); field=$(𝔽); parameter=:field)") +end + +# +# Representer specific implementations in their corrsponding subfiles +# +include("SymplecticGrassmannStiefel.jl") +include("SymplecticGrassmannProjector.jl") diff --git a/src/manifolds/SymplecticGrassmannProjector.jl b/src/manifolds/SymplecticGrassmannProjector.jl new file mode 100644 index 0000000000..8a9235a3de --- /dev/null +++ b/src/manifolds/SymplecticGrassmannProjector.jl @@ -0,0 +1,91 @@ +@doc raw""" + check_point(M::SymplecticGrassmann, p::ProjectorPoint; kwargs...) + +Check whether `p` is a valid point on the [`SymplecticGrassmann`](@ref), +``\operatorname{SpGr}(2n, 2k)``, that is a propoer symplectic projection: + +* ``p^2 = p``, that is ``p`` is a projection +* ``\operatorname{rank}(p) = 2k``, that is, the supspace projected onto is of right dimension +* ``p^+ = p`` the projection is symplectic. +""" +function check_point(M::SymplecticGrassmann, p::ProjectorPoint; kwargs...) + n, k = get_parameter(M.size) + c = p.value * p.value + if !isapprox(c, p.value; kwargs...) + return DomainError( + norm(c - p.value), + "The poin $(p) is not equal to its square $c, so it does not lie on $M.", + ) + end + if !isapprox(p.value, symplectic_inverse(p.value); kwargs...) + return DomainError( + norm(p.value - symplectic_inverse(p.value)), + "The point $(p) is not equal to its symplectic inverse p^+, so it does not lie on $M.", + ) + end + k2 = rank(p.value; kwargs...) + if k2 != 2 * k + return DomainError( + k2, + "The point $(p) is a projector of rank $k2 and not of rank $(2*k), so it does not lie on $(M).", + ) + end + return nothing +end + +@doc raw""" + check_vector(M::SymplecticGrassmann, p::ProjectorPoint, X::ProjectorTVector; kwargs...) + +Check whether `X` is a valid tangent vector at `p` on the [`SymplecticGrassmann`](@ref), +``\operatorname{SpGr}(2n, 2k)`` manifold by verifying that it + +* ``X^+ = X`` +* ``X = Xp + pX`` + +For details see Proposition 4.2 in [BendokatZimmermann:2021](@cite) and the definition of ``\mathfrak{sp}_P(2n)`` before, +especially the ``\bar{Ω}``, which is the representation for ``X`` used here. +""" +function check_vector( + M::SymplecticGrassmann, + p::ProjectorPoint, + X::ProjectorTVector; + kwargs..., +) + n, k = get_parameter(M.size) + if !isapprox((Hamiltonian(X.value)^+).value, X.value; kwargs...) + return DomainError( + norm((Hamiltonian(X.value)^+).value - X.value), + ( + "The matrix X is not in the tangent space at $p of $M, since X is not equal to ist symplectic inverse." + ), + ) + end + XppX = X.value * p.value .+ p.value * X.value + if !isapprox(X.value, XppX; kwargs...) + return DomainError( + norm(XppX - X.value), + ( + "The matrix X is not in the tangent space at $p of $M, since X is not equal to Xp + pX." + ), + ) + end + return nothing +end + +embed!(::SymplecticGrassmann, q, p::ProjectorPoint) = copyto!(q, p.value) +function embed!(::SymplecticGrassmann, Y, p::ProjectorPoint, X::ProjectorTVector) + return copyto!(Y, X.value) +end +embed(::SymplecticGrassmann, p::ProjectorPoint) = p.value +embed(::SymplecticGrassmann, p::ProjectorPoint, X::ProjectorTVector) = X.value + +function get_embedding( + ::SymplecticGrassmann{TypeParameter{Tuple{n,k}},𝔽}, + p::ProjectorPoint, +) where {n,k,𝔽} + return Euclidean(2n, 2n; field=𝔽) +end +function get_embedding(M::SymplecticGrassmann{Tuple{Int,Int},𝔽}, ::ProjectorPoint) where {𝔽} + n, _ = get_parameter(M.size) + return Euclidean(2n, 2n; field=𝔽, parameter=:field) +end diff --git a/src/manifolds/SymplecticGrassmannStiefel.jl b/src/manifolds/SymplecticGrassmannStiefel.jl new file mode 100644 index 0000000000..c667ff781a --- /dev/null +++ b/src/manifolds/SymplecticGrassmannStiefel.jl @@ -0,0 +1,159 @@ +@doc raw""" + check_point(M::SymplecticGrassmann, p; kwargs...) + +Check whether `p` is a valid point on the [`SymplecticGrassmann`](@ref), +``\operatorname{SpGr}(2n, 2k)`` manifold by verifying that it +is a valid representer of an equivalence class of the corersponding +[`SymplecticStiefel`](@ref) manifold. +""" +function check_point(M::SymplecticGrassmann, p; kwargs...) + n, k = get_parameter(M.size) + return check_point(SymplecticStiefel(2 * n, 2 * k), p; kwargs...) +end + +@doc raw""" + check_vector(M::SymplecticGrassmann, p, X; kwargs...) + +Check whether `X` is a valid tangent vector at `p` on the [`SymplecticGrassmann`](@ref), +``\operatorname{SpGr}(2n, 2k)`` manifold by verifying that it +is a valid representer of an equivalence class of the corersponding +[`SymplecticStiefel`](@ref) manifolds tangent space at `p`. +""" +function check_vector(M::SymplecticGrassmann, p, X; kwargs...) + n, k = get_parameter(M.size) + return check_vector(SymplecticStiefel(2 * n, 2 * k), p, X; kwargs...) +end + +embed(::SymplecticGrassmann, p) = p +embed(::SymplecticGrassmann, p, X) = X +embed!(::SymplecticGrassmann, q, p) = copyto!(q, p) +embed!(::SymplecticGrassmann, Y, p, X) = copyto!(Y, X) +embed!(::SymplecticGrassmann, q, p::StiefelPoint) = copyto!(q, p.value) +embed!(::SymplecticGrassmann, Y, p::StiefelPoint, X::StiefelTVector) = copyto!(Y, X.value) +embed(::SymplecticGrassmann, p::StiefelPoint) = p.value +embed(::SymplecticGrassmann, p::StiefelPoint, X::StiefelTVector) = X.value + +@doc raw""" + exp(::SymplecticGrassmann, p, X) + exp!(M::SymplecticGrassmann, q, p, X) + +Compute the exponential mapping + +```math + \exp\colon T\mathrm{SpGr}(2n, 2k) → \mathrm{SpGr}(2n, 2k) +``` + +when representing points and tangent vectors as symplectic bases and their tangents, i.e. +on the [`SymplecticStiefel`](@ref) manifold. Then we can just pass this on to [`exp(::SymplecticStiefel, p, X)`](@ref). +""" +exp(::SymplecticGrassmann, p, X) + +function exp!(M::SymplecticGrassmann, q, p, X) + n, k = get_parameter(M.size) + exp!(SymplecticStiefel(2 * n, 2 * k), q, p, X) + return q +end + +function get_embedding(::SymplecticGrassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return SymplecticStiefel(2n, 2k, 𝔽) +end +function get_embedding(M::SymplecticGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_parameter(M.size) + return SymplecticStiefel(2n, 2k, 𝔽; parameter=:field) +end + +@doc raw""" + inner(::SymplecticGrassmann, p, X, Y) + +Compute the Riemannian inner product ``g^{\mathrm{SpGr}}_p(X,Y)`` +on the [`SymplecticGrassmann`](@ref) manifold `\mathrm{SpGr}``. + +For the case where ``p`` is represented by a point on the [`SymplecticStiefel`](@ref) manifold +acting as a representant of its equivalence class ``[p] \in \mathrm{SpGr}`` +and the tangent vectors ``X,Y \in \mathrm{Hor}_p^π\operatorname{SpSt}(2n,2k)`` +are horizontal tangent vectors. + +Then the inner product reads according to Proposition Lemma 4.8 [BendokatZimmermann:2021](@cite). + +```math +g^{\mathrm{SpGr}}_p(X,Y) = \operatorname{tr}\bigl( + (p^{\mathrm{T}}p)^{-1}X^{\mathrm{T}}(I_{2n} - pp^+)Y + \bigr), +``` + +where ``I_{2n}`` denotes the identity matrix and ``(⋅)^+`` the [`symplectic_inverse`](@ref). +""" +function inner(M::SymplecticGrassmann, p, X, Y) + n, k = get_parameter(M.size) + J = SymplecticElement(p, X, Y) # in BZ21 also J + # Procompute lu(p'p) since we solve a^{-1}* 3 times + a = lu(p' * p) # note that p'p is symmetric, thus so is its inverse c=a^{-1} + # we split the original trace into two one with I -> (X'Yc) + # 1) we permute X' and Y c to c^{\mathrm{T}}Y^{\mathrm{T}}X = a\(Y'X) (avoids a large interims matrix) + # 2) the second we compute as c (X'p)(p^+Y) since both brackets are the smaller matrices + return tr(a \ (Y' * X)) - tr( + a \ ((X' * p) * symplectic_inverse_times(SymplecticStiefel(2 * n, 2 * k), p, Y)), + ) +end + +@doc raw""" + inverse_retract(::SymplecticGrassmann, p, q, ::CayleyInverseRetraction) + inverse_retract!(::SymplecticGrassmann, q, p, X, ::CayleyInverseRetraction) + +Compute the Cayley Inverse Retraction on the Symplectic Grassmann manifold, +when the points are represented as symplectic bases, i.e. on the [`SymplecticStiefel`](@ref). + +Here we can directly employ the `CaleyInverseRetraction` on the symplectic Stiefel manifold. +""" +inverse_retract(::SymplecticGrassmann, p, q, ::CayleyInverseRetraction) + +function inverse_retract_cayley!(M::SymplecticGrassmann, X, p, q) + n, k = get_parameter(M.size) + return inverse_retract_cayley!(SymplecticStiefel(2 * n, 2 * k), X, p, q) +end + +@doc raw""" + retract(::SymplecticGrassmann, p, X, ::CayleyRetraction) + retract!(::SymplecticGrassmann, q, p, X, ::CayleyRetraction) + +Compute the Cayley retraction on the Symplectic Grassmann manifold, +when the points are represented as symplectic bases, i.e. on the [`SymplecticStiefel`](@ref). + +Here we can directly employ the `CaleyRetraction` on the symplectic Stiefel manifold. +""" +retract(::SymplecticGrassmann, p, X, ::CayleyRetraction) + +function retract_cayley!(M::SymplecticGrassmann, q, p, X, t::Number) + n, k = get_parameter(M.size) + retract_cayley!(SymplecticStiefel(2 * n, 2 * k), q, p, X, t) + return q +end + +@doc raw""" + riemannian_gradient(M::SymplecticGrassmann, p, Y) + +Given a gradient ``Y = \operatorname{grad} \tilde f(p)`` in the embedding ``ℝ^{2n×2k}`` or at +least around the [`SymplecticGrassmann`](@ref) `M` where `p` (the embedding of) a point on `M`, +and the restriction ``\tilde f`` to the [`SymplecticStiefel`](@ref) be invariant for the equivalence classes. +In other words ``f(p) = f(qp)`` for ``q \in \mathrm{Sp}(2k, ℝ)``, +where ``\mathrm{Sp}(2k, ℝ)`` denotes the [`SymplecticMatrices`](@ref) manifold. +Then the Riemannian gradient ``X = \operatorname{grad} f(p)`` is given by + +```math + X = J_{2n}^THJ_{2k}p^{\mathrm{T}}p - J_{2n}^TpJ_{2k}H^{\mathrm{T}}p, +``` + +where ``J_{2n}`` denotes the [`SymplecticElement`](@ref), and +``H = (I_{2n} - pp^+)J_{2n}^{\mathrm{T}}YJ``. +""" +riemannian_gradient(M::SymplecticGrassmann, p, Y; kwargs...) + +function riemannian_gradient!(M::SymplecticGrassmann, X, p, Y; kwargs...) + n, k = get_parameter(M.size) + J = SymplecticElement(p, X) + # Since J' = -J We can write (J'YJ) = -J * (YJ) + JTYJ = (-J * (Y * J)) + H = (I - p * symplectic_inverse(p)) * JTYJ + X .= (-J * (H * J)) * (p' * p) .- JTYJ * (H' * p) + return X +end diff --git a/src/manifolds/SymplecticStiefel.jl b/src/manifolds/SymplecticStiefel.jl index 7188e0dafe..33ea6776b5 100644 --- a/src/manifolds/SymplecticStiefel.jl +++ b/src/manifolds/SymplecticStiefel.jl @@ -2,41 +2,46 @@ SymplecticStiefel{T,𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} The symplectic Stiefel manifold consists of all -$2n × 2k, \; n \geq k$ matrices satisfying the requirement +``2n×2k, n ≥ k`` matrices satisfying the requirement + ````math -\operatorname{SpSt}(2n, 2k, ℝ) - = \bigl\{ p ∈ ℝ^{2n × 2n} \, \big| \, p^{\mathrm{T}}Q_{2n}p = Q_{2k} \bigr\}, +\mathrm{SpSt}(2n, 2k, ℝ) + := \bigl\{ p ∈ ℝ^{2n×2n} \ \big| \ p^{\mathrm{T}}J_{2n}p = J_{2k} \bigr\}, ```` -where + +where ``J_{2n}`` denotes the [`SymplecticElement`](@ref) + ````math -Q_{2n} = -\begin{bmatrix} - 0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. +J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}. ```` The symplectic Stiefel tangent space at ``p`` can be parametrized as [BendokatZimmermann:2021](@cite) -````math - \begin{align*} - T_p\operatorname{SpSt}(2n, 2k) - = \{&X \in \mathbb{R}^{2n \times 2k} \;|\; p^{T}Q_{2n}X + X^{T}Q_{2n}p = 0 \}, \\ - = \{&X = pΩ + p^sB \;|\; - Ω ∈ ℝ^{2k × 2k}, Ω^+ = -Ω, \\ - &\; p^s ∈ \operatorname{SpSt}(2n, 2(n- k)), B ∈ ℝ^{2(n-k) × 2k}, \}, - \end{align*} -```` -where ``Ω \in \mathfrak{sp}(2n,F)`` is Hamiltonian and ``p^s`` means + +```math +\begin{align*} + T_p\mathrm{SpSt}(2n, 2k) + &= \{X ∈ ℝ^{2n×2k} ∣ p^{T}J_{2n}X + X^{T}J_{2n}p = 0 \}, \\ + &= \{X = pΩ + p^sB \mid + Ω ∈ ℝ^{2k×2k}, Ω^+ = -Ω, \\ + &\qquad & p^s ∈ \mathrm{SpSt}(2n, 2(n- k)), B ∈ ℝ^{2(n-k)×2k}, \}, +\end{align*} +``` + +where ``Ω ∈ \mathfrak{sp}(2n,F)`` is [`Hamiltonian`](@ref) and ``p^s`` means the symplectic complement of ``p`` s.t. ``p^{+}p^{s} = 0``. +Here ``p^+`` denotes the [`symplectic_inverse`](@ref). + +You can also use [`StiefelPoint`](@ref) and [`StiefelTVector`](@ref) with this manifold, +they are equivalent to using arrays. # Constructor SymplecticStiefel(2n::Int, 2k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) -Generate the (real-valued) symplectic Stiefel manifold of ``2n \times 2k`` -matrices which span a ``2k`` dimensional symplectic subspace of ``ℝ^{2n \times 2n}``. +Generate the (real-valued) symplectic Stiefel manifold of ``2n×2k`` +matrices which span a ``2k`` dimensional symplectic subspace of ``ℝ^{2n×2n}``. The constructor for the [`SymplecticStiefel`](@ref) manifold accepts the even column dimension ``2n`` and an even number of columns ``2k`` for -the real symplectic Stiefel manifold with elements ``p \in ℝ^{2n × 2k}``. +the real symplectic Stiefel manifold with elements ``p ∈ ℝ^{2n×2k}``. """ struct SymplecticStiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T @@ -48,6 +53,16 @@ function SymplecticStiefel( field::AbstractNumbers=ℝ; parameter::Symbol=:type, ) + two_n % 2 == 0 || throw( + ArgumentError( + "The first matrix size of the symplectic Stiefel manifold must be even, but was $(two_n).", + ), + ) + two_k % 2 == 0 || throw( + ArgumentError( + "The second matrix size of the symplectic Stiefel manifold must be even. but was $(two_k).", + ), + ) size = wrap_type_parameter(parameter, (div(two_n, 2), div(two_k, 2))) return SymplecticStiefel{typeof(size),field}(size) end @@ -56,6 +71,9 @@ function active_traits(f, ::SymplecticStiefel, args...) return merge_traits(IsEmbeddedManifold(), IsDefaultMetric(RealSymplecticMetric())) end +# Define Stiefel as the array fallback +ManifoldsBase.@default_manifold_fallbacks SymplecticStiefel StiefelPoint StiefelTVector value value + function ManifoldsBase.default_inverse_retraction_method(::SymplecticStiefel) return CayleyInverseRetraction() end @@ -66,11 +84,11 @@ ManifoldsBase.default_retraction_method(::SymplecticStiefel) = CayleyRetraction( canonical_project(::SymplecticStiefel, p_Sp) canonical_project!(::SymplecticStiefel, p, p_Sp) -Define the canonical projection from ``\operatorname{Sp}(2n, 2n)`` onto -``\operatorname{SpSt}(2n, 2k)``, by projecting onto the first ``k`` columns +Define the canonical projection from ``\mathrm{Sp}(2n, 2n)`` onto +``\mathrm{SpSt}(2n, 2k)``, by projecting onto the first ``k`` columns and the ``n + 1``'th onto the ``n + k``'th columns [BendokatZimmermann:2021](@cite). -It is assumed that the point ``p`` is on ``\operatorname{Sp}(2n, 2n)``. +It is assumed that the point ``p`` is on ``\mathrm{Sp}(2n, 2n)``. """ function canonical_project(M::SymplecticStiefel, p_Sp) n, k = get_parameter(M.size) @@ -89,31 +107,14 @@ end check_point(M::SymplecticStiefel, p; kwargs...) Check whether `p` is a valid point on the [`SymplecticStiefel`](@ref), -$\operatorname{SpSt}(2n, 2k)$ manifold. -That is, the point has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and $p^{+}p$ is -(approximately) the identity, -where for $A \in \mathbb{R}^{2n \times 2k}$, -$A^{+} = Q_{2k}^{\mathrm{T}}A^{\mathrm{T}}Q_{2n}$ is the symplectic inverse, with -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). +``\mathrm{SpSt}(2n, 2k)`` manifold, that is ``p^{+}p`` is the identity, +``(⋅)^+`` denotes the [`symplectic_inverse`](@ref). """ -function check_point( - M::SymplecticStiefel, - p::T; - atol::Real=sqrt(prod(representation_size(M))) * eps(real(float(number_eltype(T)))), - kwargs..., -) where {T} +function check_point(M::SymplecticStiefel{<:Any,ℝ}, p; kwargs...) # Perform check that the matrix lives on the real symplectic manifold: - expected_zero = norm(inv(M, p) * p - I) - if !isapprox(expected_zero, 0; atol=atol, kwargs...) + if !isapprox(inv(M, p) * p, I; kwargs...) return DomainError( - expected_zero, + norm(inv(M, p) * p - I), ( "The point p does not lie on $(M) because its symplectic" * " inverse composed with itself is not the identity." @@ -124,48 +125,27 @@ function check_point( end @doc raw""" - check_vector(M::Symplectic, p, X; kwargs...) + check_vector(M::SymplecticMatrices, p, X; kwargs...) Checks whether `X` is a valid tangent vector at `p` on the [`SymplecticStiefel`](@ref), -``\operatorname{SpSt}(2n, 2k)`` manifold. First recall the definition of the symplectic -inverse for $A \in \mathbb{R}^{2n \times 2k}$, -$A^{+} = Q_{2k}^{\mathrm{T}}A^{\mathrm{T}}Q_{2n}$ is the symplectic inverse, with -````math - Q_{2n} = - \begin{bmatrix} - 0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -The we check that ``H = p^{+}X \in 𝔤_{2k}``, where ``𝔤`` -is the Lie Algebra of the symplectic group ``\operatorname{Sp}(2k)``, -characterized as [BendokatZimmermann:2021](@cite), -````math - 𝔤_{2k} = \{H \in ℝ^{2k \times 2k} \;|\; H^+ = -H \}. -```` -The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). +``\mathrm{SpSt}(2n, 2k)`` manifold. + +The check consists of verifying that ``H = p^{+}X ∈ 𝔤_{2k}``, where ``𝔤`` +is the Lie Algebra of the symplectic group ``\mathrm{Sp}(2k)``, that is +the set of [`HamiltonianMatrices`])(@ref), where ``(⋅)^+`` denotes the [`symplectic_inverse`](@ref). """ check_vector(::SymplecticStiefel, ::Any...) -function check_vector( - M::SymplecticStiefel{S,𝔽}, - p, - X::T; - atol::Real=sqrt(prod(representation_size(M))) * eps(real(float(number_eltype(T)))), - kwargs..., -) where {S,T,𝔽} +function check_vector(M::SymplecticStiefel{S,𝔽}, p, X::T; kwargs...) where {S,T,𝔽} n, k = get_parameter(M.size) # From Bendokat-Zimmermann: T_pSpSt(2n, 2k) = \{p*H | H^{+} = -H \} - H = inv(M, p) * X # ∈ ℝ^{2k × 2k}, should be Hamiltonian. - H_star = inv(Symplectic(2k, 𝔽), H) - hamiltonian_identity_norm = norm(H + H_star) + H = inv(M, p) * X # ∈ ℝ^{2k×2k}, should be Hamiltonian. - if !isapprox(hamiltonian_identity_norm, 0; atol=atol, kwargs...) + if !is_hamiltonian(H; kwargs...) return DomainError( - hamiltonian_identity_norm, + norm(Matrix(Hamiltonian(H)^+) + H), ( - "The matrix X is not in the tangent space at point p of the" * - " $(M) manifold, as p^{+}X is not a Hamiltonian matrix." + "The matrix X is not in the tangent space at point p of $M at $p, since p^{+}X is not a Hamiltonian matrix." ), ) end @@ -177,102 +157,105 @@ end exp!(M::SymplecticStiefel, q, p, X) Compute the exponential mapping -````math - \operatorname{exp}\colon T\operatorname{SpSt}(2n, 2k) - \rightarrow \operatorname{SpSt}(2n, 2k) -```` -at a point ``p \in \operatorname{SpSt}(2n, 2k)`` -in the direction of ``X \in T_p\operatorname{SpSt}(2n, 2k)``. +```math + \exp\colon T\mathrm{SpSt}(2n, 2k) → \mathrm{SpSt}(2n, 2k) +``` +at a point ``p ∈ \mathrm{SpSt}(2n, 2k)`` +in the direction of ``X ∈ T_p\mathrm{SpSt}(2n, 2k)``. The tangent vector ``X`` can be written in the form ``X = \bar{\Omega}p`` [BendokatZimmermann:2021](@cite), with -````math - \bar{\Omega} = X (p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}} - + Q_{2n}p(p^{\mathrm{T}}p)^{-1}X^{\mathrm{T}}(I_{2n} - Q_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}}Q_{2n})Q_{2n} - \in ℝ^{2n \times 2n}, -```` -where ``Q_{2n}`` is the [`SymplecticMatrix`](@ref). Using this expression for ``X``, + +```math + \bar{\Omega} = X (p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}} + + J_{2n}p(p^{\mathrm{T}}p)^{-1}X^{\mathrm{T}}(I_{2n} + - J_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}}J_{2n})J_{2n} + ∈ ℝ^{2n×2n}, +``` + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). + +Using this expression for ``X``, the exponential mapping can be computed as + ````math - \operatorname{exp}_p(X) = \operatorname{Exp}([\bar{\Omega} - \bar{\Omega}^{\mathrm{T}}]) - \operatorname{Exp}(\bar{\Omega}^{\mathrm{T}})p, + \exp_p(X) = \operatorname{Exp}([\bar{\Omega} - \bar{\Omega}^{\mathrm{T}}]) + \operatorname{Exp}(\bar{\Omega}^{\mathrm{T}})p, ```` -where ``\operatorname{Exp}(\cdot)`` denotes the matrix exponential. +where ``\operatorname{Exp}(⋅)`` denotes the matrix exponential. Computing the above mapping directly however, requires taking matrix exponentials -of two ``2n \times 2n`` matrices, which is computationally expensive when ``n`` +of two ``2n×2n`` matrices, which is computationally expensive when ``n`` increases. Therefore we instead follow [BendokatZimmermann:2021](@cite) who express the above exponential mapping in a way which only requires taking matrix exponentials -of an ``8k \times 8k`` matrix and a ``4k \times 4k`` matrix. +of an ``8k×8k`` matrix and a ``4k×4k`` matrix. To this end, first define -````math -\bar{A} = Q_{2k}p^{\mathrm{T}}X(p^{\mathrm{T}}p)^{-1}Q_{2k} + - (p^{\mathrm{T}}p)^{-1}X^{\mathrm{T}}(p - Q_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}Q_{2k}) \in ℝ^{2k \times 2k}, -```` +```math +\bar{A} = J_{2k}p^{\mathrm{T}}X(p^{\mathrm{T}}p)^{-1}J_{2k} + + (p^{\mathrm{T}}p)^{-1}X^{\mathrm{T}}(p - J_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}J_{2k}) ∈ ℝ^{2k×2k}, +``` + and -````math -\bar{H} = (I_{2n} - pp^+)Q_{2n}X(p^{\mathrm{T}}p)^{-1}Q_{2k} \in ℝ^{2n \times 2k}. -```` + +```math +\bar{H} = (I_{2n} - pp^+)J_{2n}X(p^{\mathrm{T}}p)^{-1}J_{2k} ∈ ℝ^{2n×2k}. +``` + We then let ``\bar{\Delta} = p\bar{A} + \bar{H}``, and define the matrices -````math + +```math γ = \left[\left(I_{2n} - \frac{1}{2}pp^+\right)\bar{\Delta} \quad - -p \right] \in ℝ^{2n \times 4k}, -```` + -p \right] ∈ ℝ^{2n×4k}, +``` and ````math - λ = \left[Q_{2n}^{\mathrm{T}}pQ_{2k} \quad + λ = \left[J_{2n}^{\mathrm{T}}pJ_{2k} \quad \left(\bar{\Delta}^+\left(I_{2n} - - \frac{1}{2}pp^+\right)\right)^{\mathrm{T}}\right] \in ℝ^{2n \times 4k}. + - \frac{1}{2}pp^+\right)\right)^{\mathrm{T}}\right] ∈ ℝ^{2n×4k}. ```` With the above defined matrices it holds that ``\bar{\Omega} = λγ^{\mathrm{T}}``. As a last preliminary step, concatenate ``γ`` and ``λ`` to define the matrices -``Γ = [λ \quad -γ] \in ℝ^{2n \times 8k}`` and -``Λ = [γ \quad λ] \in ℝ^{2n \times 8k}``. +``Γ = [λ \quad -γ] ∈ ℝ^{2n×8k}`` and +``Λ = [γ \quad λ] ∈ ℝ^{2n×8k}``. With these matrix constructions done, we can compute the exponential mapping as -````math - \operatorname{exp}_p(X) = - Γ \operatorname{Exp}(ΛΓ^{\mathrm{T}}) - \begin{bmatrix} - 0_{4k} \\ - I_{4k} - \end{bmatrix} - \operatorname{Exp}(λγ^{\mathrm{T}}) - \begin{bmatrix} - 0_{2k} \\ - I_{2k} - \end{bmatrix}. -```` +```math + \exp_p(X) = Γ \operatorname{Exp}(ΛΓ^{\mathrm{T}}) + \begin{bmatrix} 0_{4k} \\ I_{4k} \end{bmatrix} + \operatorname{Exp}(λγ^{\mathrm{T}}) + \begin{bmatrix} 0_{2k} \\ I_{2k} \end{bmatrix}. +``` + which only requires computing the matrix exponentials of -``ΛΓ^{\mathrm{T}} \in ℝ^{8k \times 8k}`` and ``λγ^{\mathrm{T}} \in ℝ^{4k \times 4k}``. +``ΛΓ^{\mathrm{T}} ∈ ℝ^{8k×8k}`` and ``λγ^{\mathrm{T}} ∈ ℝ^{4k×4k}``. """ exp(::SymplecticStiefel, p, X) function exp!(M::SymplecticStiefel, q, p, X) n, k = get_parameter(M.size) - Q = SymplecticMatrix(p, X) - pT_p = lu(p' * p) # ∈ ℝ^{2k × 2k} + J = SymplecticElement(p, X) + pT_p = lu(p' * p) # ∈ ℝ^{2k×2k} - C = pT_p \ X' # ∈ ℝ^{2k × 2n} + C = pT_p \ X' # ∈ ℝ^{2k×2n} # Construct A-bar: - # First A-term: Q * (p^{\mathrm{T}} * C^{\mathrm{T}}) * Q - A_bar = rmul!(lmul!(Q, (p' * C')), Q) + # First A-term: J * (p^{\mathrm{T}} * C^{\mathrm{T}}) * J + A_bar = rmul!(lmul!(J, (p' * C')), J) A_bar .+= C * p # Second A-term, use C-memory: - rmul!(C, Q') # C*Q^{\mathrm{T}} -> C - C_QT = C + rmul!(C, J') # C*J^{\mathrm{T}} -> C + C_JT = C - # Subtract C*Q^{\mathrm{T}}*p*(pT_p)^{-1}*Q: + # Subtract C*J^{\mathrm{T}}*p*(pT_p)^{-1}*J: # A_bar is "star-skew symmetric" (A^+ = -A). - A_bar .-= rmul!((C_QT * p) / pT_p, Q) + A_bar .-= rmul!((C_JT * p) / pT_p, J) # Construct H_bar: - # First H-term: Q * (C_QT * Q)' * Q -> Q * C' * Q = Q * (X / pT_p) * Q - H_bar = rmul!(lmul!(Q, rmul!(C_QT, Q)'), Q) + # First H-term: J * (C_JT * J)' * J -> J * C' * J = J * (X / pT_p) * J + H_bar = rmul!(lmul!(J, rmul!(C_JT, J)'), J) # Subtract second H-term: H_bar .-= p * symplectic_inverse_times(M, p, H_bar) @@ -283,17 +266,17 @@ function exp!(M::SymplecticStiefel, q, p, X) Δ_bar = H_bar γ_1 = Δ_bar - (1 / 2) .* p * symplectic_inverse_times(M, p, Δ_bar) - γ = [γ_1 -p] # ∈ ℝ^{2n × 4k} + γ = [γ_1 -p] # ∈ ℝ^{2n×4k} - Δ_bar_star = rmul!(Q' * Δ_bar', Q) - λ_1 = lmul!(Q', p * Q) + Δ_bar_star = rmul!(J' * Δ_bar', J) + λ_1 = lmul!(J', p * J) λ_2 = (Δ_bar_star .- (1 / 2) .* (Δ_bar_star * p) * λ_1')' - λ = [λ_1 λ_2] # ∈ ℝ^{2n × 4k} + λ = [λ_1 λ_2] # ∈ ℝ^{2n×4k} - Γ = [λ -γ] # ∈ ℝ^{2n × 8k} - Λ = [γ λ] # ∈ ℝ^{2n × 8k} + Γ = [λ -γ] # ∈ ℝ^{2n×8k} + Λ = [γ λ] # ∈ ℝ^{2n×8k} - # At last compute the (8k × 8k) and (4k × 4k) matrix exponentials: + # At last compute the (8k×8k) and (4k×4k) matrix exponentials: q .= Γ * (exp(Λ' * Γ)[:, (4k + 1):end]) * (exp(λ' * γ)[:, (2k + 1):end]) return q end @@ -309,33 +292,35 @@ end @doc raw""" get_total_space(::SymplecticStiefel) -Return the total space of the [`SymplecticStiefel`](@ref) manifold, which is the corresponding [`Symplectic`](@ref) manifold. +Return the total space of the [`SymplecticStiefel`](@ref) manifold, which is the corresponding [`SymplecticMatrices`](@ref) manifold. """ function get_total_space(::SymplecticStiefel{TypeParameter{Tuple{n,k}},ℝ}) where {n,k} - return Symplectic(2 * n) + return SymplecticMatrices(2 * n) end function get_total_space(M::SymplecticStiefel{Tuple{Int,Int},ℝ}) - n, k = get_parameter(M.size) - return Symplectic(2 * n; parameter=:field) + n, _ = get_parameter(M.size) + return SymplecticMatrices(2 * n; parameter=:field) end @doc raw""" inner(M::SymplecticStiefel, p, X. Y) -Compute the Riemannian inner product ``g^{\operatorname{SpSt}}`` at -``p \in \operatorname{SpSt}`` between tangent vectors ``X, X \in T_p\operatorname{SpSt}``. +Compute the Riemannian inner product ``g^{\mathrm{SpSt}}`` at +``p ∈ \mathrm{SpSt}`` of tangent vectors ``Y, X ∈ T_p\mathrm{SpSt}``. Given by Proposition 3.10 in [BendokatZimmermann:2021](@cite). -````math -g^{\operatorname{SpSt}}_p(X, Y) - = \operatorname{tr}\left(X^{\mathrm{T}}\left(I_{2n} - - \frac{1}{2}Q_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}}Q_{2n}\right)Y(p^{\mathrm{T}}p)^{-1}\right). -```` +```math +g^{\mathrm{SpSt}}_p(X, Y) + = \operatorname{tr}\Bigl( + X^{\mathrm{T}}\bigl( + I_{2n} - \frac{1}{2}J_{2n}^{\mathrm{T}} p(p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}}J_{2n} + \bigr) Y (p^{\mathrm{T}}p)^{-1}\Bigr). +``` """ function inner(::SymplecticStiefel, p, X, Y) - Q = SymplecticMatrix(p, X, Y) + J = SymplecticElement(p, X, Y) # in BZ21 also J # Procompute lu(p'p) since we solve a^{-1}* 3 times a = lu(p' * p) # note that p'p is symmetric, thus so is its inverse c=a^{-1} - b = Q' * p + b = J' * p # we split the original trace into two one with I->(X'Yc) # and the other with 1/2 X'b c b' Y c # 1) we permute X' and Y c to c^{\mathrm{T}}Y^{\mathrm{T}}X = a\(Y'X) (avoids a large interims matrix) @@ -348,37 +333,33 @@ end inv(::SymplecticStiefel, A) inv!(::SymplecticStiefel, q, p) -Compute the symplectic inverse ``A^+`` of matrix ``A ∈ ℝ^{2n × 2k}``. Given a matrix +Compute the symplectic inverse ``A^+`` of matrix ``A ∈ ℝ^{2n×2k}``. +Given a matrix ````math -A ∈ ℝ^{2n × 2k},\quad +A ∈ ℝ^{2n×2k},\quad A = \begin{bmatrix} A_{1, 1} & A_{1, 2} \\ A_{2, 1} & A_{2, 2} -\end{bmatrix},\; A_{i, j} \in ℝ^{2n × 2k} +\end{bmatrix}, \quad A_{i, j} ∈ ℝ^{2n×2k} ```` + the symplectic inverse is defined as: -````math -A^{+} := Q_{2k}^{\mathrm{T}} A^{\mathrm{T}} Q_{2n}, -```` -where -````math -Q_{2n} = -\begin{bmatrix} -0_n & I_n \\ - -I_n & 0_n -\end{bmatrix}. -```` -For any ``p \in \operatorname{SpSt}(2n, 2k)`` we have that ``p^{+}p = I_{2k}``. + +```math +A^{+} := J_{2k}^{\mathrm{T}} A^{\mathrm{T}} J_{2n}, +``` + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). The symplectic inverse of a matrix A can be expressed explicitly as: -````math +```math A^{+} = -\begin{bmatrix} - A_{2, 2}^{\mathrm{T}} & -A_{1, 2}^{\mathrm{T}} \\[1.2mm] - -A_{2, 1}^{\mathrm{T}} & A_{1, 1}^{\mathrm{T}} -\end{bmatrix}. -```` + \begin{bmatrix} + A_{2, 2}^{\mathrm{T}} & -A_{1, 2}^{\mathrm{T}} \\[1.2mm] + -A_{2, 1}^{\mathrm{T}} & A_{1, 1}^{\mathrm{T}} + \end{bmatrix}. +``` """ function Base.inv(M::SymplecticStiefel, p) q = similar(p') @@ -408,32 +389,23 @@ end inverse_retract(::SymplecticStiefel, p, q, ::CayleyInverseRetraction) inverse_retract!(::SymplecticStiefel, q, p, X, ::CayleyInverseRetraction) -Compute the Cayley Inverse Retraction ``X = \mathcal{L}_p^{\operatorname{SpSt}}(q)`` +Compute the Cayley Inverse Retraction ``X = \mathcal{L}_p^{\mathrm{SpSt}}(q)`` such that the Cayley Retraction from ``p`` along ``X`` lands at ``q``, i.e. ``\mathcal{R}_p(X) = q`` [BendokatZimmermann:2021](@cite). -First, recall the definition the standard symplectic matrix -````math -Q = -\begin{bmatrix} - 0 & I \\ --I & 0 -\end{bmatrix} -```` -as well as the symplectic inverse of a matrix ``A``, ``A^{+} = Q^{\mathrm{T}} A^{\mathrm{T}} Q``. - -For ``p, q ∈ \operatorname{SpSt}(2n, 2k, ℝ)`` then, we can define the +For ``p, q ∈ \mathrm{SpSt}(2n, 2k, ℝ)`` we can define the inverse cayley retraction as long as the following matrices exist. ````math - U = (I + p^+ q)^{-1} \in ℝ^{2k \times 2k}, + U = (I + p^+ q)^{-1} ∈ ℝ^{2k×2k}, \quad - V = (I + q^+ p)^{-1} \in ℝ^{2k \times 2k}. + V = (I + q^+ p)^{-1} ∈ ℝ^{2k×2k}, ```` -If that is the case, the inverse cayley retration at ``p`` applied to ``q`` is +where ``(⋅)^+`` denotes the [`symplectic_inverse`](@ref). + +THen the inverse retraction reads ````math -\mathcal{L}_p^{\operatorname{Sp}}(q) = 2p\bigl(V - U\bigr) + 2\bigl((p + q)U - p\bigr) - ∈ T_p\operatorname{Sp}(2n). +\mathcal{L}_p^{\mathrm{Sp}}(q) = 2p\bigl(V - U\bigr) + 2\bigl((p + q)U - p\bigr) ∈ T_p\mathrm{Sp}(2n). ```` """ inverse_retract(::SymplecticStiefel, p, q, ::CayleyInverseRetraction) @@ -456,10 +428,10 @@ is_flat(M::SymplecticStiefel) = false @doc raw""" manifold_dimension(::SymplecticStiefel) -Returns the dimension of the symplectic Stiefel manifold embedded in ``ℝ^{2n \times 2k}``, +Returns the dimension of the symplectic Stiefel manifold embedded in ``ℝ^{2n×2k}``, i.e. [BendokatZimmermann:2021](@cite) ````math - \operatorname{dim}(\operatorname{SpSt}(2n, 2k)) = (4n - 2k + 1)k. + \operatorname{dim}(\mathrm{SpSt}(2n, 2k)) = (4n - 2k + 1)k. ```` """ function manifold_dimension(M::SymplecticStiefel) @@ -471,114 +443,127 @@ end project(::SymplecticStiefel, p, A) project!(::SymplecticStiefel, Y, p, A) -Given a point ``p \in \operatorname{SpSt}(2n, 2k)``, -project an element ``A \in \mathbb{R}^{2n \times 2k}`` onto -the tangent space ``T_p\operatorname{SpSt}(2n, 2k)`` relative to -the euclidean metric of the embedding ``\mathbb{R}^{2n \times 2k}``. +Given a point ``p ∈ \mathrm{SpSt}(2n, 2k)``, +project an element ``A ∈ ℝ^{2n×2k}`` onto +the tangent space ``T_p\mathrm{SpSt}(2n, 2k)`` relative to +the euclidean metric of the embedding ``ℝ^{2n×2k}``. -That is, we find the element ``X \in T_p\operatorname{SpSt}(2n, 2k)`` +That is, we find the element ``X ∈ T_p\mathrm{SpSt}(2n, 2k)`` which solves the constrained optimization problem ````math - \operatorname{min}_{X \in \mathbb{R}^{2n \times 2k}} \frac{1}{2}||X - A||^2, \quad + \operatorname{min}_{X ∈ ℝ^{2n×2k}} \frac{1}{2}||X - A||^2, \quad \text{s.t.}\; - h(X)\colon= X^{\mathrm{T}} Q p + p^{\mathrm{T}} Q X = 0, + h(X)\colon= X^{\mathrm{T}} J p + p^{\mathrm{T}} J X = 0, ```` -where ``h : \mathbb{R}^{2n \times 2k} \rightarrow \operatorname{skew}(2k)`` defines -the restriction of ``X`` onto the tangent space ``T_p\operatorname{SpSt}(2n, 2k)``. +where ``h : ℝ^{2n×2k} → \operatorname{skew}(2k)`` defines +the restriction of ``X`` onto the tangent space ``T_p\mathrm{SpSt}(2n, 2k)``. """ project(::SymplecticStiefel, p, A) function project!(::SymplecticStiefel, Y, p, A) - Q = SymplecticMatrix(Y, p, A) - Q_p = Q * p + J = SymplecticElement(Y, p, A) + Jp = J * p function h(X) - XT_Q_p = X' * Q_p - return XT_Q_p .- XT_Q_p' + XTJp = X' * Jp + return XTJp .- XTJp' end # Solve for Λ (Lagrange mutliplier): - pT_p = p' * p # (2k × 2k) + pT_p = p' * p # (2k×2k) Λ = sylvester(pT_p, pT_p, h(A) ./ 2) - Y[:, :] = A .- Q_p * (Λ .- Λ') + Y[:, :] = A .- Jp * (Λ .- Λ') return Y end @doc raw""" - rand(M::SymplecticStiefel; vector_at=nothing, - hamiltonian_norm=(vector_at === nothing ? 1/2 : 1.0)) + rand(M::SymplecticStiefel; vector_at=nothing, σ = 1.0) -Generate a random point ``p \in \operatorname{SpSt}(2n, 2k)`` or -a random tangent vector ``X \in T_p\operatorname{SpSt}(2n, 2k)`` -if `vector_at` is set to a point ``p \in \operatorname{Sp}(2n)``. +Generate a random point ``p ∈ \mathrm{SpSt}(2n, 2k)`` or +a random tangent vector ``X ∈ T_p\mathrm{SpSt}(2n, 2k)`` +if `vector_at` is set to a point ``p ∈ \mathrm{Sp}(2n)``. -A random point on ``\operatorname{SpSt}(2n, 2k)`` is found by first generating a -random point on the symplectic manifold ``\operatorname{Sp}(2n)``, +A random point on ``\mathrm{SpSt}(2n, 2k)`` is found by first generating a +random point on the symplectic manifold ``\mathrm{Sp}(2n)``, and then projecting onto the Symplectic Stiefel manifold using the -[`canonical_project`](@ref) ``π_{\operatorname{SpSt}(2n, 2k)}``. -That is, ``p = π_{\operatorname{SpSt}(2n, 2k)}(p_{\operatorname{Sp}})``. +[`canonical_project`](@ref) ``π_{\mathrm{SpSt}(2n, 2k)}``. +That is, ``p = π_{\mathrm{SpSt}(2n, 2k)}(p_{\mathrm{Sp}})``. -To generate a random tangent vector in ``T_p\operatorname{SpSt}(2n, 2k)`` +To generate a random tangent vector in ``T_p\mathrm{SpSt}(2n, 2k)`` this code exploits the second tangent vector space parametrization of -[`SymplecticStiefel`](@ref), showing that any ``X \in T_p\operatorname{SpSt}(2n, 2k)`` +[`SymplecticStiefel`](@ref), that any ``X ∈ T_p\mathrm{SpSt}(2n, 2k)`` can be written as ``X = pΩ_X + p^sB_X``. To generate random tangent vectors at ``p`` then, this function sets ``B_X = 0`` -and generates a random Hamiltonian matrix ``Ω_X \in \mathfrak{sp}(2n,F)`` with -Frobenius norm of `hamiltonian_norm` before returning ``X = pΩ_X``. +and generates a random Hamiltonian matrix ``Ω_X ∈ \mathfrak{sp}(2n,F)`` with +Frobenius norm of `σ` before returning ``X = pΩ_X``. """ -function Random.rand( - M::SymplecticStiefel; +rand(M::SymplecticStiefel; σ::Real=1.0, kwargs...) + +function Random.rand!( + rng::AbstractRNG, + M::SymplecticStiefel, + pX; vector_at=nothing, - hamiltonian_norm=(vector_at === nothing ? 1 / 2 : 1.0), + hamiltonian_norm=nothing, + σ=hamiltonian_norm === nothing ? 1.0 : hamiltonian_norm, ) + !(hamiltonian_norm === nothing) && Base.depwarn( + Random.rand!, + "hamiltonian_norm is deprecated as a keyword, please use the default σ.", + ) n, k = get_parameter(M.size) if vector_at === nothing - return canonical_project(M, rand(Symplectic(2n); hamiltonian_norm=hamiltonian_norm)) + canonical_project!(M, pX, rand(rng, SymplecticMatrices(2n); σ=σ)) + return pX else - return random_vector(M, vector_at; hamiltonian_norm=hamiltonian_norm) + return random_vector!(rng, M, pX, vector_at; σ=σ) end end -function random_vector(M::SymplecticStiefel, p::AbstractMatrix; hamiltonian_norm=1.0) - n, k = get_parameter(M.size) - Ω = rand_hamiltonian(Symplectic(2k); frobenius_norm=hamiltonian_norm) - return p * Ω +function random_vector!(rng, M::SymplecticStiefel, X, p; σ=1.0) + k = get_parameter(M.size)[2] + Ω = @view(X[1:(2k), 1:(2k)]) # use this memory + rand!(rng, HamiltonianMatrices(2k), Ω; σ=σ) + X .= p * Ω + return X end @doc raw""" retract(::SymplecticStiefel, p, X, ::CayleyRetraction) retract!(::SymplecticStiefel, q, p, X, ::CayleyRetraction) -Compute the Cayley retraction on the Symplectic Stiefel manifold, computed inplace -of `q` from `p` along `X`. +Compute the Cayley retraction on the Symplectic Stiefel manifold, +from `p` along `X` (computed inplace of `q`). -Given a point ``p \in \operatorname{SpSt}(2n, 2k)``, every tangent vector -``X \in T_p\operatorname{SpSt}(2n, 2k)`` is of the form +Given a point ``p ∈ \mathrm{SpSt}(2n, 2k)``, every tangent vector +``X ∈ T_p\mathrm{SpSt}(2n, 2k)`` is of the form ``X = \tilde{\Omega}p``, with ````math \tilde{\Omega} = \left(I_{2n} - \frac{1}{2}pp^+\right)Xp^+ - - pX^+\left(I_{2n} - \frac{1}{2}pp^+\right) \in ℝ^{2n \times 2n}, + pX^+\left(I_{2n} - \frac{1}{2}pp^+\right) ∈ ℝ^{2n×2n}, ```` as shown in Proposition 3.5 of [BendokatZimmermann:2021](@cite). Using this representation of ``X``, the Cayley retraction -on ``\operatorname{SpSt}(2n, 2k)`` is defined pointwise as +on ``\mathrm{SpSt}(2n, 2k)`` is defined pointwise as ````math \mathcal{R}_p(X) = \operatorname{cay}\left(\frac{1}{2}\tilde{\Omega}\right)p. ```` The operator ``\operatorname{cay}(A) = (I - A)^{-1}(I + A)`` is the Cayley transform. -However, the computation of an ``2n \times 2n`` matrix inverse in the expression -above can be reduced down to inverting a ``2k \times 2k`` matrix due to Proposition +However, the computation of an ``2n×2n`` matrix inverse in the expression +above can be reduced down to inverting a ``2k×2k`` matrix due to Proposition 5.2 of [BendokatZimmermann:2021](@cite). Let ``A = p^+X`` and ``H = X - pA``. Then an equivalent expression for the Cayley retraction defined pointwise above is -````math - \mathcal{R}_p(X) = -p + (H + 2p)(H^+H/4 - A/2 + I_{2k})^{-1}. -```` -It is this expression we compute inplace of `q`. + +```math + \mathcal{R}_p(X) = -p + (H + 2p)(H^+H/4 - A/2 + I_{2k})^{-1}. +``` + +This expression is computed inplace of `q`. """ retract(::SymplecticStiefel, p, X, ::CayleyRetraction) @@ -587,7 +572,7 @@ function retract_cayley!(M::SymplecticStiefel, q, p, X, t::Number) # Define intermediate matrices for later use: A = symplectic_inverse_times(M, p, tX) - H = tX .- p * A # Allocates (2n × 2k). + H = tX .- p * A # Allocates (2n×2k). # A = I - A/2 + H^{+}H/4: A .= (symplectic_inverse_times(M, H, H) ./ 4) .- (A ./ 2) @@ -609,16 +594,18 @@ provided that the gradient of the function ``\tilde f``, which is `f` continued is given by `Y`. The metric in the embedding is the Euclidean metric. The manifold gradient `X` is computed from `Y` as + ```math - X = Yp^{\mathrm{T}}p + Q_{2n}pY^{\mathrm{T}}Q_{2n}p, + X = Yp^{\mathrm{T}}p + J_{2n}pY^{\mathrm{T}}J_{2n}p, ``` -where ``Q_{2n}`` is the [`SymplecticMatrix`](@ref). + +where ``J_{2n} = \begin{bmatrix} 0_n & I_n \\ -I_n & 0_n \end{bmatrix}`` denotes the [`SymplecticElement`](@ref). """ function riemannian_gradient(::SymplecticStiefel, p, Y) - Q_p = SymplecticMatrix(p, Y) * p - return Y * (p' * p) .+ Q_p * (Y' * Q_p) + Jp = SymplecticElement(p, Y) * p + return Y * (p' * p) .+ Jp * (Y' * Jp) end function riemannian_gradient!( @@ -628,32 +615,32 @@ function riemannian_gradient!( Y; embedding_metric::EuclideanMetric=EuclideanMetric(), ) - Q_p = SymplecticMatrix(p, Y) * p - X .= Y * (p' * p) .+ Q_p * (Y' * Q_p) + Jp = SymplecticElement(p, Y) * p + X .= Y * (p' * p) .+ Jp * (Y' * Jp) return X end function Base.show(io::IO, ::SymplecticStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return print(io, "SymplecticStiefel($(2n), $(2k), $(𝔽))") + return print(io, "SymplecticStiefel($(2n), $(2k); field=$(𝔽))") end function Base.show(io::IO, M::SymplecticStiefel{Tuple{Int,Int},𝔽}) where {𝔽} n, k = get_parameter(M.size) - return print(io, "SymplecticStiefel($(2n), $(2k), $(𝔽); parameter=:field)") + return print(io, "SymplecticStiefel($(2n), $(2k); field=$(𝔽); parameter=:field)") end @doc raw""" symplectic_inverse_times(::SymplecticStiefel, p, q) symplectic_inverse_times!(::SymplecticStiefel, A, p, q) -Directly compute the symplectic inverse of ``p \in \operatorname{SpSt}(2n, 2k)``, -multiplied with ``q \in \operatorname{SpSt}(2n, 2k)``. +Directly compute the symplectic inverse of ``p ∈ \mathrm{SpSt}(2n, 2k)``, +multiplied with ``q ∈ \mathrm{SpSt}(2n, 2k)``. That is, this function efficiently computes -``p^+q = (Q_{2k}p^{\mathrm{T}}Q_{2n})q \in ℝ^{2k \times 2k}``, -where ``Q_{2n}, Q_{2k}`` are the [`SymplecticMatrix`](@ref) -of sizes ``2n \times 2n`` and ``2k \times 2k`` respectively. +``p^+q = (J_{2k}p^{\mathrm{T}}J_{2n})q ∈ ℝ^{2k×2k}``, +where ``J_{2n}, J_{2k}`` are the [`SymplecticElement`](@ref) +of sizes ``2n×2n`` and ``2k×2k`` respectively. This function performs this common operation without allocating more than -a ``2k \times 2k`` matrix to store the result in, or in the case of the in-place +a ``2k×2k`` matrix to store the result in, or in the case of the in-place function, without allocating memory at all. """ function symplectic_inverse_times(M::SymplecticStiefel, p, q) diff --git a/src/manifolds/Torus.jl b/src/manifolds/Torus.jl index 6a5b934b78..000ca54aea 100644 --- a/src/manifolds/Torus.jl +++ b/src/manifolds/Torus.jl @@ -1,7 +1,7 @@ @doc raw""" Torus{N} <: AbstractPowerManifold -The n-dimensional torus is the $n$-dimensional product of the [`Circle`](@ref). +The n-dimensional torus is the ``n``-dimensional product of the [`Circle`](@ref). The [`Circle`](@ref) is stored internally within `M.manifold`, such that all functions of [`AbstractPowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.AbstractPowerManifold) can be used directly. diff --git a/src/manifolds/Tucker.jl b/src/manifolds/Tucker.jl index 414d00e62c..5ad07de410 100644 --- a/src/manifolds/Tucker.jl +++ b/src/manifolds/Tucker.jl @@ -2,33 +2,33 @@ @doc raw""" Tucker{T, D, 𝔽} <: AbstractManifold{𝔽} -The manifold of ``N_1 \times \dots \times N_D`` real-valued or complex-valued tensors of +The manifold of ``N_1×\dots×N_D`` real-valued or complex-valued tensors of fixed multilinear rank ``(R_1, \dots, R_D)`` . If ``R_1 = \dots = R_D = 1``, this is the Segre manifold, i.e., the set of rank-1 tensors. # Representation in HOSVD format -Let ``\mathbb{F}`` be the real or complex numbers. +Let ``𝔽`` be the real or complex numbers. Any tensor ``p`` on the Tucker manifold can be represented as a multilinear product in HOSVD [DeLathauwerDeMoorVanderwalle:2000](@cite) form ```math -p = (U_1,\dots,U_D) \cdot \mathcal{C} +p = (U_1,\dots,U_D) ⋅ \mathcal{C} ``` -where ``\mathcal C \in \mathbb{F}^{R_1 \times \dots \times R_D}`` and, for ``d=1,\dots,D``, -the matrix ``U_d \in \mathbb{F}^{N_d \times R_d}`` contains the singular vectors of the +where ``\mathcal C \in 𝔽^{R_1×\dots×R_D}`` and, for ``d=1,\dots,D``, +the matrix ``U_d \in 𝔽^{N_d×R_d}`` contains the singular vectors of the ``d``th unfolding of ``\mathcal{A}`` # Tangent space The tangent space to the Tucker manifold at -``p = (U_1,\dots,U_D) \cdot \mathcal{C}`` is [KochLubich:2010](@cite) +``p = (U_1,\dots,U_D) ⋅ \mathcal{C}`` is [KochLubich:2010](@cite) ```math T_p \mathcal{M} = \bigl\{ -(U_1,\dots,U_D) \cdot \mathcal{C}^\prime +(U_1,\dots,U_D) ⋅ \mathcal{C}^\prime + \sum_{d=1}^D \bigl( (U_1, \dots, U_{d-1}, U_d^\prime, U_{d+1}, \dots, U_D) - \cdot \mathcal{C} + ⋅ \mathcal{C} \bigr) \bigr\} ``` @@ -124,8 +124,8 @@ Tangent vector to the `D`-th order [`Tucker`](@ref) manifold at represented as ````math X = -(U_1,\dots,U_D) \cdot \mathcal{C}^\prime + -\sum_{d=1}^D (U_1,\dots,U_{d-1},U_d^\prime,U_{d+1},\dots,U_D) \cdot \mathcal{C} +(U_1,\dots,U_D) ⋅ \mathcal{C}^\prime + +\sum_{d=1}^D (U_1,\dots,U_{d-1},U_d^\prime,U_{d+1},\dots,U_D) ⋅ \mathcal{C} ```` where ``U_d^\mathrm{H} U_d^\prime = 0``. @@ -444,7 +444,7 @@ end get_basis(:: Tucker, p::TuckerPoint, basisType::DefaultOrthonormalBasis{𝔽, TangentSpaceType}) where 𝔽 An implicitly stored basis of the tangent space to the Tucker manifold. -Assume ``p = (U_1,\dots,U_D) \cdot \mathcal{C}`` is in HOSVD format and that, for +Assume ``p = (U_1,\dots,U_D) ⋅ \mathcal{C}`` is in HOSVD format and that, for ``d=1,\dots,D``, the singular values of the ``d``'th unfolding are ``\sigma_{dj}``, with ``j = 1,\dots,R_d``. The basis of the tangent space is as follows: [DewaeleBreidingVannieuwenhoven:2021](@cite) @@ -453,13 +453,13 @@ The basis of the tangent space is as follows: [DewaeleBreidingVannieuwenhoven:20 \bigl\{ (U_1,\dots,U_D) e_i \bigr\} \cup \bigl\{ -(U_1,\dots, \sigma_{dj}^{-1} U_d^{\perp} e_i e_j^T,\dots,U_D) \cdot \mathcal{C} +(U_1,\dots, \sigma_{dj}^{-1} U_d^{\perp} e_i e_j^T,\dots,U_D) ⋅ \mathcal{C} \bigr\} ```` for all ``d = 1,\dots,D`` and all canonical basis vectors ``e_i`` and ``e_j``. Every ``U_d^\perp`` is such that ``[U_d \quad U_d^{\perp}]`` forms an orthonormal basis -of ``\mathbb{R}^{N_d}``. +of ``ℝ^{N_d}``. """ function get_basis( ::Tucker, @@ -634,7 +634,7 @@ end @doc raw""" manifold_dimension(::Tucker) -The dimension of the manifold of ``N_1 \times \dots \times N_D`` tensors of multilinear +The dimension of the manifold of ``N_1×\dots×N_D`` tensors of multilinear rank ``(R_1, \dots, R_D)``, i.e. ````math \mathrm{dim}(\mathcal{M}) = \prod_{d=1}^D R_d + \sum_{d=1}^D R_d (N_d - R_d). diff --git a/src/statistics.jl b/src/statistics.jl index e4cad6c6fd..aff3e6e1d1 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -85,7 +85,7 @@ as the point that satisfies the minimizer ````math \argmin_{y ∈ \mathcal M} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{\mathcal M}^2(y,x_i), ```` -where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). +where ``\mathrm{d}_{\mathcal M}`` denotes the Riemannian [`distance`](@ref). In the general case, the [`GradientDescentEstimation`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions/#ManifoldsBase.GradientDescentEstimation) is used to compute the mean. mean( @@ -445,7 +445,7 @@ Compute the (optionally weighted) Riemannian median of the vector `x` of points ````math \argmin_{y ∈ \mathcal M} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{\mathcal M}(y,x_i), ```` -where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). +where ``\mathrm{d}_{\mathcal M}`` denotes the Riemannian [`distance`](@ref). This function is nonsmooth (i.e nondifferentiable). In the general case, the [`CyclicProximalPointEstimation`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions/#ManifoldsBase.CyclicProximalPointEstimation) is used to compute the @@ -549,7 +549,7 @@ The parameter ``α\in (0,2]`` is a step size. The algorithm is further described in [FletcherVenkatasubramanianJoshi:2008](@cite), especially the update rule in Eq. (6), i.e. Let ``q_{k}`` denote the current -iterate, $n$ the number of points ``x_1,\ldots,x_n``, and +iterate, ``n`` the number of points ``x_1,\ldots,x_n``, and ```math I_k = \bigl\{ i \in \{1,\ldots,n\} \big| x_i \neq q_k \bigr\} @@ -566,7 +566,7 @@ X = \frac{1}{s}\sum_{i\in I_k} \frac{w_i}{d_{\mathcal M}(q_k,x_i)}\log_{q_k}x_i s = \sum_{i\in I_k} \frac{w_i}{d_{\mathcal M}(q_k,x_i)}, ``` -and where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). +and where ``\mathrm{d}_{\mathcal M}`` denotes the Riemannian [`distance`](@ref). Optionally, pass `retraction` and `inverse_retraction` method types to specify the (inverse) retraction, which by default use the exponential and logarithmic map, diff --git a/src/utils.jl b/src/utils.jl index d12937d426..c45413cd42 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -2,7 +2,7 @@ @doc raw""" usinc(θ::Real) -Unnormalized version of `sinc` function, i.e. $\operatorname{usinc}(θ) = \frac{\sin(θ)}{θ}$. +Unnormalized version of `sinc` function, i.e. ``\operatorname{usinc}(θ) = \frac{\sin(θ)}{θ}``. This is equivalent to `sinc(θ/π)`. """ @inline usinc(θ::Real) = θ == 0 ? one(θ) : isinf(θ) ? zero(θ) : sin(θ) / θ @@ -10,8 +10,8 @@ This is equivalent to `sinc(θ/π)`. @doc raw""" usinc_from_cos(x::Real) -Unnormalized version of `sinc` function, i.e. $\operatorname{usinc}(θ) = \frac{\sin(θ)}{θ}$, -computed from $x = cos(θ)$. +Unnormalized version of `sinc` function, i.e. ``\operatorname{usinc}(θ) = \frac{\sin(θ)}{θ}``, +computed from ``x = cos(θ)``. """ @inline function usinc_from_cos(x::Real) return if x >= 1 @@ -140,15 +140,15 @@ mul!_safe(Y, A, B) = (Y === A || Y === B) ? copyto!(Y, A * B) : mul!(Y, A, B) @doc raw""" realify(X::AbstractMatrix{T𝔽}, 𝔽::AbstractNumbers) -> Y::AbstractMatrix{<:Real} -Given a matrix $X ∈ 𝔽^{n × n}$, compute $Y ∈ ℝ^{m × m}$, where $m = n \operatorname{dim}_𝔽$, -and $\operatorname{dim}_𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the number field $𝔽$, using -the map $ϕ \colon X ↦ Y$, that preserves the matrix product, so that for all -$C,D ∈ 𝔽^{n × n}$, +Given a matrix ``X ∈ 𝔽^{n×n}``, compute ``Y ∈ ℝ^{m×m}``, where ``m = n \operatorname{dim}_𝔽``, +and ``\operatorname{dim}_𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the number field ``𝔽``, using +the map ``ϕ \colon X ↦ Y``, that preserves the matrix product, so that for all +``C,D ∈ 𝔽^{n×n}``, ````math ϕ(C) ϕ(D) = ϕ(CD). ```` See [`realify!`](@ref) for an in-place version, and [`unrealify!`](@ref) to compute the -inverse of $ϕ$. +inverse of ``ϕ``. """ function realify(X, 𝔽) n = LinearAlgebra.checksquare(X) @@ -168,8 +168,8 @@ realify!(Y, X, 𝔽) @doc raw""" realify!(Y::AbstractMatrix{<:Real}, X::AbstractMatrix{<:Complex}, ::typeof(ℂ)) -Given a complex matrix $X = A + iB ∈ ℂ^{n × n}$, compute its realified matrix -$Y ∈ ℝ^{2n × 2n}$, written +Given a complex matrix ``X = A + iB ∈ ℂ^{n×n}``, compute its realified matrix +``Y ∈ ℝ^{2n×2n}``, written where ````math Y = \begin{pmatrix}A & -B \\ B & A \end{pmatrix}. @@ -188,10 +188,10 @@ end @doc raw""" unrealify!(X::AbstractMatrix{T𝔽}, Y::AbstractMatrix{<:Real}, 𝔽::AbstractNumbers[, n]) -Given a real matrix $Y ∈ ℝ^{m × m}$, where $m = n \operatorname{dim}_𝔽$, and -$\operatorname{dim}_𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the number field $𝔽$, compute -in-place its equivalent matrix $X ∈ 𝔽^{n × n}$. Note that this function does not check that -$Y$ has a valid structure to be un-realified. +Given a real matrix ``Y ∈ ℝ^{m×m}``, where ``m = n \operatorname{dim}_𝔽``, and +``\operatorname{dim}_𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the number field ``𝔽``, compute +in-place its equivalent matrix ``X ∈ 𝔽^{n×n}``. Note that this function does not check that +``Y`` has a valid structure to be un-realified. See [`realify!`](@ref) for the inverse of this function. """ @@ -229,7 +229,7 @@ end @doc raw""" vec2skew!(X, v, k) -create a skew symmetric matrix inplace in `X` of size $k\times k$ from a vector `v`, +create a skew symmetric matrix inplace in `X` of size ``k×k`` from a vector `v`, for example for `v=[1,2,3]` and `k=3` this yields ````julia diff --git a/test/ambiguities.jl b/test/ambiguities.jl index b46f568017..4214a4feff 100644 --- a/test/ambiguities.jl +++ b/test/ambiguities.jl @@ -1,5 +1,18 @@ +include("header.jl") +""" + has_type_in_signature(sig, T::Type) + +Test whether the signature `sig` has an argument of type `T` as one of its parameters. +""" +function has_type_in_signature(sig, T::Type) + return any(map(Base.unwrap_unionall(sig.sig).parameters) do x + xw = Base.rewrap_unionall(x, sig.sig) + return (xw isa Type ? xw : xw.T) <: T + end) +end + @testset "Ambiguities" begin - if VERSION.prerelease == () && !Sys.iswindows() && VERSION < v"1.10.0" + if VERSION.prerelease == () && !Sys.iswindows() && VERSION < v"1.11.0" mbs = Test.detect_ambiguities(ManifoldsBase) # Interims solution until we follow what was proposed in # https://discourse.julialang.org/t/avoid-ambiguities-with-individual-number-element-identity/62465/2 diff --git a/test/approx_inverse_retraction.jl b/test/approx_inverse_retraction.jl index 7d9b40ea12..cdf492b2ea 100644 --- a/test/approx_inverse_retraction.jl +++ b/test/approx_inverse_retraction.jl @@ -1,7 +1,7 @@ using NLsolve using LinearAlgebra -include("utils.jl") +include("header.jl") Random.seed!(10) diff --git a/test/differentiation.jl b/test/differentiation.jl index 35a9dca2d7..3db2a3745c 100644 --- a/test/differentiation.jl +++ b/test/differentiation.jl @@ -1,6 +1,6 @@ # A bit of duplication of tests in ManifoldDiff.jl but it ensures that it works here too. -include("utils.jl") +include("header.jl") using Manifolds: default_differential_backend, _derivative, diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index cd6ce39923..5676cd5b4c 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: diff --git a/test/groups/connections.jl b/test/groups/connections.jl index efaaf739af..0d2e4c4a7e 100644 --- a/test/groups/connections.jl +++ b/test/groups/connections.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: connection diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 24fe8026d6..1af110b040 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using NLsolve diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index d681c818bb..d2cc2c8c22 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "General Unitary Groups" begin diff --git a/test/groups/group_operation_action.jl b/test/groups/group_operation_action.jl index f99542588b..ec7e9d569e 100644 --- a/test/groups/group_operation_action.jl +++ b/test/groups/group_operation_action.jl @@ -1,5 +1,5 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: diff --git a/test/groups/group_utils.jl b/test/groups/group_utils.jl index 2a307ab276..4254997dfc 100644 --- a/test/groups/group_utils.jl +++ b/test/groups/group_utils.jl @@ -1,31 +1,35 @@ -struct NotImplementedOperation <: AbstractGroupOperation end +s = @isdefined _group_utils_included +if !s + _group_utils_included = true + struct NotImplementedOperation <: AbstractGroupOperation end -struct NotImplementedManifold <: AbstractManifold{ℝ} end + struct NotImplementedManifold <: AbstractManifold{ℝ} end -struct NotImplementedGroupDecorator{𝔽,M<:AbstractManifold{𝔽}} <: - AbstractDecoratorManifold{𝔽} - manifold::M -end -function active_traits(f, M::NotImplementedGroupDecorator, args...) - return merge_traits(active_traits(f, M.manifold, args...), IsExplicitDecorator()) -end + struct NotImplementedGroupDecorator{𝔽,M<:AbstractManifold{𝔽}} <: + AbstractDecoratorManifold{𝔽} + manifold::M + end + function active_traits(f, M::NotImplementedGroupDecorator, args...) + return merge_traits(active_traits(f, M.manifold, args...), IsExplicitDecorator()) + end -function Manifolds.decorated_manifold(M::NotImplementedGroupDecorator) - return M.manifold -end + function Manifolds.decorated_manifold(M::NotImplementedGroupDecorator) + return M.manifold + end -struct DefaultTransparencyGroup{𝔽,M<:AbstractManifold{𝔽},A<:AbstractGroupOperation} <: - AbstractDecoratorManifold{𝔽} - manifold::M - op::A -end -function active_traits(f, M::DefaultTransparencyGroup, args...) - return merge_traits( - Manifolds.IsGroupManifold(M.op), - active_traits(f, M.manifold, args...), - ) -end + struct DefaultTransparencyGroup{𝔽,M<:AbstractManifold{𝔽},A<:AbstractGroupOperation} <: + AbstractDecoratorManifold{𝔽} + manifold::M + op::A + end + function active_traits(f, M::DefaultTransparencyGroup, args...) + return merge_traits( + Manifolds.IsGroupManifold(M.op), + active_traits(f, M.manifold, args...), + ) + end -function Manifolds.decorated_manifold(M::DefaultTransparencyGroup) - return M.manifold + function Manifolds.decorated_manifold(M::DefaultTransparencyGroup) + return M.manifold + end end diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index 6e3cbb4d8d..710167904b 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -1,7 +1,7 @@ using StaticArrays: identity_perm using Base: decode_overlong -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: diff --git a/test/groups/heisenberg.jl b/test/groups/heisenberg.jl index 1e13d24387..86c3ab4188 100644 --- a/test/groups/heisenberg.jl +++ b/test/groups/heisenberg.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Heisenberg group" begin diff --git a/test/groups/metric.jl b/test/groups/metric.jl index f759d20426..fa5b82fb44 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using OrdinaryDiffEq diff --git a/test/groups/power_group.jl b/test/groups/power_group.jl index 60000fa36c..d2aa761b94 100644 --- a/test/groups/power_group.jl +++ b/test/groups/power_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Power group" begin diff --git a/test/groups/product_group.jl b/test/groups/product_group.jl index 05e251e1a6..b1e1f7e978 100644 --- a/test/groups/product_group.jl +++ b/test/groups/product_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using RecursiveArrayTools diff --git a/test/groups/rotation_action.jl b/test/groups/rotation_action.jl index 71be2acd73..2a29167258 100644 --- a/test/groups/rotation_action.jl +++ b/test/groups/rotation_action.jl @@ -1,5 +1,5 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Rotation action" begin diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl index 2bb61eb137..8ecf8bbadc 100644 --- a/test/groups/rotation_translation_action.jl +++ b/test/groups/rotation_translation_action.jl @@ -1,5 +1,5 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Rotation-translation action" begin diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index 49c24acdd3..83611ca9bd 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Semidirect product group" begin diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index 0030a38b72..2820469949 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using ManifoldsBase: VeeOrthogonalBasis diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index 107ee1f9cf..c16f508243 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using NLsolve diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index 5ac62be630..32753dd0bf 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: LeftForwardAction, RightBackwardAction diff --git a/test/groups/translation_action.jl b/test/groups/translation_action.jl index 1348759b93..75bcee6d93 100644 --- a/test/groups/translation_action.jl +++ b/test/groups/translation_action.jl @@ -1,5 +1,5 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") @testset "Translation action" begin diff --git a/test/groups/translation_group.jl b/test/groups/translation_group.jl index 008f753c4d..c665a040a6 100644 --- a/test/groups/translation_group.jl +++ b/test/groups/translation_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") include("group_utils.jl") using Manifolds: LeftForwardAction, RightBackwardAction diff --git a/test/groups/validation_group.jl b/test/groups/validation_group.jl index 1c8db47dbb..e5853b8a0f 100644 --- a/test/groups/validation_group.jl +++ b/test/groups/validation_group.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Group wrapped in ValidationManifold" begin G = SpecialOrthogonal(3) diff --git a/test/header.jl b/test/header.jl new file mode 100644 index 0000000000..bbffe9d9fc --- /dev/null +++ b/test/header.jl @@ -0,0 +1,24 @@ + +TEST_FLOAT32 = get(ENV, "MANIFOLDS_TEST_FLOAT32", false) +TEST_DOUBLE64 = get(ENV, "MANIFOLDS_TEST_FLOAT64", false) +TEST_STATIC_SIZED = get(ENV, "MANIFOLDS_TEST_STATIC_SIZED", false) +TEST_GROUP = get(ENV, "MANIFOLDS_TEST_GROUP", "all") + +using Manifolds +using ManifoldsBase +using ManifoldsBase: number_of_coordinates, TypeParameter +import ManifoldsBase: active_traits, merge_traits + +using ManifoldDiff + +using LinearAlgebra +using Distributions +using DoubleFloats +using Quaternions +using Random +using StaticArrays +using Statistics +using StatsBase +using Test +using Graphs +using SimpleWeightedGraphs diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index ae63d1f5f9..8276a9bb40 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "CenteredMatrices" begin M = CenteredMatrices(3, 2) diff --git a/test/manifolds/cholesky_space.jl b/test/manifolds/cholesky_space.jl index 3fe165f654..d227c06ad5 100644 --- a/test/manifolds/cholesky_space.jl +++ b/test/manifolds/cholesky_space.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Cholesky Space" begin M = Manifolds.CholeskySpace(3) diff --git a/test/manifolds/circle.jl b/test/manifolds/circle.jl index bc829fb91f..8296554eb0 100644 --- a/test/manifolds/circle.jl +++ b/test/manifolds/circle.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using Manifolds: TFVector, CoTFVector diff --git a/test/manifolds/elliptope.jl b/test/manifolds/elliptope.jl index e36fae782f..465f4fb162 100644 --- a/test/manifolds/elliptope.jl +++ b/test/manifolds/elliptope.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Elliptope" begin M = Elliptope(4, 2) diff --git a/test/manifolds/essential_manifold.jl b/test/manifolds/essential_manifold.jl index a05e3edd98..02bbba2a7f 100644 --- a/test/manifolds/essential_manifold.jl +++ b/test/manifolds/essential_manifold.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Essential manifold" begin M = EssentialManifold() diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index e83572c67e..64e856177a 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using Manifolds: induced_basis using FiniteDifferences diff --git a/test/manifolds/fiber.jl b/test/manifolds/fiber.jl index 8bf9253d09..640836b455 100644 --- a/test/manifolds/fiber.jl +++ b/test/manifolds/fiber.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using RecursiveArrayTools diff --git a/test/manifolds/fiber_bundle.jl b/test/manifolds/fiber_bundle.jl index 9950c1ac67..3e2e49a653 100644 --- a/test/manifolds/fiber_bundle.jl +++ b/test/manifolds/fiber_bundle.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using RecursiveArrayTools diff --git a/test/manifolds/fixed_rank.jl b/test/manifolds/fixed_rank.jl index ef128f8e85..4536e4e66c 100644 --- a/test/manifolds/fixed_rank.jl +++ b/test/manifolds/fixed_rank.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "fixed Rank" begin M = FixedRankMatrices(3, 2, 2) diff --git a/test/manifolds/generalized_grassmann.jl b/test/manifolds/generalized_grassmann.jl index 243b3af019..0f7850daa2 100644 --- a/test/manifolds/generalized_grassmann.jl +++ b/test/manifolds/generalized_grassmann.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Generalized Grassmann" begin @testset "Real" begin diff --git a/test/manifolds/generalized_stiefel.jl b/test/manifolds/generalized_stiefel.jl index 9dc195e0e7..ebfce1d7fe 100644 --- a/test/manifolds/generalized_stiefel.jl +++ b/test/manifolds/generalized_stiefel.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Generalized Stiefel" begin @testset "Real" begin diff --git a/test/manifolds/graph.jl b/test/manifolds/graph.jl index 73f0cd5c12..4e08759f52 100644 --- a/test/manifolds/graph.jl +++ b/test/manifolds/graph.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Graph AbstractManifold" begin M = Euclidean(2) diff --git a/test/manifolds/grassmann.jl b/test/manifolds/grassmann.jl index 500ad62f5a..9f6827b274 100644 --- a/test/manifolds/grassmann.jl +++ b/test/manifolds/grassmann.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Grassmann" begin @testset "Real" begin diff --git a/test/manifolds/hamiltonian.jl b/test/manifolds/hamiltonian.jl new file mode 100644 index 0000000000..9062a7c678 --- /dev/null +++ b/test/manifolds/hamiltonian.jl @@ -0,0 +1,55 @@ +include("../header.jl") + +@testset "Hamiltonian matrices" begin + M = HamiltonianMatrices(4) + Mf = HamiltonianMatrices(4; parameter=:field) + # A has to be of the Form JS, S symmetric + p = + SymplecticElement(1.0) * [ + 1.0 2.0 0.0 0.0 + 2.0 3.0 0.0 0.0 + 0.0 0.0 1.0 0.0 + 0.0 0.0 0.0 4.0 + ] + q = + SymplecticElement(1.0) * [ + 4.0 3.0 0.0 0.0 + 3.0 2.0 0.0 0.0 + 0.0 0.0 4.0 0.0 + 0.0 0.0 0.0 1.0 + ] + @testset "Hamiltonian" begin + @test is_hamiltonian(p) + pH = Hamiltonian(p) + @test is_hamiltonian(pH) + @test Hamiltonian(pH) === pH + @test startswith("$(pH)", "Hamiltonian([") + @test size(pH) == size(p) + @test (pH^+).value == symplectic_inverse(p) + @test symplectic_inverse(pH).value == symplectic_inverse(p) + qH = Hamiltonian(q) + pqH = pH * qH + @test pqH isa Hamiltonian + @test pqH.value == p * q + pqH2 = pH + qH + @test pqH2 isa Hamiltonian + @test pqH2.value == p + q + pqH3 = pH - qH + @test pqH3 isa Hamiltonian + @test pqH3.value == p - q + @test_throws DomainError is_point(M, ones(4, 4), true) + @test_throws DomainError is_vector(M, p, ones(4, 4), true, true) + end + @testset "Basics" begin + @test repr(M) == "HamiltonianMatrices(4, ℝ)" + @test repr(Mf) == "HamiltonianMatrices(4, ℝ; parameter=:field)" + @test is_point(M, p) + @test is_point(M, Hamiltonian(p)) + @test is_vector(M, p, p) + @test get_embedding(M) == Euclidean(4, 4; field=ℝ) + @test get_embedding(Mf) == Euclidean(4, 4; field=ℝ, parameter=:field) + @test is_flat(M) + Random.seed!(42) + is_point(M, rand(M)) + end +end diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index 887627371c..f4970ca3d1 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Hyperbolic Space" begin M = Hyperbolic(2) diff --git a/test/manifolds/lorentz.jl b/test/manifolds/lorentz.jl index 59e0362818..d61cc8c32b 100644 --- a/test/manifolds/lorentz.jl +++ b/test/manifolds/lorentz.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Lorentz Manifold" begin M = Lorentz(3) diff --git a/test/manifolds/multinomial_doubly_stochastic.jl b/test/manifolds/multinomial_doubly_stochastic.jl index dd48083f72..22e55e77d9 100644 --- a/test/manifolds/multinomial_doubly_stochastic.jl +++ b/test/manifolds/multinomial_doubly_stochastic.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Multinomial doubly stochastic Matrices" begin M = MultinomialDoubleStochastic(3) diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index c04d07410f..f23fa4e760 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "MultinomialMatrices manifold" begin M = MultinomialMatrices(3, 2) diff --git a/test/manifolds/multinomial_symmetric.jl b/test/manifolds/multinomial_symmetric.jl index 58450b5bac..fcf0a2360e 100644 --- a/test/manifolds/multinomial_symmetric.jl +++ b/test/manifolds/multinomial_symmetric.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Multinomial symmetric matrices" begin M = MultinomialSymmetric(3) diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index 4f51907aea..b29678e972 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Oblique manifold" begin M = Oblique(3, 2) diff --git a/test/manifolds/positive_numbers.jl b/test/manifolds/positive_numbers.jl index c914ba0940..350e3d0373 100644 --- a/test/manifolds/positive_numbers.jl +++ b/test/manifolds/positive_numbers.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Positive Numbers" begin M = PositiveNumbers() diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index bace6ba9b7..192df1dced 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using HybridArrays, Random using StaticArrays: Dynamic diff --git a/test/manifolds/probability_simplex.jl b/test/manifolds/probability_simplex.jl index d2b6a5445d..e82af8b304 100644 --- a/test/manifolds/probability_simplex.jl +++ b/test/manifolds/probability_simplex.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Probability simplex" begin M = ProbabilitySimplex(2) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 5bbf9cd3cf..9f12478ea0 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using RecursiveArrayTools: ArrayPartition diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index 140da634cc..8a843d6096 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "ProjectiveSpace" begin @testset "Real" begin diff --git a/test/manifolds/quotient_manifold.jl b/test/manifolds/quotient_manifold.jl index 36f482439f..b1d6a2b084 100644 --- a/test/manifolds/quotient_manifold.jl +++ b/test/manifolds/quotient_manifold.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") struct DummyManifold <: AbstractManifold{ℝ} end struct DummyTotalSpace <: AbstractManifold{ℝ} end diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index 4a276b8def..dfd4aee46e 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Rotations" begin M = Rotations(2) diff --git a/test/manifolds/shape_space.jl b/test/manifolds/shape_space.jl index 777b298914..0873fe75ca 100644 --- a/test/manifolds/shape_space.jl +++ b/test/manifolds/shape_space.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "KendallsPreShapeSpace" begin M = KendallsPreShapeSpace(2, 3) diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index 7fd5a7600e..6db2139ad5 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "SkewSymmetricMatrices" begin @test SkewSymmetricMatrices(3) === SkewHermitianMatrices(3) diff --git a/test/manifolds/spd_fixed_determinant.jl b/test/manifolds/spd_fixed_determinant.jl index 21f01506fa..74c8f39ca7 100644 --- a/test/manifolds/spd_fixed_determinant.jl +++ b/test/manifolds/spd_fixed_determinant.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Isochoric matrices" begin M = SPDFixedDeterminant(2, 1.0) diff --git a/test/manifolds/spectrahedron.jl b/test/manifolds/spectrahedron.jl index ffbf6290f4..dd5737bc00 100644 --- a/test/manifolds/spectrahedron.jl +++ b/test/manifolds/spectrahedron.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Spectrahedron" begin M = Spectrahedron(4, 2) diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 3c04cb7be2..9031742fb2 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using Manifolds: induced_basis using ManifoldsBase: TFVector diff --git a/test/manifolds/sphere_symmetric_matrices.jl b/test/manifolds/sphere_symmetric_matrices.jl index 255a35e0bc..f5d8e02779 100644 --- a/test/manifolds/sphere_symmetric_matrices.jl +++ b/test/manifolds/sphere_symmetric_matrices.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "SphereSymmetricMatrices" begin M = SphereSymmetricMatrices(3) diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index 6154ce2a28..d075384a36 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Stiefel" begin @testset "Real" begin diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index 5e0ce75a1e..79a164dc97 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "SymmetricMatrices" begin M = SymmetricMatrices(3, ℝ) diff --git a/test/manifolds/symmetric_positive_definite.jl b/test/manifolds/symmetric_positive_definite.jl index e8d0e6c091..b01662de22 100644 --- a/test/manifolds/symmetric_positive_definite.jl +++ b/test/manifolds/symmetric_positive_definite.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Symmetric Positive Definite Matrices" begin M1 = SymmetricPositiveDefinite(3) diff --git a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl index 83eae0b079..d0e7e4fd79 100644 --- a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl +++ b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Symmetric Positive Semidefinite Matrices of Fixed Rank" begin @testset "Real Matrices" begin diff --git a/test/manifolds/symplectic.jl b/test/manifolds/symplectic.jl index e618d11968..deaa1c8767 100644 --- a/test/manifolds/symplectic.jl +++ b/test/manifolds/symplectic.jl @@ -1,347 +1,272 @@ -include("../utils.jl") +include("../header.jl") using FiniteDifferences using Manifolds: RiemannianProjectionBackend using ManifoldDiff -@testset "Symplectic" begin - @testset "Real" begin - Sp_2 = Symplectic(2 * 1) - Metr_Sp_2 = MetricManifold(Sp_2, RealSymplecticMetric()) - - p_2 = [0.0 1.0/2.0; -2.0 -2.0] - X1 = [ - -0.121212 0.121212 - 0.969697 -1.0 - ] - X2 = [ - 0.0 0.0 - 0.0 -1.0 - ] - - Sp_6 = Symplectic(6) - points = [ - [ - 1 1 3 0 0 0 - -1 -2 -4 0 0 0 - 3 0 7 0 0 0 - 3 4 10 14 5 -6 - -5 -9 -19 7 2 -3 - 0 0 0 -2 -1 1 - ], - [ - 0 0 0 -2 1 5 - 0 0 0 0 -1 -2 - 0 0 0 -1 -2 -3 - -1 2 -1 9 -4 -21 - -7 11 -5 -2 10 24 - 3 -4 2 -2 -7 -13 - ], - [ - 6 -1 11 -10 8 6 - 2 0 3 23 -14 -14 - -3 0 -5 -1 5 1 - 0 0 0 0 -1 0 - 0 0 0 5 -3 -3 - 0 0 0 3 -4 -2 - ], - [ - 2 -1 5 -4 4 2 - 2 0 3 11 -6 -6 - -1 0 -2 -4 7 3 - 0 0 0 0 -1 0 - 0 0 0 2 -1 -1 - 0 0 0 3 -4 -2 - ], +@testset "SymplecticMatrices" begin + M = SymplecticMatrices(2) + Metr_Sp_2 = MetricManifold(M, RealSymplecticMetric()) + + p_2 = [0.0 1.0/2.0; -2.0 -2.0] + X1 = [ + -0.121212 0.121212 + 0.969697 -1.0 + ] + X2 = [ + 0.0 0.0 + 0.0 -1.0 + ] + + Sp_6 = SymplecticMatrices(6) + points = [ + [ + 1.0 1.0 3.0 0.0 0.0 0.0 + -1.0 -2.0 -4.0 0.0 0.0 0.0 + 3.0 0.0 7.0 0.0 0.0 0.0 + 3.0 4.0 10.0 14.0 5.0 -6.0 + -5.0 -9.0 -19.0 7.0 2.0 -3.0 + 0.0 0.0 0.0 -2.0 -1.0 1.0 + ], + [ + 0.0 0.0 0.0 -2.0 1.0 5.0 + 0.0 0.0 0.0 0.0 -1.0 -2.0 + 0.0 0.0 0.0 -1.0 -2.0 -3.0 + -1.0 2.0 -1.0 9.0 -4.0 -21.0 + -7.0 11.0 -5.0 -2.0 10.0 24.0 + 3.0 -4.0 2.0 -2.0 -7.0 -13.0 + ], + [ + 6.0 -1.0 11.0 -10.0 8.0 6.0 + 2.0 0.0 3.0 23.0 -14.0 -14.0 + -3.0 0.0 -5.0 -1.0 5.0 1.0 + 0.0 0.0 0.0 0.0 -1.0 0.0 + 0.0 0.0 0.0 5.0 -3.0 -3.0 + 0.0 0.0 0.0 3.0 -4.0 -2.0 + ], + [ + 2.0 -1.0 5.0 -4.0 4.0 2.0 + 2.0 0.0 3.0 11.0 -6.0 -6.0 + -1.0 0.0 -2.0 -4.0 7.0 3.0 + 0.0 0.0 0.0 0.0 -1.0 0.0 + 0.0 0.0 0.0 2.0 -1.0 -1.0 + 0.0 0.0 0.0 3.0 -4.0 -2.0 + ], + ] + + large_tr_norm_points = [ + [ + 0.0 -3.0 -5.0 0.0 0.0 0.0 + -2.0 -3.0 4.0 0.0 0.0 0.0 + -3.0 -5.0 5.0 0.0 0.0 0.0 + 11.0 27.0 -5.0 5.0 -2.0 1.0 + -37.0 -29.0 117.0 40.0 -15.0 9.0 + 22.0 30.0 -47.0 -27.0 10.0 -6.0 + ], + [ + 0.0 0.0 0.0 7.0 -2.0 1.0 + 0.0 0.0 0.0 19.0 -5.0 3.0 + 0.0 0.0 0.0 -6.0 2.0 -1.0 + -1.0 1.0 8.0 -14.0 4.0 -2.0 + 0.0 -1.0 -2.0 -94.0 26.0 -15.0 + -1.0 -2.0 3.0 45.0 -11.0 7.0 + ], + ] + + @testset "Basics" begin + @test repr(M) == "SymplecticMatrices($(2), ℝ)" + @test_throws ArgumentError SymplecticMatrices(3) + @test representation_size(M) == (2, 2) + @test !is_flat(M) + + @test is_point(M, p_2) + @test_throws DomainError is_point(M, p_2 + I; error=:error) + + @test is_vector(M, p_2, X1; atol=1.0e-6) + @test is_vector(M, p_2, X2; atol=1.0e-12) + @test is_vector(M, p_2, X1 + X2; atol=1.0e-6) + @test_throws DomainError is_vector(M, p_2, X1 + [0.1 0.1; -0.1 0.1]; error=:error) + end + @testset "Symplectic Inverse" begin + I_2n = Array(I, 2, 2) + @test Manifolds.symplectic_inverse_times(M, p_2, p_2) == I_2n + @test Manifolds.symplectic_inverse_times!(M, copy(p_2), p_2, p_2) == I_2n + @test inv(M, p_2) * p_2 == I_2n + @test inv!(M, copy(p_2)) * p_2 == I_2n + end + @testset "Embedding" begin + x = [0.0 1.0/2.0; -2.0 -2.0] + y = similar(x) + z = embed(M, x) + @test z == x + + Y = similar(X1) + embed!(M, Y, p_2, X1) + @test Y == X1 + end + @testset "Retractions and Exponential Mapping" begin + q_exp = [ + -0.0203171 0.558648 + -1.6739 -3.19344 ] + @test isapprox(exp(M, p_2, X2), q_exp; atol=1.0e-5) + @test isapprox(retract(M, p_2, X2, ExponentialRetraction()), q_exp; atol=1.0e-5) - large_tr_norm_points = [ - [ - 0 -3 -5 0 0 0 - -2 -3 4 0 0 0 - -3 -5 5 0 0 0 - 11 27 -5 5 -2 1 - -37 -29 117 40 -15 9 - 22 30 -47 -27 10 -6 - ], - [ - 0 0 0 7 -2 1 - 0 0 0 19 -5 3 - 0 0 0 -6 2 -1 - -1 1 8 -14 4 -2 - 0 -1 -2 -94 26 -15 - -1 -2 3 45 -11 7 - ], + q_cay = [ + 0.0 0.5 + -2.0 -3.0 ] - - @testset "Basics" begin - @test repr(Sp_2) == "Symplectic($(2), ℝ)" - @test representation_size(Sp_2) == (2, 2) - @test base_manifold(Sp_2) === Sp_2 - @test !is_flat(Sp_2) - - @test is_point(Sp_2, p_2) - @test_throws DomainError is_point(Sp_2, p_2 + I; error=:error) - - @test is_vector(Sp_2, p_2, X1; atol=1.0e-6) - @test is_vector(Sp_2, p_2, X2; atol=1.0e-12) - @test is_vector(Sp_2, p_2, X1 + X2; atol=1.0e-6) - @test_throws DomainError is_vector( - Sp_2, + @test retract(M, p_2, X2) == q_cay + @test retract(M, p_2, X2, CayleyRetraction()) == q_cay + + X_inv_cayley_retraction = inverse_retract(M, p_2, q_cay) + X_inv_cayley_retraction_2 = + inverse_retract(M, p_2, q_cay, CayleyInverseRetraction()) + @test X_inv_cayley_retraction == X_inv_cayley_retraction_2 + @test X_inv_cayley_retraction ≈ X2 + end + @testset "Riemannian metric" begin + X1_p_norm = 0.49259905148939337 + @test norm(M, p_2, X1) == X1_p_norm + @test norm(M, p_2, X1) == √(inner(M, p_2, X1, X1)) + + X2_p_norm = 1 / 2 + @test norm(M, p_2, X2) == X2_p_norm + @test norm(M, p_2, X2) == √(inner(M, p_2, X2, X2)) + + q_2 = retract(M, p_2, X2, ExponentialRetraction()) + approximate_p_q_geodesic_distance = 0.510564444555605 + @test isapprox(distance(M, p_2, q_2), approximate_p_q_geodesic_distance; atol=1e-14) + + # Project tangent vector into (T_pSp)^{\perp}: + Extended_Sp_2 = MetricManifold(get_embedding(M), ExtendedSymplecticMetric()) + proj_normal_X2 = Manifolds.project_normal!(Extended_Sp_2, copy(X2), p_2, X2) + @test isapprox(proj_normal_X2, zero(X2); atol=1.0e-16) + + # Project Project matrix A ∈ ℝ^{2×2} onto (T_pSp): + A_2 = [5.0 -21.5; 3.14 14.9] + A_2_proj = similar(A_2) + project!(Extended_Sp_2, A_2_proj, p_2, A_2) + @test is_vector(M, p_2, A_2_proj; atol=1.0e-16) + + # Change representer of A onto T_pSp: + @testset "Change Representer" begin + A_2_representer = change_representer( + MetricManifold(get_embedding(M), ExtendedSymplecticMetric()), + EuclideanMetric(), p_2, - X1 + [0.1 0.1; -0.1 0.1]; - error=:error, + A_2, ) - end - @testset "Symplectic Inverse" begin - I_2n = Array(I, 2, 2) - @test Manifolds.symplectic_inverse_times(Sp_2, p_2, p_2) == I_2n - @test Manifolds.symplectic_inverse_times!(Sp_2, copy(p_2), p_2, p_2) == I_2n - @test inv(Sp_2, p_2) * p_2 == I_2n - @test inv!(Sp_2, copy(p_2)) * p_2 == I_2n - end - @testset "Embedding" begin - x = [0.0 1.0/2.0; -2.0 -2.0] - y = similar(x) - z = embed(Sp_2, x) - @test z == x - - Y = similar(X1) - embed!(Sp_2, Y, p_2, X1) - @test Y == X1 - end - @testset "Retractions and Exponential Mapping" begin - q_exp = [ - -0.0203171 0.558648 - -1.6739 -3.19344 - ] - @test isapprox(exp(Sp_2, p_2, X2), q_exp; atol=1.0e-5) - @test isapprox( - retract(Sp_2, p_2, X2, ExponentialRetraction()), - q_exp; - atol=1.0e-5, - ) - - q_cay = [ - 0.0 0.5 - -2.0 -3.0 - ] - @test retract(Sp_2, p_2, X2) == q_cay - @test retract(Sp_2, p_2, X2, CayleyRetraction()) == q_cay - - X_inv_cayley_retraction = inverse_retract(Sp_2, p_2, q_cay) - X_inv_cayley_retraction_2 = - inverse_retract(Sp_2, p_2, q_cay, CayleyInverseRetraction()) - @test X_inv_cayley_retraction == X_inv_cayley_retraction_2 - @test X_inv_cayley_retraction ≈ X2 - end - @testset "Riemannian metric" begin - X1_p_norm = 0.49259905148939337 - @test norm(Sp_2, p_2, X1) == X1_p_norm - @test norm(Sp_2, p_2, X1) == √(inner(Sp_2, p_2, X1, X1)) - - X2_p_norm = 1 / 2 - @test norm(Sp_2, p_2, X2) == X2_p_norm - @test norm(Sp_2, p_2, X2) == √(inner(Sp_2, p_2, X2, X2)) - - q_2 = retract(Sp_2, p_2, X2, ExponentialRetraction()) - approximate_p_q_geodesic_distance = 0.510564444555605 - @test isapprox( - distance(Sp_2, p_2, q_2), - approximate_p_q_geodesic_distance; - atol=1e-14, - ) - - # Project tangent vector into (T_pSp)^{\perp}: - Extended_Sp_2 = MetricManifold(get_embedding(Sp_2), ExtendedSymplecticMetric()) - proj_normal_X2 = Manifolds.project_normal!(Extended_Sp_2, copy(X2), p_2, X2) - @test isapprox(proj_normal_X2, zero(X2); atol=1.0e-16) - - # Project Project matrix A ∈ ℝ^{2 × 2} onto (T_pSp): - A_2 = [5.0 -21.5; 3.14 14.9] - A_2_proj = similar(A_2) - project!(Extended_Sp_2, A_2_proj, p_2, A_2) - @test is_vector(Sp_2, p_2, A_2_proj; atol=1.0e-16) - - # Change representer of A onto T_pSp: - @testset "Change Representer" begin - A_2_representer = change_representer( - MetricManifold(get_embedding(Sp_2), ExtendedSymplecticMetric()), - EuclideanMetric(), - p_2, - A_2, - ) - @test isapprox( - inner(Sp_2, p_2, A_2_representer, X1), - tr(A_2' * X1); - atol=1.0e-12, - ) - @test isapprox( - inner(Sp_2, p_2, A_2_representer, X2), - tr(A_2' * X2); - atol=1.0e-12, - ) - @test isapprox( - inner(Sp_2, p_2, A_2_representer, A_2), - norm(A_2)^2; - atol=1.0e-12, - ) - end - end - @testset "Generate random points/tangent vectors" begin - M_big = Symplectic(20) - p_big = rand(M_big) - @test is_point(M_big, p_big; error=:error, atol=1.0e-12) - X_big = rand(M_big; vector_at=p_big) - @test is_vector(M_big, p_big, X_big; error=:error, atol=1.0e-12) - end - @testset "test_manifold(Symplectic(6), ...)" begin - @testset "Type $(Matrix{Float64})" begin - type = Matrix{Float64} - pts = convert.(type, points) - test_manifold( - Sp_6, - cat(pts, large_tr_norm_points; dims=1); - retraction_methods=[CayleyRetraction(), ExponentialRetraction()], - default_retraction_method=CayleyRetraction(), - default_inverse_retraction_method=CayleyInverseRetraction(), - test_inplace=true, - is_point_atol_multiplier=1.0e8, - is_tangent_atol_multiplier=1.0e6, - retraction_atol_multiplier=1.0e4, - test_project_tangent=true, - test_injectivity_radius=false, - test_exp_log=false, - test_representation_size=true, - ) - end - - TEST_FLOAT32 && @testset "Type $(Matrix{Float32})" begin - type = Matrix{Float64} - pts = convert.(type, points) - test_manifold( - Sp_6, - pts; - retraction_methods=[CayleyRetraction(), ExponentialRetraction()], - default_retraction_method=CayleyRetraction(), - default_inverse_retraction_method=CayleyInverseRetraction(), - test_inplace=true, - is_point_atol_multiplier=1.0e8, - is_tangent_atol_multiplier=1.0e6, - retraction_atol_multiplier=1.0e4, - test_project_tangent=true, - test_injectivity_radius=false, - test_exp_log=false, - test_representation_size=true, - ) - end - - TEST_STATIC_SIZED && @testset "Type $(MMatrix{6, 6, Float64, 36})" begin - type = MMatrix{6,6,Float64,36} - pts = convert.(type, points) - test_manifold( - Sp_6, - pts; - retraction_methods=[CayleyRetraction(), ExponentialRetraction()], - default_retraction_method=CayleyRetraction(), - default_inverse_retraction_method=CayleyInverseRetraction(), - test_inplace=true, - is_point_atol_multiplier=1.0e7, - is_tangent_atol_multiplier=1.0e6, - retraction_atol_multiplier=1.0e4, - test_project_tangent=false, # Cannot solve 'sylvester' for MMatrix-type. - test_injectivity_radius=false, - test_exp_log=false, - test_representation_size=true, - ) - end - end - - @testset "Gradient Computations" begin - test_f(p) = tr(p) - Q_grad = SymplecticMatrix(points[1]) - analytical_grad_f(p) = (1 / 2) * (p * Q_grad * p * Q_grad + p * p') - - p_grad = convert(Array{Float64}, points[1]) - fd_diff = RiemannianProjectionBackend(ManifoldDiff.FiniteDifferencesBackend()) - - @test isapprox( - Manifolds.gradient(Sp_6, test_f, p_grad, fd_diff), - analytical_grad_f(p_grad); - atol=1.0e-9, - ) - @test isapprox( - Manifolds.gradient(Sp_6, test_f, p_grad, fd_diff; extended_metric=false), - analytical_grad_f(p_grad); - atol=1.0e-9, - ) - - grad_f_p = similar(p_grad) - Manifolds.gradient!(Sp_6, test_f, grad_f_p, p_grad, fd_diff) - @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) - - Manifolds.gradient!( - Sp_6, - test_f, - grad_f_p, - p_grad, - fd_diff; - extended_metric=false, - ) - @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) + @test isapprox(inner(M, p_2, A_2_representer, X1), tr(A_2' * X1); atol=1.0e-12) + @test isapprox(inner(M, p_2, A_2_representer, X2), tr(A_2' * X2); atol=1.0e-12) + @test isapprox(inner(M, p_2, A_2_representer, A_2), norm(A_2)^2; atol=1.0e-12) end end - - @testset "SymplecticMatrix" begin - # TODO: Test for different type matrices. - @test SymplecticMatrix() == SymplecticMatrix(1) - Sp_4 = Symplectic(4) + @testset "Generate random points/tangent vectors" begin + M_big = SymplecticMatrices(20) + Random.seed!(49) + p_big = rand(M_big) + @test is_point(M_big, p_big; error=:error, atol=1.0e-9) + X_big = rand(M_big; vector_at=p_big) + @test is_vector(M_big, p_big, X_big; error=:error, atol=1.0e-9) + end + @testset "test_manifold(SymplecticMatrices(6))" begin + test_manifold( + Sp_6, + cat(points, large_tr_norm_points; dims=1); + retraction_methods=[CayleyRetraction(), ExponentialRetraction()], + default_retraction_method=CayleyRetraction(), + default_inverse_retraction_method=CayleyInverseRetraction(), + test_inplace=true, + is_point_atol_multiplier=1.0e8, + is_tangent_atol_multiplier=1.0e6, + retraction_atol_multiplier=1.0e4, + test_project_tangent=true, + test_injectivity_radius=false, + test_exp_log=false, + test_representation_size=true, + ) + end + @testset "Gradient Computations" begin + test_f(p) = tr(p) + J = SymplecticElement(points[1]) + analytical_grad_f(p) = (1 / 2) * (p * J * p * J + p * p') + + p_grad = points[1] + fd_diff = RiemannianProjectionBackend(ManifoldDiff.FiniteDifferencesBackend()) + + @test isapprox( + Manifolds.gradient(Sp_6, test_f, p_grad, fd_diff), + analytical_grad_f(p_grad); + atol=1.0e-9, + ) + @test isapprox( + Manifolds.gradient(Sp_6, test_f, p_grad, fd_diff; extended_metric=false), + analytical_grad_f(p_grad); + atol=1.0e-9, + ) + + grad_f_p = similar(p_grad) + Manifolds.gradient!(Sp_6, test_f, grad_f_p, p_grad, fd_diff) + @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) + + Manifolds.gradient!(Sp_6, test_f, grad_f_p, p_grad, fd_diff; extended_metric=false) + @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) + + X = riemannian_gradient(Sp_6, p_grad, one(p_grad)) + X2 = similar(X) + riemannian_gradient!(Sp_6, X2, p_grad, one(p_grad)) + @test isapprox(Sp_6, p_grad, X, X2) + end + @testset "SymplecticElement" begin + @test SymplecticElement() == SymplecticElement(1) + Sp_4 = SymplecticMatrices(4) pQ_1 = [ - 0 0 -2 3 - 0 0 1 -1 - -1 -1 0 0 - -3 -2 4 -4 + 0.0 0 -2.0 3.0 + 0.0 0.0 1.0 -1.0 + -1.0 -1.0 0.0 0.0 + -3.0 -2.0 4.0 -4.0 ] pQ_2 = [ - 0 0 -2 5 - 0 0 1 -2 - -2 -1 0 0 - -5 -2 4 -8 + 0.0 0.0 -2.0 5.0 + 0.0 0.0 1.0 -2.0 + -2.0 -1.0 0.0 0.0 + -5.0 -2.0 4 -8.0 ] p_odd_row = [ - 0 0 -1 3 - 0 0 1 0 - -2 -1 0 0 + 0.0 0.0 -1.0 3.0 + 0.0 0.0 1.0 0.0 + -2.0 -1.0 0.0 0.0 ] p_odd_col = [ - 0 -2 5 - 0 1 -2 - -1 0 0 - -2 4 -8 + 0.0 -2.0 5.0 + 0.0 1.0 -2.0 + -1.0 0.0 0.0 + -2.0 4.0 -8.0 ] - Q = SymplecticMatrix(pQ_1, pQ_2) - Q2 = SymplecticMatrix(1) + Q = SymplecticElement(pQ_1, pQ_2) + Q2 = SymplecticElement(1.0) @testset "Type Basics" begin @test Q == Q2 @test ndims(Q) == 2 @test copy(Q) == Q - @test eltype(SymplecticMatrix(1 // 1)) == Rational{Int64} - @test convert(SymplecticMatrix{Float64}, Q) == SymplecticMatrix(1.0) - @test "$Q" == "SymplecticMatrix{Int64}(): 1*[0 I; -I 0]" + @test eltype(SymplecticElement(1 // 1)) == Rational{Int64} + @test convert(SymplecticElement{Float64}, Q) == SymplecticElement(1.0) + @test "$Q" == "SymplecticElement{Float64}(): 1.0*[0 I; -I 0]" @test ( - "$(SymplecticMatrix(1 + 2im))" == - "SymplecticMatrix{Complex{Int64}}(): (1 + 2im)*[0 I; -I 0]" + "$(SymplecticElement(1 + 2im))" == + "SymplecticElement{Complex{Int64}}(): (1 + 2im)*[0 I; -I 0]" ) end @testset "Matrix Operations" begin - @test -Q == SymplecticMatrix(-1) - @test (2 * Q) * (5 // 6) == SymplecticMatrix(5 // 3) + @test -Q == SymplecticElement(-1.0) + @test (2 * Q) * (5 / 6) == SymplecticElement(5 / 3) @testset "Powers" begin @test inv(Q) * Q == I @test ( - inv(SymplecticMatrix(-4.0 + 8im)) * SymplecticMatrix(-4.0 + 8im) == + inv(SymplecticElement(-4.0 + 8im)) * SymplecticElement(-4.0 + 8im) == UniformScaling(1.0 + 0.0im) ) @test Q * Q == -I @@ -351,54 +276,55 @@ using ManifoldDiff end @testset "Addition (subtraction)" begin @test Q + Q == 2 * Q - @test Q - SymplecticMatrix(1.0) == SymplecticMatrix(0.0) + @test Q - SymplecticElement(1.0) == SymplecticElement(0.0) @test Q + pQ_1 == [ - 0 0 -1 3 - 0 0 1 0 - -2 -1 0 0 - -3 -3 4 -4 + 0.0 0.0 -1.0 3.0 + 0.0 0.0 1.0 0.0 + -2.0 -1.0 0.0 0.0 + -3.0 -3.0 4.0 -4.0 ] @test Q - pQ_1 == [ - 0 0 3 -3 - 0 0 -1 2 - 0 1 0 0 - 3 1 -4 4 + 0.0 0.0 3.0 -3.0 + 0.0 0.0 -1.0 2.0 + 0.0 1.0 0.0 0.0 + 3.0 1.0 -4.0 4.0 ] @test pQ_1 - Q == [ - 0 0 -3 3 - 0 0 1 -2 - 0 -1 0 0 - -3 -1 4 -4 + 0.0 0.0 -3.0 3.0 + 0.0 0.0 1.0 -2.0 + 0.0 -1.0 0.0 0.0 + -3.0 -1.0 4.0 -4.0 ] @test (pQ_1 + Q) == (Q + pQ_1) @test_throws ArgumentError Q + p_odd_row end @testset "Transpose-Adjoint" begin - @test Q' == SymplecticMatrix(-1) - @test transpose(SymplecticMatrix(10)) == SymplecticMatrix(-10) - @test transpose(SymplecticMatrix(1 - 2.0im)) == SymplecticMatrix(-1 + 2.0im) + @test Q' == SymplecticElement(-1.0) + @test transpose(SymplecticElement(10.0)) == SymplecticElement(-10.0) + @test transpose(SymplecticElement(1 - 2.0im)) == + SymplecticElement(-1 + 2.0im) @test adjoint(Q) == -Q - @test adjoint(SymplecticMatrix(1 - 2.0im)) == SymplecticMatrix(-1 - 2.0im) - @test adjoint(SymplecticMatrix(-1im)) == SymplecticMatrix(-1im) - @test adjoint(SymplecticMatrix(2.0)) == SymplecticMatrix(-2.0) + @test adjoint(SymplecticElement(1 - 2.0im)) == SymplecticElement(-1 - 2.0im) + @test adjoint(SymplecticElement(-1im)) == SymplecticElement(-1im) + @test adjoint(SymplecticElement(2.0)) == SymplecticElement(-2.0) end @testset "Inplace mul!" begin z1 = [1 + 2im; 1 - 2im] @test lmul!(Q, copy(z1)) == Q * z1 @test lmul!(Q, copy(p_odd_col)) == [ - -1 0 0 - -2 4 -8 - 0 2 -5 - 0 -1 2 + -1 0 0.0 + -2 4 -8.0 + 0 2 -5.0 + 0 -1 2.0 ] @test_throws ArgumentError lmul!(Q, copy(p_odd_row)) @test rmul!(copy(z1'), Q) == z1' * Q @test rmul!(copy(p_odd_row), Q) == [ - 1 -3 0 0 - -1 0 0 0 - 0 0 -2 -1 + 1 -3 0 0.0 + -1 0 0 0.0 + 0 0 -2 -1.0 ] @test_throws ArgumentError rmul!(copy(p_odd_col), Q) @@ -422,8 +348,8 @@ using ManifoldDiff end end @testset "field parameter" begin - Sp_2 = Symplectic(2; parameter=:field) - @test typeof(get_embedding(Sp_2)) === Euclidean{Tuple{Int,Int},ℝ} - @test repr(Sp_2) == "Symplectic(2, ℝ; parameter=:field)" + M = SymplecticMatrices(2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SymplecticMatrices(2, ℝ; parameter=:field)" end end diff --git a/test/manifolds/symplecticgrassmann.jl b/test/manifolds/symplecticgrassmann.jl new file mode 100644 index 0000000000..8bd7f1f1cf --- /dev/null +++ b/test/manifolds/symplecticgrassmann.jl @@ -0,0 +1,126 @@ +include("../header.jl") + +@testset "Symplectic Grassmann" begin + M = SymplecticGrassmann(6, 4) + Mf = SymplecticGrassmann(6, 4; parameter=:field) + p = [ + 0.0 0.0 -5.0 -1.0 + 0.0 0.0 9.0 -2.0 + 0.0 0.0 -2.0 1.0 + -2.0 -9.0 -3.0 6.0 + -3.0 -13.0 -21.0 9.0 + -8.0 -36.0 18.0 -6.0 + ] + q = [ + 0.0 0.0 -3.0 1.0 + 0.0 0.0 8.0 -3.0 + 0.0 0.0 -2.0 1.0 + -1.0 -4.0 -6.0 3.0 + -1.0 -3.0 -21.0 9.0 + -2.0 -6.0 18.0 -6.0 + ] + X = [ + 0.0 0.0 4.25 4.25 + 0.0 0.0 0.125 0.125 + 0.0 0.0 -1.125 -1.125 + 3.625 18.125 -10.875 -10.875 + 5.0 25.0 -9.0 -9.0 + 13.5 67.5 4.5 4.5 + ] + Y = [ + -0.02648060 0.00416977 0.01130802 0.01015956 + 0.01718954 -0.00680433 0.02364406 -0.00083272 + 0.00050392 0.00191916 -0.01035902 -0.00079734 + 0.01811917 -0.02307032 -0.04297277 -0.05409099 + -0.02529516 0.00959934 -0.08594555 -0.06117803 + -0.02823014 0.00029946 -0.04196034 -0.04145413 + ] + @testset "Basics" begin + @test repr(M) == "SymplecticGrassmann(6, 4; field=ℝ)" + @test repr(Mf) == "SymplecticGrassmann(6, 4; field=ℝ; parameter=:field)" + @test manifold_dimension(M) == 4 * (6 - 4) + for _M in [M, Mf] + @test is_point(M, p) + @test is_vector(_M, p, X) + end + @test get_embedding(M) == SymplecticStiefel(6, 4) + end + @testset "Embedding / Total Space" begin + @test get_embedding(M) == SymplecticStiefel(6, 4) + pE = similar(p) + embed!(M, pE, p) + @test p == pE + embed!(M, pE, StiefelPoint(p)) + @test p == pE + @test embed(M, StiefelPoint(p)) == p + XE = similar(X) + embed!(M, XE, p, X) + @test XE == X + embed!(M, XE, StiefelPoint(p), StiefelTVector(X)) + @test XE == X + @test embed(M, StiefelPoint(p), StiefelTVector(X)) == X + end + @testset "Expnential and Retractions" begin + @test inner(M, p, X, X) ≈ norm(M, p, X)^2 + N = get_embedding(M) + @test isapprox(N, exp(M, p, X), exp(N, p, X)) + rtm = CayleyRetraction() + r = retract(M, p, X, rtm) + @test is_point(M, r) + irtm = CayleyInverseRetraction() + X2 = inverse_retract(M, p, r, irtm) + @test isapprox(M, p, X, X2) + @test is_vector(M, p, X2) + end + @testset "Riemannian Gradient conversion" begin + A = Matrix{Float64}(I, 6, 6)[:, 1:4] + Z = riemannian_gradient(M, p, A) + Z2 = similar(Z) + riemannian_gradient!(M, Z2, p, A) + @test isapprox(M, Z2, Z) + # How can we better test that this is a correct gradient? + # Or what can we further test here? + end + @testset "Projector representation" begin + # cf. Propo 4.3 BendokatZimmermann, φ and + φ(p) = p * symplectic_inverse(p) + # for dφ the proof we keve to consider their Ω, hence the /p + function dφ(p, X) + # \bar Ω is Xp^+ + pX^+ + return X * symplectic_inverse(p) + p * symplectic_inverse(X) + end + pP = ProjectorPoint(φ(p)) + Xe = dφ(p, X) + XP = ProjectorTVector(Xe) + @test is_point(M, pP) + # Fix + @test is_vector(M, pP, XP; atol=1e-9, error=:error) + Pf1 = zeros(6, 6) + Pf1[1, 2] = 1.0 + # No projector + @test_throws DomainError is_point(M, ProjectorPoint(Pf1), true) + Pf2 = zeros(6, 6) + Pf2[1, 1] = 1.0 + # Pf2 not equal to its symplectic inverse + @test_throws DomainError is_point(M, ProjectorPoint(Pf2), true) + ps = p[:, [1, 3]] # This is on SpSt(6,2) + Pf3 = φ(ps) # too low rank + @test_throws DomainError is_point(M, ProjectorPoint(Pf3), true) + + # Not Hamiltonian + Xf1 = ProjectorTVector(Matrix{Float64}(I, 6, 6)) + @test_throws DomainError is_vector(M, pP, Xf1; error=:error) + # X^+ = X, but Xp + pX not correct + Xf2 = ProjectorTVector(0.5 .* (symplectic_inverse(X * X') + X * X')) + @test_throws DomainError is_vector(M, pP, Xf2; error=:error) + @test get_embedding(M, pP) == Euclidean(6, 6) + get_embedding(Mf, pP) == Euclidean(6, 6; parameter=:field) + @test embed(M, pP) == pP.value + pE = zeros(6, 6) + embed!(M, pE, pP) + @test pE == pP.value + @test embed(M, pP, XP) == XP.value + embed!(M, pE, pP, XP) + @test pE == XP.value + end +end diff --git a/test/manifolds/symplecticstiefel.jl b/test/manifolds/symplecticstiefel.jl index db18123d23..bee161cd0b 100644 --- a/test/manifolds/symplecticstiefel.jl +++ b/test/manifolds/symplecticstiefel.jl @@ -1,10 +1,10 @@ -include("../utils.jl") +include("../header.jl") using FiniteDifferences using Manifolds: RiemannianProjectionBackend using ManifoldDiff function Ω(::SymplecticStiefel, p, X) - Q = SymplecticMatrix(X, p) + Q = SymplecticElement(X, p) pT_p = lu(p' * p) inv_pTp_pT = pT_p \ p' @@ -22,296 +22,291 @@ function exp_naiive!(M::SymplecticStiefel, q, p, X) end @testset "SymplecticStiefel" begin - @testset "Real" begin - SpSt_6_4 = SymplecticStiefel(2 * 3, 2 * 2) + M = SymplecticStiefel(6, 4) - p_6_4 = Array{Float64}([ - 0 0 -5 -1 - 0 0 9 -2 - 0 0 -2 1 - -2 -9 -3 6 - -3 -13 -21 9 - -8 -36 18 -6 - ]) - q_6_4 = Array{Float64}([ - 0 0 -3 1 - 0 0 8 -3 - 0 0 -2 1 - -1 -4 -6 3 - -1 -3 -21 9 - -2 -6 18 -6 - ]) - X1 = [ - 0.0 0.0 4.25 4.25 - 0.0 0.0 0.125 0.125 - 0.0 0.0 -1.125 -1.125 - 3.625 18.125 -10.875 -10.875 - 5.0 25.0 -9.0 -9.0 - 13.5 67.5 4.5 4.5 - ] - X2 = [ - -0.02648060 0.00416977 0.01130802 0.01015956 - 0.01718954 -0.00680433 0.02364406 -0.00083272 - 0.00050392 0.00191916 -0.01035902 -0.00079734 - 0.01811917 -0.02307032 -0.04297277 -0.05409099 - -0.02529516 0.00959934 -0.08594555 -0.06117803 - -0.02823014 0.00029946 -0.04196034 -0.04145413 - ] + p_6_4 = [ + 0.0 0.0 -5.0 -1.0 + 0.0 0.0 9.0 -2.0 + 0.0 0.0 -2.0 1.0 + -2.0 -9.0 -3.0 6.0 + -3.0 -13.0 -21.0 9.0 + -8.0 -36.0 18.0 -6.0 + ] + q_6_4 = [ + 0.0 0.0 -3.0 1.0 + 0.0 0.0 8.0 -3.0 + 0.0 0.0 -2.0 1.0 + -1.0 -4.0 -6.0 3.0 + -1.0 -3.0 -21.0 9.0 + -2.0 -6.0 18.0 -6.0 + ] + X1 = [ + 0.0 0.0 4.25 4.25 + 0.0 0.0 0.125 0.125 + 0.0 0.0 -1.125 -1.125 + 3.625 18.125 -10.875 -10.875 + 5.0 25.0 -9.0 -9.0 + 13.5 67.5 4.5 4.5 + ] + X2 = [ + -0.02648060 0.00416977 0.01130802 0.01015956 + 0.01718954 -0.00680433 0.02364406 -0.00083272 + 0.00050392 0.00191916 -0.01035902 -0.00079734 + 0.01811917 -0.02307032 -0.04297277 -0.05409099 + -0.02529516 0.00959934 -0.08594555 -0.06117803 + -0.02823014 0.00029946 -0.04196034 -0.04145413 + ] - points = [ - [ - 0 0 2 6 - 0 0 1 -4 - 0 0 0 1 - -1 0 -7 1 - 1 0 -4 -24 - 10 -1 4 -4 - ], - [ - 0 0 5 8 - 0 0 -2 -6 - 0 0 0 1 - 1 0 -4 3 - 3 0 -19 -34 - 10 -1 1 -6 - ], - [ - 5 -3 0 0 - 11 -6 0 0 - -2 1 0 0 - 8 -5 -3 -3 - 0 0 2 1 - 5 -3 3 -2 - ], - [ - 1 -1 0 0 - 3 -2 0 0 - 0 0 0 0 - 2 -2 -2 -3 - 0 0 1 1 - 1 -1 1 -2 - ], - [ - 0 0 0 0 - 0 0 -1 0 - 0 0 4 -1 - -1 -10 -5 2 - 1 4 -2 0 - 0 1 -16 4 - ], - [ - 0 0 0 0 - 0 0 -1 0 - 0 0 4 -1 - 0 -6 -5 2 - 1 4 -2 0 - 0 1 -16 4 - ], - ] + points = [ + [ + 0 0 2 6 + 0 0 1 -4 + 0 0 0 1 + -1 0 -7 1 + 1 0 -4 -24 + 10 -1 4 -4 + ], + [ + 0 0 5 8 + 0 0 -2 -6 + 0 0 0 1 + 1 0 -4 3 + 3 0 -19 -34 + 10 -1 1 -6 + ], + [ + 5 -3 0 0 + 11 -6 0 0 + -2 1 0 0 + 8 -5 -3 -3 + 0 0 2 1 + 5 -3 3 -2 + ], + [ + 1 -1 0 0 + 3 -2 0 0 + 0 0 0 0 + 2 -2 -2 -3 + 0 0 1 1 + 1 -1 1 -2 + ], + [ + 0 0 0 0 + 0 0 -1 0 + 0 0 4 -1 + -1 -10 -5 2 + 1 4 -2 0 + 0 1 -16 4 + ], + [ + 0 0 0 0 + 0 0 -1 0 + 0 0 4 -1 + 0 -6 -5 2 + 1 4 -2 0 + 0 1 -16 4 + ], + ] - close_points = [ - [ - -2 1 -2 -14 - 1 0 1 11 - 1 0 4 9 - 0 0 0 1 - 0 0 0 -1 - 0 0 1 3 - ], - [ - -3 1 -2 -10 - 1 0 1 7 - 1 0 4 10 - 0 0 0 1 - 0 0 0 0 - 0 0 1 3 - ], - [ - -2 1 -6 -26 - 1 0 5 23 - 3 -1 2 3 - 0 0 1 4 - 0 0 0 -1 - 0 0 1 3 - ], - ] + close_points = [ + [ + -2 1 -2 -14 + 1 0 1 11 + 1 0 4 9 + 0 0 0 1 + 0 0 0 -1 + 0 0 1 3 + ], + [ + -3 1 -2 -10 + 1 0 1 7 + 1 0 4 10 + 0 0 0 1 + 0 0 0 0 + 0 0 1 3 + ], + [ + -2 1 -6 -26 + 1 0 5 23 + 3 -1 2 3 + 0 0 1 4 + 0 0 0 -1 + 0 0 1 3 + ], + ] - @testset "Basics" begin - @test repr(SpSt_6_4) == "SymplecticStiefel(6, 4, ℝ)" - @test representation_size(SpSt_6_4) == (6, 4) - @test base_manifold(SpSt_6_4) === SpSt_6_4 - @test get_total_space(SpSt_6_4) == Symplectic(6) - @test !is_flat(SpSt_6_4) + @testset "Basics" begin + @test_throws ArgumentError SymplecticStiefel(5, 4) + @test_throws ArgumentError SymplecticStiefel(6, 3) + @test repr(M) == "SymplecticStiefel(6, 4; field=ℝ)" + @test representation_size(M) == (6, 4) + @test base_manifold(M) === M + @test get_total_space(M) == SymplecticMatrices(6) + @test !is_flat(M) - @test is_point(SpSt_6_4, p_6_4) - @test_throws DomainError is_point(SpSt_6_4, 2 * p_6_4; error=:error) + @test is_point(M, p_6_4) + @test_throws DomainError is_point(M, 2 * p_6_4; error=:error) - @test is_vector(SpSt_6_4, p_6_4, X1; atol=1.0e-12) - @test is_vector(SpSt_6_4, p_6_4, X2; atol=1.0e-6) - @test_throws DomainError is_vector( - SpSt_6_4, - p_6_4, - X2; - error=:error, - atol=1.0e-12, - ) - @test is_vector(SpSt_6_4, p_6_4, X1 + X2; atol=1.0e-6) - @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X1 + p_6_4; error=:error) - end - @testset "Symplectic Inverse" begin - I_2k = Array(I, 4, 4) - @test Manifolds.symplectic_inverse_times(SpSt_6_4, p_6_4, p_6_4) == I_2k - @test Manifolds.symplectic_inverse_times!( - SpSt_6_4, - zeros(4, 4), - p_6_4, - p_6_4, - ) == I_2k - @test inv(SpSt_6_4, p_6_4) * p_6_4 == I_2k - @test inv!(SpSt_6_4, copy(p_6_4'), p_6_4) * p_6_4 == I_2k - end - @testset "Embedding" begin - x = [ - 1 1 -9 7 - -1 -1 -13 11 - 7 8 -22 19 - 0 0 7 -6 - 0 0 -1 1 - 0 0 -1 1 - ] - y = similar(x) - z = embed(SpSt_6_4, x) - @test z == x - - Y = similar(X1) - embed!(SpSt_6_4, Y, p_6_4, X1) - @test Y == X1 - end - @testset "Retractions and Exponential Mapping" begin - @test isapprox(retract(SpSt_6_4, p_6_4, X1), q_6_4; atol=1.0e-12) - @test isapprox( - retract(SpSt_6_4, p_6_4, X1, CayleyRetraction()), - q_6_4; - atol=1.0e-12, - ) + @test is_vector(M, p_6_4, X1; atol=1.0e-12) + @test is_vector(M, p_6_4, X2; atol=1.0e-6) + @test_throws DomainError is_vector(M, p_6_4, X2; error=:error, atol=1.0e-12) + @test is_vector(M, p_6_4, X1 + X2; atol=1.0e-6) + @test_throws DomainError is_vector(M, p_6_4, X1 + p_6_4; error=:error) + end + @testset "Symplectic Inverse" begin + I_2k = Array(I, 4, 4) + @test Manifolds.symplectic_inverse_times(M, p_6_4, p_6_4) == I_2k + @test Manifolds.symplectic_inverse_times!(M, zeros(4, 4), p_6_4, p_6_4) == I_2k + @test inv(M, p_6_4) * p_6_4 == I_2k + @test inv!(M, copy(p_6_4'), p_6_4) * p_6_4 == I_2k + end + @testset "Embedding" begin + x = [ + 1 1 -9 7 + -1 -1 -13 11 + 7 8 -22 19 + 0 0 7 -6 + 0 0 -1 1 + 0 0 -1 1 + ] + y = similar(x) + z = embed(M, x) + @test z == x - X_inv_cayley_retraction = inverse_retract(SpSt_6_4, p_6_4, q_6_4) - X_inv_cayley_retraction_2 = - inverse_retract(SpSt_6_4, p_6_4, q_6_4, CayleyInverseRetraction()) - @test isapprox(X_inv_cayley_retraction, X_inv_cayley_retraction_2; atol=1.0e-16) - @test isapprox(X_inv_cayley_retraction, X1; atol=1.0e-12) - end - @testset "Riemannian Metric" begin - X1_norm = 37.85466645 - @test isapprox(norm(SpSt_6_4, p_6_4, X1), X1_norm; atol=1.0e-8) - @test isapprox( - norm(SpSt_6_4, p_6_4, X1), - √inner(SpSt_6_4, p_6_4, X1, X1); - atol=1.0e-8, - ) + Y = similar(X1) + embed!(M, Y, p_6_4, X1) + @test Y == X1 + end + @testset "Retractions and Exponential Mapping" begin + @test isapprox(retract(M, p_6_4, X1), q_6_4; atol=1.0e-12) + @test isapprox(retract(M, p_6_4, X1, CayleyRetraction()), q_6_4; atol=1.0e-12) - X2_norm = 1.0 - @test isapprox(norm(SpSt_6_4, p_6_4, X2), X2_norm; atol=1.0e-6) - @test isapprox( - norm(SpSt_6_4, p_6_4, X2), - √inner(SpSt_6_4, p_6_4, X2, X2); - atol=1.0e-6, - ) + X_inv_cayley_retraction = inverse_retract(M, p_6_4, q_6_4) + X_inv_cayley_retraction_2 = + inverse_retract(M, p_6_4, q_6_4, CayleyInverseRetraction()) + @test isapprox(X_inv_cayley_retraction, X_inv_cayley_retraction_2; atol=1.0e-16) + @test isapprox(X_inv_cayley_retraction, X1; atol=1.0e-12) + end + @testset "Riemannian Metric" begin + X1_norm = 37.85466645 + @test isapprox(norm(M, p_6_4, X1), X1_norm; atol=1.0e-8) + @test isapprox(norm(M, p_6_4, X1), √inner(M, p_6_4, X1, X1); atol=1.0e-8) - # Project Project matrix A ∈ ℝ^{6 × 4} onto (T_pSpSt): - A_6_4 = Array{Float64}([ - -7 2 12 0 - 4 0 1 -2 - -1 -1 4 0 - -18 4 -1 5 - 7 0 -2 11 - 2 2 -2 9 - ]) - A_6_4_proj = similar(A_6_4) - Manifolds.project!(SpSt_6_4, A_6_4_proj, p_6_4, A_6_4) - @test is_vector(SpSt_6_4, p_6_4, A_6_4_proj; error=:error, atol=2.0e-12) - end - @testset "Generate random points/tangent vectors" begin - M_big = SymplecticStiefel(20, 10) - p_big = rand(M_big) - @test is_point(M_big, p_big; error=:error, atol=1.0e-14) - X_big = rand(M_big; vector_at=p_big, hamiltonian_norm=1.0) - @test is_vector(M_big, p_big, X_big; error=:error, atol=1.0e-14) - end - @testset "test_manifold(Symplectic(6), ...)" begin - types = [Matrix{Float64}] - TEST_FLOAT32 && push!(types, Matrix{Float32}) - TEST_STATIC_SIZED && push!(types, MMatrix{6,4,Float64,24}) - for type in types - @testset "Type $(type)" begin - @testset "CayleyRetraction" begin - test_manifold( - SpSt_6_4, - convert.(type, points); - retraction_methods=[CayleyRetraction()], - default_retraction_method=CayleyRetraction(), - default_inverse_retraction_method=CayleyInverseRetraction(), - test_inplace=true, - is_point_atol_multiplier=1.0e4, - is_tangent_atol_multiplier=1.0e3, - retraction_atol_multiplier=1.0e1, - test_project_tangent=(type != MMatrix{6,4,Float64,24}), - test_injectivity_radius=false, - test_exp_log=false, - test_representation_size=true, - ) - end + X2_norm = 1.0 + @test isapprox(norm(M, p_6_4, X2), X2_norm; atol=1.0e-6) + @test isapprox(norm(M, p_6_4, X2), √inner(M, p_6_4, X2, X2); atol=1.0e-6) - @testset "ExponentialRetraction" begin - test_manifold( - SpSt_6_4, - convert.(type, close_points); - retraction_methods=[ExponentialRetraction()], - default_retraction_method=ExponentialRetraction(), - default_inverse_retraction_method=CayleyInverseRetraction(), - test_inplace=true, - is_point_atol_multiplier=1.0e11, - is_tangent_atol_multiplier=1.0e2, - retraction_atol_multiplier=1.0e4, - test_project_tangent=(type != MMatrix{6,4,Float64,24}), - test_injectivity_radius=false, - test_exp_log=false, - test_representation_size=true, - ) - end + # Project Project matrix A ∈ ℝ^{6×4} onto (T_pSpSt): + A_6_4 = Array{Float64}([ + -7 2 12 0 + 4 0 1 -2 + -1 -1 4 0 + -18 4 -1 5 + 7 0 -2 11 + 2 2 -2 9 + ]) + A_6_4_proj = similar(A_6_4) + Manifolds.project!(M, A_6_4_proj, p_6_4, A_6_4) + @test is_vector(M, p_6_4, A_6_4_proj; error=:error, atol=2.0e-12) + end + @testset "Generate random points/tangent vectors" begin + M_big = SymplecticStiefel(20, 10) + Random.seed!(49) + p_big = rand(M_big) + @test is_point(M_big, p_big; error=:error, atol=1e-9) + X_big = rand(M_big; vector_at=p_big) + @test is_vector(M_big, p_big, X_big; error=:error, atol=1e-9) + end + @testset "test_manifold(SymplecticMatrices(6), ...)" begin + types = [Matrix{Float64}] + TEST_FLOAT32 && push!(types, Matrix{Float32}) + TEST_STATIC_SIZED && push!(types, MMatrix{6,4,Float64,24}) + for type in types + @testset "Type $(type)" begin + @testset "CayleyRetraction" begin + test_manifold( + M, + convert.(type, points); + retraction_methods=[CayleyRetraction()], + default_retraction_method=CayleyRetraction(), + default_inverse_retraction_method=CayleyInverseRetraction(), + test_inplace=true, + is_point_atol_multiplier=1.0e4, + is_tangent_atol_multiplier=1.0e3, + retraction_atol_multiplier=1.0e1, + test_project_tangent=(type != MMatrix{6,4,Float64,24}), + test_injectivity_radius=false, + test_exp_log=false, + test_representation_size=true, + ) end - end # for - end - @testset "Gradient Computations" begin - Q_grad = SymplecticMatrix(points[1]) - function test_f(p) - k = size(p)[2] - return tr(p[1:k, 1:k]) - end - function analytical_grad_f(p) - n, k = size(p) - euc_grad_f = [Array(I, k, k); zeros((n - k), k)] - return Q_grad * p * (euc_grad_f') * Q_grad * p + euc_grad_f * p' * p + @testset "ExponentialRetraction" begin + test_manifold( + M, + convert.(type, close_points); + retraction_methods=[ExponentialRetraction()], + default_retraction_method=ExponentialRetraction(), + default_inverse_retraction_method=CayleyInverseRetraction(), + test_inplace=true, + is_point_atol_multiplier=1.0e11, + is_tangent_atol_multiplier=1.0e2, + retraction_atol_multiplier=1.0e4, + test_project_tangent=(type != MMatrix{6,4,Float64,24}), + test_injectivity_radius=false, + test_exp_log=false, + test_representation_size=true, + ) + end end - p_grad = convert(Array{Float64}, points[1]) - fd_diff = RiemannianProjectionBackend(ManifoldDiff.FiniteDifferencesBackend()) + end # for + end + @testset "Canonical project" begin + E = SymplecticMatrices(6) + p = [ + 1.0 1.0 3.0 0.0 0.0 0.0 + -1.0 -2.0 -4.0 0.0 0.0 0.0 + 3.0 0.0 7.0 0.0 0.0 0.0 + 3.0 4 10.0 14.0 5.0 -6.0 + -5.0 -9.0 -19.0 7.0 2.0 -3.0 + 0.0 0.0 0.0 -2.0 -1.0 1.0 + ] + @test is_point(E, p) + M = SymplecticStiefel(6, 4) + q = canonical_project(M, p) + @test is_point(M, q) + q2 = similar(q) + canonical_project!(M, q2, p) + @test isapprox(M, q, q2) + end + @testset "Gradient Computations" begin + Q_grad = SymplecticElement(points[1]) + function test_f(p) + k = size(p)[2] + return tr(p[1:k, 1:k]) + end + function analytical_grad_f(p) + n, k = size(p) + euc_grad_f = [Array(I, k, k); zeros((n - k), k)] + return Q_grad * p * (euc_grad_f') * Q_grad * p + euc_grad_f * p' * p + end + p_grad = convert(Array{Float64}, points[1]) + fd_diff = RiemannianProjectionBackend(ManifoldDiff.FiniteDifferencesBackend()) - @test isapprox( - Manifolds.gradient(SpSt_6_4, test_f, p_grad, fd_diff), - analytical_grad_f(p_grad); - atol=1.0e-9, - ) + @test isapprox( + Manifolds.gradient(M, test_f, p_grad, fd_diff), + analytical_grad_f(p_grad); + atol=1.0e-9, + ) - grad_f_p = similar(p_grad) - Manifolds.gradient!(SpSt_6_4, test_f, grad_f_p, p_grad, fd_diff) - @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) - end + grad_f_p = similar(p_grad) + Manifolds.gradient!(M, test_f, grad_f_p, p_grad, fd_diff) + @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) end @testset "field parameter" begin - SpSt_6_4 = SymplecticStiefel(2 * 3, 2 * 2; parameter=:field) - @test typeof(get_embedding(SpSt_6_4)) === Euclidean{Tuple{Int,Int},ℝ} - @test repr(SpSt_6_4) == "SymplecticStiefel(6, 4, ℝ; parameter=:field)" - @test get_total_space(SpSt_6_4) == Symplectic(6; parameter=:field) + M = SymplecticStiefel(6, 4; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SymplecticStiefel(6, 4; field=ℝ; parameter=:field)" + @test get_total_space(M) == SymplecticMatrices(6; parameter=:field) end end diff --git a/test/manifolds/torus.jl b/test/manifolds/torus.jl index ddab72ceef..78abc15313 100644 --- a/test/manifolds/torus.jl +++ b/test/manifolds/torus.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") @testset "Torus" begin M = Torus(2) diff --git a/test/manifolds/tucker.jl b/test/manifolds/tucker.jl index 762662cbcb..a8b6e131ba 100644 --- a/test/manifolds/tucker.jl +++ b/test/manifolds/tucker.jl @@ -1,5 +1,5 @@ using ManifoldsBase: LinearAlgebra -include("../utils.jl") +include("../header.jl") @testset "Tucker" begin n⃗ = (4, 5, 6) diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index 6584e8b48e..4683363658 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using Quaternions diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index 370aacbafa..c0d7d0cae6 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -1,4 +1,4 @@ -include("../utils.jl") +include("../header.jl") using RecursiveArrayTools diff --git a/test/metric.jl b/test/metric.jl index b9fd75b17a..6441406310 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -6,7 +6,7 @@ import ManifoldsBase: default_retraction_method import Manifolds: solve_exp_ode using Manifolds: InducedBasis, connection, get_chart_index, induced_basis, mean!, median! using ManifoldDiff: FiniteDifferencesBackend -include("utils.jl") +include("header.jl") struct TestEuclidean{N} <: AbstractManifold{ℝ} end struct TestEuclideanMetric <: AbstractMetric end diff --git a/test/notation.jl b/test/notation.jl index 294cc39a7c..f9d9a8f9a2 100644 --- a/test/notation.jl +++ b/test/notation.jl @@ -1,4 +1,4 @@ -include("utils.jl") +include("header.jl") @testset "Test Notation" begin M = Sphere(2) diff --git a/test/recipes.jl b/test/recipes.jl index f595fa3044..b5af2da28e 100644 --- a/test/recipes.jl +++ b/test/recipes.jl @@ -1,6 +1,6 @@ using RecipesBase, Plots, Colors #using VisualRegressionTests, -include("utils.jl") +include("header.jl") # Note that the `false`s avoid popups and the tests directly fail. # If you have changed something and need to recreate the reference in the test avoids to start Gtk diff --git a/test/runtests.jl b/test/runtests.jl index afb113cc85..bc740b3112 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,11 +1,16 @@ -include("utils.jl") +include("header.jl") @info "Manifolds.jl Test settings:\n\n" * "Testing Float32: $(TEST_FLOAT32)\n" * "Testing Double64: $(TEST_DOUBLE64)\n" * "Testing Static: $(TEST_STATIC_SIZED)\n\n" * "Test group: $(TEST_GROUP)\n\n" * - "These settings are stored in environment variables, see in test/utils.jl" + "These settings are stored in environment variables, see in test/header.jl" + +function include_test(path) + @info "Testing $path" + @time include(path) # show basic timing, (this will print a newline at end) +end @testset "Manifolds.jl" begin if TEST_GROUP ∈ ["all", "test_manifolds"] @@ -144,6 +149,7 @@ include("utils.jl") include_test("manifolds/generalized_grassmann.jl") include_test("manifolds/generalized_stiefel.jl") include_test("manifolds/grassmann.jl") + include_test("manifolds/hamiltonian.jl") include_test("manifolds/hyperbolic.jl") include_test("manifolds/lorentz.jl") include_test("manifolds/multinomial_doubly_stochastic.jl") @@ -163,6 +169,7 @@ include("utils.jl") include_test("manifolds/spd_fixed_determinant.jl") include_test("manifolds/symmetric_positive_semidefinite_fixed_rank.jl") include_test("manifolds/symplectic.jl") + include_test("manifolds/symplecticgrassmann.jl") include_test("manifolds/symplecticstiefel.jl") include_test("manifolds/tucker.jl") include_test("manifolds/unitary_matrices.jl") diff --git a/test/statistics.jl b/test/statistics.jl index 160537bc00..7cbb10b753 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -1,4 +1,4 @@ -include("utils.jl") +include("header.jl") using StatsBase: AbstractWeights, pweights using Random: GLOBAL_RNG, seed! import ManifoldsBase: diff --git a/test/utils.jl b/test/utils.jl deleted file mode 100644 index 6e53f06d56..0000000000 --- a/test/utils.jl +++ /dev/null @@ -1,50 +0,0 @@ -TEST_FLOAT32 = get(ENV, "MANIFOLDS_TEST_FLOAT32", false) -TEST_DOUBLE64 = get(ENV, "MANIFOLDS_TEST_FLOAT64", false) -TEST_STATIC_SIZED = get(ENV, "MANIFOLDS_TEST_STATIC_SIZED", false) -TEST_GROUP = get(ENV, "MANIFOLDS_TEST_GROUP", "all") - -using Manifolds -using ManifoldsBase -using ManifoldsBase: number_of_coordinates, TypeParameter -import ManifoldsBase: active_traits, merge_traits - -using ManifoldDiff - -using LinearAlgebra -using Distributions -using DoubleFloats -using Quaternions -using Random -using StaticArrays -using Statistics -using StatsBase -using Test -using Graphs -using SimpleWeightedGraphs - -function include_test(path) - @info "Testing $path" - @time include(path) # show basic timing, (this will print a newline at end) -end - -function our_ambiguities(m=Base) - ambigs = Test.detect_ambiguities(m) - modules_we_care_about = - [Base, LinearAlgebra, Manifolds, ManifoldsBase, StaticArrays, Statistics, StatsBase] - our_ambigs = filter(ambigs) do (m1, m2) - we_care = m1.module in modules_we_care_about && m2.module in modules_we_care_about - return we_care && (m1.module === Manifolds || m2.module === Manifolds) - end - return our_ambigs -end - -""" - has_type_in_signature(sig, T) - Test whether the signature `sig` has an argument of type `T` as one of its paramaters -""" -function has_type_in_signature(sig, T::Type) - return any(map(Base.unwrap_unionall(sig.sig).parameters) do x - xw = Base.rewrap_unionall(x, sig.sig) - return (xw isa Type ? xw : xw.T) <: T - end) -end diff --git a/tutorials/getstarted.qmd b/tutorials/getstarted.qmd index 283d79f777..be3edad047 100644 --- a/tutorials/getstarted.qmd +++ b/tutorials/getstarted.qmd @@ -73,7 +73,7 @@ manifold_dimension(M₁) The $d$-dimensional ``[hyperbolic space](@ref HyperbolicSpace)``{=commonmark} is usually represented in $\mathbb R^{d+1}$ as the set of points $p\in\mathbb R^3$ fulfilling ```math -p_1^2+p_2^2+\cdots+p_d^2-p_{d+1}^2 = -1. +p_1^2+p_2^2+⋅s+p_d^2-p_{d+1}^2 = -1. ``` We define the manifold using @@ -190,7 +190,7 @@ Note that nested power manifolds are combined into one as in M₄₂ = M₄^4 ``` -which represents $2\times 4$ – matrices of hyperbolic points represented in $3\times 2\times 4$ arrays. +which represents $2×4$ – matrices of hyperbolic points represented in $3×2×4$ arrays. We can – alternatively – use a power manifold with nested arrays diff --git a/tutorials/integration.qmd b/tutorials/integration.qmd index 29ae261dad..b22501c695 100644 --- a/tutorials/integration.qmd +++ b/tutorials/integration.qmd @@ -13,7 +13,7 @@ Pkg.develop(PackageSpec(; path=(@__DIR__) * "/../")) using Markdown ``` -This part of documentation covers integration of scalar functions defined on manifolds $f \colon \mathcal{M} \to \mathbb{R}$: +This part of documentation covers integration of scalar functions defined on manifolds $f \colon \mathcal{M} \to ℝ$: ```math \int_{\mathcal M} f(p) \mathrm{d}p @@ -55,7 +55,7 @@ We used the function [`manifold_volume`](@ref) to get the volume of the set over We will now try to verify that volume density correction correctly changes probability density of an exponential-wrapped normal distribution. `pdf_tangent_space` (defined in the next code block) represents probability density of a normally distributed random variable $X_T$ in the tangent space $T_p \mathcal{M}$. -Its probability density (with respect to the Lebesgue measure of the tangent space) is $f_{X_T}\colon T_p \mathcal{M} \to \mathbb{R}$. +Its probability density (with respect to the Lebesgue measure of the tangent space) is $f_{X_T}\colon T_p \mathcal{M} \to ℝ$. `pdf_manifold` (defined below) refers to the probability density of the distribution $X_M$ from the tangent space $T_p \mathcal{M}$ wrapped using exponential map on the manifold. The formula for probability density with respect to pushforward measure of the Lebesgue measure in the tangent space reads @@ -105,7 +105,7 @@ the density at point $q$ is defined as f(q) = \frac{1}{n h^d} \sum_{i=1}^n \frac{1}{\theta_q(\log_q(p_i))}K\left( \frac{d(q, p_i)}{h} \right), ``` -where $h$ is the bandwidth, a small positive number less than the injectivity radius of $\mathcal M$ and $K\colon\mathbb{R}\to\mathbb{R}$ is a kernel function. +where $h$ is the bandwidth, a small positive number less than the injectivity radius of $\mathcal M$ and $K\colonℝ\toℝ$ is a kernel function. Note that Pelletier's estimator can only use radially-symmetric kernels. The radially symmetric multivariate Epanechnikov kernel used in the example below is described in [LangreneMarin:2019](@cite).