diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 269ccf374c..b4e6b3d858 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ This can best be achieved by adding a docstring to the method with a general sig ````julia struct MyManifold <: Manifold end - @doc doc""" + @doc raw""" exp(M::MyManifold, x, v) Describe the function. diff --git a/docs/make.jl b/docs/make.jl index cd76e470f3..ba37b67b3d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,7 +4,8 @@ makedocs( # for development, we disable prettyurls format = Documenter.HTML(prettyurls = false), modules = [Manifolds, ManifoldsBase], - sitename = "Manifolds", + authors = "Seth Axen, Mateusz Baran, Ronny Bergmann, and contributors.", + sitename = "Manifolds.jl", pages = [ "Home" => "index.md", "ManifoldsBase.jl" => "interface.md", @@ -38,6 +39,7 @@ makedocs( "Statistics" => "statistics.md", "Distributions" => "distributions.md", "Orthonormal bases" => "orthonormal_bases.md", + "Notation" => "notation.md", "Library" => [ "Number systems" => "lib/numbers.md", "Public" => "lib/public.md", diff --git a/docs/src/interface.md b/docs/src/interface.md index 5e95085621..8dd3f3215d 100644 --- a/docs/src/interface.md +++ b/docs/src/interface.md @@ -36,6 +36,7 @@ Allocation of new points is performed using a custom mechanism that relies on th For more complex types, such as nested representations of [`PowerManifold`](@ref) (see [`NestedPowerRepresentation`](@ref)), [`FVector`](@ref) types, checked types like [`ArrayMPoint`](@ref) and more it operates differently. While `similar` only concerns itself with the higher level of nested structures, `allocate` maps itself through all levels of nesting until a simple array of numbers is reached and then calls `similar`. The difference can be most easily seen in the following example: + ```julia julia> x = similar([[1.0], [2.0]]) 2-element Array{Array{Float64,1},1}: @@ -56,6 +57,8 @@ Stacktrace: julia> y[1] 1-element Array{Float64,1}: 6.90031725726027e-310 + ``` + * [`allocate_result`](@ref) allocates a result of a particular function (for example [`exp`], [`flat`], etc.) on a particular manifold with particular arguments. It takes into account the possibility that different arguments may have different numeric [`number_eltype`](@ref) types thorough the [`ManifoldsBase.allocate_result_type`](@ref) function. diff --git a/docs/src/lib/internals.md b/docs/src/lib/internals.md index 463ac53bdd..7a88f5c699 100644 --- a/docs/src/lib/internals.md +++ b/docs/src/lib/internals.md @@ -13,11 +13,9 @@ Manifolds.SizedAbstractArray ```@docs Manifolds._gradient Manifolds._jacobian -ManifoldsBase.allocate_result_type Manifolds.eigen_safe Manifolds.find_pv Manifolds.log_safe -ManifoldsBase.number_eltype Manifolds.size_to_tuple Manifolds.select_from_tuple Manifolds.usinc diff --git a/docs/src/lib/public.md b/docs/src/lib/public.md index 2bd494df2f..bcbe27f2e3 100644 --- a/docs/src/lib/public.md +++ b/docs/src/lib/public.md @@ -3,7 +3,6 @@ Documentation for `Manifolds.jl`'s public interface. ```@docs -allocate Manifolds.ShapeSpecification submanifold_component submanifold_components diff --git a/docs/src/manifolds/euclidean.md b/docs/src/manifolds/euclidean.md index ae0ba0f3ce..6ec6aa9243 100644 --- a/docs/src/manifolds/euclidean.md +++ b/docs/src/manifolds/euclidean.md @@ -1,6 +1,6 @@ # Euclidean space -The Euclidean space $\mathbb R^n$ is a simple model space, since it has curvature constantly zero everywhere; hence, nearly all operations simplify. +The Euclidean space $ℝ^n$ is a simple model space, since it has curvature constantly zero everywhere; hence, nearly all operations simplify. ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/graph.md b/docs/src/manifolds/graph.md index 091c381ba5..b82fb4dcae 100644 --- a/docs/src/manifolds/graph.md +++ b/docs/src/manifolds/graph.md @@ -3,9 +3,10 @@ For a given graph $G(V,E)$ implemented using [`LightGraphs.jl`](https://juliagraphs.github.io/LightGraphs.jl/latest/), the [`GraphManifold`](@ref) models a [`PowerManifold`](@ref) 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: +## Example + +To make a graph manifold over $ℝ^2$ with three vertices and two edges, one can use -To make a graph manifold over $\mathbb{R}^2$ with three vertices and two edges, one can use ```@example using Manifolds using LightGraphs @@ -18,6 +19,7 @@ add_edge!(G, 1, 2) add_edge!(G, 2, 3) N = GraphManifold(G, M, VertexManifold()) ``` + It supports all [`AbstractPowerManifold`](@ref) operations (it is based on [`NestedPowerRepresentation`](@ref)) and furthermore it is possible to compute a graph logarithm: ```@setup graph-1 diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 4265bdb1ba..f675cc3317 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # Product manifold -Product manifold $M = M_1 \times M_2 \times \dots M_n$ of manifolds $M_1, M_2, \dots, M_n$. -Points on the product manifold can be constructed using [`ProductRepr`](@ref) with canonical projections $\Pi_i \colon M \to M_i$ for $i \in 1, 2, \dots, n$ provided by [`submanifold_component`](@ref). +Product manifold $M = M_1 × M_2 × … M_n$ of manifolds $M_1, M_2, …, M_n$. +Points on the product manifold can be constructed using [`ProductRepr`](@ref) with canonical projections $Π_i : M → M_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](@ref). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index ffff05dddb..57cee3a049 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -1,9 +1,9 @@ # Rotations -The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $\mathbb R^{n\times n}$, i.e. +The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n× n}$, i.e. -$\mathrm{SO}(n) = \bigl\{R \in \mathbb{R}^{n\times n} \big| RR^{\mathrm{T}} = -R^{\mathrm{T}}R = \mathrm{I}_n, \det(R) = 1 \bigr\}$ +$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n × n} \big| RR^{\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. See also [`SpecialOrthogonal`](@ref), which is this manifold equipped with the group operation. @@ -12,9 +12,9 @@ Tangent vectors are represented by elements of the corresponding Lie algebra, wh This convention allows for more efficient operations on tangent vectors. Tangent spaces at different points are different vector spaces. -Let $L_R\colon \mathrm{SO}(n) \to \mathrm{SO}(n)$ where $R \in \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 $\mathrm{I}_n$ by the differential of $L_R$ at identity, $(\mathrm{d}L_R)_{\mathrm{I}_n} \colon T_{\mathrm{I}_n} \mathrm{SO}(n) \to T_R \mathrm{SO}(n)$. -For a tangent vector at the identity rotation $v \in T_{\mathrm{I}_n} \mathrm{SO}(n)$ the matrix representation of the corresponding tangent vector $w$ at a rotation $R$ can be obtained by matrix multiplication: $w=Rv \in 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)$. +For a tangent vector at the identity rotation $v ∈ T_{I_n} \mathrm{SO}(n)$ the matrix representation of the corresponding tangent vector $w$ at a rotation $R$ can be obtained by matrix multiplication: $w=Rv ∈ T_R \mathrm{SO}(n)$. You can compare the functions [`log!(::Manifolds.Rotations, v, x, y)`](@ref) and [`exp!(::Manifolds.Rotations, y, x, v)`](@ref) to see how it works in practice. ```@autodocs diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 12a4f478d2..fcdc1a19dc 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -\mathcal P(n) = \bigl\{ A \in \mathbb R^{n\times n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0\neq x \in\mathbb R^n \bigr\} +\mathcal P(n) = \bigl\{ A ∈ ℝ^{n × n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x ∈ ℝ^n \bigr\} ``` ```@docs diff --git a/docs/src/manifolds/torus.md b/docs/src/manifolds/torus.md index 5ea42ed038..e147057a35 100644 --- a/docs/src/manifolds/torus.md +++ b/docs/src/manifolds/torus.md @@ -1,11 +1,11 @@ # Torus -The torus $\mathbb T^d \equiv [-π,π)^d$ is modeled as an [`AbstractPowerManifold`](@ref) of +The torus $𝕋^d \cong [-π,π)^d$ is modeled as an [`AbstractPowerManifold`](@ref) of the (real-valued) [`Circle`](@ref) and uses [`MultidimentionalArrayPowerRepresentation`](@ref). -Points on the torus are hence row vectors, $x\in\mathbb R^{d}$. +Points on the torus are hence row vectors, $x ∈ ℝ^{d}$. ## Example -The following code can be used to make a three-dimensional torus $\mathbb{T}^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 M = Torus(3) diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index f4d9ffbda7..8173772fc4 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -1,7 +1,7 @@ # Vector bundles -Vector bundle $E$ is a manifold that is built on top of another manifold $M$ (base space). -It is characterized by a continuous function $\Pi \colon E \to M$, such that for each point $x \in M$ the preimage of $x$ by $\Pi$, $\Pi^{-1}(\{x\})$, has a structure of a vector space. +Vector 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$, such that for each point $p ∈ \mathcal M$ the preimage of $p$ by $Π$, $Π^{-1}(\{p\})$, has a structure of a vector space. These vector spaces are called fibers. Bundle projection can be performed using function [`bundle_projection`](@ref). diff --git a/docs/src/notation.md b/docs/src/notation.md new file mode 100644 index 0000000000..f6e1a0dd76 --- /dev/null +++ b/docs/src/notation.md @@ -0,0 +1,41 @@ +# Notation overview + +Since manifolds include a reasonable amount of elements and functions, the following list tries to keep an overview of used notation throughout `Manifolds.jl`. +The order is alphabetically by name. +They might be used in a plain form within the code or when referring to that code. +This is for example the case the calligraphic symbols. + +Within the documented functions the utf8 symbols are used whenever possible, +as long as that renders still in $\TeX$ within this documentation. + +| Symbol | Description | Also used | Comment | +|:--:|:--------------- |:--:|:-- | +| $\times$ | Cartesian product of two manifolds | | see [`ProductManifold`](@ref) | +| $^{\wedge}$ | (n-ary) Cartesian power of a manifold | | see [`PowerManifold`](@ref) | +| $T^*_p \mathcal M$ | the cotangent space at $p$ | | | +| $\xi$ | a cotangent vector from $T^*_p \mathcal M$ | $\xi_1, \xi_2,\ldots,\eta,\zeta$ | sometimes written with base point $\xi_p$. | +| $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)$ | | +| $F$ | a fiber | | | +| $\mathbb F$ | a field | | field a manifold is based on, usually $\mathbb F \in \{\mathbb R,\mathbb C\}$ | +| $\gamma$ | a geodesic | $\gamma_{p;q}$, $\gamma_{p,X}$ | connecting two points $p,q$ or starting in $p$ with velocity $X$. | +| $\circ$ | a group operation | | +| $\cdot^\mathrm{H}$ | Hermitian or conjugate transposed| | +| $e$ | identity element of a group | | +| $I_k$ | identity matrix of size $k\times 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)$ | +| $\mathfrak g$ | a Lie algebra | | +| $\mathcal{G}$ | a (Lie) group | | +| $\mathcal M$ | a manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | +| $\operatorname{Exp}$ | the matrix exponential | | +| $\operatorname{Log}$ | the matrix logarithm | | +| $\mathcal P_{q\gets p}X$ | parallel transport | | of the vector $X$ from $T_p\mathcal M$ to $T_q\mathcal M$ +| $p$ | a point on $\mathcal M$ | $p_1, p_2, \ldots,q$ | for 3 points one might use $x,y,z$ | +| $\Xi$ | a set of tangent vectors | $\{X_1,\ldots,X_n\}$ | | +| $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 | | +| $B$ | a vector bundle | | +| $0_k$ | the $k\times k$ zero matrix. | | diff --git a/docs/src/orthonormal_bases.md b/docs/src/orthonormal_bases.md index 748bec43d1..fe784fbb60 100644 --- a/docs/src/orthonormal_bases.md +++ b/docs/src/orthonormal_bases.md @@ -1,8 +1,8 @@ # Orthonormal bases The following functions and types provide support for orthonormal bases of the tangent space of different manifolds. -An orthonormal basis of the tangent space $T_x M$ of (real) dimension $N$ has a real-coefficient basis $e_1, e_2, \dots, e_N$ if $\mathrm{Re}(g_x(e_i, e_j)) = \delta_{ij}$ for each $i,j \in \{1, 2, \dots, N\}$ where $g_x$ is the Riemannian metric at point $x$. -A vector $v$ from the tangent space $T_x M$ can be expressed as a sum $v = v^i e_i$ where coefficients $v^i$ are calculated as $v^i = \mathrm{Re}(g_x(v, e_i))$. +An orthonormal basis of the tangent space $T_x \mathcal M$ of (real) dimension $N$ has a real-coefficient basis $e_1, e_2, …, e_N$ if $\mathrm{Re}(g_x(e_i, e_j)) = \delta_{ij}$ for each $i,j ∈ \{1, 2, …, N\}$ where $g_x$ is the Riemannian metric at point $x$. +A vector $v$ from the tangent space $T_x \mathcal M$ can be expressed in Einstein notation as a sum $v = v^i e_i$ where coefficients $v^i$ are calculated as $v^i = \mathrm{Re}(g_x(v, e_i))$. The main types are: * [`ArbitraryOrthonormalBasis`](@ref), which is designed to work when no special properties of the tangent space basis are required. diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 1e12df8e30..3a835dc2f9 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -108,63 +108,63 @@ Abstract type for defining statistical estimation methods. """ abstract type AbstractEstimationMethod end -@doc doc""" - hat(M::Manifold, x, vⁱ) +@doc raw""" + hat(M::Manifold, p, Xⁱ) -Given a basis $e_i$ on the tangent space at a point $x$ and tangent -component vector $v^i$, compute the equivalent vector representation -$v=v^i e_i$, where Einstein summation notation is used: +Given a basis $e_i$ on the tangent space at a point `p` and tangent +component vector $X^i$, compute the equivalent vector representation +$X=X^i e_i$, where Einstein summation notation is used: ````math -\wedge: v^i \mapsto v^i e_i +∧ : X^i ↦ X^i e_i ```` For array manifolds, this converts a vector representation of the tangent vector to an array representation. The [`vee`](@ref) map is the `hat` map's inverse. """ -function hat(M::Manifold, x, vⁱ) - v = allocate_result(M, hat, x, vⁱ) - return hat!(M, v, x, vⁱ) +function hat(M::Manifold, p, Xⁱ) + X = allocate_result(M, hat, p, Xⁱ) + return hat!(M, X, p, Xⁱ) end -function hat!(M::Manifold, v, x, vⁱ) - is_decorator_manifold(M) === Val(true) && return hat!(base_manifold(M), v, x, vⁱ) - error("hat! operator not defined for manifold $(typeof(M)), array $(typeof(v)), point $(typeof(x)), and vector $(typeof(vⁱ))") +function hat!(M::Manifold, X, p, Xⁱ) + is_decorator_manifold(M) === Val(true) && return hat!(base_manifold(M), X, p, Xⁱ) + error("hat! operator not defined for manifold $(typeof(M)), array $(typeof(X)), point $(typeof(p)), and vector $(typeof(Xⁱ))") end -@doc doc""" - vee(M::Manifold, x, v) +@doc raw""" + vee(M::Manifold, p, X) -Given a basis $e_i$ on the tangent space at a point $x$ and tangent -vector $v$, compute the vector components $v^i$, such that $v = v^i e_i$, where +Given a basis $e_i$ on the tangent space at a point `p` and tangent +vector `X`, compute the vector components $X^i$, such that $X = X^i e_i$, where Einstein summation notation is used: ````math -\vee: v^i e_i \mapsto v^i +\vee : X^i e_i ↦ X^i ```` For array manifolds, this converts an array representation of the tangent vector to a vector representation. The [`hat`](@ref) map is the `vee` map's inverse. """ -function vee(M::Manifold, x, v) - vⁱ = allocate_result(M, vee, x, v) - return vee!(M, vⁱ, x, v) +function vee(M::Manifold, p, X) + Xⁱ = allocate_result(M, vee, p, X) + return vee!(M, Xⁱ, p, X) end -function vee!(M::Manifold, vⁱ, x, v) - is_decorator_manifold(M) === Val(true) && return vee!(base_manifold(M), vⁱ, x, v) - error("vee! operator not defined for manifold $(typeof(M)), vector $(typeof(vⁱ)), point $(typeof(x)), and array $(typeof(v))") +function vee!(M::Manifold, Xⁱ, p, X) + is_decorator_manifold(M) === Val(true) && return vee!(base_manifold(M), Xⁱ, p, X) + error("vee! operator not defined for manifold $(typeof(M)), vector $(typeof(Xⁱ)), point $(typeof(p)), and array $(typeof(X))") end -function allocate_result(M::Manifold, f::typeof(vee), x, v) - T = allocate_result_type(M, f, (x, v)) - return allocate(x, T, manifold_dimension(M)) +function allocate_result(M::Manifold, f::typeof(vee), p, X) + T = allocate_result_type(M, f, (p, X)) + return allocate(p, T, manifold_dimension(M)) end -function allocate_result(M::Manifold, f::typeof(vee), x::StaticArray, v) - T = allocate_result_type(M, f, (x, v)) - return allocate(x, T, Size(manifold_dimension(M))) +function allocate_result(M::Manifold, f::typeof(vee), p::StaticArray, X) + T = allocate_result_type(M, f, (p, X)) + return allocate(p, T, Size(manifold_dimension(M))) end """ diff --git a/src/autodiff.jl b/src/autodiff.jl index aaebd1f92b..5533024018 100644 --- a/src/autodiff.jl +++ b/src/autodiff.jl @@ -40,22 +40,22 @@ Get vector of currently valid AD backends. adbackends() = _adbackends """ - _gradient(f, x::Number, backend = adbackend()) -> Number - _gradient(f, x::Array, backend = adbackend()) -> Array + _gradient(f, p::Number, backend = adbackend()) -> Number + _gradient(f, p::Array, backend = adbackend()) -> Array -Compute gradient of function `f` at point `x` using AD backend `backend`. +Compute gradient of function `f` at `x` using AD backend `backend`. """ -_gradient(f, x) = _gradient(f, x, Val(adbackend())) -_gradient(f, x, backend::Symbol) = _gradient(f, x, Val(adbackend(backend))) +_gradient(f, p) = _gradient(f, p, Val(adbackend())) +_gradient(f, p, backend::Symbol) = _gradient(f, p, Val(adbackend(backend))) """ - _jacobian(f, x::Array, backend = adbackend()) -> Array + _jacobian(f, p, backend = adbackend()) -> Array -Compute Jacobian matrix of function `f` at point `x` using AD backend `backend`. +Compute Jacobian matrix of function `f` at `p` using AD backend `backend`. Inputs and outputs of `f` are vectorized. """ -_jacobian(f, x) = _jacobian(f, x, Val(adbackend())) -_jacobian(f, x, backend::Symbol) = _jacobian(f, x, Val(adbackend(backend))) +_jacobian(f, p) = _jacobian(f, p, Val(adbackend())) +_jacobian(f, p, backend::Symbol) = _jacobian(f, p, Val(adbackend(backend))) # Finite differences @@ -64,14 +64,14 @@ adbackend!(:finitedifferences) _default_fdm() = central_fdm(5, 1) -_gradient(f, x, backend::Val{:finitedifferences}) = _gradient(f, x, _default_fdm()) +_gradient(f, p, backend::Val{:finitedifferences}) = _gradient(f, p, _default_fdm()) -function _gradient(f, x, fdm::FiniteDifferences.FiniteDifferenceMethod) - return FiniteDifferences.grad(fdm, f, x)[1] +function _gradient(f, p, fdm::FiniteDifferences.FiniteDifferenceMethod) + return FiniteDifferences.grad(fdm, f, p)[1] end -_jacobian(f, x, backend::Val{:finitedifferences}) = _jacobian(f, x, _default_fdm()) +_jacobian(f, p, backend::Val{:finitedifferences}) = _jacobian(f, p, _default_fdm()) -function _jacobian(f, x, fdm::FiniteDifferences.FiniteDifferenceMethod) - return FiniteDifferences.jacobian(fdm, f, x)[1] +function _jacobian(f, p, fdm::FiniteDifferences.FiniteDifferenceMethod) + return FiniteDifferences.jacobian(fdm, f, p)[1] end diff --git a/src/distributions.jl b/src/distributions.jl index 63bee904e9..b9c83404f0 100644 --- a/src/distributions.jl +++ b/src/distributions.jl @@ -10,12 +10,12 @@ struct FVectorvariate <: VariateForm end FVectorSupport(space::Manifold, VectorBundleFibers) Value support for vector bundle fiber-valued distributions (values from a fiber of a vector -bundle at point `x` from the given manifold). +bundle at a `point` from the given manifold). For example used for tangent vector-valued distributions. """ struct FVectorSupport{TSpace<:VectorBundleFibers,T} <: ValueSupport space::TSpace - x::T + point::T end """ diff --git a/src/groups/array_manifold.jl b/src/groups/array_manifold.jl index e0ee5955af..54e58628bb 100644 --- a/src/groups/array_manifold.jl +++ b/src/groups/array_manifold.jl @@ -3,38 +3,38 @@ array_value(e::Identity) = e array_point(x) = ArrayMPoint(x) array_point(e::Identity) = e -function inv(M::ArrayManifold, x; kwargs...) - is_manifold_point(M, x, true; kwargs...) - y = array_point(inv(M.manifold, array_value(x))) - is_manifold_point(M, y, true; kwargs...) - return y +function inv(M::ArrayManifold, p; kwargs...) + is_manifold_point(M, p, true; kwargs...) + q = array_point(inv(M.manifold, array_value(p))) + is_manifold_point(M, q, true; kwargs...) + return q end -function inv!(M::ArrayManifold, y, x; kwargs...) - is_manifold_point(M, x, true; kwargs...) - inv!(M.manifold, array_value(y), array_value(x)) - is_manifold_point(M, y, true; kwargs...) - return y +function inv!(M::ArrayManifold, q, p; kwargs...) + is_manifold_point(M, p, true; kwargs...) + inv!(M.manifold, array_value(q), array_value(p)) + is_manifold_point(M, q, true; kwargs...) + return q end -function identity(M::ArrayManifold, x; kwargs...) - is_manifold_point(M, x, true; kwargs...) - y = array_point(identity(M.manifold, array_value(x))) +function identity(M::ArrayManifold, p; kwargs...) + is_manifold_point(M, p, true; kwargs...) + y = array_point(identity(M.manifold, array_value(p))) is_manifold_point(M, y, true; kwargs...) return y end -function identity!(M::ArrayManifold, y, x; kwargs...) - is_manifold_point(M, x, true; kwargs...) - identity!(M.manifold, array_value(y), array_value(x)) - is_manifold_point(M, y, true; kwargs...) - return y +function identity!(M::ArrayManifold, q, p; kwargs...) + is_manifold_point(M, p, true; kwargs...) + identity!(M.manifold, array_value(q), array_value(p)) + is_manifold_point(M, q, true; kwargs...) + return q end -function compose(M::ArrayManifold, x, y; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - z = array_point(compose(M.manifold, array_value(x), array_value(y))) +function compose(M::ArrayManifold, p, q; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + z = array_point(compose(M.manifold, array_value(p), array_value(q))) is_manifold_point(M, z, true; kwargs...) return z end @@ -47,10 +47,10 @@ function compose!(M::ArrayManifold, z, x, y; kwargs...) return z end -function translate(M::ArrayManifold, x, y, conv::ActionDirection; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - z = array_point(translate(M.manifold, array_value(x), array_value(y), conv)) +function translate(M::ArrayManifold, p, q, conv::ActionDirection; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + z = array_point(translate(M.manifold, array_value(p), array_value(q), conv)) is_manifold_point(M, z, true; kwargs...) return z end @@ -63,10 +63,10 @@ function translate!(M::ArrayManifold, z, x, y, conv::ActionDirection; kwargs...) return z end -function inverse_translate(M::ArrayManifold, x, y, conv::ActionDirection; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - z = array_point(inverse_translate(M.manifold, array_value(x), array_value(y), conv)) +function inverse_translate(M::ArrayManifold, p, q, conv::ActionDirection; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + z = array_point(inverse_translate(M.manifold, array_value(p), array_value(q), conv)) is_manifold_point(M, z, true; kwargs...) return z end @@ -79,76 +79,76 @@ function inverse_translate!(M::ArrayManifold, z, x, y, conv::ActionDirection; kw return z end -function translate_diff(M::ArrayManifold, x, y, v, conv::ActionDirection; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - is_tangent_vector(M, y, v, true; kwargs...) - vout = ArrayTVector(translate_diff( +function translate_diff(M::ArrayManifold, p, q, X, conv::ActionDirection; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + is_tangent_vector(M, q, X, true; kwargs...) + Y = ArrayTVector(translate_diff( M.manifold, - array_value(x), - array_value(y), - array_value(v), + array_value(p), + array_value(q), + array_value(X), conv, )) - xy = translate(M, x, y, conv) - is_tangent_vector(M, xy, vout, true; kwargs...) - return vout + pq = translate(M, p, q, conv) + is_tangent_vector(M, pq, Y, true; kwargs...) + return Y end -function translate_diff!(M::ArrayManifold, vout, x, y, v, conv::ActionDirection; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - is_tangent_vector(M, y, v, true; kwargs...) +function translate_diff!(M::ArrayManifold, Y, p, q, X, conv::ActionDirection; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + is_tangent_vector(M, q, X, true; kwargs...) translate_diff!( M.manifold, - array_value(vout), - array_value(x), - array_value(y), - array_value(v), + array_value(Y), + array_value(p), + array_value(q), + array_value(X), conv, ) - xy = translate(M, x, y, conv) - is_tangent_vector(M, xy, vout, true; kwargs...) - return vout + xy = translate(M, p, q, conv) + is_tangent_vector(M, xy, Y, true; kwargs...) + return Y end -function inverse_translate_diff(M::ArrayManifold, x, y, v, conv::ActionDirection; kwargs...) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - is_tangent_vector(M, y, v, true; kwargs...) - vout = ArrayTVector(inverse_translate_diff( +function inverse_translate_diff(M::ArrayManifold, p, q, X, conv::ActionDirection; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + is_tangent_vector(M, q, X, true; kwargs...) + Y = ArrayTVector(inverse_translate_diff( M.manifold, - array_value(x), - array_value(y), - array_value(v), + array_value(p), + array_value(q), + array_value(X), conv, )) - xinvy = inverse_translate(M, x, y, conv) - is_tangent_vector(M, xinvy, vout, true; kwargs...) - return vout + xinvy = inverse_translate(M, p, q, conv) + is_tangent_vector(M, xinvy, Y, true; kwargs...) + return Y end function inverse_translate_diff!( M::ArrayManifold, - vout, - x, - y, - v, + Y, + p, + q, + X, conv::ActionDirection; kwargs..., ) - is_manifold_point(M, x, true; kwargs...) - is_manifold_point(M, y, true; kwargs...) - is_tangent_vector(M, y, v, true; kwargs...) + is_manifold_point(M, p, true; kwargs...) + is_manifold_point(M, q, true; kwargs...) + is_tangent_vector(M, q, X, true; kwargs...) inverse_translate_diff!( M.manifold, - array_value(vout), - array_value(x), - array_value(y), - array_value(v), + array_value(Y), + array_value(p), + array_value(q), + array_value(X), conv, ) - xinvy = inverse_translate(M, x, y, conv) - is_tangent_vector(M, xinvy, vout, true; kwargs...) - return vout -end + xinvy = inverse_translate(M, p, q, conv) + is_tangent_vector(M, xinvy, Y, true; kwargs...) + return Y +end \ No newline at end of file diff --git a/src/groups/circle_group.jl b/src/groups/circle_group.jl index 3d4dd7ead9..ed7656d238 100644 --- a/src/groups/circle_group.jl +++ b/src/groups/circle_group.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" CircleGroup <: GroupManifold{Circle{ℂ},MultiplicationOperation} The circle group is the complex circle ([`Circle(ℂ)`](@ref)) equipped with @@ -10,39 +10,39 @@ CircleGroup() = GroupManifold(Circle{ℂ}(), MultiplicationOperation()) show(io::IO, ::CircleGroup) = print(io, "CircleGroup()") -function compose(G::CircleGroup, x::AbstractVector, y::AbstractVector) - return map(compose, repeated(G), x, y) +function compose(G::CircleGroup, p::AbstractVector, q::AbstractVector) + return map(compose, repeated(G), p, q) end compose!(G::CircleGroup, z, x, y) = copyto!(z, compose(G, x, y)) -identity(::CircleGroup, x::AbstractVector) = map(one, x) +identity(::CircleGroup, p::AbstractVector) = map(one, p) identity(G::GT, e::Identity{GT}) where {GT<:CircleGroup} = e -identity!(::CircleGroup, y::AbstractVector, x) = copyto!(y, 1) +identity!(::CircleGroup, q::AbstractVector, p) = copyto!(q, 1) identity!(::GT, y::AbstractVector, ::Identity{GT}) where {GT<:CircleGroup} = copyto!(y, 1) -inv(G::CircleGroup, x::AbstractVector) = map(inv, repeated(G), x) +inv(G::CircleGroup, p::AbstractVector) = map(inv, repeated(G), p) inv(G::GT, e::Identity{GT}) where {GT<:CircleGroup} = e function inverse_translate( ::CircleGroup, - x::AbstractVector, - y::AbstractVector, + p::AbstractVector, + q::AbstractVector, ::LeftAction, ) - return map(/, y, x) + return map(/, q, p) end function inverse_translate( ::CircleGroup, - x::AbstractVector, - y::AbstractVector, + p::AbstractVector, + q::AbstractVector, ::RightAction, ) - return map(/, y, x) + return map(/, q, p) end -translate_diff(::GT, x, y, v, ::ActionDirection) where {GT<:CircleGroup} = map(*, x, v) +translate_diff(::GT, p, q, X, ::ActionDirection) where {GT<:CircleGroup} = map(*, p, X) function translate_diff( ::GT, ::Identity{GT}, @@ -53,6 +53,6 @@ function translate_diff( return v end -function translate_diff!(G::CircleGroup, vout, x, y, v, conv::ActionDirection) - return copyto!(vout, translate_diff(G, x, y, v, conv)) +function translate_diff!(G::CircleGroup, Y, p, q, X, conv::ActionDirection) + return copyto!(Y, translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/group.jl b/src/groups/group.jl index 6d1840bbee..053c0888f3 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -1,19 +1,19 @@ -@doc doc""" +@doc raw""" AbstractGroupOperation -Abstract type for smooth binary operations $∘$ on elements of a Lie group $G$: +Abstract type for smooth binary operations $\circ$ on elements of a Lie group $G$: ```math -∘: G × G → G +\circ : G × G → G ``` An operation can be either defined for a specific [`AbstractGroupManifold`](@ref) or in general, by defining for an operation `Op` the following methods: - identity!(::AbstractGroupManifold{Op}, y, x) - identity(::AbstractGroupManifold{Op}, x) - inv!(::AbstractGroupManifold{Op}, y, x) - inv(::AbstractGroupManifold{Op}, x) - compose(::AbstractGroupManifold{Op}, x, y) - compose!(::AbstractGroupManifold{Op}, z, x, y) + identity!(::AbstractGroupManifold{Op}, q, q) + identity(::AbstractGroupManifold{Op}, p) + inv!(::AbstractGroupManifold{Op}, q, p) + inv(::AbstractGroupManifold{Op}, p) + compose(::AbstractGroupManifold{Op}, p, q) + compose!(::AbstractGroupManifold{Op}, x, p, q) Note that a manifold is connected with an operation by wrapping it with a decorator, [`AbstractGroupManifold`](@ref). In typical cases the concrete wrapper @@ -21,7 +21,7 @@ Note that a manifold is connected with an operation by wrapping it with a decora """ abstract type AbstractGroupOperation end -@doc doc""" +@doc raw""" AbstractGroupManifold{<:AbstractGroupOperation} <: Manifold Abstract type for a Lie group, a group that is also a smooth manifold with an @@ -79,75 +79,75 @@ end # GroupManifold forwards ######################## -function check_tangent_vector(G::GroupManifold, x, v; kwargs...) - return check_tangent_vector(G.manifold, x, v; kwargs...) +function check_tangent_vector(G::GroupManifold, p, X; kwargs...) + return check_tangent_vector(G.manifold, p, X; kwargs...) end -distance(G::GroupManifold, x, y) = distance(G.manifold, x, y) -exp(G::GroupManifold, x, v) = exp(G.manifold, x, v) -exp!(G::GroupManifold, y, x, v) = exp!(G.manifold, y, x, v) +distance(G::GroupManifold, p, q) = distance(G.manifold, p, q) +exp(G::GroupManifold, p, X) = exp(G.manifold, p, X) +exp!(G::GroupManifold, q, p, X) = exp!(G.manifold, q, p, X) injectivity_radius(G::GroupManifold) = injectivity_radius(G.manifold) -injectivity_radius(G::GroupManifold, x) = injectivity_radius(G.manifold, x) -function injectivity_radius(G::GroupManifold, x, method::AbstractRetractionMethod) - return injectivity_radius(G.manifold, x, method) +injectivity_radius(G::GroupManifold, p) = injectivity_radius(G.manifold, p) +function injectivity_radius(G::GroupManifold, p, method::AbstractRetractionMethod) + return injectivity_radius(G.manifold, p, method) end -inner(G::GroupManifold, x, v, w) = inner(G.manifold, x, v, w) -inverse_retract(G::GroupManifold, x, y) = inverse_retract(G.manifold, x, y) -function inverse_retract(G::GroupManifold, x, y, method::AbstractInverseRetractionMethod) - return inverse_retract(G.manifold, x, y, method) +inner(G::GroupManifold, p, X, Y) = inner(G.manifold, p, X, Y) +inverse_retract(G::GroupManifold, p, q) = inverse_retract(G.manifold, p, q) +function inverse_retract(G::GroupManifold, p, q, method::AbstractInverseRetractionMethod) + return inverse_retract(G.manifold, p, q, method) end -inverse_retract!(G::GroupManifold, v, x, y) = inverse_retract!(G.manifold, v, x, y) +inverse_retract!(G::GroupManifold, X, p, q) = inverse_retract!(G.manifold, X, p, q) function inverse_retract!( G::GroupManifold, - v, - x, - y, + X, + p, + q, method::AbstractInverseRetractionMethod, ) - return inverse_retract!(G.manifold, v, x, y, method) + return inverse_retract!(G.manifold, X, p, q, method) end -function inverse_retract!(G::GroupManifold, v, x, y, method::LogarithmicInverseRetraction) - return inverse_retract!(G.manifold, v, x, y, method) +function inverse_retract!(G::GroupManifold, X, p, q, method::LogarithmicInverseRetraction) + return inverse_retract!(G.manifold, X, p, q, method) end -isapprox(G::GroupManifold, x, y; kwargs...) = isapprox(G.manifold, x, y; kwargs...) -isapprox(G::GroupManifold, x, v, w; kwargs...) = isapprox(G.manifold, x, v, w; kwargs...) -log(G::GroupManifold, x, y) = log(G.manifold, x, y) -log!(G::GroupManifold, v, x, y) = log!(G.manifold, v, x, y) -norm(G::GroupManifold, x, v) = norm(G.manifold, x, v) -project_point(G::GroupManifold, x) = project_point(G.manifold, x) -project_point!(G::GroupManifold, y, x) = project_point!(G.manifold, y, x) -project_tangent(G::GroupManifold, x, v) = project_tangent(G.manifold, x, v) -project_tangent!(G::GroupManifold, w, x, v) = project_tangent!(G.manifold, w, x, v) -retract(G::GroupManifold, x, v) = retract(G.manifold, x, v) -function retract(G::GroupManifold, x, v, method::AbstractRetractionMethod) - return retract(G.manifold, x, v, method) +isapprox(G::GroupManifold, p, q; kwargs...) = isapprox(G.manifold, p, q; kwargs...) +isapprox(G::GroupManifold, p, X, w; kwargs...) = isapprox(G.manifold, p, X, w; kwargs...) +log(G::GroupManifold, p, q) = log(G.manifold, p, q) +log!(G::GroupManifold, X, p, q) = log!(G.manifold, X, p, q) +norm(G::GroupManifold, p, X) = norm(G.manifold, p, X) +project_point(G::GroupManifold, p) = project_point(G.manifold, p) +project_point!(G::GroupManifold, q, p) = project_point!(G.manifold, q, p) +project_tangent(G::GroupManifold, p, X) = project_tangent(G.manifold, p, X) +project_tangent!(G::GroupManifold, Y, p, X) = project_tangent!(G.manifold, Y, p, X) +retract(G::GroupManifold, p, X) = retract(G.manifold, p, X) +function retract(G::GroupManifold, p, X, method::AbstractRetractionMethod) + return retract(G.manifold, p, X, method) end -retract!(G::GroupManifold, y, x, v) = retract!(G.manifold, y, x, v) -function retract!(G::GroupManifold, y, x, v, method::AbstractRetractionMethod) - return retract!(G.manifold, y, x, v, method) +retract!(G::GroupManifold, q, p, X) = retract!(G.manifold, q, p, X) +function retract!(G::GroupManifold, q, p, X, method::AbstractRetractionMethod) + return retract!(G.manifold, q, p, X, method) end -function retract!(G::GroupManifold, y, x, v, method::ExponentialRetraction) - return retract!(G.manifold, y, x, v, method) +function retract!(G::GroupManifold, q, p, X, method::ExponentialRetraction) + return retract!(G.manifold, q, p, X, method) end -function vector_transport_along!(G::GroupManifold, vto, x, v, c, args...) - return vector_transport_along!(G.manifold, vto, x, v, c, args...) +function vector_transport_along!(G::GroupManifold, Y, p, X, c, args...) + return vector_transport_along!(G.manifold, Y, p, X, c, args...) end -function vector_transport_along(G::GroupManifold, x, v, c, args...) - return vector_transport_along(G.manifold, x, v, c, args...) +function vector_transport_along(G::GroupManifold, p, X, c, args...) + return vector_transport_along(G.manifold, p, X, c, args...) end -function vector_transport_direction!(G::GroupManifold, vto, x, v, vdir, args...) - return vector_transport_direction!(G.manifold, vto, x, v, vdir, args...) +function vector_transport_direction!(G::GroupManifold, Y, p, X, V, args...) + return vector_transport_direction!(G.manifold, Y, p, X, V, args...) end -function vector_transport_direction(G::GroupManifold, x, v, vdir, args...) - return vector_transport_direction(G.manifold, x, v, vdir, args...) +function vector_transport_direction(G::GroupManifold, p, X, V, args...) + return vector_transport_direction(G.manifold, p, X, V, args...) end -function vector_transport_to!(G::GroupManifold, vto, x, v, y, args...) - return vector_transport_to!(G.manifold, vto, x, v, y, args...) +function vector_transport_to!(G::GroupManifold, Y, p, X, q, args...) + return vector_transport_to!(G.manifold, Y, p, X, q, args...) end -function vector_transport_to(G::GroupManifold, x, v, y, args...) - return vector_transport_to(G.manifold, x, v, y, args...) +function vector_transport_to(G::GroupManifold, p, X, q, args...) + return vector_transport_to(G.manifold, p, X, q, args...) end -zero_tangent_vector(G::GroupManifold, x) = zero_tangent_vector(G.manifold, x) -zero_tangent_vector!(G::GroupManifold, y, x) = zero_tangent_vector!(G.manifold, y, x) +zero_tangent_vector(G::GroupManifold, p) = zero_tangent_vector(G.manifold, p) +zero_tangent_vector!(G::GroupManifold, q, p) = zero_tangent_vector!(G.manifold, q, p) ################### # Action directions @@ -187,7 +187,7 @@ switch_direction(::RightAction) = LeftAction() # General Identity element methods ################################## -@doc doc""" +@doc raw""" Identity(G::AbstractGroupManifold) The group identity element $e ∈ G$. @@ -202,99 +202,92 @@ Identity(M::Manifold, ::Val{false}) = error("Identity not implemented for manifo show(io::IO, e::Identity) = print(io, "Identity($(e.group))") -(e::Identity)(x) = identity(e.group, x) +(e::Identity)(p) = identity(e.group, p) # To ensure allocate_result_type works number_eltype(e::Identity) = Bool copyto!(e::TE, ::TE) where {TE<:Identity} = e -copyto!(x, ::TE) where {TE<:Identity} = identity!(e.group, x, e) -copyto!(x::AbstractArray, e::TE) where {TE<:Identity} = identity!(e.group, x, e) +copyto!(p, ::TE) where {TE<:Identity} = identity!(e.group, p, e) +copyto!(p::AbstractArray, e::TE) where {TE<:Identity} = identity!(e.group, p, e) -isapprox(x, e::Identity; kwargs...) = isapprox(e::Identity, x; kwargs...) -isapprox(e::Identity, x; kwargs...) = isapprox(e.group, e, x; kwargs...) +isapprox(p, e::Identity; kwargs...) = isapprox(e::Identity, p; kwargs...) +isapprox(e::Identity, p; kwargs...) = isapprox(e.group, e, p; kwargs...) isapprox(e::E, ::E; kwargs...) where {E<:Identity} = true -function check_manifold_point(M::Manifold, x::Identity; kwargs...) +function check_manifold_point(M::Manifold, p::Identity; kwargs...) if is_decorator_group(M) === Val(true) - return check_manifold_point(base_group(M), x; kwargs...) + return check_manifold_point(base_group(M), p; kwargs...) end - return DomainError(x, "The identity element $(x) does not belong to $(M).") + return DomainError(p, "The identity element $(p) does not belong to $(M).") end -function check_manifold_point(G::GroupManifold, x::Identity; kwargs...) - x === Identity(G) && return nothing - return DomainError(x, "The identity element $(x) does not belong to $(G).") +function check_manifold_point(G::GroupManifold, p::Identity; kwargs...) + p === Identity(G) && return nothing + return DomainError(p, "The identity element $(p) does not belong to $(G).") end -function check_manifold_point(G::GroupManifold, x; kwargs...) - return check_manifold_point(G.manifold, x; kwargs...) +function check_manifold_point(G::GroupManifold, p; kwargs...) + return check_manifold_point(G.manifold, p; kwargs...) end ########################## # Group-specific functions ########################## -@doc doc""" - inv(G::AbstractGroupManifold, x) +@doc raw""" + inv(G::AbstractGroupManifold, p) -Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x \circ x^{-1} = x^{-1} \circ x = e ∈ G$. +Inverse $p^{-1} ∈ G$ of an element $p ∈ G$, such that +$p \circ p^{-1} = p^{-1} \circ p = e ∈ G$. """ -inv(M::Manifold, x) = inv(M, x, is_decorator_manifold(M)) -inv(M::Manifold, x, ::Val{true}) = inv(M.manifold, x) -function inv(M::Manifold, x, ::Val{false}) - return error("inv not implemented on $(typeof(M)) for points $(typeof(x))") +inv(M::Manifold, p) = inv(M, p, is_decorator_manifold(M)) +inv(M::Manifold, p, ::Val{true}) = inv(M.manifold, p) +function inv(M::Manifold, p, ::Val{false}) + return error("inv not implemented on $(typeof(M)) for points $(typeof(p))") end -function inv(G::AbstractGroupManifold, x) - y = allocate_result(G, inv, x) - return inv!(G, y, x) +function inv(G::AbstractGroupManifold, p) + q = allocate_result(G, inv, p) + return inv!(G, q, p) end -@doc doc""" - inv!(G::AbstractGroupManifold, y, x) - -Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x \circ x^{-1} = x^{-1} \circ x = e ∈ G$. -The result is saved to `y`. -""" -inv!(M::Manifold, y, x) = inv!(M, y, x, is_decorator_manifold(M)) -inv!(M::Manifold, y, x, ::Val{true}) = inv!(M.manifold, y, x) -function inv!(M::Manifold, y, x, ::Val{false}) - return error("inv! not implemented on $(typeof(M)) for points $(typeof(x))") +inv!(M::Manifold, q, p) = inv!(M, q, p, is_decorator_manifold(M)) +inv!(M::Manifold, q, p, ::Val{true}) = inv!(M.manifold, q, p) +function inv!(M::Manifold, q, p, ::Val{false}) + return error("inv! not implemented on $(typeof(M)) for points $(typeof(p))") end -@doc doc""" - identity(G::AbstractGroupManifold, x) +@doc raw""" + identity(G::AbstractGroupManifold, p) -Identity element $e ∈ G$, such that for any element $x ∈ G$, $x \circ e = e \circ x = x$. -The returned element is of a similar type to `x`. +Identity element $e ∈ G$, such that for any element $p ∈ G$, $p \circ e = e \circ p = p$. +The returned element is of a similar type to `p`. """ -identity(M::Manifold, x) = identity(M, x, is_decorator_manifold(M)) -identity(M::Manifold, x, ::Val{true}) = identity(M.manifold, x) -function identity(M::Manifold, x, ::Val{false}) - return error("identity not implemented on $(typeof(M)) for points $(typeof(x))") +identity(M::Manifold, p) = identity(M, p, is_decorator_manifold(M)) +identity(M::Manifold, p, ::Val{true}) = identity(M.manifold, p) +function identity(M::Manifold, p, ::Val{false}) + return error("identity not implemented on $(typeof(M)) for points $(typeof(p))") end -function identity(G::AbstractGroupManifold, x) - y = allocate_result(G, identity, x) - return identity!(G, y, x) +function identity(G::AbstractGroupManifold, p) + y = allocate_result(G, identity, p) + return identity!(G, y, p) end -identity!(M::Manifold, y, x) = identity!(M, y, x, is_decorator_manifold(M)) -identity!(M::Manifold, y, x, ::Val{true}) = identity!(M.manifold, y, x) +identity!(M::Manifold, q, p) = identity!(M, q, p, is_decorator_manifold(M)) +identity!(M::Manifold, q, p, ::Val{true}) = identity!(M.manifold, q, p) function identity!(M::Manifold, y, x, ::Val{false}) return error("identity! not implemented on $(typeof(M)) for points $(typeof(y)) and $(typeof(x))") end -isapprox(M::Manifold, x, e::Identity; kwargs...) = isapprox(M, e, x; kwargs...) -function isapprox(M::Manifold, e::Identity, x; kwargs...) - is_decorator_group(M) === Val(true) && return isapprox(base_group(M), e, x; kwargs...) - error("isapprox not implemented for manifold $(typeof(M)) and points $(typeof(e)) and $(typeof(x))") +isapprox(M::Manifold, p, e::Identity; kwargs...) = isapprox(M, e, p; kwargs...) +function isapprox(M::Manifold, e::Identity, p; kwargs...) + is_decorator_group(M) === Val(true) && return isapprox(base_group(M), e, p; kwargs...) + error("isapprox not implemented for manifold $(typeof(M)) and points $(typeof(e)) and $(typeof(p))") end function isapprox(M::Manifold, e::E, ::E; kwargs...) where {E<:Identity} is_decorator_group(M) === Val(true) && return isapprox(base_group(M), e, e; kwargs...) error("isapprox not implemented for manifold $(typeof(M)) and points $(typeof(e)) and $(typeof(e))") end -function isapprox(G::GT, e::Identity{GT}, x; kwargs...) where {GT<:AbstractGroupManifold} - return isapprox(G, identity(G, x), x; kwargs...) +function isapprox(G::GT, e::Identity{GT}, p; kwargs...) where {GT<:AbstractGroupManifold} + return isapprox(G, identity(G, p), p; kwargs...) end function isapprox( ::GT, @@ -304,254 +297,232 @@ function isapprox( ) where {GT<:AbstractGroupManifold,E<:Identity{GT}} return true end -function isapprox(G::GT, x, e::Identity{GT}; kwargs...) where {GT<:GroupManifold} - return isapprox(G, e, x; kwargs...) +function isapprox(G::GT, p, e::Identity{GT}; kwargs...) where {GT<:GroupManifold} + return isapprox(G, e, p; kwargs...) end -function isapprox(G::GT, e::Identity{GT}, x; kwargs...) where {GT<:GroupManifold} - return isapprox(G, identity(G, x), x; kwargs...) +function isapprox(G::GT, e::Identity{GT}, p; kwargs...) where {GT<:GroupManifold} + return isapprox(G, identity(G, p), p; kwargs...) end isapprox(::GT, ::E, ::E; kwargs...) where {GT<:GroupManifold,E<:Identity{GT}} = true -@doc doc""" +@doc raw""" compose(G::AbstractGroupManifold, x, y) Compose elements $x,y ∈ G$ using the group operation $x \circ y$. """ -compose(M::Manifold, x, y) = compose(M, x, y, is_decorator_manifold(M)) -compose(M::Manifold, x, y, ::Val{true}) = compose(M.manifold, x, y) -function compose(M::Manifold, x, y, ::Val{false}) - return error("compose not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y))") +compose(M::Manifold, p, q) = compose(M, p, q, is_decorator_manifold(M)) +compose(M::Manifold, p, q, ::Val{true}) = compose(M.manifold, p, q) +function compose(M::Manifold, p, q, ::Val{false}) + return error("compose not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q))") end -function compose(G::AbstractGroupManifold, x, y) - z = allocate_result(G, compose, x, y) - return compose!(G, z, x, y) +function compose(G::AbstractGroupManifold, p, q) + x = allocate_result(G, compose, p, q) + return compose!(G, x, p, q) end -compose!(M::Manifold, z, x, y) = compose!(M, z, x, y, is_decorator_manifold(M)) -compose!(M::Manifold, z, x, y, ::Val{true}) = compose!(M.manifold, z, x, y) -function compose!(M::Manifold, z, x, y, ::Val{false}) - return error("compose! not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y))") +compose!(M::Manifold, x, p, q) = compose!(M, x, p, q, is_decorator_manifold(M)) +compose!(M::Manifold, x, p, q, ::Val{true}) = compose!(M.manifold, x, p, q) +function compose!(M::Manifold, x, p, q, ::Val{false}) + return error("compose! not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q))") end -_action_order(x, y, conv::LeftAction) = (x, y) -_action_order(x, y, conv::RightAction) = (y, x) +_action_order(p, q, conv::LeftAction) = (p, q) +_action_order(p, q, conv::RightAction) = (q, p) -@doc doc""" - translate(G::AbstractGroupManifold, x, y[, conv::ActionDirection=LeftAction()]) +@doc raw""" + translate(G::AbstractGroupManifold, p, q) + translate(G::AbstractGroupManifold, p, q, conv::ActionDirection=LeftAction()]) -For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified convention, either -left $L_x$ or right $R_x$, defined as +For group elements $p,q ∈ G$, translate $q$ by $p$ with the specified convention, either +left $L_p$ or right $R_q$, defined as ```math \begin{aligned} -L_x &: y ↦ x \circ y\\ -R_x &: y ↦ y \circ x. +L_p &: q ↦ p \circ q\\ +R_p &: q ↦ q \circ p. \end{aligned} ``` """ -translate(M::Manifold, x, y) = translate(M, x, y, LeftAction()) -function translate(M::Manifold, x, y, conv::ActionDirection) - return translate(M, x, y, conv, is_decorator_manifold(M)) +translate(M::Manifold, p, q) = translate(M, p, q, LeftAction()) +function translate(M::Manifold, p, q, conv::ActionDirection) + return translate(M, p, q, conv, is_decorator_manifold(M)) end -function translate(M::Manifold, x, y, conv::ActionDirection, ::Val{true}) - return translate(M.manifold, x, y, conv) +function translate(M::Manifold, p, q, conv::ActionDirection, ::Val{true}) + return translate(M.manifold, p, q, conv) end -function translate(M::Manifold, x, y, conv::ActionDirection, ::Val{false}) - return error("translate not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y)) and direction $(typeof(conv))") +function translate(M::Manifold, p, q, conv::ActionDirection, ::Val{false}) + return error("translate not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q)) and direction $(typeof(conv))") end -function translate(G::AbstractGroupManifold, x, y, conv::ActionDirection) - return compose(G, _action_order(x, y, conv)...) +function translate(G::AbstractGroupManifold, p, q, conv::ActionDirection) + return compose(G, _action_order(p, q, conv)...) end -@doc doc""" - translate!(G::AbstractGroupManifold, z, x, y[, conv::ActionDirection=LeftAction()]) - -For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified convention, either -left $L_x$ or right $R_x$, defined as -```math -\begin{aligned} -L_x &: y ↦ x \circ y\\ -R_x &: y ↦ y \circ x. -\end{aligned} -``` -Result of the operation is saved in `z`. -""" -translate!(M::Manifold, z, x, y) = translate!(M, z, x, y, LeftAction()) -function translate!(M::Manifold, z, x, y, conv::ActionDirection) - return translate!(M, z, x, y, conv, is_decorator_manifold(M)) +translate!(M::Manifold, x, p, q) = translate!(M, x, p, q, LeftAction()) +function translate!(M::Manifold, x, p, q, conv::ActionDirection) + return translate!(M, x, p, q, conv, is_decorator_manifold(M)) end -function translate!(M::Manifold, z, x, y, conv::ActionDirection, ::Val{true}) - return translate!(M.manifold, z, x, y, conv) +function translate!(M::Manifold, x, p, q, conv::ActionDirection, ::Val{true}) + return translate!(M.manifold, x, p, q, conv) end -function translate!(M::Manifold, z, x, y, conv::ActionDirection, ::Val{false}) - return error("translate! not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y)) and direction $(typeof(conv))") +function translate!(M::Manifold, x, p, q, conv::ActionDirection, ::Val{false}) + return error("translate! not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q)) and direction $(typeof(conv))") end -function translate!(G::AbstractGroupManifold, z, x, y, conv::ActionDirection) - return compose!(G, z, _action_order(x, y, conv)...) +function translate!(G::AbstractGroupManifold, x, p, q, conv::ActionDirection) + return compose!(G, x, _action_order(p, q, conv)...) end -@doc doc""" - inverse_translate(G::AbstractGroupManifold, x, y, [conv::ActionDirection=Left()]) +@doc raw""" + inverse_translate(G::AbstractGroupManifold, p, q) + inverse_translate(G::AbstractGroupManifold, p, q, conv::ActionDirection=Left()) -For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified convention, -either left $L_x^{-1}$ or right $R_x^{-1}$, defined as +For group elements $p, q ∈ G$, inverse translate $q$ by $p$ with the specified convention, +either left $L_p^{-1}$ or right $R_p^{-1}$, defined as ```math \begin{aligned} -L_x^{-1} &: y ↦ x^{-1} \circ y\\ -R_x^{-1} &: y ↦ y \circ x^{-1}. +L_p^{-1} &: q ↦ p^{-1} \circ q\\ +R_p^{-1} &: q ↦ q \circ p^{-1}. \end{aligned} ``` """ -inverse_translate(M::Manifold, x, y) = inverse_translate(M, x, y, LeftAction()) -function inverse_translate(M::Manifold, x, y, conv::ActionDirection) - return inverse_translate(M, x, y, conv, is_decorator_manifold(M)) +inverse_translate(M::Manifold, p, q) = inverse_translate(M, p, q, LeftAction()) +function inverse_translate(M::Manifold, p, q, conv::ActionDirection) + return inverse_translate(M, p, q, conv, is_decorator_manifold(M)) end -function inverse_translate(M::Manifold, x, y, conv::ActionDirection, ::Val{true}) - return inverse_translate(M.manifold, x, y, conv) +function inverse_translate(M::Manifold, p, q, conv::ActionDirection, ::Val{true}) + return inverse_translate(M.manifold, p, q, conv) end -function inverse_translate(M::Manifold, x, y, conv::ActionDirection, ::Val{false}) - return error("inverse_translate not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y)) and direction $(typeof(conv))") +function inverse_translate(M::Manifold, p, q, conv::ActionDirection, ::Val{false}) + return error("inverse_translate not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q)) and direction $(typeof(conv))") end -function inverse_translate(G::AbstractGroupManifold, x, y, conv::ActionDirection) - return translate(G, inv(G, x), y, conv) +function inverse_translate(G::AbstractGroupManifold, p, q, conv::ActionDirection) + return translate(G, inv(G, p), q, conv) end -@doc doc""" - inverse_translate!(G::AbstractGroupManifold, z, x, y, [conv::ActionDirection=Left()]) - -For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified convention, -either left $L_x^{-1}$ or right $R_x^{-1}$, defined as -```math -\begin{aligned} -L_x^{-1} &: y ↦ x^{-1} \circ y\\ -R_x^{-1} &: y ↦ y \circ x^{-1}. -\end{aligned} -``` -Result is saved in `z`. -""" -inverse_translate!(M::Manifold, z, x, y) = inverse_translate!(M, z, x, y, LeftAction()) -function inverse_translate!(M::Manifold, z, x, y, conv::ActionDirection) - return inverse_translate!(M, z, x, y, conv, is_decorator_manifold(M)) +inverse_translate!(M::Manifold, x, p, q) = inverse_translate!(M, x, p, q, LeftAction()) +function inverse_translate!(M::Manifold, x, p, q, conv::ActionDirection) + return inverse_translate!(M, x, p, q, conv, is_decorator_manifold(M)) end -function inverse_translate!(M::Manifold, z, x, y, conv::ActionDirection, ::Val{true}) - return inverse_translate!(M.manifold, z, x, y, conv) +function inverse_translate!(M::Manifold, x, p, q, conv::ActionDirection, ::Val{true}) + return inverse_translate!(M.manifold, x, p, q, conv) end -function inverse_translate!(M::Manifold, z, x, y, conv::ActionDirection, ::Val{false}) - return error("inverse_translate! not implemented on $(typeof(M)) for elements $(typeof(x)) and $(typeof(y)) and direction $(typeof(conv))") +function inverse_translate!(M::Manifold, x, p, q, conv::ActionDirection, ::Val{false}) + return error("inverse_translate! not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q)) and direction $(typeof(conv))") end -function inverse_translate!(G::AbstractGroupManifold, z, x, y, conv::ActionDirection) - return translate!(G, z, inv(G, x), y, conv) +function inverse_translate!(G::AbstractGroupManifold, x, p, q, conv::ActionDirection) + return translate!(G, x, inv(G, p), q, conv) end -@doc doc""" - translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=LeftAction()]) +@doc raw""" + translate_diff(G::AbstractGroupManifold, p, q, X) + translate_diff(G::AbstractGroupManifold, p, q, X, conv::ActionDirection=LeftAction()) -For group elements $x,y ∈ G$ and tangent vector $v ∈ T_y G$, compute the action of the -differential of the translation by $x$ on $v$, written as $(\mathrm{d}τ_x)_y (v)$, with the +For group elements $p, q ∈ G$ and tangent vector $X ∈ T_q G$, compute the action of the +differential of the translation by $p$ on $X$, written as $(\mathrm{d}τ_p)_q (X)$, with the specified left or right convention. The differential transports vectors: ```math \begin{aligned} -(\mathrm{d}L_x)_y (v) &: T_y G → T_{x \circ y} G\\ -(\mathrm{d}R_x)_y (v) &: T_y G → T_{y \circ x} G\\ +(\mathrm{d}L_p)_q (X) &: T_q G → T_{p \circ q} G\\ +(\mathrm{d}R_p)_q (X) &: T_q G → T_{q \circ p} G\\ \end{aligned} ``` """ -translate_diff(M::Manifold, x, y, v) = translate_diff(M, x, y, v, LeftAction()) -function translate_diff(M::Manifold, x, y, v, conv::ActionDirection) - return translate_diff(M, x, y, v, conv, is_decorator_manifold(M)) +translate_diff(M::Manifold, p, q, X) = translate_diff(M, p, q, X, LeftAction()) +function translate_diff(M::Manifold, p, q, X, conv::ActionDirection) + return translate_diff(M, p, q, X, conv, is_decorator_manifold(M)) end -function translate_diff(M::Manifold, x, y, v, conv::ActionDirection, ::Val{true}) - return translate_diff(M.manifold, x, y, v, conv) +function translate_diff(M::Manifold, p, q, X, conv::ActionDirection, ::Val{true}) + return translate_diff(M.manifold, p, q, X, conv) end -function translate_diff(M::Manifold, x, y, v, conv::ActionDirection, ::Val{false}) - return error("translate_diff not implemented on $(typeof(G)) for elements $(typeof(vout)), $(typeof(x)) and $(typeof(y)), vector $(typeof(v)), and direction $(typeof(conv))") +function translate_diff(M::Manifold, p, q, X, conv::ActionDirection, ::Val{false}) + return error("translate_diff not implemented on $(typeof(G)) for elements $(typeof(p)) and $(typeof(q)), vector $(typeof(X)), and direction $(typeof(conv))") end -function translate_diff(G::AbstractGroupManifold, x, y, v, conv::ActionDirection) - xy = translate(G, x, y, conv) - vout = zero_tangent_vector(G, xy) - translate_diff!(G, vout, x, y, v, conv) - return vout +function translate_diff(G::AbstractGroupManifold, p, q, X, conv::ActionDirection) + pq = translate(G, p, q, conv) + Y = zero_tangent_vector(G, pq) + translate_diff!(G, Y, p, q, X, conv) + return Y end -function translate_diff!(M::Manifold, vout, x, y, v) - return translate_diff!(M, vout, x, y, v, LeftAction()) +function translate_diff!(M::Manifold, Y, p, q, X) + return translate_diff!(M, Y, p, q, X, LeftAction()) end -function translate_diff!(M::Manifold, vout, x, y, v, conv::ActionDirection) - return translate_diff!(M, vout, x, y, v, conv, is_decorator_manifold(M)) +function translate_diff!(M::Manifold, Y, p, q, X, conv::ActionDirection) + return translate_diff!(M, Y, p, q, X, conv, is_decorator_manifold(M)) end -function translate_diff!(M::Manifold, vout, x, y, v, conv::ActionDirection, ::Val{true}) - return translate_diff!(M.manifold, vout, x, y, v, conv) +function translate_diff!(M::Manifold, Y, p, q, X, conv::ActionDirection, ::Val{true}) + return translate_diff!(M.manifold, Y, p, q, X, conv) end -function translate_diff!(M::Manifold, vout, x, y, v, conv::ActionDirection, ::Val{false}) - return error("translate_diff! not implemented on $(typeof(M)) for elements $(typeof(vout)), $(typeof(x)) and $(typeof(y)), vector $(typeof(v)), and direction $(typeof(conv))") +function translate_diff!(M::Manifold, Y, p, q, X, conv::ActionDirection, ::Val{false}) + return error("translate_diff! not implemented on $(typeof(M)) for elements $(typeof(Y)), $(typeof(p)) and $(typeof(q)), vector $(typeof(X)), and direction $(typeof(conv))") end -@doc doc""" - inverse_translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=Left()]) +@doc raw""" + inverse_translate_diff(G::AbstractGroupManifold, p, q, X) + inverse_translate_diff(G::AbstractGroupManifold, p, q, X, conv::ActionDirection=Left()) + +For group elements $p, q ∈ G$ and tangent vector $X ∈ T_q G$, compute the inverse of the +action of the differential of the translation by $p$ on $X$, written as +$((\mathrm{d}τ_p)_q)^{-1} (X) = (\mathrm{d}τ_{p^{-1}})_q (X)$, with the specified left or +right convention. The differential transports vectors as -For group elements $x,y ∈ G$ and tangent vector $v ∈ T_y G$, compute the inverse of the -action of the differential of the translation by $x$ on $v$, written as -$((\mathrm{d}τ_x)_y)^{-1} (v) = (\mathrm{d}τ_{x^{-1}})_y (v)$, with the specified left or -right convention. The differential transports vectors: ```math \begin{aligned} -((\mathrm{d}L_x)_y)^{-1} (v) &: T_y G → T_{x^{-1} \circ y} G\\ -((\mathrm{d}R_x)_y)^{-1} (v) &: T_y G → T_{y \circ x^{-1}} G\\ +((\mathrm{d}L_p)_q)^{-1} (X) &: T_q G → T_{p^{-1} \circ q} G\\ +((\mathrm{d}R_p)_q)^{-1} (X) &: T_q G → T_{q \circ p^{-1}} G\\ \end{aligned} ``` """ -function inverse_translate_diff(M::Manifold, x, y, v) - return inverse_translate_diff(M, x, y, v, LeftAction()) +function inverse_translate_diff(M::Manifold, p, q, X) + return inverse_translate_diff(M, p, q, X, LeftAction()) end -function inverse_translate_diff(M::Manifold, x, y, v, conv::ActionDirection) - return inverse_translate_diff(M, x, y, v, conv, is_decorator_manifold(M)) +function inverse_translate_diff(M::Manifold, p, q, X, conv::ActionDirection) + return inverse_translate_diff(M, p, q, X, conv, is_decorator_manifold(M)) end -function inverse_translate_diff(M::Manifold, x, y, v, conv::ActionDirection, ::Val{true}) - return inverse_translate_diff(M.manifold, x, y, v, conv) +function inverse_translate_diff(M::Manifold, p, q, X, conv::ActionDirection, ::Val{true}) + return inverse_translate_diff(M.manifold, p, q, X, conv) end -function inverse_translate_diff(M::Manifold, x, y, v, conv::ActionDirection, ::Val{false}) - return error("inverse_translate_diff not implemented on $(typeof(M)) for elements $(typeof(vout)), $(typeof(x)) and $(typeof(y)), vector $(typeof(v)), and direction $(typeof(conv))") +function inverse_translate_diff(M::Manifold, p, q, X, conv::ActionDirection, ::Val{false}) + return error("inverse_translate_diff not implemented on $(typeof(M)) for elements $(typeof(p)) and $(typeof(q)), vector $(typeof(X)), and direction $(typeof(conv))") end -function inverse_translate_diff(G::AbstractGroupManifold, x, y, v, conv::ActionDirection) - return translate_diff(G, inv(G, x), y, v, conv) +function inverse_translate_diff(G::AbstractGroupManifold, p, q, X, conv::ActionDirection) + return translate_diff(G, inv(G, p), q, X, conv) end -function inverse_translate_diff!(M::Manifold, vout, x, y, v) - return inverse_translate_diff!(M, vout, x, y, v, LeftAction()) +function inverse_translate_diff!(M::Manifold, Y, p, q, X) + return inverse_translate_diff!(M, Y, p, q, X, LeftAction()) end -function inverse_translate_diff!(M::Manifold, vout, x, y, v, conv::ActionDirection) - return inverse_translate_diff!(M, vout, x, y, v, conv, is_decorator_manifold(M)) +function inverse_translate_diff!(M::Manifold, Y, p, q, X, conv::ActionDirection) + return inverse_translate_diff!(M, Y, p, q, X, conv, is_decorator_manifold(M)) end -function inverse_translate_diff!( - M::Manifold, - vout, - x, - y, - v, +function inverse_translate_diff!(M::Manifold, + Y, + p, + q, + X, conv::ActionDirection, ::Val{true}, ) - return inverse_translate_diff!(M.manifold, vout, x, y, v, conv) + return inverse_translate_diff!(M.manifold, Y, p, q, X, conv) end function inverse_translate_diff!( M::Manifold, - vout, - x, - y, - v, + Y, + p, + q, + X, conv::ActionDirection, ::Val{false}, ) - return error("inverse_translate_diff! not implemented on $(typeof(M)) for elements $(typeof(vout)), $(typeof(x)) and $(typeof(y)), vector $(typeof(v)), and direction $(typeof(conv))") + return error("inverse_translate_diff! not implemented on $(typeof(M)) for elements $(typeof(Y)), $(typeof(p)) and $(typeof(q)), vector $(typeof(X)), and direction $(typeof(conv))") end function inverse_translate_diff!( G::AbstractGroupManifold, - vout, - x, - y, - v, + Y, + p, + q, + X, conv::ActionDirection, ) - return translate_diff!(G, vout, inv(G, x), y, v, conv) + return translate_diff!(G, Y, inv(G, p), q, X, conv) end ################################# @@ -568,46 +539,46 @@ struct AdditionOperation <: AbstractGroupOperation end const AdditionGroup = AbstractGroupManifold{AdditionOperation} +(e::Identity{G}) where {G<:AdditionGroup} = e -+(::Identity{G}, x) where {G<:AdditionGroup} = x -+(x, ::Identity{G}) where {G<:AdditionGroup} = x ++(::Identity{G}, p) where {G<:AdditionGroup} = p ++(p, ::Identity{G}) where {G<:AdditionGroup} = p +(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e -(e::Identity{G}) where {G<:AdditionGroup} = e --(::Identity{G}, x) where {G<:AdditionGroup} = -x --(x, ::Identity{G}) where {G<:AdditionGroup} = x +-(::Identity{G}, p) where {G<:AdditionGroup} = -p +-(p, ::Identity{G}) where {G<:AdditionGroup} = p -(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e -*(e::Identity{G}, x) where {G<:AdditionGroup} = e -*(x, e::Identity{G}) where {G<:AdditionGroup} = e +*(e::Identity{G}, p) where {G<:AdditionGroup} = e +*(p, e::Identity{G}) where {G<:AdditionGroup} = e *(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e zero(e::Identity{G}) where {G<:AdditionGroup} = e -identity(::AdditionGroup, x) = zero(x) +identity(::AdditionGroup, p) = zero(p) -identity!(::AdditionGroup, y, x) = fill!(y, 0) +identity!(::AdditionGroup, q, p) = fill!(q, 0) -inv(::AdditionGroup, x) = -x +inv(::AdditionGroup, p) = -p -inv!(::AdditionGroup, y, x) = copyto!(y, -x) +inv!(::AdditionGroup, q, p) = copyto!(q, -p) -compose(::AdditionGroup, x, y) = x + y +compose(::AdditionGroup, p, q) = p + q -function compose!(::GT, z, x, y) where {GT<:AdditionGroup} - x isa Identity{GT} && return copyto!(z, y) - y isa Identity{GT} && return copyto!(z, x) - z .= x .+ y - return z +function compose!(::GT, x, p, q) where {GT<:AdditionGroup} + p isa Identity{GT} && return copyto!(x, q) + q isa Identity{GT} && return copyto!(x, p) + x .= p .+ q + return x end -translate_diff(::AdditionGroup, x, y, v, ::ActionDirection) = v +translate_diff(::AdditionGroup, p, q, X, ::ActionDirection) = X -translate_diff!(::AdditionGroup, vout, x, y, v, ::ActionDirection) = copyto!(vout, v) +translate_diff!(::AdditionGroup, Y, p, q, X, ::ActionDirection) = copyto!(Y, X) -inverse_translate_diff(::AdditionGroup, x, y, v, ::ActionDirection) = v +inverse_translate_diff(::AdditionGroup, p, q, X, ::ActionDirection) = X -function inverse_translate_diff!(::AdditionGroup, vout, x, y, v, ::ActionDirection) - return copyto!(vout, v) +function inverse_translate_diff!(::AdditionGroup, Y, p, q, X, ::ActionDirection) + return copyto!(Y, X) end ####################################### @@ -624,16 +595,16 @@ struct MultiplicationOperation <: AbstractGroupOperation end const MultiplicationGroup = AbstractGroupManifold{MultiplicationOperation} *(e::Identity{G}) where {G<:MultiplicationGroup} = e -*(::Identity{G}, x) where {G<:MultiplicationGroup} = x -*(x, ::Identity{G}) where {G<:MultiplicationGroup} = x +*(::Identity{G}, p) where {G<:MultiplicationGroup} = p +*(p, ::Identity{G}) where {G<:MultiplicationGroup} = p *(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e -/(x, ::Identity{G}) where {G<:MultiplicationGroup} = x -/(::Identity{G}, x) where {G<:MultiplicationGroup} = inv(x) +/(p, ::Identity{G}) where {G<:MultiplicationGroup} = p +/(::Identity{G}, p) where {G<:MultiplicationGroup} = inv(p) /(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e -\(x, ::Identity{G}) where {G<:MultiplicationGroup} = inv(x) -\(::Identity{G}, x) where {G<:MultiplicationGroup} = x +\(p, ::Identity{G}) where {G<:MultiplicationGroup} = inv(p) +\(::Identity{G}, p) where {G<:MultiplicationGroup} = p \(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e inv(e::Identity{G}) where {G<:MultiplicationGroup} = e @@ -644,32 +615,32 @@ transpose(e::Identity{G}) where {G<:MultiplicationGroup} = e LinearAlgebra.det(::Identity{<:MultiplicationGroup}) = 1 -LinearAlgebra.mul!(y, e::Identity{G}, x) where {G<:MultiplicationGroup} = copyto!(y, x) -LinearAlgebra.mul!(y, x, e::Identity{G}) where {G<:MultiplicationGroup} = copyto!(y, x) -function LinearAlgebra.mul!(y, e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} - return identity!(e.group, y, e) +LinearAlgebra.mul!(q, e::Identity{G}, p) where {G<:MultiplicationGroup} = copyto!(q, p) +LinearAlgebra.mul!(q, p, e::Identity{G}) where {G<:MultiplicationGroup} = copyto!(q, p) +function LinearAlgebra.mul!(q, e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} + return identity!(e.group, q, e) end -identity(::MultiplicationGroup, x) = one(x) +identity(::MultiplicationGroup, p) = one(p) -function identity!(G::GT, y, x) where {GT<:MultiplicationGroup} - isa(x, Identity{GT}) || return copyto!(y, one(x)) - error("identity! not implemented on $(typeof(G)) for points $(typeof(y)) and $(typeof(x))") +function identity!(G::GT, q, p) where {GT<:MultiplicationGroup} + isa(p, Identity{GT}) || return copyto!(q, one(p)) + error("identity! not implemented on $(typeof(G)) for points $(typeof(q)) and $(typeof(p))") end -identity!(::MultiplicationGroup, y::AbstractMatrix, x) = copyto!(y, I) +identity!(::MultiplicationGroup, q::AbstractMatrix, p) = copyto!(q, I) -inv(::MultiplicationGroup, x) = inv(x) +inv(::MultiplicationGroup, p) = inv(p) -inv!(G::MultiplicationGroup, y, x) = copyto!(y, inv(G, x)) +inv!(G::MultiplicationGroup, q, p) = copyto!(q, inv(G, p)) -compose(::MultiplicationGroup, x, y) = x * y +compose(::MultiplicationGroup, p, q) = p * q # TODO: z might alias with x or y, we might be able to optimize it if it doesn't. -compose!(::MultiplicationGroup, z, x, y) = copyto!(z, x * y) +compose!(::MultiplicationGroup, x, p, q) = copyto!(x, p * q) -inverse_translate(::MultiplicationGroup, x, y, ::LeftAction) = x \ y -inverse_translate(::MultiplicationGroup, x, y, ::RightAction) = y / x +inverse_translate(::MultiplicationGroup, p, q, ::LeftAction) = p \ q +inverse_translate(::MultiplicationGroup, p, q, ::RightAction) = q / p -function inverse_translate!(G::MultiplicationGroup, z, x, y, conv::ActionDirection) - return copyto!(z, inverse_translate(G, x, y, conv)) +function inverse_translate!(G::MultiplicationGroup, x, p, q, conv::ActionDirection) + return copyto!(x, inverse_translate(G, p, q, conv)) end diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 2067480fd1..3925c579a0 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -19,7 +19,7 @@ The manifold the action `A` acts upon. """ g_manifold(A::AbstractGroupAction) = error("g_manifold not implemented for $(typeof(A)).") -allocate_result(A::AbstractGroupAction, f, x...) = allocate_result(g_manifold(A), f, x...) +allocate_result(A::AbstractGroupAction, f, p...) = allocate_result(g_manifold(A), f, p...) """ direction(::AbstractGroupAction{AD}) -> AD @@ -28,150 +28,150 @@ Get the direction of the action """ direction(::AbstractGroupAction{AD}) where {AD} = AD() -@doc doc""" - apply(A::AbstractGroupAction, a, x) +@doc raw""" + apply(A::AbstractGroupAction, a, p) -Apply action `a` to the point `x`. The action is specified by `A`. +Apply action `a` to the point `p`. The action is specified by `A`. Unless otherwise specified, right actions are defined in terms of the left action. For -point $x ∈ M$ and action element $a$, the right action is +point $p ∈ \mathcal M$ and action element $a$, the right action is ````math -x ⋅ a ≐ a^{-1} ⋅ x. +p ⋅ a ≐ a^{-1} ⋅ p. ```` """ -function apply(A::AbstractGroupAction, a, x) - y = allocate_result(A, apply, x, a) - return apply!(A, y, a, x) +function apply(A::AbstractGroupAction, a, p) + y = allocate_result(A, apply, p, a) + return apply!(A, y, a, p) end """ - apply!(A::AbstractGroupAction, y, a, x) + apply!(A::AbstractGroupAction, q, a, p) -Apply action `a` to the point `x` with the rule specified by `A`. -The result is saved in `y`. +Apply action `a` to the point `p` with the rule specified by `A`. +The result is saved in `q`. """ -function apply!(A::AbstractGroupAction{LeftAction}, y, a, x) - error("apply! not implemented for action $(typeof(A)) and points $(typeof(y)), $(typeof(x)) and $(typeof(a)).") +function apply!(A::AbstractGroupAction{LeftAction}, q, a, p) + error("apply! not implemented for action $(typeof(A)) and points $(typeof(q)), $(typeof(p)) and $(typeof(a)).") end -function apply!(A::AbstractGroupAction{RightAction}, y, a, x) +function apply!(A::AbstractGroupAction{RightAction}, q, a, p) ainv = inv(base_group(A), a) - return apply!(switch_direction(A), y, ainv, x) + return apply!(switch_direction(A), q, ainv, p) end """ - inverse_apply(A::AbstractGroupAction, a, x) + inverse_apply(A::AbstractGroupAction, a, p) -Apply inverse of action `a` to the point `x`. The action is specified by `A`. +Apply inverse of action `a` to the point `p`. The action is specified by `A`. """ -function inverse_apply(A::AbstractGroupAction, a, x) - y = allocate_result(A, inverse_apply, x, a) - return inverse_apply!(A, y, a, x) +function inverse_apply(A::AbstractGroupAction, a, p) + y = allocate_result(A, inverse_apply, p, a) + return inverse_apply!(A, y, a, p) end """ - inverse_apply!(A::AbstractGroupAction, y, a, x) + inverse_apply!(A::AbstractGroupAction, q, a, p) -Apply inverse of action `a` to the point `x` with the rule specified by `A`. -The result is saved in `y`. +Apply inverse of action `a` to the point `p` with the rule specified by `A`. +The result is saved in `q`. """ -function inverse_apply!(A::AbstractGroupAction, y, a, x) +function inverse_apply!(A::AbstractGroupAction, q, a, p) inva = inv(base_group(A), a) - return apply!(A, y, inva, x) + return apply!(A, q, inva, p) end -@doc doc""" - apply_diff(A::AbstractGroupAction, a, x, v) +@doc raw""" + apply_diff(A::AbstractGroupAction, a, p, X) -For group point $x ∈ M$ and tangent vector $v ∈ T_x M$, compute the action of the -differential of the action of $a ∈ G$ on $v$, specified by rule `A`. Written as -$(\mathrm{d}τ_a)_x (v)$, with the specified left or right convention, the differential +For group point $p ∈ \mathcal M$ and tangent vector $X ∈ T_p \mathcal M$, compute the action of the +differential of the action of $a ∈ G$ on $X$, specified by rule `A`. Written as +$(\mathrm{d}τ_a)_p (X)$, with the specified left or right convention, the differential transports vectors ````math \begin{aligned} -(\mathrm{d}L_a)_x (v) &: T_x M → T_{a ⋅ x} M\\ -(\mathrm{d}R_a)_x (v) &: T_x M → T_{x ⋅ a} M +(\mathrm{d}L_a)_p (X) &: T_p \mathcal M → T_{a ⋅ p} \mathcal M\\ +(\mathrm{d}R_a)_p (X) &: T_p \mathcal M → T_{p ⋅ a} \mathcal M \end{aligned} ```` """ -function apply_diff(A::AbstractGroupAction, a, x, v) - return error("apply_diff not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(x)), and vector $(typeof(v))") +function apply_diff(A::AbstractGroupAction, a, p, X) + return error("apply_diff not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), and vector $(typeof(X))") end -function apply_diff!(A::AbstractGroupAction, vout, a, x, v) - return error("apply_diff! not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(x)), vectors $(typeof(vout)) and $(typeof(v))") +function apply_diff!(A::AbstractGroupAction, Y, a, p, X) + return error("apply_diff! not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), vectors $(typeof(Y)) and $(typeof(X))") end -@doc doc""" - inverse_apply_diff(A::AbstractGroupAction, a, x, v) +@doc raw""" + inverse_apply_diff(A::AbstractGroupAction, a, p, X) -For group point $x ∈ M$ and tangent vector $v ∈ T_x M$, compute the action of the -differential of the inverse action of $a ∈ G$ on $v$, specified by rule `A`. Written as -$(\mathrm{d}τ_a)_x^{-1} (v)$, with the specified left or right convention, the +For group point $p ∈ \mathcal M$ and tangent vector $X ∈ T_p \mathcal M$, compute the action of the +differential of the inverse action of $a ∈ G$ on $X$, specified by rule `A`. Written as +$(\mathrm{d}τ_a)_p^{-1} (X)$, with the specified left or right convention, the differential transports vectors ````math \begin{aligned} -(\mathrm{d}L_a)_x^{-1} (v) &: T_x M → T_{a^{-1} ⋅ x} M\\ -(\mathrm{d}R_a)_x^{-1} (v) &: T_x M → T_{x ⋅ a^{-1}} M +(\mathrm{d}L_a)_p^{-1} (X) &: T_p \mathcal M → T_{a^{-1} ⋅ p} \mathcal M\\ +(\mathrm{d}R_a)_p^{-1} (X) &: T_p \mathcal M → T_{p ⋅ a^{-1}} \mathcal M \end{aligned} ```` """ -function inverse_apply_diff(A::AbstractGroupAction, a, x, v) - return apply_diff(A, inv(base_group(A), a), x, v) +function inverse_apply_diff(A::AbstractGroupAction, a, p, X) + return apply_diff(A, inv(base_group(A), a), p, X) end -function inverse_apply_diff!(A::AbstractGroupAction, vout, a, x, v) - return apply_diff!(A, vout, inv(base_group(A), a), x, v) +function inverse_apply_diff!(A::AbstractGroupAction, Y, a, p, X) + return apply_diff!(A, Y, inv(base_group(A), a), p, X) end compose(A::AbstractGroupAction{LeftAction}, a, b) = compose(base_group(A), a, b) compose(A::AbstractGroupAction{RightAction}, a, b) = compose(base_group(A), b, a) -compose!(A::AbstractGroupAction{LeftAction}, y, a, b) = compose!(base_group(A), y, a, b) -compose!(A::AbstractGroupAction{RightAction}, y, a, b) = compose!(base_group(A), y, b, a) +compose!(A::AbstractGroupAction{LeftAction}, q, a, b) = compose!(base_group(A), q, a, b) +compose!(A::AbstractGroupAction{RightAction}, q, a, b) = compose!(base_group(A), q, b, a) -@doc doc""" - optimal_alignment(A::AbstractGroupAction, x1, x2) +@doc raw""" + optimal_alignment(A::AbstractGroupAction, p, q) -Calculate an action element of action `A` that acts upon `x1` to produce -the element closest to `x2` in the metric of the G-manifold: +Calculate an action element of action `A` that acts upon `p` to produce +the element closest to `q` in the metric of the G-manifold: ```math -\arg\min_{g ∈ G} d_M(g ⋅ x_1, x_2) +\arg\min_{g ∈ G} d_M(g ⋅ p, q) ``` -where $G$ is the group that acts on the G-manifold $M$. +where $G$ is the group that acts on the G-manifold $\mathcal M$. """ -function optimal_alignment(A::AbstractGroupAction, x1, x2) - error("optimal_alignment not implemented for $(typeof(A)) and points $(typeof(x1)) and $(typeof(x2)).") +function optimal_alignment(A::AbstractGroupAction, p, q) + error("optimal_alignment not implemented for $(typeof(A)) and points $(typeof(p)) and $(typeof(q)).") end """ - optimal_alignment!(A::AbstractGroupAction, y, x1, x2) + optimal_alignment!(A::AbstractGroupAction, y, p, q) -Calculate an action element of action `A` that acts upon `x1` to produce the element closest -to `x2`. -The result is written to `y`. +Calculate an action element of action `A` that acts upon `p` to produce the element closest +to `q`. +The result is written to `x`. """ -function optimal_alignment!(A::AbstractGroupAction, y, x1, x2) - return copyto!(y, optimal_alignment(A, x1, x2)) +function optimal_alignment!(A::AbstractGroupAction, x, p, q) + return copyto!(x, optimal_alignment(A, p, q)) end -@doc doc""" +@doc raw""" center_of_orbit( A::AbstractGroupAction, pts, - q, + p, mean_method::AbstractEstimationMethod = GradientDescentEstimation() ) -Calculate an action element $g$ of action `A` that is the mean element of the orbit of `q` +Calculate an action element $g$ of action `A` that is the mean element of the orbit of `p` with respect to given set of points `pts`. The [`mean`](@ref) is calculated using the method `mean_method`. -The orbit of $q$ with respect to the action of a group $G$ is the set +The orbit of $p$ with respect to the action of a group $G$ is the set ````math -O = \{ g ⋅ q : g ∈ G \}. -``` +O = \{ g ⋅ p : g ∈ G \}. +```` This function is useful for computing means on quotients of manifolds by a Lie group action. """ function center_of_orbit( @@ -180,7 +180,6 @@ function center_of_orbit( q, mean_method::AbstractEstimationMethod = GradientDescentEstimation(), ) - alignments = map(p -> optimal_alignment(A, q, p), pts) return mean(g_manifold(A), alignments, mean_method) end diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index 12a8cb6725..be8e59dc80 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" GroupOperationAction(group::AbstractGroupManifold, AD::ActionDirection = LeftAction()) Action of a group upon itself via left or right translation. @@ -26,52 +26,52 @@ function switch_direction(A::GroupOperationAction) return GroupOperationAction(A.group, switch_direction(direction(A))) end -apply(A::GroupOperationAction, a, x) = translate(A.group, a, x, direction(A)) +apply(A::GroupOperationAction, a, p) = translate(A.group, a, p, direction(A)) -apply!(A::GroupOperationAction, y, a, x) = translate!(A.group, y, a, x, direction(A)) +apply!(A::GroupOperationAction, y, a, p) = translate!(A.group, y, a, p, direction(A)) -function inverse_apply(A::GroupOperationAction, a, x) - return inverse_translate(A.group, a, x, direction(A)) +function inverse_apply(A::GroupOperationAction, a, p) + return inverse_translate(A.group, a, p, direction(A)) end -function inverse_apply!(A::GroupOperationAction, y, a, x) - return inverse_translate!(A.group, y, a, x, direction(A)) +function inverse_apply!(A::GroupOperationAction, q, a, p) + return inverse_translate!(A.group, q, a, p, direction(A)) end -function apply_diff(A::GroupOperationAction, a, x, v) - return translate_diff(A.group, a, x, v, direction(A)) +function apply_diff(A::GroupOperationAction, a, p, X) + return translate_diff(A.group, a, p, X, direction(A)) end -function apply_diff!(A::GroupOperationAction, vout, a, x, v) - return translate_diff!(A.group, vout, a, x, v, direction(A)) +function apply_diff!(A::GroupOperationAction, Y, a, p, X) + return translate_diff!(A.group, Y, a, p, X, direction(A)) end -function inverse_apply_diff(A::GroupOperationAction, a, x, v) - return inverse_translate_diff(A.group, a, x, v, direction(A)) +function inverse_apply_diff(A::GroupOperationAction, a, p, X) + return inverse_translate_diff(A.group, a, p, X, direction(A)) end -function inverse_apply_diff!(A::GroupOperationAction, vout, a, x, v) - return inverse_translate_diff!(A.group, vout, a, x, v, direction(A)) +function inverse_apply_diff!(A::GroupOperationAction, Y, a, p, X) + return inverse_translate_diff!(A.group, Y, a, p, X, direction(A)) end -function optimal_alignment(A::GroupOperationAction, x1, x2) - return inverse_apply(switch_direction(A), x1, x2) +function optimal_alignment(A::GroupOperationAction, p, q) + return inverse_apply(switch_direction(A), p, q) end -function optimal_alignment!(A::GroupOperationAction, y, x1, x2) - return inverse_apply!(switch_direction(A), y, x1, x2) +function optimal_alignment!(A::GroupOperationAction, x, p, q) + return inverse_apply!(switch_direction(A), x, p, q) end function center_of_orbit( A::GroupOperationAction, pts::AbstractVector, - q, + p, mean_method::AbstractEstimationMethod, ) m = mean(A.group, pts, mean_method) - return inverse_apply(switch_direction(A), q, m) + return inverse_apply(switch_direction(A), p, m) end -function center_of_orbit(A::GroupOperationAction, pts::AbstractVector, q) +function center_of_orbit(A::GroupOperationAction, pts::AbstractVector, p) m = mean(A.group, pts) - return inverse_apply(switch_direction(A), q, m) + return inverse_apply(switch_direction(A), p, m) end diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index d700763f71..b2b1e00a46 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -54,215 +54,215 @@ function submanifold_components( return Identity.(M.manifolds) end -inv(G::ProductGroup, x) = inv(G.manifold, x) +inv(G::ProductGroup, p) = inv(G.manifold, p) inv(::GT, e::Identity{GT}) where {GT<:ProductGroup} = e function inv(M::ProductManifold, x::ProductRepr) return ProductRepr(map(inv, M.manifolds, submanifold_components(M, x))...) end -function inv(M::ProductManifold, x) - y = allocate_result(M, inv, x) - return inv!(M, y, x) +function inv(M::ProductManifold, p) + q = allocate_result(M, inv, p) + return inv!(M, q, p) end -inv!(G::ProductGroup, y, x) = inv!(G.manifold, y, x) -function inv!(M::ProductManifold, y, x) - map(inv!, M.manifolds, submanifold_components(M, y), submanifold_components(M, x)) - return y +inv!(G::ProductGroup, q, p) = inv!(G.manifold, q, p) +function inv!(M::ProductManifold, q, p) + map(inv!, M.manifolds, submanifold_components(M, q), submanifold_components(M, p)) + return q end -identity(G::ProductGroup, x) = identity(G.manifold, x) +identity(G::ProductGroup, p) = identity(G.manifold, p) identity(::GT, e::Identity{GT}) where {GT<:ProductGroup} = e -function identity(M::ProductManifold, x::ProductRepr) - return ProductRepr(map(identity, M.manifolds, submanifold_components(M, x))...) +function identity(M::ProductManifold, p::ProductRepr) + return ProductRepr(map(identity, M.manifolds, submanifold_components(M, p))...) end -function identity(M::ProductManifold, x) - y = allocate_result(M, identity, x) - return identity!(M, y, x) +function identity(M::ProductManifold, p) + q = allocate_result(M, identity, p) + return identity!(M, q, p) end -identity!(G::ProductGroup, y, x) = identity!(G.manifold, y, x) -function identity!(M::ProductManifold, y, x) - map(identity!, M.manifolds, submanifold_components(M, y), submanifold_components(M, x)) - return y +identity!(G::ProductGroup, q, p) = identity!(G.manifold, q, p) +function identity!(M::ProductManifold, q, p) + map(identity!, M.manifolds, submanifold_components(M, q), submanifold_components(M, p)) + return q end -compose(G::ProductGroup, x, y) = compose(G.manifold, x, y) -compose(G::GT, ::Identity{GT}, x) where {GT<:ProductGroup} = x -compose(G::GT, x, ::Identity{GT}) where {GT<:ProductGroup} = x +compose(G::ProductGroup, p, q) = compose(G.manifold, p, q) +compose(G::GT, ::Identity{GT}, p) where {GT<:ProductGroup} = p +compose(G::GT, p, ::Identity{GT}) where {GT<:ProductGroup} = p compose(G::GT, e::E, ::E) where {GT<:ProductGroup,E<:Identity{GT}} = e -function compose(M::ProductManifold, x::ProductRepr, y::ProductRepr) +function compose(M::ProductManifold, p::ProductRepr, q::ProductRepr) return ProductRepr(map( compose, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), )...) end -function compose(M::ProductManifold, x, y) - z = allocate_result(M, compose, x, y) - return compose!(M, z, x, y) +function compose(M::ProductManifold, p, q) + z = allocate_result(M, compose, p, q) + return compose!(M, z, p, q) end -compose!(G::ProductGroup, z, x, y) = compose!(G.manifold, z, x, y) -function compose!(M::ProductManifold, z, x, y) +compose!(G::ProductGroup, x, p, q) = compose!(G.manifold, x, p, q) +function compose!(M::ProductManifold, x, p, q) map( compose!, M.manifolds, - submanifold_components(M, z), submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), ) - return z + return x end -translate(G::ProductGroup, x, y, conv::ActionDirection) = translate(G.manifold, x, y, conv) +translate(G::ProductGroup, p, q, conv::ActionDirection) = translate(G.manifold, p, q, conv) function translate( M::ProductManifold, - x::ProductRepr, - y::ProductRepr, + p::ProductRepr, + q::ProductRepr, conv::ActionDirection, ) return ProductRepr(map( translate, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), repeated(conv), )...) end -function translate(M::ProductManifold, x, y, conv::ActionDirection) - z = allocate_result(M, translate, x, y) - return translate!(M, z, x, y, conv) +function translate(M::ProductManifold, p, q, conv::ActionDirection) + x = allocate_result(M, translate, p, q) + return translate!(M, x, p, q, conv) end -function translate!(G::ProductGroup, z, x, y, conv::ActionDirection) - return translate!(G.manifold, z, x, y, conv) +function translate!(G::ProductGroup, x, p, q, conv::ActionDirection) + return translate!(G.manifold, x, p, q, conv) end -function translate!(M::ProductManifold, z, x, y, conv::ActionDirection) +function translate!(M::ProductManifold, x, p, q, conv::ActionDirection) map( translate!, M.manifolds, - submanifold_components(M, z), submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), repeated(conv), ) - return z + return x end -function inverse_translate(G::ProductGroup, x, y, conv::ActionDirection) - return inverse_translate(G.manifold, x, y, conv) +function inverse_translate(G::ProductGroup, p, q, conv::ActionDirection) + return inverse_translate(G.manifold, p, q, conv) end function inverse_translate( M::ProductManifold, - x::ProductRepr, - y::ProductRepr, + p::ProductRepr, + q::ProductRepr, conv::ActionDirection, ) return ProductRepr(map( inverse_translate, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), repeated(conv), )...) end -function inverse_translate(M::ProductManifold, x, y, conv::ActionDirection) - z = allocate_result(M, inverse_translate, x, y) - return inverse_translate!(M, z, x, y, conv) +function inverse_translate(M::ProductManifold, p, q, conv::ActionDirection) + x = allocate_result(M, inverse_translate, p, q) + return inverse_translate!(M, x, p, q, conv) end -function inverse_translate!(G::ProductGroup, z, x, y, conv::ActionDirection) - return inverse_translate!(G.manifold, z, x, y, conv) +function inverse_translate!(G::ProductGroup, x, p, q, conv::ActionDirection) + return inverse_translate!(G.manifold, x, p, q, conv) end -function inverse_translate!(M::ProductManifold, z, x, y, conv::ActionDirection) +function inverse_translate!(M::ProductManifold, x, p, q, conv::ActionDirection) map( inverse_translate!, M.manifolds, - submanifold_components(M, z), submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), repeated(conv), ) - return z + return x end -function translate_diff(G::ProductGroup, x, y, v, conv::ActionDirection) - return translate_diff(G.manifold, x, y, v, conv) +function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) + return translate_diff(G.manifold, p, q, X, conv) end function translate_diff( M::ProductManifold, - x::ProductRepr, - y::ProductRepr, - v::ProductRepr, + p::ProductRepr, + q::ProductRepr, + X::ProductRepr, conv::ActionDirection, ) return ProductRepr(map( translate_diff, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), - submanifold_components(M, v), + submanifold_components(M, p), + submanifold_components(M, q), + submanifold_components(M, X), repeated(conv), )...) end -function translate_diff(M::ProductManifold, x, y, v, conv::ActionDirection) - vout = allocate_result(M, translate_diff, v, x, y) - return translate_diff!(M, vout, x, y, v, conv) +function translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) + Y = allocate_result(M, translate_diff, X, p, q) + return translate_diff!(M, Y, p, q, X, conv) end -function translate_diff!(G::ProductGroup, vout, x, y, v, conv::ActionDirection) - return translate_diff!(G.manifold, vout, x, y, v, conv) +function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) + return translate_diff!(G.manifold, Y, p, q, X, conv) end -function translate_diff!(M::ProductManifold, vout, x, y, v, conv::ActionDirection) +function translate_diff!(M::ProductManifold, Y, p, q, X, conv::ActionDirection) map( translate_diff!, M.manifolds, - submanifold_components(vout), - submanifold_components(M, x), - submanifold_components(M, y), - submanifold_components(M, v), + submanifold_components(Y), + submanifold_components(M, p), + submanifold_components(M, q), + submanifold_components(M, X), repeated(conv), ) - return vout + return Y end -function inverse_translate_diff(G::ProductGroup, x, y, v, conv::ActionDirection) - return inverse_translate_diff(G.manifold, x, y, v, conv) +function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) + return inverse_translate_diff(G.manifold, p, q, X, conv) end function inverse_translate_diff( M::ProductManifold, - x::ProductRepr, - y::ProductRepr, - v::ProductRepr, + p::ProductRepr, + q::ProductRepr, + X::ProductRepr, conv::ActionDirection, ) return ProductRepr(map( inverse_translate_diff, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), - submanifold_components(M, v), + submanifold_components(M, p), + submanifold_components(M, q), + submanifold_components(M, X), repeated(conv), )...) end -function inverse_translate_diff(M::ProductManifold, x, y, v, conv::ActionDirection) - vout = allocate_result(M, inverse_translate_diff, v, x, y) - return inverse_translate_diff!(M, vout, x, y, v, conv) +function inverse_translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) + Y = allocate_result(M, inverse_translate_diff, X, p, q) + return inverse_translate_diff!(M, Y, p, q, X, conv) end -function inverse_translate_diff!(G::ProductGroup, vout, x, y, v, conv::ActionDirection) - return inverse_translate_diff!(G.manifold, vout, x, y, v, conv) +function inverse_translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) + return inverse_translate_diff!(G.manifold, Y, p, q, X, conv) end -function inverse_translate_diff!(M::ProductManifold, vout, x, y, v, conv::ActionDirection) +function inverse_translate_diff!(M::ProductManifold, Y, p, q, X, conv::ActionDirection) map( inverse_translate_diff!, M.manifolds, - submanifold_components(vout), - submanifold_components(M, x), - submanifold_components(M, y), - submanifold_components(M, v), + submanifold_components(Y), + submanifold_components(M, p), + submanifold_components(M, q), + submanifold_components(M, X), repeated(conv), ) - return vout + return Y end diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index f40b7629b8..58ed606934 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" RotationAction( M::Manifold, SOn::SpecialOrthogonal, @@ -10,7 +10,7 @@ Euclidean-like manifold `M` of dimension `n`. """ struct RotationAction{TM<:Manifold,TSO<:SpecialOrthogonal,TAD<:ActionDirection} <: AbstractGroupAction{TAD} - M::TM + manifold::TM SOn::TSO end @@ -23,7 +23,7 @@ function RotationAction( end function show(io::IO, A::RotationAction) - print(io, "RotationAction($(A.M), $(A.SOn), $(direction(A)))") + print(io, "RotationAction($(A.manifold), $(A.SOn), $(direction(A)))") end const RotationActionOnVector{N,F,TAD} = RotationAction{ @@ -34,52 +34,52 @@ const RotationActionOnVector{N,F,TAD} = RotationAction{ base_group(A::RotationAction) = A.SOn -g_manifold(A::RotationAction) = A.M +g_manifold(A::RotationAction) = A.manifold function switch_direction(A::RotationAction{TM,TSO,TAD}) where {TM,TSO,TAD} - return RotationAction(A.M, A.SOn, switch_direction(TAD())) + return RotationAction(A.manifold, A.SOn, switch_direction(TAD())) end -apply(A::RotationActionOnVector{N,F,LeftAction}, a, x) where {N,F} = a * x -function apply(A::RotationActionOnVector{N,F,RightAction}, a, x) where {N,F} - return inv(base_group(A), a) * x +apply(A::RotationActionOnVector{N,F,LeftAction}, a, p) where {N,F} = a * p +function apply(A::RotationActionOnVector{N,F,RightAction}, a, p) where {N,F} + return inv(base_group(A), a) * p end -apply!(A::RotationActionOnVector{N,F,LeftAction}, y, a, x) where {N,F} = mul!(y, a, x) +apply!(A::RotationActionOnVector{N,F,LeftAction}, q, a, p) where {N,F} = mul!(q, a, p) -function inverse_apply(A::RotationActionOnVector{N,F,LeftAction}, a, x) where {N,F} - return inv(base_group(A), a) * x +function inverse_apply(A::RotationActionOnVector{N,F,LeftAction}, a, p) where {N,F} + return inv(base_group(A), a) * p end -inverse_apply(A::RotationActionOnVector{N,F,RightAction}, a, x) where {N,F} = a * x +inverse_apply(A::RotationActionOnVector{N,F,RightAction}, a, p) where {N,F} = a * p -apply_diff(A::RotationActionOnVector{N,F,LeftAction}, a, x, v) where {N,F} = a * v -function apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, x, v) where {N,F} - return inv(base_group(A), a) * v +apply_diff(A::RotationActionOnVector{N,F,LeftAction}, a, p, X) where {N,F} = a * X +function apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, p, X) where {N,F} + return inv(base_group(A), a) * X end -function apply_diff!(A::RotationActionOnVector{N,F,LeftAction}, vout, a, x, v) where {N,F} - return mul!(vout, a, v) +function apply_diff!(A::RotationActionOnVector{N,F,LeftAction}, Y, a, p, X) where {N,F} + return mul!(Y, a, X) end -function apply_diff!(A::RotationActionOnVector{N,F,RightAction}, vout, a, x, v) where {N,F} - return mul!(vout, inv(base_group(A), a), v) +function apply_diff!(A::RotationActionOnVector{N,F,RightAction}, Y, a, p, X) where {N,F} + return mul!(Y, inv(base_group(A), a), X) end -function inverse_apply_diff(A::RotationActionOnVector{N,F,LeftAction}, a, x, v) where {N,F} - return inv(base_group(A), a) * v +function inverse_apply_diff(A::RotationActionOnVector{N,F,LeftAction}, a, p, X) where {N,F} + return inv(base_group(A), a) * X end -inverse_apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, x, v) where {N,F} = a * v +inverse_apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, p, X) where {N,F} = a * X -function optimal_alignment(A::RotationActionOnVector{N,T,LeftAction}, x1, x2) where {N,T} - is_manifold_point(A.M, x1, true) - is_manifold_point(A.M, x2, true) +function optimal_alignment(A::RotationActionOnVector{N,T,LeftAction}, p, q) where {N,T} + is_manifold_point(A.manifold, p, true) + is_manifold_point(A.manifold, q, true) - Xmul = x1 * transpose(x2) + Xmul = p * transpose(q) F = svd(Xmul) L = size(Xmul)[2] UVt = F.U * F.Vt Ostar = det(UVt) ≥ 0 ? UVt : F.U * Diagonal([i < L ? 1 : -1 for i = 1:L]) * F.Vt return convert(typeof(Xmul), Ostar) end -function optimal_alignment(A::RotationActionOnVector{N,T,RightAction}, x1, x2) where {N,T} - return optimal_alignment(switch_direction(A), x2, x1) +function optimal_alignment(A::RotationActionOnVector{N,T,RightAction}, p, q) where {N,T} + return optimal_alignment(switch_direction(A), q, p) end diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 0f578696fc..9837f8b32b 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -16,11 +16,11 @@ end const SemidirectProductGroup{N,H,A} = GroupManifold{ProductManifold{Tuple{N,H}},SemidirectProductOperation{A}} -@doc doc""" +@doc raw""" SemidirectProductGroup(N::GroupManifold, H::GroupManifold, A::AbstractGroupAction) A group that is the semidirect product of a normal group $N$ and a subgroup $H$, written -$G = N ⋊_θ H$, where $θ: H × N \to N$ is an automorphism action +$G = N ⋊_θ H$, where $θ: H × N → N$ is an automorphism action of $H$ on $N$. The group $G$ has the composition rule ````math @@ -48,142 +48,142 @@ function show(io::IO, G::SemidirectProductGroup) print(io, "SemidirectProductGroup($(N), $(H), $(A))") end -_padpoint!(G::SemidirectProductGroup, y) = y +_padpoint!(G::SemidirectProductGroup, q) = q -_padvector!(G::SemidirectProductGroup, v) = v +_padvector!(G::SemidirectProductGroup, X) = X inv(G::GT, e::Identity{GT}) where {GT<:SemidirectProductGroup} = e -function inv!(G::SemidirectProductGroup, y, x) +function inv!(G::SemidirectProductGroup, q, p) M = base_manifold(G) N, H = M.manifolds A = G.op.action - nx, hx = submanifold_components(G, x) - ny, hy = submanifold_components(G, y) + nx, hx = submanifold_components(G, p) + ny, hy = submanifold_components(G, q) inv!(H, hy, hx) ninv = inv(N, nx) apply!(A, ny, hy, ninv) - @inbounds _padpoint!(G, y) - return y + @inbounds _padpoint!(G, q) + return q end -inv!(G::AG, y, e::Identity{AG}) where {AG<:SemidirectProductGroup} = identity!(G, y, e) +inv!(G::AG, p, e::Identity{AG}) where {AG<:SemidirectProductGroup} = identity!(G, p, e) identity(G::GT, e::Identity{GT}) where {GT<:SemidirectProductGroup} = e -function identity!(G::SemidirectProductGroup, y, x) +function identity!(G::SemidirectProductGroup, q, p) M = base_manifold(G) N, H = M.manifolds - nx, hx = submanifold_components(G, x) - ny, hy = submanifold_components(G, y) + nx, hx = submanifold_components(G, p) + ny, hy = submanifold_components(G, q) identity!(N, ny, nx) identity!(H, hy, hx) - @inbounds _padpoint!(G, y) - return y + @inbounds _padpoint!(G, q) + return q end identity!(G::GT, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} = e -compose(G::GT, x, e::Identity{GT}) where {GT<:SemidirectProductGroup} = x -compose(G::GT, e::Identity{GT}, x) where {GT<:SemidirectProductGroup} = x +compose(G::GT, p, e::Identity{GT}) where {GT<:SemidirectProductGroup} = p +compose(G::GT, e::Identity{GT}, p) where {GT<:SemidirectProductGroup} = p compose(G::GT, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} = e -function compose!(G::SemidirectProductGroup, z, x, y) +function compose!(G::SemidirectProductGroup, x, p, q) M = base_manifold(G) N, H = M.manifolds A = G.op.action - nx, hx = submanifold_components(G, x) - ny, hy = submanifold_components(G, y) - nz, hz = submanifold_components(G, z) + nx, hx = submanifold_components(G, p) + ny, hy = submanifold_components(G, q) + nz, hz = submanifold_components(G, x) compose!(H, hz, hx, hy) zₙtmp = apply(A, hx, ny) compose!(N, nz, nx, zₙtmp) - @inbounds _padpoint!(G, z) - return z + @inbounds _padpoint!(G, x) + return x end -compose!(G::GT, z, ::Identity{GT}, y) where {GT<:SemidirectProductGroup} = copyto!(z, y) -compose!(G::GT, z, x, ::Identity{GT}) where {GT<:SemidirectProductGroup} = copyto!(z, x) -function compose!(G::GT, z, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} - return identity!(G, z, e) +compose!(G::GT, x, ::Identity{GT}, q) where {GT<:SemidirectProductGroup} = copyto!(x, q) +compose!(G::GT, x, p, ::Identity{GT}) where {GT<:SemidirectProductGroup} = copyto!(x, p) +function compose!(G::GT, x, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} + return identity!(G, x, e) end -function translate_diff!(G::SemidirectProductGroup, vout, x, y, v, conv::LeftAction) +function translate_diff!(G::SemidirectProductGroup, Y, p, q, X, conv::LeftAction) M = base_manifold(G) N, H = M.manifolds A = G.op.action - nx, hx = submanifold_components(G, x) - ny, hy = submanifold_components(G, y) - nv, hv = submanifold_components(G, v) - nvout, hvout = submanifold_components(G, vout) - translate_diff!(H, hvout, hx, hy, hv, conv) - nw = apply_diff(A, hx, ny, nv) - nz = apply(A, hx, ny) - translate_diff!(N, nvout, nx, nz, nw, conv) - @inbounds _padvector!(G, vout) - return vout -end - -function hat!(G::SemidirectProductGroup, V, x, v) + np, hp = submanifold_components(G, p) + nq, hq = submanifold_components(G, q) + nX, hX = submanifold_components(G, X) + nY, hY = submanifold_components(G, Y) + translate_diff!(H, hY, hp, hq, hX, conv) + nZ = apply_diff(A, hp, nq, nX) + nr = apply(A, hp, nq) + translate_diff!(N, nY, np, nr, nZ, conv) + @inbounds _padvector!(G, Y) + return Y +end + +function hat!(G::SemidirectProductGroup, Y, p, X) M = base_manifold(G) N, H = M.manifolds dimN = manifold_dimension(N) dimH = manifold_dimension(H) - @assert length(v) == dimN + dimH - nx, hx = submanifold_components(G, x) - nV, hV = submanifold_components(G, V) - hat!(N, nV, nx, view(v, 1:dimN)) - hat!(H, hV, hx, view(v, dimN+1:dimN+dimH)) - @inbounds _padvector!(G, V) - return V + @assert length(X) == dimN + dimH + nx, hx = submanifold_components(G, p) + nV, hV = submanifold_components(G, Y) + hat!(N, nV, nx, view(X, 1:dimN)) + hat!(H, hV, hx, view(X, dimN+1:dimN+dimH)) + @inbounds _padvector!(G, Y) + return Y end -function vee!(G::SemidirectProductGroup, v, x, V) +function vee!(G::SemidirectProductGroup, Y, p, X) M = base_manifold(G) N, H = M.manifolds dimN = manifold_dimension(N) dimH = manifold_dimension(H) - @assert length(v) == dimN + dimH - nx, hx = submanifold_components(G, x) - nV, hV = submanifold_components(G, V) - vee!(N, view(v, 1:dimN), nx, nV) - vee!(H, view(v, dimN+1:dimN+dimH), hx, hV) - return v + @assert length(Y) == dimN + dimH + nx, hx = submanifold_components(G, p) + nV, hV = submanifold_components(G, X) + vee!(N, view(Y, 1:dimN), nx, nV) + vee!(H, view(Y, dimN+1:dimN+dimH), hx, hV) + return Y end -function zero_tangent_vector(G::SemidirectProductGroup, x) - v = allocate_result(G, zero_tangent_vector, x) - zero_tangent_vector!(G, v, x) - return v +function zero_tangent_vector(G::SemidirectProductGroup, p) + X = allocate_result(G, zero_tangent_vector, p) + zero_tangent_vector!(G, X, p) + return X end -function zero_tangent_vector!(G::SemidirectProductGroup, v, x) +function zero_tangent_vector!(G::SemidirectProductGroup, X, p) M = base_manifold(G) N, H = M.manifolds - nx, hx = submanifold_components(G, x) - nv, hv = submanifold_components(G, v) + nx, hx = submanifold_components(G, p) + nv, hv = submanifold_components(G, X) zero_tangent_vector!(N, nv, nx) zero_tangent_vector!(H, hv, hx) - return v + return X end -function isapprox(G::SemidirectProductGroup, x, y; kwargs...) +function isapprox(G::SemidirectProductGroup, p, q; kwargs...) M = base_manifold(G) N, H = M.manifolds - nx, hx = submanifold_components(G, x) - ny, hy = submanifold_components(G, y) + nx, hx = submanifold_components(G, p) + ny, hy = submanifold_components(G, q) return isapprox(N, nx, ny; kwargs...) && isapprox(H, hx, hy; kwargs...) end -function isapprox(G::SemidirectProductGroup, x, v, w; kwargs...) +function isapprox(G::SemidirectProductGroup, p, X, Y; kwargs...) M = base_manifold(G) N, H = M.manifolds - nx, hx = submanifold_components(G, x) - nv, hv = submanifold_components(G, v) - nw, hw = submanifold_components(G, w) + nx, hx = submanifold_components(G, p) + nv, hv = submanifold_components(G, X) + nw, hw = submanifold_components(G, Y) return isapprox(N, nx, nv, nw; kwargs...) && isapprox(H, hx, hv, hw; kwargs...) end -function isapprox(G::GT, x, e::Identity{GT}; kwargs...) where {GT<:SemidirectProductGroup} - return isapprox(G, e, x; kwargs...) +function isapprox(G::GT, p, e::Identity{GT}; kwargs...) where {GT<:SemidirectProductGroup} + return isapprox(G, e, p; kwargs...) end -function isapprox(G::GT, e::Identity{GT}, x; kwargs...) where {GT<:SemidirectProductGroup} - return isapprox(G, identity(G, x), x; kwargs...) +function isapprox(G::GT, e::Identity{GT}, p; kwargs...) where {GT<:SemidirectProductGroup} + return isapprox(G, identity(G, p), p; kwargs...) end function isapprox( ::GT, diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 007812ec1e..41e90481e1 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" SpecialEuclidean(n) Special Euclidean group $\mathrm{SE}(n)$, the group of rigid motions. @@ -21,7 +21,7 @@ SemidirectProductGroup(Tn, SOn, RotationAction(Tn, SOn)) ``` Points on $\mathrm{SE}(n)$ may be represented as points on the underlying product manifold -$\mathrm{T}(n) \times \mathrm{SO}(n)$ or as affine matrices with size `(n + 1, n + 1)`. +$\mathrm{T}(n) × \mathrm{SO}(n)$ or as affine matrices with size `(n + 1, n + 1)`. """ const SpecialEuclidean{N} = SemidirectProductGroup{ TranslationGroup{Tuple{N},ℝ}, @@ -40,54 +40,54 @@ show(io::IO, ::SpecialEuclidean{n}) where {n} = print(io, "SpecialEuclidean($(n) Base.@propagate_inbounds function submanifold_component( ::SpecialEuclidean{n}, - x::AbstractMatrix, + p::AbstractMatrix, ::Val{1}, ) where {n} - return view(x, 1:n, n + 1) + return view(p, 1:n, n + 1) end Base.@propagate_inbounds function submanifold_component( ::SpecialEuclidean{n}, - x::AbstractMatrix, + p::AbstractMatrix, ::Val{2}, ) where {n} - return view(x, 1:n, 1:n) + return view(p, 1:n, 1:n) end -function submanifold_components(G::SpecialEuclidean{n}, x::AbstractMatrix) where {n} - @assert size(x) == (n + 1, n + 1) - @inbounds t = submanifold_component(G, x, Val(1)) - @inbounds R = submanifold_component(G, x, Val(2)) +function submanifold_components(G::SpecialEuclidean{n}, p::AbstractMatrix) where {n} + @assert size(p) == (n + 1, n + 1) + @inbounds t = submanifold_component(G, p, Val(1)) + @inbounds R = submanifold_component(G, p, Val(2)) return (t, R) end Base.@propagate_inbounds function _padpoint!( ::SpecialEuclidean{n}, - y::AbstractMatrix, + q::AbstractMatrix, ) where {n} for i ∈ 1:n - y[n+1, i] = 0 + q[n+1, i] = 0 end - y[n+1, n+1] = 1 - return y + q[n+1, n+1] = 1 + return q end Base.@propagate_inbounds function _padvector!( ::SpecialEuclidean{n}, - v::AbstractMatrix, + X::AbstractMatrix, ) where {n} for i ∈ 1:n+1 - v[n+1, i] = 0 + X[n+1, i] = 0 end - return v + return X end -compose(::SpecialEuclidean, x::AbstractMatrix, y::AbstractMatrix) = x * y +compose(::SpecialEuclidean, p::AbstractMatrix, q::AbstractMatrix) = p * q function compose!( ::SpecialEuclidean, - z::AbstractMatrix, x::AbstractMatrix, - y::AbstractMatrix, + p::AbstractMatrix, + q::AbstractMatrix, ) - return mul!(z, x, y) + return mul!(x, p, q) end diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index fea9038c47..4aa38b2d57 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" SpecialOrthogonal{n} <: GroupManifold{Rotations{n},MultiplicationOperation} Special orthogonal group $\mathrm{SO}(n)$ represented by rotation matrices. @@ -12,22 +12,22 @@ SpecialOrthogonal(n) = SpecialOrthogonal{n}(Rotations(n), MultiplicationOperatio show(io::IO, ::SpecialOrthogonal{n}) where {n} = print(io, "SpecialOrthogonal($(n))") -inv(::SpecialOrthogonal, x) = transpose(x) +inv(::SpecialOrthogonal, p) = transpose(p) -inverse_translate(G::SpecialOrthogonal, x, y, conv::LeftAction) = inv(G, x) * y -inverse_translate(G::SpecialOrthogonal, x, y, conv::RightAction) = y * inv(G, x) +inverse_translate(G::SpecialOrthogonal, p, q, conv::LeftAction) = inv(G, p) * q +inverse_translate(G::SpecialOrthogonal, p, q, conv::RightAction) = q * inv(G, p) -translate_diff(::SpecialOrthogonal, x, y, v, ::LeftAction) = v -translate_diff(G::SpecialOrthogonal, x, y, v, ::RightAction) = inv(G, x) * v * x +translate_diff(::SpecialOrthogonal, p, q, X, ::LeftAction) = X +translate_diff(G::SpecialOrthogonal, p, q, X, ::RightAction) = inv(G, p) * X * p -function translate_diff!(G::SpecialOrthogonal, vout, x, y, v, conv::ActionDirection) - return copyto!(vout, translate_diff(G, x, y, v, conv)) +function translate_diff!(G::SpecialOrthogonal, Y, p, q, X, conv::ActionDirection) + return copyto!(Y, translate_diff(G, p, q, X, conv)) end -function inverse_translate_diff(G::SpecialOrthogonal, x, y, v, conv::ActionDirection) - return translate_diff(G, inv(G, x), y, v, conv) +function inverse_translate_diff(G::SpecialOrthogonal, p, q, X, conv::ActionDirection) + return translate_diff(G, inv(G, p), q, X, conv) end -function inverse_translate_diff!(G::SpecialOrthogonal, vout, x, y, v, conv::ActionDirection) - return copyto!(vout, inverse_translate_diff(G, x, y, v, conv)) +function inverse_translate_diff!(G::SpecialOrthogonal, Y, p, q, X, conv::ActionDirection) + return copyto!(Y, inverse_translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 14793ad472..722155afdc 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" TranslationAction( M::Manifold, Rn::TranslationGroup, @@ -12,7 +12,7 @@ The left and right actions are equivalent. """ struct TranslationAction{TM<:Manifold,TRn<:TranslationGroup,TAD<:ActionDirection} <: AbstractGroupAction{TAD} - M::TM + manifold::TM Rn::TRn end @@ -25,31 +25,31 @@ function TranslationAction( end function show(io::IO, A::TranslationAction) - print(io, "TranslationAction($(A.M), $(A.Rn), $(direction(A)))") + print(io, "TranslationAction($(A.manifold), $(A.Rn), $(direction(A)))") end base_group(A::TranslationAction) = A.Rn -g_manifold(A::TranslationAction) = A.M +g_manifold(A::TranslationAction) = A.manifold function switch_direction(A::TranslationAction{TM,TRN,TAD}) where {TM,TRN,TAD} - return TranslationAction(A.M, A.Rn, switch_direction(TAD())) + return TranslationAction(A.manifold, A.Rn, switch_direction(TAD())) end -apply(A::TranslationAction, a, x) = x + a +apply(A::TranslationAction, a, p) = p + a -apply!(A::TranslationAction{M,G}, y, a, x) where {M,G} = (y .= x .+ a) -apply!(A::TranslationAction{M,G}, y, e::Identity{G}, x) where {M,G} = copyto!(y, x) +apply!(A::TranslationAction{M,G}, q, a, p) where {M,G} = (q .= p .+ a) +apply!(A::TranslationAction{M,G}, q, e::Identity{G}, p) where {M,G} = copyto!(q, p) -inverse_apply(A::TranslationAction, a, x) = x - a +inverse_apply(A::TranslationAction, a, p) = p - a -inverse_apply!(A::TranslationAction{M,G}, y, a, x) where {M,G} = (y .= x .- a) -inverse_apply!(A::TranslationAction{M,G}, y, e::Identity{G}, x) where {M,G} = copyto!(y, x) +inverse_apply!(A::TranslationAction{M,G}, q, a, p) where {M,G} = (q .= p .- a) +inverse_apply!(A::TranslationAction{M,G}, q, e::Identity{G}, p) where {M,G} = copyto!(q, p) -apply_diff(A::TranslationAction, a, x, v) = v +apply_diff(A::TranslationAction, a, p, X) = X -apply_diff!(A::TranslationAction, vout, a, x, v) = copyto!(vout, v) +apply_diff!(A::TranslationAction, Y, a, p, X) = copyto!(Y, X) -inverse_apply_diff(A::TranslationAction, a, x, v) = v +inverse_apply_diff(A::TranslationAction, a, p, X) = X -inverse_apply_diff!(A::TranslationAction, vout, a, x, v) = copyto!(vout, v) +inverse_apply_diff!(A::TranslationAction, Y, a, p, X) = copyto!(Y, X) diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 0f3423782b..7b00d40eb4 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" TranslationGroup{T<:Tuple,F} <: GroupManifold{Euclidean{T,F},AdditionOperation} Translation group $\mathrm{T}(n)$ represented by translation arrays. @@ -7,7 +7,7 @@ Translation group $\mathrm{T}(n)$ represented by translation arrays. TranslationGroup(n₁,...,nᵢ; field = 𝔽) Generate the translation group on -$𝔽^{n₁,\dots,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isomorphic to group itself. +$𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isomorphic to group itself. """ const TranslationGroup{T<:Tuple,F} = GroupManifold{Euclidean{T,F},AdditionOperation} diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 8b3b0bbe20..ded18cc9f3 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" CholeskySpace{N} <: Manifold The manifold of lower triangular matrices with positive diagonal and @@ -9,7 +9,7 @@ are for example summarized in Table 1 of [^Lin2019]. CholeskySpace(n) -Generate the manifold of $n\times n$ lower triangular matrices with positive diagonal. +Generate the manifold of $n× n$ lower triangular matrices with positive diagonal. [^Lin2019]: > Lin, Zenhua: "Riemannian Geometry of Symmetric Positive Definite Matrices via @@ -19,160 +19,163 @@ struct CholeskySpace{N} <: Manifold end CholeskySpace(n::Int) = CholeskySpace{n}() -@doc doc""" - check_manifold_point(M::CholeskySpace, x; kwargs...) +@doc raw""" + check_manifold_point(M::CholeskySpace, p; kwargs...) -Check whether the matrix `x` lies on the [`CholeskySpace`](@ref) `M`, i.e. +Check whether the matrix `p` lies on the [`CholeskySpace`](@ref) `M`, i.e. it's size fits the manifold, it is a lower triangular matrix and has positive entries on the diagonal. The tolerance for the tests can be set using the `kwargs...`. """ -function check_manifold_point(M::CholeskySpace, x; kwargs...) - if size(x) != representation_size(M) +function check_manifold_point(M::CholeskySpace, p; kwargs...) + if size(p) != representation_size(M) return DomainError( - size(x), - "The point $(x) does not lie on $(M), since its size is not $(representation_size(M)).", + size(p), + "The point $(p) does not lie on $(M), since its size is not $(representation_size(M)).", ) end - if !isapprox(norm(strictlyUpperTriangular(x)), 0.0; kwargs...) + if !isapprox(norm(strictlyUpperTriangular(p)), 0.0; kwargs...) return DomainError( - norm(UpperTriangular(x) - Diagonal(x)), - "The point $(x) does not lie on $(M), since it strictly upper triangular nonzero entries", + norm(UpperTriangular(p) - Diagonal(p)), + "The point $(p) does not lie on $(M), since it strictly upper triangular nonzero entries", ) end - if any(diag(x) .<= 0) + if any(diag(p) .<= 0) return DomainError( - min(diag(x)...), - "The point $(x) does not lie on $(M), since it hast nonpositive entries on the diagonal", + min(diag(p)...), + "The point $(p) does not lie on $(M), since it hast nonpositive entries on the diagonal", ) end return nothing end """ - check_tangent_vector(M::CholeskySpace, x, v; kwargs... ) + check_tangent_vector(M::CholeskySpace, p, X; kwargs... ) -Checks whether `v` is a tangent vector to `x` on the [`CholeskySpace`](@ref) `M`, i.e. -atfer [`check_manifold_point`](@ref)`(M,x)`, `v` has to be of same dimension as `x` +Check whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. +after [`check_manifold_point`](@ref)`(M,p)`, `X` has to have the same dimension as `x` and a symmetric matrix. The tolerance for the tests can be set using the `kwargs...`. """ -function check_tangent_vector(M::CholeskySpace, x, v; kwargs...) - mpe = check_manifold_point(M, x) +function check_tangent_vector(M::CholeskySpace, p, X; kwargs...) + mpe = check_manifold_point(M, p) mpe !== nothing && return mpe - if size(v) != representation_size(M) + if size(X) != representation_size(M) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $(M) since its size does not match $(representation_size(M)).", + size(X), + "The vector $(X) is not a tangent to a point on $(M) since its size does not match $(representation_size(M)).", ) end - if !isapprox(norm(strictlyUpperTriangular(v)), 0.0; kwargs...) + if !isapprox(norm(strictlyUpperTriangular(X)), 0.0; kwargs...) return DomainError( - norm(UpperTriangular(v) - Diagonal(v)), - "The matrix $(v) is not a tangent vector at $(x) (represented as an element of the Lie algebra) since it is not lower triangular.", + norm(UpperTriangular(X) - Diagonal(X)), + "The matrix $(X) is not a tangent vector at $(p) (represented as an element of the Lie algebra) since it is not lower triangular.", ) end return nothing end -@doc doc""" - distance(M::CholeskySpace, x, y) +@doc raw""" + distance(M::CholeskySpace, p, q) Compute the Riemannian distance on the [`CholeskySpace`](@ref) `M` between two -matrices `x`, `y` that are lower triangular with positive diagonal. The formula +matrices `p`, `q` that are lower triangular with positive diagonal. The formula reads ````math -d_{\mathcal M}(x,y) = \sqrt{\sum_{i>j} (x_{ij}-y_{ij})^2 + -\sum_{j=1}^m (\log x_{jj} - \log y_{jj})^2 +d_{\mathcal M}(p,q) = \sqrt{\sum_{i>j} (p_{ij}-q_{ij})^2 + +\sum_{j=1}^m (\log p_{jj} - \log q_{jj})^2 } ```` """ -function distance(::CholeskySpace, x, y) +function distance(::CholeskySpace, p, q) return sqrt( - sum((strictlyLowerTriangular(x) - strictlyLowerTriangular(y)) .^ 2) + - sum((log.(diag(x)) - log.(diag(y))) .^ 2), + sum((strictlyLowerTriangular(p) - strictlyLowerTriangular(q)) .^ 2) + + sum((log.(diag(p)) - log.(diag(q))) .^ 2), ) end -@doc doc""" - exp(M::CholeskySpace, x, v) +@doc raw""" + exp(M::CholeskySpace, p, X) -Compute the exponential map on the [`CholeskySpace`](@ref) `M` eminating from the lower -triangular matrix with positive diagonal `x` towards the lower triangular matrix `v` +Compute the exponential map on the [`CholeskySpace`](@ref) `M` emanating from the lower +triangular matrix with positive diagonal `p` towards the lower triangular matrix `X` The formula reads ````math -\exp_x v = \lfloor x \rfloor + \lfloor v \rfloor + \operatorname{diag}(x) -\operatorname{diag}(x)\exp\bigl( \operatorname{diag}(v)\operatorname{diag}(x)^{-1}\bigr), +\exp_p X = ⌊ p ⌋ + ⌊ X ⌋ + \operatorname{diag}(p) +\operatorname{diag}(p)\exp\bigl( \operatorname{diag}(X)\operatorname{diag}(p)^{-1}\bigr), ```` -where $\lfloor x\rfloor$ denotes the strictly lower triangular matrix of $x$ and -$\operatorname{diag}(x)$ the diagonal matrix of $x$ +where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, +and $\operatorname{diag}$ extracts the diagonal matrix. """ exp(::CholeskySpace, ::Any...) -function exp!(::CholeskySpace, y, x, v) - y .= ( - strictlyLowerTriangular(x) + - strictlyLowerTriangular(v) + - Diagonal(diag(x)) * Diagonal(exp.(diag(v) ./ diag(x))) +function exp!(::CholeskySpace, q, p, X) + q .= ( + strictlyLowerTriangular(p) + + strictlyLowerTriangular(X) + + Diagonal(diag(p)) * Diagonal(exp.(diag(X) ./ diag(p))) ) - return y + return q end -@doc doc""" - inner(M::CholeskySpace, x, v, w) +@doc raw""" + inner(M::CholeskySpace, p, X, Y) Compute the inner product on the [`CholeskySpace`](@ref) `M` at the -lower triangular matric with positive diagonal `x` and the two tangent vectors -`v`,`w`, i.e they are both lower triangular matrices with arbitrary diagonal. +lower triangular matric with positive diagonal `p` and the two tangent vectors +`X`,`Y`, i.e they are both lower triangular matrices with arbitrary diagonal. The formula reads ````math - g_{x}(v,w) = \sum_{i>j} v_{ij}w_{ij} + \sum_{j=1}^m v_{ii}w_{ii}x_{ii}^{-2} +g_p(X,Y) = \sum_{i>j} X_{ij}Y_{ij} + \sum_{j=1}^m X_{ii}Y_{ii}p_{ii}^{-2} ```` """ -function inner(::CholeskySpace, x, v, w) +function inner(::CholeskySpace, p, X, Y) return ( - sum(strictlyLowerTriangular(v) .* strictlyLowerTriangular(w)) + - sum(diag(v) .* diag(w) ./ (diag(x) .^ 2)) + sum(strictlyLowerTriangular(X) .* strictlyLowerTriangular(Y)) + + sum(diag(X) .* diag(Y) ./ (diag(p) .^ 2)) ) end -@doc doc""" - log(M::CholeskySpace, v, x, y) +@doc raw""" + log(M::CholeskySpace, X, p, q) -Compute the logarithmic map on the [`CholeskySpace`](@ref) `M` for the geodesic eminating -from the lower triangular matrix with positive diagonal `x` towards `y`. +Compute the logarithmic map on the [`CholeskySpace`](@ref) `M` for the geodesic emanating +from the lower triangular matrix with positive diagonal `p` towards `q`. The formula reads ````math -\log_x v = \lfloor x \rfloor - \lfloor y \rfloor -+\operatorname{diag}(x)\log\bigl(\operatorname{diag}(y)\operatorname{diag}(x)^{-1}\bigr), +\log_p q = ⌊ p ⌋ - ⌊ q ⌋ + \operatorname{diag}(p)\log\bigl(\operatorname{diag}(q)\operatorname{diag}(p)^{-1}\bigr), ```` -where $\lfloor x\rfloor$ denotes the strictly lower triangular matrix of $x$ and -$\operatorname{diag}(x)$ the diagonal matrix of $x$ +where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, +and $\operatorname{diag}$ extracts the diagonal matrix. """ log(::Cholesky, ::Any...) -function log!(::CholeskySpace, v, x, y) +function log!(::CholeskySpace, X, p, q) return copyto!( - v, - strictlyLowerTriangular(y) - strictlyLowerTriangular(x) + - Diagonal(diag(x) .* log.(diag(y) ./ diag(x))), + X, + strictlyLowerTriangular(q) - strictlyLowerTriangular(p) + + Diagonal(diag(p) .* log.(diag(q) ./ diag(p))), ) end -@doc doc""" +@doc raw""" manifold_dimension(M::CholeskySpace) -Return the manifold dimension for the [`CholeskySpace`](@ref) `M`, i.e. $\frac{N(N+1)}{2}$. +Return the manifold dimension for the [`CholeskySpace`](@ref) `M`, i.e. + +````math + \dim(\mathcal M) = \frac{N(N+1)}{2}. +```` """ @generated manifold_dimension(::CholeskySpace{N}) where {N} = div(N * (N + 1), 2) -@doc doc""" +@doc raw""" reporesentation_size(M::CholeskySpace) Return the representation size for the [`CholeskySpace`](@ref)`{N}` `M`, i.e. `(N,N)`. @@ -182,38 +185,38 @@ Return the representation size for the [`CholeskySpace`](@ref)`{N}` `M`, i.e. `( show(io::IO, ::CholeskySpace{N}) where {N} = print(io, "CholeskySpace($(N))") # two small helpers for strictly lower and upper triangulars -strictlyLowerTriangular(x) = LowerTriangular(x) - Diagonal(diag(x)) +strictlyLowerTriangular(p) = LowerTriangular(p) - Diagonal(diag(p)) -strictlyUpperTriangular(x) = UpperTriangular(x) - Diagonal(diag(x)) +strictlyUpperTriangular(p) = UpperTriangular(p) - Diagonal(diag(p)) -@doc doc""" - vector_transport_to(M::CholeskySpace, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::CholeskySpace, p, X, q, ::ParallelTransport) -Parallely transport the tangent vector `v` at `x` along the geodesic to `y` -on to the [`CholeskySpace`](@ref) manifold `M`. The formula reads +Parallely transport the tangent vector `X` at `p` along the geodesic to `q` +on the [`CholeskySpace`](@ref) manifold `M`. The formula reads ````math -\mathcal P_{y\gets x}(v) = \lfloor v \rfloor -+ \operatorname{diag}(y)\operatorname{diag}(x)^{-1}\operatorname{diag}(v), +\mathcal P_{q←p}(X) = ⌊ X ⌋ ++ \operatorname{diag}(q)\operatorname{diag}(p)^{-1}\operatorname{diag}(X), ```` -where $\lfloor\cdot\rfloor$ denotes the strictly lower triangular matrix, +where $⌊\cdot⌋$ denotes the strictly lower triangular matrix, and $\operatorname{diag}$ extracts the diagonal matrix. """ vector_transport_to(::CholeskySpace, ::Any, ::Any, ::Any, ::ParallelTransport) -function vector_transport_to!(::CholeskySpace, vto, x, v, y, ::ParallelTransport) +function vector_transport_to!(::CholeskySpace, Y, p, X, q, ::ParallelTransport) return copyto!( - vto, - strictlyLowerTriangular(x) + Diagonal(diag(y) .* diag(v) ./ diag(x)), + Y, + strictlyLowerTriangular(p) + Diagonal(diag(q) .* diag(X) ./ diag(p)), ) end -@doc doc""" - zero_tangent_vector(M::CholeskySpace, x) +@doc raw""" + zero_tangent_vector(M::CholeskySpace, p) -Return the zero tangent vector on the [`CholeskySpace`](@ref) `M` at `x`. +Return the zero tangent vector on the [`CholeskySpace`](@ref) `M` at `p`. """ zero_tangent_vector(::CholeskySpace, ::Any...) -zero_tangent_vector!(M::CholeskySpace, v, x) = fill!(v, 0) +zero_tangent_vector!(M::CholeskySpace, X, p) = fill!(X, 0) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index fde047338e..3e3ee85b5d 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -1,8 +1,8 @@ -@doc doc""" +@doc raw""" Circle{F} <: Manifold -The circle $\mathbb S^1$ as a manifold ere manifold represented by -real-valued data in $[-\pi,\pi)$ or complex-valued data $z\in \mathbb C$ of absolute value +The circle $𝕊^1$ is a manifold here represented by +real-valued points in $[-π,π)$ or complex-valued points $z ∈ ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor @@ -16,63 +16,62 @@ struct Circle{F} <: Manifold where {F<:AbstractNumbers} end Circle(f::AbstractNumbers = ℝ) = Circle{f}() -@doc doc""" - check_manifold_point(M::Circle, x) +@doc raw""" + check_manifold_point(M::Circle, p) -Check whether `x` is a point on the [`Circle`](@ref) `M`. -For the real-valued case, `x` is an angle and hence it checks that $x \in [-\pi,\pi)$. -for the complex-valued case its a unit number, $x \in \mathbb C$ with $\lvert x \rvert = 1$. +Check whether `p` is a point on the [`Circle`](@ref) `M`. +For the real-valued case, `x` is an angle and hence it checks that $p ∈ [-π,π)$. +for the complex-valued case, it is a unit number, $p ∈ ℂ$ with $\lvert p \rvert = 1$. """ check_manifold_point(::Circle, ::Any...) -function check_manifold_point(M::Circle{ℝ}, x; kwargs...) - if !isapprox(sym_rem(x), x; kwargs...) +function check_manifold_point(M::Circle{ℝ}, p; kwargs...) + if !isapprox(sym_rem(p), p; kwargs...) return DomainError( - x, - "The point $(x) does not lie on $(M), since its is not in [-π,π).", + p, + "The point $(p) does not lie on $(M), since its is not in [-π,π).", ) end return nothing end -function check_manifold_point(M::Circle{ℂ}, x; kwargs...) - if !isapprox(sum(abs.(x)), 1.0; kwargs...) +function check_manifold_point(M::Circle{ℂ}, p; kwargs...) + if !isapprox(sum(abs.(p)), 1.0; kwargs...) return DomainError( - abs(x), - "The point $(x) does not lie on the $(M) since its norm is not 1.", + abs(p), + "The point $(p) does not lie on the $(M) since its norm is not 1.", ) end return nothing end """ - check_tangent_vector(M::Circle, x, v) + check_tangent_vector(M::Circle, p, X) -Check whether `v` is a tangent vector in the tangent space of `x` on the +Check whether `X` is a tangent vector in the tangent space of `p` on the [`Circle`](@ref) `M`. -For the real-valued case represented by angles all `v` are valid, since the tangent space is -the whole real line. -For the complex-valued case `v` has to lie on the line parallel to the tangent line at `x` -in the complex plane, i.e. the inner product is zero. +For the real-valued case represented by angles, all `X` are valid, since the tangent space is the whole real line. +For the complex-valued case `X` has to lie on the line parallel to the tangent line at `p` +in the complex plane, i.e. their inner product has to be zero. """ check_tangent_vector(::Circle{ℝ}, ::Any...; ::Any...) -function check_tangent_vector(M::Circle{ℝ}, x, v; kwargs...) - perr = check_manifold_point(M, x) +function check_tangent_vector(M::Circle{ℝ}, p, X; kwargs...) + perr = check_manifold_point(M, p) return perr # if x is valid all v that are real numbers are valid end -function check_tangent_vector(M::Circle{ℂ}, x, v; kwargs...) - perr = check_manifold_point(M, x) +function check_tangent_vector(M::Circle{ℂ}, p, X; kwargs...) + perr = check_manifold_point(M, p) perr === nothing || return perr - if !isapprox(abs(complex_dot(x, v)), 0.0; kwargs...) + if !isapprox(abs(complex_dot(p, X)), 0.0; kwargs...) return DomainError( - abs(complex_dot(x, v)), - "The value $(v) is not a tangent vector to $(x) on $(M), since it is not orthogonal in the embedding.", + abs(complex_dot(p, X)), + "The value $(X) is not a tangent vector to $(p) on $(M), since it is not orthogonal in the embedding.", ) end return nothing end -@doc doc""" +@doc raw""" complex_dot(a, b) Compute the inner product of two (complex) numbers with in the complex plane. @@ -80,114 +79,113 @@ Compute the inner product of two (complex) numbers with in the complex plane. complex_dot(a, b) = dot(map(real, a), map(real, b)) + dot(map(imag, a), map(imag, b)) complex_dot(a::Number, b::Number) = (real(a) * real(b) + imag(a) * imag(b)) -@doc doc""" - distance(M::Circle, x, y) +@doc raw""" + distance(M::Circle, p, q) Compute the distance on the [`Circle`](@ref) `M`, which is -the absolute value of the symmetric remainder of `x` and `y` for the real-valued +the absolute value of the symmetric remainder of `p` and `q` for the real-valued case and the angle between both complex numbers in the Gaussian plane for the complex-valued case. """ distance(::Circle, ::Any...) -distance(::Circle{ℝ}, x::Real, y::Real) = abs(sym_rem(x - y)) -distance(::Circle{ℝ}, x, y) = abs(sum(sym_rem.(x - y))) -distance(::Circle{ℂ}, x, y) = acos(clamp(complex_dot(x, y), -1, 1)) +distance(::Circle{ℝ}, p::Real, q::Real) = abs(sym_rem(p - q)) +distance(::Circle{ℝ}, p, q) = abs(sum(sym_rem.(p - q))) +distance(::Circle{ℂ}, p, q) = acos(clamp(complex_dot(p, q), -1, 1)) -@doc doc""" - exp(M::Circle, x, v) +@doc raw""" + exp(M::Circle, p, X) Compute the exponential map on the [`Circle`](@ref). ````math -\exp_xv = (x+v)_{2\pi}, +\exp_p X = (p+X)_{2π}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, -i.e. in $[-\pi,\pi)$. +where $(\cdot)_{2π}$ is the (symmetric) remainder with respect to division by $2π$, i.e. in $[-π,π)$. -For the complex-valued case the formula is the same as for the [`Sphere`](@ref) -applied to valuedin the complex plane. +For the complex-valued case, the same formula as for the [`Sphere`](@ref) $𝕊^1$ is applied to values in the +complex plane. """ exp(::Circle, ::Any...) -exp(::Circle{ℝ}, x::Real, v::Real) = sym_rem(x + v) +exp(::Circle{ℝ}, p::Real, X::Real) = sym_rem(p + X) function exp(M::Circle{ℂ}, x::Number, v::Number) θ = norm(M, x, v) return cos(θ) * x + usinc(θ) * v end -exp!(::Circle{ℝ}, y, x, v) = (y .= sym_rem(x + v)) -function exp!(M::Circle{ℂ}, y, x, v) - θ = norm(M, x, v) - y .= cos(θ) * x + usinc(θ) * v - return y +exp!(::Circle{ℝ}, q, p, X) = (q .= sym_rem(p + X)) +function exp!(M::Circle{ℂ}, q, p, X) + θ = norm(M, p, X) + q .= cos(θ) * p + usinc(θ) * X + return q end -flat(M::Circle, x::Number, w::TFVector) = FVector(CotangentSpace, w.data) +flat(M::Circle, p::Number, X::TFVector) = FVector(CotangentSpace, X.data) -flat!(::Circle, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(::Circle, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) -function get_basis(M::Circle{ℝ}, x, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) +function get_basis(M::Circle{ℝ}, p, B::DiagonalizingOrthonormalBasis) + sbv = sign(B.frame_direction[1]) vs = @SVector [@SVector [sbv == 0 ? one(sbv) : sbv]] return PrecomputedDiagonalizingOrthonormalBasis(vs, @SVector [0]) end -get_coordinates(M::Circle{ℝ}, x, v, B::ArbitraryOrthonormalBasis) = v -function get_coordinates(M::Circle{ℝ}, x, v, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) - return v .* (sbv == 0 ? 1 : sbv) +get_coordinates(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X +function get_coordinates(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) + sbv = sign(B.frame_direction[1]) + return X .* (sbv == 0 ? 1 : sbv) end """ get_coordinates(M::Circle{ℂ}, x, v, B::ArbitraryOrthonormalBasis) Return tangent vector coordinates in the Lie algebra of the circle. """ -function get_coordinates(M::Circle{ℂ}, x, v, B::ArbitraryOrthonormalBasis) - v, x = v[1], x[1] - w = imag(v) * real(x) - real(v) * imag(x) +function get_coordinates(M::Circle{ℂ}, p, X, B::ArbitraryOrthonormalBasis) + X, p = X[1], p[1] + w = imag(X) * real(p) - real(X) * imag(p) return @SVector [w] end -get_vector(M::Circle{ℝ}, x, v, B::ArbitraryOrthonormalBasis) = v -function get_vector(M::Circle{ℝ}, x, v, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) - return v .* (sbv == 0 ? 1 : sbv) +get_vector(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X +function get_vector(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) + sbv = sign(B.frame_direction[1]) + return X .* (sbv == 0 ? 1 : sbv) end """ - get_vector(M::Circle{ℂ}, x, v, B::ArbitraryOrthonormalBasis) + get_vector(M::Circle{ℂ}, p, X, B::ArbitraryOrthonormalBasis) Return tangent vector from the coordinates in the Lie algebra of the circle. """ -get_vector(M::Circle{ℂ}, x, v, B::ArbitraryOrthonormalBasis) = @SVector [1im * v[1] * x[1]] +get_vector(M::Circle{ℂ}, p, X, B::ArbitraryOrthonormalBasis) = @SVector [1im * X[1] * p[1]] -@doc doc""" - injectivity_radius(M::Circle[, x]) +@doc raw""" + injectivity_radius(M::Circle[, p]) -Return the injectivity radius on the [`Circle`](@ref) `M`, i.e. $\pi$. +Return the injectivity radius on the [`Circle`](@ref) `M`, i.e. $π$. """ injectivity_radius(::Circle, args...) = π -@doc doc""" - inner(M::Circle, x, w, v) +@doc raw""" + inner(M::Circle, p, X, Y) -Compute the inner product of the two tangent vectors `w,v` from the tangent plane at `x` on +Compute the inner product of the two tangent vectors `X,Y` from the tangent plane at `p` on the [`Circle`](@ref) `M` using the restriction of the metric from the embedding, i.e. ````math -g_x(v,w) = w*v +g_p(X,Y) = X*Y ```` for the real case and ````math -g_x(v,w) = v^\mathrm{T}w +g_p(X,Y) = Y^\mathrm{T}X ```` for the complex case interpreting complex numbers in the Gaussian plane. """ inner(::Circle, ::Any...) -@inline inner(::Circle{ℝ}, x, w, v) = dot(v, w) -@inline inner(::Circle{ℝ}, x::Real, w::Real, v::Real) = v * w -@inline inner(::Circle{ℂ}, x, w, v) = complex_dot(w, v) +@inline inner(::Circle{ℝ}, p, X, Y) = dot(X, Y) +@inline inner(::Circle{ℝ}, p::Real, X::Real, Y::Real) = X * Y +@inline inner(::Circle{ℂ}, p, X, Y) = complex_dot(X, Y) function inverse_retract(M::Circle, x::Number, y::Number) return inverse_retract(M, x, y, LogarithmicInverseRetraction()) @@ -196,179 +194,177 @@ function inverse_retract(M::Circle, x::Number, y::Number, ::LogarithmicInverseRe return log(M, x, y) end -@doc doc""" - log(M::Circle, x, y) +@doc raw""" + log(M::Circle, p, q) Compute the logarithmic map on the [`Circle`](@ref) `M`. ````math -\exp_xv = (y,x)_{2\pi}, +\log_p q = (q-p)_{2π}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, -i.e. in $[-\pi,\pi)$. +where $(\cdot)_{2π}$ is the (symmetric) remainder with respect to division by $2π$, i.e. in $[-π,π)$. -For the complex-valued case the formula is the same as for the [`Sphere`](@ref) -applied to valuedin the complex plane. +For the complex-valued case, the same formula as for the [`Sphere`](@ref) $𝕊^1$ is applied to values in the +complex plane. """ log(::Circle, ::Any...) -log(::Circle{ℝ}, x::Real, y::Real) = sym_rem(y - x) -function log(M::Circle{ℂ}, x::Number, y::Number) - cosθ = complex_dot(x, y) +log(::Circle{ℝ}, p::Real, q::Real) = sym_rem(q - p) +function log(M::Circle{ℂ}, p::Number, q::Number) + cosθ = complex_dot(p, q) if cosθ ≈ -1 # appr. opposing points, return deterministic choice from set-valued log - v = real(x) ≈ 1 ? 1im : 1 + 0im - v = v - complex_dot(x, v) * x - v *= π / norm(v) + X = real(p) ≈ 1 ? 1im : 1 + 0im + X = X - complex_dot(p, X) * p + X *= π / norm(X) else cosθ = cosθ > 1 ? one(cosθ) : cosθ θ = acos(cosθ) - v = (y - cosθ * x) / usinc(θ) + X = (q - cosθ * p) / usinc(θ) end - return project_tangent(M, x, v) + return project_tangent(M, p, X) end -log!(::Circle{ℝ}, v, x, y) = (v .= sym_rem(y - x)) -function log!(M::Circle{ℂ}, v, x, y) - cosθ = complex_dot(x, y) +log!(::Circle{ℝ}, X, p, q) = (X .= sym_rem(q - p)) +function log!(M::Circle{ℂ}, X, p, q) + cosθ = complex_dot(p, q) if cosθ ≈ -1 - v .= sum(real.(x)) ≈ 1 ? 1.0im : 1.0 + 0.0im - v .= v - complex_dot(x, v) * x - v .*= π / norm(v) + X .= sum(real.(p)) ≈ 1 ? 1.0im : 1.0 + 0.0im + X .= X - complex_dot(p, X) * p + X .*= π / norm(X) else cosθ = cosθ > 1 ? one(cosθ) : cosθ θ = acos(cosθ) - v .= (y - cosθ * x) / usinc(θ) + X .= (q - cosθ * p) / usinc(θ) end - return project_tangent!(M, v, x, v) + return project_tangent!(M, X, p, X) end -@doc doc""" +@doc raw""" manifold_dimension(M::Circle) Return the dimension of the [`Circle`](@ref) `M`, -i.e. $\operatorname{dim}(\mathbb S^1) = 1$. +i.e. $\dim(𝕊^1) = 1$. """ manifold_dimension(::Circle) = 1 -@doc doc""" +@doc raw""" mean(M::Circle, x::AbstractVector[, w::AbstractWeights]) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` on the -[`Circle`](@ref) $\mathbb S^1$ by the wrapped mean, i.e. the remainder of the -mean modulo 2π. +Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` of points on the [`Circle`](@ref) $𝕊^1$, +which is computed with wrapped mean, i.e. the remainder of the mean modulo 2π. """ mean(::Circle, ::Any) mean(::Circle, x::Array{<:Real}; kwargs...) = sym_rem(sum(x)) mean(::Circle, x::Array{<:Real}, w::AbstractVector; kwargs...) = sym_rem(sum(w .* x)) -@inline norm(::Circle, x, v) = sum(abs, v) +@inline norm(::Circle, p, X) = sum(abs, X) -@doc doc""" - project_point(M::Circle, x) +@doc raw""" + project_point(M::Circle, p) -Project a point `x` onto the [`Circle`](@ref) `M`. -For the real-valued case this is the remainder with respect to modulus $2\pi$. -For the complex-valued case the result is the projection of `x` onto the unit circle in the +Project a point `p` onto the [`Circle`](@ref) `M`. +For the real-valued case this is the remainder with respect to modulus $2π$. +For the complex-valued case the result is the projection of `p` onto the unit circle in the complex plane. """ project_point(::Circle, ::Any) -project_point(::Circle{ℝ}, x::Real) = sym_rem(x) -project_point(::Circle{ℂ}, x::Number) = x / abs(x) +project_point(::Circle{ℝ}, p::Real) = sym_rem(p) +project_point(::Circle{ℂ}, p::Number) = p / abs(p) -project_point!(::Circle{ℝ}, x) = (x .= sym_rem(x)) -project_point!(::Circle{ℂ}, x) = (x .= x / sum(abs.(x))) +project_point!(::Circle{ℝ}, p) = (p .= sym_rem(p)) +project_point!(::Circle{ℂ}, p) = (p .= p / sum(abs.(p))) -@doc doc""" - project_tangent(M::Circle, x, v) +@doc raw""" + project_tangent(M::Circle, p, X) -Project a value `v` onto the tangent space of the point `x` on the [`Circle`](@ref) `M`. +Project a value `X` onto the tangent space of the point `p` on the [`Circle`](@ref) `M`. For the real-valued case this is just the identity. -For the complex valued case `v` is projected onto the line in the complex plane -that is parallel to the tangent to `x` on the unit circle and contains `0`. +For the complex valued case `X` is projected onto the line in the complex plane +that is parallel to the tangent to `p` on the unit circle and contains `0`. """ -project_tangent(::Circle{ℝ}, x::Real, v::Real) = v -project_tangent(::Circle{ℂ}, x::Number, v::Number) = v - complex_dot(x, v) * x +project_tangent(::Circle, ::Any, ::Any) +project_tangent(::Circle{ℝ}, p::Real, X::Real) = X +project_tangent(::Circle{ℂ}, p::Number, X::Number) = X - complex_dot(p, X) * p -project_tangent!(::Circle{ℝ}, w, x, v) = (w .= v) -project_tangent!(::Circle{ℂ}, w, x, v) = (w .= v - complex_dot(x, v) * x) +project_tangent!(::Circle{ℝ}, Y, p, X) = (Y .= X) +project_tangent!(::Circle{ℂ}, Y, p, X) = (Y .= X - complex_dot(p, X) * p) -retract(M::Circle, x, y) = retract(M, x, y, ExponentialRetraction()) -retract(M::Circle, x, y, m::ExponentialRetraction) = exp(M, x, y) +retract(M::Circle, p, q) = retract(M, p, q, ExponentialRetraction()) +retract(M::Circle, p, q, m::ExponentialRetraction) = exp(M, p, q) representation_size(::Circle) = () -sharp(M::Circle, x::Number, w::CoTFVector) = FVector(TangentSpace, w.data) +sharp(M::Circle, p::Number, ξ::CoTFVector) = FVector(TangentSpace, ξ.data) -sharp!(M::Circle, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::Circle, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) show(io::IO, ::Circle{F}) where {F} = print(io, "Circle($(F))") -@doc doc""" +@doc raw""" sym_rem(x,[T=π]) Compute symmetric remainder of `x` with respect to the interall 2*`T`, i.e. -`(x+T)%2T`, where the default for `T` is $\pi$ +`(x+T)%2T`, where the default for `T` is $π$ """ function sym_rem(x::N, T = π) where {N<:Number} return (x ≈ T ? convert(N, -T) : rem(x, convert(N, 2 * T), RoundNearest)) end sym_rem(x, T = π) where {N} = map(sym_rem, x, Ref(T)) -@doc doc""" - vector_transport_to(M::Circle, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::Circle, p, X, q, ::ParallelTransport) -Compute the parallel transport of `v` from the tangent space at `x` to the tangent space at -`y` on the [`Circle`](@ref) `M`. +Compute the parallel transport of `X` from the tangent space at `p` to the tangent space at +`q` on the [`Circle`](@ref) `M`. For the real-valued case this results in the identity. For the complex-valud case, the formula is the same as for the [`Sphere`](@ref)`(1)` in the complex plane. ````math -P_{y\gets x}(v) = v - \frac{\langle \log_xy,v\rangle_x}{d^2_{\mathbb C}(x,y)} -\bigl(\log_xy + \log_yx \bigr), +\mathcal P_{q←p} X = X - \frac{⟨\log_p q,X⟩_p}{d^2_{ℂ}(p,q)} +\bigl(\log_p q + \log_q p \bigr), ```` where [`log`](@ref) denotes the logarithmic map on `M`. """ vector_transport_to(::Circle, ::Any, ::Any, ::Any, ::ParallelTransport) -vector_transport_to(::Circle{ℝ}, x::Real, v::Real, y::Real, ::ParallelTransport) = v +vector_transport_to(::Circle{ℝ}, p::Real, X::Real, q::Real, ::ParallelTransport) = X function vector_transport_to( M::Circle{ℂ}, - x::Number, - v::Number, - y::Number, + p::Number, + X::Number, + q::Number, ::ParallelTransport, ) - v_xy = log(M, x, y) - vl = norm(M, x, v_xy) - vto = v + v_xy = log(M, p, q) + vl = norm(M, p, v_xy) + Y = X if vl > 0 - factor = 2 * complex_dot(v, y) / (abs(x + y)^2) - vto -= factor .* (x + y) + factor = 2 * complex_dot(X, q) / (abs(p + q)^2) + Y -= factor .* (p + q) end - return vto + return Y end -vector_transport_to!(::Circle{ℝ}, w, x, v, y, ::ParallelTransport) = (w .= v) -function vector_transport_to!(M::Circle{ℂ}, vto, x, v, y, ::ParallelTransport) - v_xy = log(M, x, y) - vl = norm(M, x, v_xy) - vto .= v +vector_transport_to!(::Circle{ℝ}, Y, p, X, q, ::ParallelTransport) = (Y .= X) +function vector_transport_to!(M::Circle{ℂ}, Y, p, X, q, ::ParallelTransport) + v_xy = log(M, p, q) + vl = norm(M, p, v_xy) + Y .= X if vl > 0 - factor = 2 * complex_dot(v, y) / (sum(abs.(x + y) .^ 2)) - vto .-= factor .* (x + y) + factor = 2 * complex_dot(X, q) / (sum(abs.(p + q) .^ 2)) + Y .-= factor .* (p + q) end - return vto + return Y end function vector_transport_direction( M::Circle, - x::Number, - v::Number, - vdir::Number, + p::Number, + X::Number, + Y::Number, m::AbstractVectorTransportMethod, ) - y = exp(M, x, vdir) - return vector_transport_to(M, x, v, y, m) + y = exp(M, p, Y) + return vector_transport_to(M, p, X, y, m) end -zero_tangent_vector(::Circle, x::Number) = zero(x) - -zero_tangent_vector!(::Circle, v, x) = fill!(v, 0) +zero_tangent_vector(::Circle, p::Number) = zero(p) +zero_tangent_vector!(::Circle, X, p) = fill!(X, 0) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index b5769a7c3d..380528169a 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -1,25 +1,25 @@ -@doc doc""" +@doc raw""" Euclidean{T<:Tuple} <: Manifold -Euclidean vector space $\mathbb R^n$. +Euclidean vector space $ℝ^n$. # Constructor Euclidean(n) -Generate the $n$-dimensional vector space $\mathbb R^n$. +Generate the $n$-dimensional vector space $ℝ^n$. Euclidean(n₁,n₂,...,nᵢ; field=ℝ) -Generate the vector space of $k=n_1n_2\cdot\ldots n_i$ values, i.e. the -$\mathbb F^{n_1, n_2, \ldots, n_d}$ whose -elements are interpreted as $n_1 \times,n_2\times\cdots\times n_i$ arrays. +Generate the vector space of $k=n_1n_2\cdot… n_i$ values, i.e. the +$𝔽^{n_1, n_2,…, n_d}$ whose +elements are interpreted as $n_1 ×,n_2 × … × n_i$ arrays. For $d=2$ we obtain a matrix space. The default `field=ℝ` can also be set to `field=ℂ`. The dimension of this space is $k \dim_ℝ 𝔽$, where $\dim_ℝ 𝔽$ is the [`real_dimension`](@ref) of the field $𝔽$. """ -struct Euclidean{N<:Tuple,T} <: Manifold where {N,T<:AbstractNumbers} end +struct Euclidean{N<:Tuple,F} <: Manifold where {N,F<:AbstractNumbers} end function Euclidean(n::Vararg{Int,N}; field::AbstractNumbers = ℝ) where {N} return Euclidean{Tuple{n...},field}() @@ -45,7 +45,7 @@ function ^(::Euclidean{T,F}, n::NTuple{N,Int}) where {T,F,N} return Euclidean{Tuple{T.parameters...,n...},F}() end -det_local_metric(M::MetricManifold{<:Manifold,EuclideanMetric}, x) = one(eltype(x)) +det_local_metric(M::MetricManifold{<:Manifold,EuclideanMetric}, p) = one(eltype(p)) """ distance(M::Euclidean, x, y) @@ -54,125 +54,125 @@ Compute the Euclidean distance between two points on the [`Euclidean`](@ref) manifold `M`, i.e. for vectors it's just the norm of the difference, for matrices and higher order arrays, the matrix and ternsor Frobenius norm, respectively. """ -distance(::Euclidean, x, y) = norm(x .- y) +distance(::Euclidean, p, q) = norm(p .- q) -@doc doc""" - exp(M::Euclidean, x, v) +@doc raw""" + exp(M::Euclidean, p, X) Compute the exponential map on the [`Euclidean`](@ref) manifold `M` from `x` in direction -`v`, which in this case is just +`X`, which in this case is just ````math -\exp_x v = x + v. +\exp_p X = p + X. ```` """ exp(::Euclidean, ::Any...) -exp!(M::Euclidean, y, x, v) = (y .= x .+ v) +exp!(M::Euclidean, q, p, X) = (q .= p .+ X) """ - flat(M::Euclidean, x, w) + flat(M::Euclidean, p, X) -Transform a tangent vector into a cotangent. Since they can directly be identified in the +Transform a tangent vector `X` into a cotangent. Since they can directly be identified in the [`Euclidean`](@ref) case, this yields just the identity for a tangent vector `w` in the -tangent space of `x` on `M`. The result is returned also in place in `v`. +tangent space of `p` on `M`. """ flat(::Euclidean, ::Any...) -flat!(M::Euclidean, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(M::Euclidean, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) -function get_basis(M::Euclidean{<:Tuple,ℝ}, x, B::ArbitraryOrthonormalBasis) - vecs = [_euclidean_basis_vector(x, i) for i in eachindex(x)] +function get_basis(M::Euclidean{<:Tuple,ℝ}, p, B::ArbitraryOrthonormalBasis) + vecs = [_euclidean_basis_vector(p, i) for i in eachindex(p)] return PrecomputedOrthonormalBasis(vecs) end -function get_basis(M::Euclidean{<:Tuple,ℂ}, x, B::ArbitraryOrthonormalBasis) - vecs = [_euclidean_basis_vector(x, i) for i in eachindex(x)] +function get_basis(M::Euclidean{<:Tuple,ℂ}, p, B::ArbitraryOrthonormalBasis) + vecs = [_euclidean_basis_vector(p, i) for i in eachindex(p)] return PrecomputedOrthonormalBasis([vecs; im * vecs]) end -function get_basis(M::Euclidean, x, B::DiagonalizingOrthonormalBasis) - vecs = get_basis(M, x, ArbitraryOrthonormalBasis()).vectors - kappas = zeros(real(eltype(x)), manifold_dimension(M)) +function get_basis(M::Euclidean, p, B::DiagonalizingOrthonormalBasis) + vecs = get_basis(M, p, ArbitraryOrthonormalBasis()).vectors + kappas = zeros(real(eltype(p)), manifold_dimension(M)) return PrecomputedDiagonalizingOrthonormalBasis(vecs, kappas) end -function get_coordinates(M::Euclidean{<:Tuple,ℝ}, x, v, B::ArbitraryOrDiagonalizingBasis) +function get_coordinates(M::Euclidean{<:Tuple,ℝ}, p, X, B::ArbitraryOrDiagonalizingBasis) S = representation_size(M) PS = prod(S) - return reshape(v, PS) + return reshape(X, PS) end -function get_coordinates(M::Euclidean{<:Tuple,ℂ}, x, v, B::ArbitraryOrDiagonalizingBasis) +function get_coordinates(M::Euclidean{<:Tuple,ℂ}, p, X, B::ArbitraryOrDiagonalizingBasis) S = representation_size(M) PS = prod(S) - return [reshape(real(v), PS); reshape(imag(v), PS)] + return [reshape(real(X), PS); reshape(imag(X), PS)] end -function get_vector(M::Euclidean{<:Tuple,ℝ}, x, v, B::ArbitraryOrDiagonalizingBasis) +function get_vector(M::Euclidean{<:Tuple,ℝ}, p, X, B::ArbitraryOrDiagonalizingBasis) S = representation_size(M) - return reshape(v, S) + return reshape(X, S) end -function get_vector(M::Euclidean{<:Tuple,ℂ}, x, v, B::ArbitraryOrDiagonalizingBasis) +function get_vector(M::Euclidean{<:Tuple,ℂ}, p, X, B::ArbitraryOrDiagonalizingBasis) S = representation_size(M) - N = div(length(v), 2) - return reshape(v[1:N] + im * v[N+1:end], S) + N = div(length(X), 2) + return reshape(X[1:N] + im * X[N+1:end], S) end -function hat(M::Euclidean{N,ℝ}, x, vⁱ) where {N} +function hat(M::Euclidean{N,ℝ}, p, vⁱ) where {N} return reshape(vⁱ, representation_size(TangentBundleFibers(M))) end -hat!(::Euclidean{N,ℝ}, v, x, vⁱ) where {N} = copyto!(v, vⁱ) +hat!(::Euclidean{N,ℝ}, v, p, vⁱ) where {N} = copyto!(v, vⁱ) -@doc doc""" +@doc raw""" injectivity_radius(M::Euclidean) -Return the injectivity radius on the [`Euclidean`](@ref) `M`, which is $\infty$. +Return the injectivity radius on the [`Euclidean`](@ref) `M`, which is $∞$. """ injectivity_radius(::Euclidean) = Inf -@doc doc""" - inner(M::Euclidean, x, v, w) +@doc raw""" + inner(M::Euclidean, p, X, Y) Compute the inner product on the [`Euclidean`](@ref) `M`, which is just the inner product on the real-valued or complex valued vector space -of arrays (or tensors) of size $n_1\times n_2 \times \cdots \times n_i$, i.e. +of arrays (or tensors) of size $n_1 × n_2 × … × n_i$, i.e. ````math -g_x(v,w) = \sum_{k\in I} \overline{v}_{k} w_{k}, +g_p(X,Y) = \sum_{k ∈ I} \overline{X}_{k} Y_{k}, ```` -where $I$ is the set of integer vectors $k\in\mathbb N^i$, such that for all +where $I$ is the set of vectors $k ∈ ℕ^i$, such that for all $1 \leq j \leq i$ it holds $1\leq k_j \leq n_j$. For the special case of $i\leq 2$, i.e. matrices and vectors, this simplifies to ````math -g_x(v,w) = w^{\mathrm{H}}v, +g_p(Y,Y) = X^{\mathrm{H}}Y, ```` where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ inner(::Euclidean, ::Any...) -@inline inner(::Euclidean, x, v, w) = dot(v, w) -@inline inner(::MetricManifold{<:Manifold,EuclideanMetric}, x, v, w) = dot(v, w) +@inline inner(::Euclidean, p, X, Y) = dot(X, Y) +@inline inner(::MetricManifold{<:Manifold,EuclideanMetric}, p, X, Y) = dot(X, Y) -inverse_local_metric(M::MetricManifold{<:Manifold,EuclideanMetric}, x) = local_metric(M, x) +inverse_local_metric(M::MetricManifold{<:Manifold,EuclideanMetric}, p) = local_metric(M, p) is_default_metric(::Euclidean, ::EuclideanMetric) = Val(true) -function local_metric(::MetricManifold{<:Manifold,EuclideanMetric}, x) - return Diagonal(ones(SVector{size(x, 1),eltype(x)})) +function local_metric(::MetricManifold{<:Manifold,EuclideanMetric}, p) + return Diagonal(ones(SVector{size(p, 1),eltype(p)})) end -@doc doc""" - log(M::Euclidean, x, y) +@doc raw""" + log(M::Euclidean, p, q) -Compute the logarithmic map on the [`Euclidean`](@ref) `M` from `x` to `y`, +Compute the logarithmic map on the [`Euclidean`](@ref) `M` from `p` to `q`, which in this case is just ````math -\log_x y = y - x. +\log_p q = q-p. ```` """ log(::Euclidean, ::Any...) -log!(M::Euclidean, v, x, y) = (v .= y .- x) +log!(M::Euclidean, X, p, q) = (X .= q .- p) -log_local_metric_density(M::MetricManifold{<:Manifold,EuclideanMetric}, x) = zero(eltype(x)) +log_local_metric_density(M::MetricManifold{<:Manifold,EuclideanMetric}, p) = zero(eltype(p)) @generated _product_of_dimensions(::Euclidean{N}) where {N} = prod(N.parameters) @@ -198,8 +198,8 @@ function mean( end mean(::Euclidean, x::AbstractVector; kwargs...) = mean(x) -function mean!(M::Euclidean, y, x::AbstractVector, w::AbstractVector; kwargs...) - return mean!(M, y, x, w, GeodesicInterpolation(); kwargs...) +function mean!(M::Euclidean, p, x::AbstractVector, w::AbstractVector; kwargs...) + return mean!(M, p, x, w, GeodesicInterpolation(); kwargs...) end function mean_and_var(::Euclidean{Tuple{1}}, x::AbstractVector{<:Number}; kwargs...) @@ -230,63 +230,63 @@ function median( return median(x, w) end -function median!(::Euclidean{Tuple{1}}, y, x::AbstractVector; kwargs...) - return copyto!(y, [median(vcat(x...))]) +function median!(::Euclidean{Tuple{1}}, p, x::AbstractVector; kwargs...) + return copyto!(p, [median(vcat(x...))]) end -function median!(::Euclidean{Tuple{1}}, y, x::AbstractVector, w::AbstractWeights; kwargs...) - return copyto!(y, [median(vcat(x...), w)]) +function median!(::Euclidean{Tuple{1}}, p, x::AbstractVector, w::AbstractWeights; kwargs...) + return copyto!(p, [median(vcat(x...), w)]) end -@doc doc""" - norm(M::Euclidean, x, v) +@doc raw""" + norm(M::Euclidean, p, X) -Compute the norm of a tangent vector `v` at `x` on the [`Euclidean`](@ref) +Compute the norm of a tangent vector `X` at `p` on the [`Euclidean`](@ref) `M`, i.e. since every tangent space can be identified with `M` itself -in this case, just the (Frobenius) norm of `v`. +in this case, just the (Frobenius) norm of `X`. """ -norm(::Euclidean, x, v) = norm(v) -norm(::MetricManifold{<:Manifold,EuclideanMetric}, x, v) = norm(v) +norm(::Euclidean, p, X) = norm(X) +norm(::MetricManifold{<:Manifold,EuclideanMetric}, p, X) = norm(X) """ - normal_tvector_distribution(M::Euclidean, x, σ) + normal_tvector_distribution(M::Euclidean, p, σ) Normal distribution in ambient space with standard deviation `σ` -projected to tangent space at `x`. +projected to tangent space at `p`. """ -function normal_tvector_distribution(M::Euclidean{Tuple{N}}, x, σ) where {N} - d = Distributions.MvNormal(zero(x), σ) - return ProjectedFVectorDistribution(TangentBundleFibers(M), x, d, project_vector!, x) +function normal_tvector_distribution(M::Euclidean{Tuple{N}}, p, σ) where {N} + d = Distributions.MvNormal(zero(p), σ) + return ProjectedFVectorDistribution(TangentBundleFibers(M), p, d, project_vector!, p) end -@doc doc""" - project_point(M::Euclidean, x) +@doc raw""" + project_point(M::Euclidean, p) -Project an arbitrary point `x` onto the [`Euclidean`](@ref) `M`, which +Project an arbitrary point `p` onto the [`Euclidean`](@ref) manifold `M`, which is of course just the identity map. """ project_point(::Euclidean, ::Any...) -project_point!(M::Euclidean, x) = x +project_point!(M::Euclidean, p) = p """ - project_tangent(M::Euclidean, x, v) + project_tangent(M::Euclidean, p, X) -Project an arbitrary vector `v` into the tangent space of a point `x` on the +Project an arbitrary vector `X` into the tangent space of a point `p` on the [`Euclidean`](@ref) `M`, which is just the identity, since any tangent space of `M` can be identified with all of `M`. """ project_tangent(::Euclidean, ::Any...) -project_tangent!(M::Euclidean, w, x, v) = copyto!(w, v) +project_tangent!(M::Euclidean, Y, p, X) = copyto!(Y, X) """ - projected_distribution(M::Euclidean, d, [x]) + projected_distribution(M::Euclidean, d, [p]) Wrap the standard distribution `d` into a manifold-valued distribution. Generated -points will be of similar type to `x`. By default, the type is not changed. +points will be of similar type to `p`. By default, the type is not changed. """ -function projected_distribution(M::Euclidean, d, x) - return ProjectedPointDistribution(M, d, project_point!, x) +function projected_distribution(M::Euclidean, d, p) + return ProjectedPointDistribution(M, d, project_point!, p) end function projected_distribution(M::Euclidean, d) return ProjectedPointDistribution(M, d, project_point!, rand(d)) @@ -301,38 +301,38 @@ Return the array dimensions required to represent an element on the @generated representation_size(::Euclidean{N}) where {N} = size_to_tuple(N) """ - sharp(M::Euclidean, x, w) + sharp(M::Euclidean, p, ξ) -since cotangent and tangent vectors can directly be identified in the [`Euclidean`](@ref) -case, this yields just the identity for a cotangent vector `w` in the tangent space -of `x` on `M`. +Transform the cotangent vector `ξ` at `p` on the [`Euclidean`](@ref) `M` to a tangent vector `X`. +Since cotangent and tangent vectors can directly be identified in the [`Euclidean`](@ref) +case, this yields just the identity. """ sharp(::Euclidean, ::Any...) -sharp!(M::Euclidean, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::Euclidean, X::TFVector, x, ξ::CoTFVector) = copyto!(X, ξ) function show(io::IO, ::Euclidean{N,F}) where {N,F} print(io, "Euclidean($(join(N.parameters, ", ")); field = $(F))") end """ - vector_transport_to(M::Euclidean, x, v, y, ::ParallelTransport) + vector_transport_to(M::Euclidean, p, X, q, ::ParallelTransport) -Parallely transport the vector `v` from the tangent space at `x` to the tangent space at `y` +Parallely transport the vector `X` from the tangent space at `p` to the tangent space at `q` on the [`Euclidean`](@ref) `M`, which simplifies to the identity. """ vector_transport_to(::Euclidean, ::Any, ::Any, ::Any, ::ParallelTransport) -vector_transport_to!(M::Euclidean, vto, x, v, y, ::ParallelTransport) = copyto!(vto, v) +vector_transport_to!(M::Euclidean, Y, p, X, q, ::ParallelTransport) = copyto!(Y, X) var(::Euclidean, x::AbstractVector; kwargs...) = sum(var(x; kwargs...)) function var(::Euclidean, x::AbstractVector{T}, m::T; kwargs...) where {T} return sum(var(x; mean = m, kwargs...)) end -vee(::Euclidean{N,ℝ}, x, v) where {N} = vec(v) +vee(::Euclidean{N,ℝ}, p, X) where {N} = vec(X) -vee!(::Euclidean{N,ℝ}, vⁱ, x, v) where {N} = copyto!(vⁱ, v) +vee!(::Euclidean{N,ℝ}, Xⁱ, p, X) where {N} = copyto!(Xⁱ, X) """ zero_tangent_vector(M::Euclidean, x) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 86bd776355..06d6e16314 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,39 +1,40 @@ -@doc doc""" - FixedRankMatrices{m,n,k,T} <: Manifold +@doc raw""" + FixedRankMatrices{m,n,k,F} <: Manifold -The manifold of $m\times n$ real-valued (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 -\mathcal M = \{ x \in \mathbb R^{m\times n} : \operatorname{rank}(x) = k \}. +\{ p ∈ 𝔽^{m × n} : \operatorname{rank}(p) = k \}, ```` +where $𝔽 ∈ \{ℝ,ℂ\}$ and the rank is the number of linearly independent columns of a matrix. + # Representation with 3 matrix factors -A point $x\in\mathcal M$ can be stored using orthonormal matrices -$U\in\mathbb R^{m\times k}$, $V\in\mathbb R^{n\times k}$ as well as the $k$ singular -values of $x = USV^\mathrm{T}$. 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 +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 = USV^\mathrm{H}$, where $\cdot^{\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. -The tangent space $T_x\mathcal M$ at a point $x\in\mathcal M$ with $x=USV^\mathrm{T}$ +The tangent space $T_p \mathcal M$ at a point $p ∈ \mathcal M$ with $p=USV^\mathrm{H}$ is given by ````math -T_x\mathcal M = \bigl\{ UMV^\mathrm{T} + U_xV^\mathrm{T} + UV_x^\mathrm{T} : - M \in \mathbb R^{k\times k}, - U_x \in \mathbb R^{m\times k}, - V_x \in \mathbb R^{n\times k} +T_p\mathcal M = \bigl\{ UMV^\mathrm{T} + U_pV^\mathrm{H} + UV_p^\mathrm{H} : + M ∈ 𝔽^{k × k}, + U_p ∈ 𝔽^{m × k}, + V_p ∈ 𝔽^{n × k} \text{ s.t. } - U_x^\mathrm{T}U = 0_k, - V_x^\mathrm{T}V = 0_k + U_p^\mathrm{H}U = 0_k, + V_p^\mathrm{H}V = 0_k \bigr\}, ```` -where $0_k$ is the $k\times 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 $\mathbb R^{m\times n}$ to the tangent bundle. This implementation follows[^Vandereycken2013]. +on $ℝ^{m × n}$ to the tangent bundle[^Vandereycken2013]. # Constructor - FixedRankMatrics(m,n,k,t=ℝ) + FixedRankMatrics(m, n, k[, t=ℝ]) -Generate the manifold of `m`-by-`n` real-valued matrices of rank `k`. +Generate the manifold of `m`-by-`n` (real-valued) matrices of rank `k`. [^Vandereycken2013]: > Bart Vandereycken: "Low-rank matrix completion by Riemannian Optimization, @@ -41,18 +42,18 @@ Generate the manifold of `m`-by-`n` real-valued matrices of rank `k`. > doi: [10.1137/110845768](https://doi.org/10.1137/110845768), > arXiv: [1209.3834](https://arxiv.org/abs/1209.3834). """ -struct FixedRankMatrices{M,N,K,T} <: Manifold end -function FixedRankMatrices(m::Int, n::Int, k::Int, t::AbstractNumbers = ℝ) - return FixedRankMatrices{m,n,k,t}() +struct FixedRankMatrices{M,N,K,F} <: Manifold end +function FixedRankMatrices(m::Int, n::Int, k::Int, f::AbstractNumbers = ℝ) + return FixedRankMatrices{m,n,k,f}() end -@doc doc""" +@doc raw""" SVDMPoint <: MPoint A point on a certain manifold, where the data is stored in a svd like fashion, -i.e. in the form $USV^\mathrm{T}$, where this structure stores $U$, $S$ and -$V^\mathrm{T}$. The storage might also be shortened to just $k$ singular values -and accordingly shortened $U$ (columns) and $V^\mathrm{T}$ (rows) +i.e. in the form $USV^\mathrm{H}$, where this structure stores $U$, $S$ and +$V^\mathrm{H}$. The storage might also be shortened to just $k$ singular values +and accordingly shortened $U$ (columns) and $V^\mathrm{T}$ (rows). # Constructors * `SVDMPoint(A)` for a matrix `A`, stores its svd factors (i.e. implicitly $k=\min\{m,n\}$) @@ -77,16 +78,17 @@ SVDMPoint(S::SVD, k::Int) = SVDMPoint(S.U, S.S, S.Vt, k) SVDMPoint(U, S, Vt, k::Int) = SVDMPoint(U[:, 1:k], S[1:k], Vt[1:k, :]) ==(x::SVDMPoint, y::SVDMPoint) = (x.U == y.U) && (x.S == y.S) && (x.Vt == y.Vt) -@doc doc""" +@doc raw""" UMVTVector <: TVector -A tangent vector that can be described as a product $UMV^\mathrm{T}$, at least -together with its base point, see for example [`FixedRankMatrices`](@ref) +A tangent vector that can be described as a product $UMV^\mathrm{H}$, at least +together with its base point, see for example [`FixedRankMatrices`](@ref). This +vector structure stores the additionally (to the point) required fields. # Constructors * `UMVTVector(U,M,Vt)` store umv factors to initialize the `UMVTVector` * `UMVTVector(U,M,Vt,k)` store the umv factors after shortening them down to - inner dimensions $k$, i.e. in $UMV^\mathrm{T}$, $M\in\mathbb R^{k\times k}$ + inner dimensions $k$, i.e. in $UMV^\mathrm{H}$, where $M$ is a $k × k$ matrix. """ struct UMVTVector{TU<:AbstractMatrix,TM<:AbstractMatrix,TVt<:AbstractMatrix} <: TVector U::TU @@ -107,19 +109,20 @@ UMVTVector(U, M, Vt, k::Int) = UMVTVector(U[:, 1:k], M[1:k, 1:k], Vt[1:k, :]) +(v::UMVTVector) = UMVTVector(v.U, v.M, v.Vt) ==(v::UMVTVector, w::UMVTVector) = (v.U == w.U) && (v.M == w.M) && (v.Vt == w.Vt) -@doc doc""" - check_manifold_point(M::FixedRankMatrices{m,n,k},x; kwargs...) +@doc raw""" + check_manifold_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) Check whether the matrix or [`SVDMPoint`](@ref) `x` ids a valid point on the -[`FixedRankMatrices`](@ref)`{m,n,k}` `M`, i.e. is (or represents) an `m`-by`n` matrix of +[`FixedRankMatrices`](@ref)`{m,n,k,𝔽}` `M`, i.e. is an `m`-by`n` matrix of rank `k`. For the [`SVDMPoint`](@ref) the internal representation also has to have the right -shape, i.e. `x.U` and `x.Vt` have to be unitary. +shape, i.e. `p.U` and `p.Vt` have to be unitary. The keyword arguments are passed to the +`rank` function that verifies the rank of `p`. """ -function check_manifold_point(M::FixedRankMatrices{m,n,k}, x; kwargs...) where {m,n,k} - r = rank(x; kwargs...) - s = "The point $(x) does not lie on the manifold of fixed rank matrices of size ($(m),$(n)) witk rank $(k), " - if size(x) != (m, n) - return DomainError(size(x), string(s, "since its size is wrong.")) +function check_manifold_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) where {m,n,k} + r = rank(p; kwargs...) + s = "The point $(p) does not lie on the manifold of fixed rank matrices of size ($(m),$(n)) witk rank $(k), " + if size(p) != (m, n) + return DomainError(size(p), string(s, "since its size is wrong.")) end if r > k return DomainError(r, string(s, "since its rank is too large ($(r)).")) @@ -156,79 +159,76 @@ function check_manifold_point( return nothing end -@doc doc""" - check_tangent_vector(M:FixedRankMatrices{m,n,k}, x, v) +@doc raw""" + check_tangent_vector(M:FixedRankMatrices{m,n,k}, p, X) -Check whether the tangent [`UMVTVector`](@ref) `v` is from the tangent space of -the [`SVDMPoint`](@ref) `x` on the [`FixedRankMatrices`](@ref) `M`, i.e. that -`v.U` and `v.Vt` are (columnwise) orthogonal to `x.U` and `x.Vt`, respectively, -and its dimensions are consistent with `x` and `M`, i.e. correspond to `m`-by-`n` -matrices of rank `k`. +Check whether the tangent [`UMVTVector`](@ref) `X` is from the tangent space of the [`SVDMPoint`](@ref) `p` on the +[`FixedRankMatrices`](@ref) `M`, i.e. that `v.U` and `v.Vt` are (columnwise) orthogonal to `x.U` and `x.Vt`, +respectively, and its dimensions are consistent with `p` and `X.M`, i.e. correspond to `m`-by-`n` matrices of rank `k`. """ function check_tangent_vector( M::FixedRankMatrices{m,n,k}, - x::SVDMPoint, - v::UMVTVector; + p::SVDMPoint, + X::UMVTVector; kwargs..., ) where {m,n,k} - c = check_manifold_point(M, x) + c = check_manifold_point(M, p) c === nothing || return c - if (size(v.U) != (m, k)) || (size(v.Vt) != (k, n)) || (size(v.M) != (k, k)) + if (size(X.U) != (m, k)) || (size(X.Vt) != (k, n)) || (size(X.M) != (k, k)) return DomainError( - cat(size(v.U), size(v.M), size(v.Vt), dims = 1), - "The tangent vector $(v) is not a tangent vector to $(x) on the fixed rank matrices since the matrix dimensions to not fit (expected $(m)x$(k), $(k)x$(k), $(k)x$(n)).", + cat(size(X.U), size(X.M), size(X.Vt), dims = 1), + "The tangent vector $(X) is not a tangent vector to $(p) on the fixed rank matrices since the matrix dimensions to not fit (expected $(m)x$(k), $(k)x$(k), $(k)x$(n)).", ) end - if !isapprox(v.U' * x.U, zeros(k, k); kwargs...) + if !isapprox(X.U' * p.U, zeros(k, k); kwargs...) return DomainError( - norm(v.U' * x.U - zeros(k, k)), - "The tangent vector $(v) is not a tangent vector to $(x) on the fixed rank matrices since v.U'x.U is not zero. ", + norm(X.U' * p.U - zeros(k, k)), + "The tangent vector $(X) is not a tangent vector to $(p) on the fixed rank matrices since v.U'x.U is not zero. ", ) end - if !isapprox(v.Vt * x.Vt', zeros(k, k); kwargs...) + if !isapprox(X.Vt * p.Vt', zeros(k, k); kwargs...) return DomainError( - norm(v.Vt * x.Vt - zeros(k, k)), - "The tangent vector $(v) is not a tangent vector to $(x) on the fixed rank matrices since v.V'x.V is not zero.", + norm(X.Vt * p.Vt - zeros(k, k)), + "The tangent vector $(X) is not a tangent vector to $(p) on the fixed rank matrices since v.V'x.V is not zero.", ) end end -@doc doc""" - inner(M::FixedRankMatrices, x::SVDMPoint, v::UMVTVector, w::UMVTVector) +@doc raw""" + inner(M::FixedRankMatrices, p::SVDMPoint, X::UMVTVector, Y::UMVTVector) -Compute the inner product of `v` and `w` in the tangent space of `x` on the -[`FixedRankMatrices`](@ref) `M`, which is inherited from the embedding, i.e. can be computed -using `dot` on the elements (`U`, `Vt`, `M`) of `v` and `w`. +Compute the inner product of `X` and `Y` in the tangent space of `p` on the [`FixedRankMatrices`](@ref) `M`, +which is inherited from the embedding, i.e. can be computed using `dot` on the elements (`U`, `Vt`, `M`) of `X` and `Y`. """ function inner(::FixedRankMatrices, x::SVDMPoint, v::UMVTVector, w::UMVTVector) return dot(v.U, w.U) + dot(v.M, w.M) + dot(v.Vt, w.Vt) end -function isapprox(::FixedRankMatrices, x::SVDMPoint, y::SVDMPoint; kwargs...) - return isapprox(x.U * Diagonal(x.S) * x.Vt, y.U * Diagonal(y.S) * y.Vt; kwargs...) +function isapprox(::FixedRankMatrices, p::SVDMPoint, q::SVDMPoint; kwargs...) + return isapprox(p.U * Diagonal(p.S) * p.Vt, q.U * Diagonal(q.S) * q.Vt; kwargs...) end function isapprox( ::FixedRankMatrices, - x::SVDMPoint, - v::UMVTVector, - w::UMVTVector; + p::SVDMPoint, + X::UMVTVector, + Y::UMVTVector; kwargs..., ) return isapprox( - x.U * v.M * x.Vt + v.U * x.Vt + x.U * v.Vt, - x.U * w.M * x.Vt + w.U * x.Vt + x.U * w.Vt; + p.U * X.M * p.Vt + X.U * p.Vt + p.U * X.Vt, + p.U * Y.M * p.Vt + Y.U * p.Vt + p.U * Y.Vt; kwargs..., ) end -@doc doc""" +@doc raw""" manifold_dimension(M::FixedRankMatrices{m,n,k,𝔽}) Return the manifold dimension for the `𝔽`-valued [`FixedRankMatrices`](@ref) `M` of dimension `m`x`n` of rank `k`, namely ````math -k(m + n - k) \dim_ℝ 𝔽, +\dim(\mathcal M) = k(m + n - k) \dim_ℝ 𝔽, ```` where $\dim_ℝ 𝔽$ is the [`real_dimension`](@ref) of `𝔽`. @@ -237,47 +237,40 @@ function manifold_dimension(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} return (m + n - k) * k * real_dimension(𝔽) end -@doc doc""" - project_tangent(M, x, A) - project_tangent(M, x, v) - -Project the matrix $A\in\mathbb R^{m,n}$ or a [`UMVTVector`](@ref) `v` from the embedding or -another tangent space onto the tangent space at $x$ on the [`FixedRankMatrices`](@ref) `M`, -further decomposing the result into $v=UMV$, i.e. a [`UMVTVector`](@ref) following -Section 3 in [^Vandereycken2013]. +@doc raw""" + project_tangent(M, p, A) + project_tangent(M, p, X) -[^Vandereycken2013]: - > Bart Vandereycken: "Low-rank matrix completion by Riemannian Optimization, - > SIAM Journal on Optiomoization, 23(2), pp. 1214–1236, 2013. - > doi: [10.1137/110845768](https://doi.org/10.1137/110845768), - > arXiv: [1209.3834](https://arxiv.org/abs/1209.3834). +Project the matrix $A ∈ ℝ^{m,n}$ or a [`UMVTVector`](@ref) `X` from the embedding or +another tangent space onto the tangent space at $p$ on the [`FixedRankMatrices`](@ref) `M`, +further decomposing the result into $X=UMV$, i.e. a [`UMVTVector`](@ref). """ project_tangent(::FixedRankMatrices, ::Any...) function project_tangent!( ::FixedRankMatrices, - vto::UMVTVector, - x::SVDMPoint, + Y::UMVTVector, + p::SVDMPoint, A::AbstractMatrix, ) - av = A * (x.Vt') - uTav = x.U' * av - aTu = A' * x.U - vto.M .= uTav - vto.U .= A * x.Vt' - x.U * uTav - vto.Vt .= (aTu - x.Vt' * uTav')' - return vto + av = A * (p.Vt') + uTav = p.U' * av + aTu = A' * p.U + Y.M .= uTav + Y.U .= A * p.Vt' - p.U * uTav + Y.Vt .= (aTu - p.Vt' * uTav')' + return Y end function project_tangent!( M::FixedRankMatrices, - vto::UMVTVector, - x::SVDMPoint, - v::UMVTVector, + Y::UMVTVector, + p::SVDMPoint, + X::UMVTVector, ) - return project_tangent!(M, vto, x, v.U * v.M * v.Vt) + return project_tangent!(M, Y, p, X.U * X.M * X.Vt) end -@doc doc""" +@doc raw""" representation_size(M::FixedRankMatrices{m,n,k}) Return the element size of a point on the [`FixedRankMatrices`](@ref) `M`, i.e. @@ -285,134 +278,134 @@ the size of matrices on this manifold $(m,n)$. """ @generated representation_size(::FixedRankMatrices{m,n}) where {m,n} = (m, n) -@doc doc""" - retract(M, x, v, ::PolarRetraction) +@doc raw""" + retract(M, p, X, ::PolarRetraction) Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by computing ````math - y = U_kS_kV_k^\mathrm{T}, + q = U_kS_kV_k^\mathrm{H}, ```` -where $U_k S_k V_k^\mathrm{T}$ is the shortened singular value decomposition $USV=x+v$, -in the sense that $S_k$ is the diagonal matrix of size $k\times k$ with the $k$ largest +where $U_k S_k V_k^\mathrm{H}$ is the shortened singular value decomposition $USV=p+X$, +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) function retract!( ::FixedRankMatrices{M,N,k}, - y::SVDMPoint, - x::SVDMPoint, - v::UMVTVector, + q::SVDMPoint, + p::SVDMPoint, + X::UMVTVector, ::PolarRetraction, ) where {M,N,k} - s = svd(x.U * Diagonal(x.S) * x.Vt + (x.U * v.M * x.Vt + v.U * x.Vt + v.U * v.Vt)) - y.U .= s.U[:, 1:k] - y.S .= s.S[1:k] - y.Vt .= s.Vt[1:k, :] - return y + s = svd(p.U * Diagonal(p.S) * p.Vt + (p.U * X.M * p.Vt + X.U * p.Vt + X.U * X.Vt)) + q.U .= s.U[:, 1:k] + q.S .= s.S[1:k] + q.Vt .= s.Vt[1:k, :] + return q end function show(io::IO, ::FixedRankMatrices{M,N,K,T}) where {M,N,K,T} print(io, "FixedRankMatrices($(M), $(N), $(K), $(T))") end -function show(io::IO, mime::MIME"text/plain", x::SVDMPoint) +function show(io::IO, mime::MIME"text/plain", p::SVDMPoint) pre = " " - summary(io, x) + summary(io, p) println(io, "\nU factor:") - su = sprint(show, "text/plain", x.U; context = io, sizehint = 0) + su = sprint(show, "text/plain", p.U; context = io, sizehint = 0) su = replace(su, '\n' => "\n$(pre)") println(io, pre, su) println(io, "singular values:") - ss = sprint(show, "text/plain", x.S; context = io, sizehint = 0) + ss = sprint(show, "text/plain", p.S; context = io, sizehint = 0) ss = replace(ss, '\n' => "\n$(pre)") println(io, pre, ss) println(io, "Vt factor:") - sv = sprint(show, "text/plain", x.Vt; context = io, sizehint = 0) + sv = sprint(show, "text/plain", p.Vt; context = io, sizehint = 0) sv = replace(sv, '\n' => "\n$(pre)") print(io, pre, sv) end -function show(io::IO, mime::MIME"text/plain", v::UMVTVector) +function show(io::IO, mime::MIME"text/plain", X::UMVTVector) pre = " " - summary(io, v) + summary(io, X) println(io, "\nU factor:") - su = sprint(show, "text/plain", v.U; context = io, sizehint = 0) + su = sprint(show, "text/plain", X.U; context = io, sizehint = 0) su = replace(su, '\n' => "\n$(pre)") println(io, pre, su) println(io, "M factor:") - sm = sprint(show, "text/plain", v.M; context = io, sizehint = 0) + sm = sprint(show, "text/plain", X.M; context = io, sizehint = 0) sm = replace(sm, '\n' => "\n$(pre)") println(io, pre, sm) println(io, "Vt factor:") - sv = sprint(show, "text/plain", v.Vt; context = io, sizehint = 0) + sv = sprint(show, "text/plain", X.Vt; context = io, sizehint = 0) sv = replace(sv, '\n' => "\n$(pre)") print(io, pre, sv) end -allocate(x::SVDMPoint) = SVDMPoint(allocate(x.U), allocate(x.S), allocate(x.Vt)) -function allocate(x::SVDMPoint, ::Type{T}) where {T} - return SVDMPoint(allocate(x.U, T), allocate(x.S, T), allocate(x.Vt, T)) +allocate(p::SVDMPoint) = SVDMPoint(allocate(p.U), allocate(p.S), allocate(p.Vt)) +function allocate(p::SVDMPoint, ::Type{T}) where {T} + return SVDMPoint(allocate(p.U, T), allocate(p.S, T), allocate(p.Vt, T)) end -allocate(v::UMVTVector) = UMVTVector(allocate(v.U), allocate(v.M), allocate(v.Vt)) -function allocate(v::UMVTVector, ::Type{T}) where {T} - return UMVTVector(allocate(v.U, T), allocate(v.M, T), allocate(v.Vt, T)) +allocate(X::UMVTVector) = UMVTVector(allocate(X.U), allocate(X.M), allocate(X.Vt)) +function allocate(X::UMVTVector, ::Type{T}) where {T} + return UMVTVector(allocate(X.U, T), allocate(X.M, T), allocate(X.Vt, T)) end -function number_eltype(x::SVDMPoint) - return typeof(one(eltype(x.U)) + one(eltype(x.S)) + one(eltype(x.Vt))) +function number_eltype(p::SVDMPoint) + return typeof(one(eltype(p.U)) + one(eltype(p.S)) + one(eltype(p.Vt))) end -function number_eltype(v::UMVTVector) - return typeof(one(eltype(v.U)) + one(eltype(v.M)) + one(eltype(v.Vt))) +function number_eltype(X::UMVTVector) + return typeof(one(eltype(X.U)) + one(eltype(X.M)) + one(eltype(X.Vt))) end -one(x::SVDMPoint) = SVDMPoint( - one(zeros(size(x.U, 1), size(x.U, 1))), - ones(length(x.S)), - one(zeros(size(x.Vt, 2), size(x.Vt, 2))), - length(x.S), +one(p::SVDMPoint) = SVDMPoint( + one(zeros(size(p.U, 1), size(p.U, 1))), + ones(length(p.S)), + one(zeros(size(p.Vt, 2), size(p.Vt, 2))), + length(p.S), ) -one(v::UMVTVector) = UMVTVector( - one(zeros(size(v.U, 1), size(v.U, 1))), - one(zeros(size(v.M))), - one(zeros(size(v.Vt, 2), size(v.Vt, 2))), - size(v.M, 1), +one(X::UMVTVector) = UMVTVector( + one(zeros(size(X.U, 1), size(X.U, 1))), + one(zeros(size(X.M))), + one(zeros(size(X.Vt, 2), size(X.Vt, 2))), + size(X.M, 1), ) -function copyto!(x::SVDMPoint, y::SVDMPoint) - copyto!(x.U, y.U) - copyto!(x.S, y.S) - copyto!(x.Vt, y.Vt) - return x +function copyto!(p::SVDMPoint, q::SVDMPoint) + copyto!(p.U, q.U) + copyto!(p.S, q.S) + copyto!(p.Vt, q.Vt) + return p end -function copyto!(v::UMVTVector, w::UMVTVector) - copyto!(v.U, w.U) - copyto!(v.M, w.M) - copyto!(v.Vt, w.Vt) - return v +function copyto!(X::UMVTVector, Y::UMVTVector) + copyto!(X.U, Y.U) + copyto!(X.M, Y.M) + copyto!(X.Vt, Y.Vt) + return X end -@doc doc""" +@doc raw""" zero_tangent_vector(M::FixedRankMatrices, x::SVDMPoint) Return a [`UMVTVector`](@ref) representing the zero tangent vector in the tangent space of `x` on the [`FixedRankMatrices`](@ref) `M`, for example all three elements of the resulting structure are zero matrices. """ -function zero_tangent_vector(::FixedRankMatrices{m,n,k}, x::SVDMPoint) where {m,n,k} +function zero_tangent_vector(::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} v = UMVTVector( - zeros(eltype(x.U), m, k), - zeros(eltype(x.S), k, k), - zeros(eltype(x.Vt), k, n), + zeros(eltype(p.U), m, k), + zeros(eltype(p.S), k, k), + zeros(eltype(p.Vt), k, n), ) return v end function zero_tangent_vector!( ::FixedRankMatrices{m,n,k}, - v::UMVTVector, - x::SVDMPoint, + X::UMVTVector, + p::SVDMPoint, ) where {m,n,k} - v.U .= zeros(eltype(v.U), m, k) - v.M .= zeros(eltype(v.M), k, k) - v.Vt .= zeros(eltype(v.Vt), k, n) - return v + X.U .= zeros(eltype(X.U), m, k) + X.M .= zeros(eltype(X.M), k, k) + X.Vt .= zeros(eltype(X.Vt), k, n) + return X end diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index 4f831e63c7..2d6036bfa3 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" GraphManifoldType This type represents the type of data on the graph that the [`GraphManifold`](@ref) @@ -6,21 +6,21 @@ represents. """ abstract type GraphManifoldType end -@doc doc""" +@doc raw""" EdgeManifoldManifold <: GraphManifoldType A type for a [`GraphManifold`](@ref) where the data is given on the edges. """ struct EdgeManifold <: GraphManifoldType end -@doc doc""" +@doc raw""" VectexGraphManifold <: GraphManifoldType A type for a [`GraphManifold`](@ref) where the data is given on the vertices. """ struct VertexManifold <: GraphManifoldType end -@doc doc""" +@doc raw""" GraphManifold{G, M, T} <: AbstractPowerManifold{M,NestedPowerRepresentation} Build a manifold, that is a [`PowerManifold`](@ref) of the [`Manifold`](@ref) `M` either on the edges or vertices @@ -46,84 +46,83 @@ end const EdgeGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,EdgeManifold} const VertexGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,VertexManifold} -@doc doc""" - check_manifold_point(M::GraphManifold, x) +@doc raw""" + check_manifold_point(M::GraphManifold, p) -Check whether `x` is a valid point on the [`GraphManifold`](@ref), i.e. its -length equals the number of vertices (for [`VertexManifold`](@ref)s) or -the number of edges (for [`EdgeManifold`](@ref)s) and that each element of `x` +Check whether `p` is a valid point on the [`GraphManifold`](@ref), i.e. its length equals the number of vertices +(for [`VertexManifold`](@ref)s) or the number of edges (for [`EdgeManifold`](@ref)s) and that each element of `p` passes the [`check_manifold_point`](@ref) test for the base manifold `M.manifold`. """ check_manifold_point(::GraphManifold, ::Any...) -function check_manifold_point(M::VertexGraphManifold, x; kwargs...) - if size(x) != (nv(M.graph),) +function check_manifold_point(M::VertexGraphManifold, p; kwargs...) + if size(p) != (nv(M.graph),) return DomainError( - length(x), - "The number of points in `x` ($(length(x))) does not match the number of nodes in the graph ($(nv(M.graph))).", + length(p), + "The number of points in `x` ($(length(p))) does not match the number of nodes in the graph ($(nv(M.graph))).", ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), nv(M.graph)) - return check_manifold_point(PM, x; kwargs...) + return check_manifold_point(PM, p; kwargs...) end -function check_manifold_point(M::EdgeGraphManifold, x; kwargs...) - if size(x) != (ne(M.graph),) +function check_manifold_point(M::EdgeGraphManifold, p; kwargs...) + if size(p) != (ne(M.graph),) return DomainError( - length(x), - "The number of points in `x` ($(size(x))) does not match the number of edges in the graph ($(ne(M.graph))).", + length(p), + "The number of points in `x` ($(size(p))) does not match the number of edges in the graph ($(ne(M.graph))).", ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), ne(M.graph)) - return check_manifold_point(PM, x; kwargs...) + return check_manifold_point(PM, p; kwargs...) end -@doc doc""" - check_tangent_vector(M::GraphManifold, x, v) +@doc raw""" + check_tangent_vector(M::GraphManifold, p, X) -Check whether `x` is a valid point on the [`GraphManifold`](@ref), and -`v` it from its tangent space, i.e. its +Check whether `p` is a valid point on the [`GraphManifold`](@ref), and +`X` it from its tangent space, i.e. its length equals the number of vertices (for [`VertexManifold`](@ref)s) or -the number of edges (for [`EdgeManifold`](@ref)s) and that each element of `v` -together with its corresponding einty of `x` passes the +the number of edges (for [`EdgeManifold`](@ref)s) and that each element of `X` +together with its corresponding entry of `p` passes the [`check_tangent_vector`](@ref) test for the base manifold `M.manifold`. """ check_tangent_vector(::GraphManifold, ::Any...) -function check_tangent_vector(M::VertexGraphManifold, x, v; kwargs...) - if size(x) != (nv(M.graph),) +function check_tangent_vector(M::VertexGraphManifold, p, X; kwargs...) + if size(p) != (nv(M.graph),) return DomainError( - length(x), - "The number of points in `x` ($(size(x)) does not match the number of nodes in the graph ($(nv(M.graph))).", + length(p), + "The number of points in `x` ($(size(p)) does not match the number of nodes in the graph ($(nv(M.graph))).", ) end - if size(v) != (nv(M.graph),) + if size(X) != (nv(M.graph),) return DomainError( - length(v), - "The number of points in `v` ($(size(v)) does not match the number of nodes in the graph ($(nv(M.graph))).", + length(X), + "The number of points in `v` ($(size(X)) does not match the number of nodes in the graph ($(nv(M.graph))).", ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), nv(M.graph)) - return check_tangent_vector(PM, x, v; kwargs...) + return check_tangent_vector(PM, p, X; kwargs...) end -function check_tangent_vector(M::EdgeGraphManifold, x, v; kwargs...) - if size(x) != (ne(M.graph),) +function check_tangent_vector(M::EdgeGraphManifold, p, X; kwargs...) + if size(p) != (ne(M.graph),) return DomainError( - length(x), - "The number of elements in `x` ($(size(x)) does not match the number of edges in the graph ($(ne(M.graph))).", + length(p), + "The number of elements in `x` ($(size(p)) does not match the number of edges in the graph ($(ne(M.graph))).", ) end - if size(v) != (ne(M.graph),) + if size(X) != (ne(M.graph),) return DomainError( - length(v), - "The number of elements in `v` ($(size(v)) does not match the number of edges in the graph ($(ne(M.graph))).", + length(X), + "The number of elements in `v` ($(size(X)) does not match the number of edges in the graph ($(ne(M.graph))).", ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), ne(M.graph)) - return check_tangent_vector(PM, x, v; kwargs...) + return check_tangent_vector(PM, p, X; kwargs...) end get_iterator(M::EdgeGraphManifold) = 1:ne(M.graph) get_iterator(M::VertexGraphManifold) = 1:nv(M.graph) -@doc doc""" +@doc raw""" incident_log(M::GraphManifold, x) Return the tangent vector on the (vertex) [`GraphManifold`](@ref), where at @@ -134,73 +133,73 @@ SimpleDiGraph If the internal graph is a `SimpleWeightedGraph` the weighted sum of the tangent vectors is computed. """ -function incident_log(M::VertexGraphManifold, x) - v = zero_tangent_vector(M, x) - return incident_log!(M, v, x) +function incident_log(M::VertexGraphManifold, p) + v = zero_tangent_vector(M, p) + return incident_log!(M, v, p) end -function incident_log!(M::VertexGraphManifold, v, x) +function incident_log!(M::VertexGraphManifold, X, p) rep_size = representation_size(M.manifold) for e in edges(M.graph) - vw = _write(M, rep_size, v, src(e)) - v[src(e)] += - log(M.manifold, _read(M, rep_size, x, src(e)), _read(M, rep_size, x, dst(e))) + vw = _write(M, rep_size, X, src(e)) + X[src(e)] += + log(M.manifold, _read(M, rep_size, p, src(e)), _read(M, rep_size, p, dst(e))) if !is_directed(M.graph) - v[dst(e)] += log( + X[dst(e)] += log( M.manifold, - _read(M, rep_size, x, dst(e)), - _read(M, rep_size, x, src(e)), + _read(M, rep_size, p, dst(e)), + _read(M, rep_size, p, src(e)), ) end end - return v + return X end function incident_log!( M::GraphManifold{<:AbstractSimpleWeightedGraph,<:Manifold,VertexManifold}, - v, - x, + X, + p, ) rep_size = representation_size(M.manifold) for e in edges(M.graph) - v[src(e)] += ( + X[src(e)] += ( get_weight(M.graph, src(e), dst(e)) * log( M.manifold, - _read(M, rep_size, x, src(e)), - _read(M, rep_size, x, dst(e)), + _read(M, rep_size, p, src(e)), + _read(M, rep_size, p, dst(e)), ) ) if !is_directed(M.graph) - v[dst(e)] += ( + X[dst(e)] += ( get_weight(M.graph, dst(e), src(e)) * log( M.manifold, - _read(M, rep_size, x, dst(e)), - _read(M, rep_size, x, src(e)), + _read(M, rep_size, p, dst(e)), + _read(M, rep_size, p, src(e)), ) ) end end - return v + return X end -@doc doc""" +@doc raw""" manifold_dimension(N::GraphManifold{G,M,VertexManifold}) returns the manifold dimension of the [`GraphManifold`](@ref) `N` on the vertices of a graph $G=(V,E)$, i.e. ````math -d_{\mathcal N} = \lvert V \rVert d_{\mathcal M}. +\dim(\mathcal N) = \lvert V \rVert \dim(\mathcal M). ```` """ function manifold_dimension(M::VertexGraphManifold) return manifold_dimension(M.manifold) * nv(M.graph) end -@doc doc""" +@doc raw""" manifold_dimension(N::GraphManifold{G,M,EdgeManifold}) returns the manifold dimension of the [`GraphManifold`](@ref) `N` on the edges of a graph $G=(V,E)$, i.e. ````math -d_{\mathcal N} = \lvert E \rVert d_{\mathcal M}. +\dim(\mathcal N) = \lvert E \rVert \dim(\mathcal M). ```` """ function manifold_dimension(M::EdgeGraphManifold) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 313ab8f9c9..6b6a7140be 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -1,22 +1,20 @@ -@doc doc""" +@doc raw""" Grassmann{n,k,F} <: Manifold -The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned -by $k$ linear independent vectors $\mathbb F^n$, where -$\mathbb F \in \{\mathbb R, \mathbb C\}$ is either the real- (or complex-) valued vectors. -This yields all $k$-dimensional subspaces of $\mathbb R^n$ for the real-valued case and all -$2k$-dimensional subspaces of $\mathbb C^n$ for the second. +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 manifold can be represented as ````math -\operatorname{Gr}(n,k) \coloneqq \bigl\{ \operatorname{span}(x) -: x \in \mathbb F^{n\times k}, \bar{x}^\mathrm{T}x = I_k\}, +\operatorname{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n × k}, p^\mathrm{H}p = I_k\}, ```` -where ${\bar\cdot}^{\mathrm{T}}$ denotes the complex conjugate transpose and -$I_k$ is the $k\times k$ identity matrix. This means, that the columns of $x$ -form an orthonormal basis of the subspace, that is a point on +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 $x$ +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 a whole equivalence class of representers. Another interpretation is, that @@ -26,22 +24,22 @@ Another interpretation is, that ```` i.e the Grassmann manifold is the quotient of the [`Stiefel`](@ref) manifold and -the orthogonal group $\operatorname{O}(k)$ of orthogonal $k\times k$ matrices. +the orthogonal group $\operatorname{O}(k)$ of orthogonal $k × k$ matrices. The tangent space at a point (subspace) $x$ is given by ````math T_x\mathrm{Gr}(n,k) = \bigl\{ -v \in \mathbb{F}^{n\times k} : -{\bar v}^{\mathrm{T}}x + {\bar x}^{\mathrm{T}}v = 0_{k} \bigr\}, +X ∈ 𝔽^{n × k} : +X^{\mathrm{H}}p + p^{\mathrm{H}}X = 0_{k} \bigr\}, ```` -where $0_{k}$ denotes the $k\times k$ zero matrix. +where $0_{k}$ denotes the $k × k$ zero matrix. -Note that a point $x\in \operatorname{Gr}(n,k)$ might be represented by -different matrices (i.e. matrices with orthonormal column vectors that span -the same subspace). Different representations of $x$ also lead to different -representation matrices for the tangent space $T_x\mathrm{Gr}(n,k)$ +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 manifold is named after [Hermann G. Graßmann](https://en.wikipedia.org/wiki/Hermann_Grassmann) (1809-1877). @@ -51,223 +49,235 @@ The manifold is named after Grassmann(n,k,F=ℝ) Generate the Grassmann manifold $\operatorname{Gr}(n,k)$, where the real-valued -case $\mathbb F = \mathbb R$ is the default. +case $𝔽 = ℝ$ is the default. """ struct Grassmann{n,k,F} <: Manifold end Grassmann(n::Int, k::Int, F::AbstractNumbers = ℝ) = Grassmann{n,k,F}() -@doc doc""" - check_manifold_point(M::Grassmann{n,k,F}, x) +@doc raw""" + check_manifold_point(M::Grassmann{n,k,F}, p) -Check whether `x` is representing a point on the [`Grassmann`](@ref) `M`, i.e. its +Check whether `p` is representing a point on the [`Grassmann`](@ref) `M`, i.e. its a `n`-by-`k` matrix of unitary column vectors and of correct `eltype` with respect to `F`. """ -function check_manifold_point(M::Grassmann{n,k,F}, x; kwargs...) where {n,k,F} - if (F === ℝ) && !(eltype(x) <: Real) +function check_manifold_point(M::Grassmann{n,k,F}, p; kwargs...) where {n,k,F} + if (F === ℝ) && !(eltype(p) <: Real) return DomainError( - eltype(x), - "The matrix $(x) is not a real-valued matrix, so it does noe lie on the Grassmann manifold of dimension ($(n),$(k)).", + eltype(p), + "The matrix $(p) is not a real-valued matrix, so it does not lie on the Grassmann manifold of dimension ($(n),$(k)).", ) end - if (F === ℂ) && !(eltype(x) <: Real) && !(eltype(x) <: Complex) + if (F === ℂ) && !(eltype(p) <: Real) && !(eltype(p) <: Complex) return DomainError( - eltype(x), - "The matrix $(x) is neiter real- nor complex-valued matrix, so it does noe lie on the complex Grassmann manifold of dimension ($(n),$(k)).", + eltype(p), + "The matrix $(p) is neither a real- nor complex-valued matrix, so it does not lie on the complex Grassmann manifold of dimension ($(n),$(k)).", ) end - if size(x) != representation_size(M) + if size(p) != representation_size(M) return DomainError( - size(x), - "The matrix $(x) is does not lie on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + size(p), + "The matrix $(p) does not lie on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", ) end - c = x' * x + c = p' * p if !isapprox(c, one(c); kwargs...) return DomainError( norm(c - one(c)), - "The point $(x) does not lie on the Grassmann manifold of dimension ($(n),$(k)), because x'x is not the unit matrix.", + "The point $(p) does not lie on the Grassmann manifold of dimension ($(n),$(k)), because x'x is not the unit matrix.", ) end end -@doc doc""" - check_tangent_vector(M::Grassmann{n,k,F}, x, v) +@doc raw""" + check_tangent_vector(M::Grassmann{n,k,F}, p, X) -Check whether `v` is a tangent vector in the tangent space of `x` on the [`Grassmann`](@ref) -`M`, i.e. that `v` is of size and type as well as that +Check whether `X` is a tangent vector in the tangent space of `p` on +the [`Grassmann`](@ref) `M`, i.e. that `X` is of size and type as well as that ````math - x^{\mathrm{H}}v + v^{\mathrm{H}}x = 0_k, + p^{\mathrm{H}}X + X^{\mathrm{H}}p = 0_k, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ -denotes the $k\times k$ zero natrix. +denotes the $k × k$ zero natrix. """ -function check_tangent_vector(G::Grassmann{n,k,F}, x, v; kwargs...) where {n,k,F} - t = check_manifold_point(G, x) +function check_tangent_vector(G::Grassmann{n,k,F}, p, X; kwargs...) where {n,k,F} + t = check_manifold_point(G, p) t === nothing || return t - if (F === ℝ) && !(eltype(v) <: Real) + if (F === ℝ) && !(eltype(X) <: Real) return DomainError( - eltype(v), - "The matrix $(v) is not a real-valued matrix, so it can not be a tangent vector to the Grassmann manifold of dimension ($(n),$(k)).", + eltype(X), + "The matrix $(X) is not a real-valued matrix, so it can not be a tangent vector to the Grassmann manifold of dimension ($(n),$(k)).", ) end - if (F === ℂ) && !(eltype(v) <: Real) && !(eltype(v) <: Complex) + if (F === ℂ) && !(eltype(X) <: Real) && !(eltype(X) <: Complex) return DomainError( - eltype(v), - "The matrix $(v) is neiter real- nor complex-valued matrix, so it can not bea tangent vector to the complex Grassmann manifold of dimension ($(n),$(k)).", + eltype(X), + "The matrix $(X) is neither a real- nor complex-valued matrix, so it can not be a tangent vector to the complex Grassmann manifold of dimension ($(n),$(k)).", ) end - if size(v) != representation_size(G) + if size(X) != representation_size(G) return DomainError( - size(v), - "The matrix $(v) is does not lie in the tangent space of $(x) on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + size(X), + "The matrix $(X) does not lie in the tangent space of $(p) on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", ) end - if !isapprox(x' * v + v' * x, zeros(k, k); kwargs...) + if !isapprox(p' * X + X' * p, zeros(k, k); kwargs...) return DomainError( - norm(x' * v + v' * x), - "The matrix $(v) is does not lie in the tangent space of $(x) on the Grassmann manifold of dimension ($(n),$(k)), since x'v + v'x is not the zero matrix.", + norm(p' * X + X' * p), + "The matrix $(X) does not lie in the tangent space of $(p) on the Grassmann manifold of dimension ($(n),$(k)), since x'v + v'x is not the zero matrix.", ) end end -@doc doc""" - distance(M::Grassmann, x, y) +@doc raw""" + distance(M::Grassmann, p, q) Compute the Riemannian distance on [`Grassmann`](@ref) manifold `M`$= \mathrm{Gr}(n,k)$. -Let $USV = x^\mathrm{H}y$ denote the SVD decomposition of -$x^\mathrm{H}y$, where $\cdot^{\mathrm{H}}$ denotes the complex +Let $USV = p^\mathrm{H}q$ denote the SVD decomposition of +$p^\mathrm{H}q$, where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. Then the distance is given by ````math -d_{\mathrm{GR}(n,k)}(x,y) = \operatorname{norm}(\operatorname{Re}(b)). +d_{\mathrm{GR}(n,k)}(p,q) = \operatorname{norm}(\operatorname{Re}(b)). ```` where -$b_{i}=\begin{cases} 0 & \text{if} \; S_i \geq 1\\ \operatorname{acos}(S_i) & \, \text{if} \; S_i<1 \end{cases}.$ +````math +b_{i}=\begin{cases} +0 & \text{if} \; S_i \geq 1\\ +\arccos(S_i) & \, \text{if} \; S_i<1. +\end{cases} +```` """ -function distance(M::Grassmann, x, y) - x ≈ y && return zero(real(eltype(x))) - a = svd(x' * y).S +function distance(M::Grassmann, p, q) + p ≈ q && return zero(real(eltype(p))) + a = svd(p' * q).S a[a.>1] .= 1 return sqrt(sum((acos.(a)) .^ 2)) end -@doc doc""" - exp(M::Grassmann, x, v) +@doc raw""" + exp(M::Grassmann, p, X) Compute the exponential map on the [`Grassmann`](@ref) `M`$= \mathrm{Gr}(n,k)$ starting in -`x` with tangent vector (direction) `v`. Let $v = USV$ denote the SVD decomposition of $v$. +`p` with tangent vector (direction) `X`. Let $X = USV$ denote the SVD decomposition of $X$. Then the exponential map is written using ````math -z = x V\cos(S)V^\mathrm{H} + U\sin(S)V^\mathrm{H}, +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. -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 $\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 + ````math -\exp_x v = Q. +\exp_p X = Q. ```` """ exp(::Grassmann, ::Any...) -function exp!(M::Grassmann, y, x, v) - norm(M, x, v) ≈ 0 && return copyto!(y, x) - d = svd(v) - z = x * d.V * Diagonal(cos.(d.S)) * d.Vt + d.U * Diagonal(sin.(d.S)) * d.Vt - # reorthonormalize - return copyto!(y, Array(qr(z).Q)) +function exp!(M::Grassmann, q, p, X) + norm(M, p, X) ≈ 0 && return copyto!(q, p) + d = svd(X) + z = p * d.V * Diagonal(cos.(d.S)) * d.Vt + d.U * Diagonal(sin.(d.S)) * d.Vt + return copyto!(q, Array(qr(z).Q)) end -injectivity_radius(::Grassmann) = π / 2 +@doc raw""" + injectivity_radius(M::Grassmann) + injectivity_radius(M::Grassmann, p) -@doc doc""" - inner(M::Grassmann, x, v, w) +Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is $\frac{π}{2}$. +""" +injectivity_radius(::Grassmann, ::Any...) = π / 2 -Compute the inner product for two tangent vectors `v`, `w` from the -tangent space of `x` on the [`Grassmann`](@ref) manifold `M`. -The formula reads +@doc raw""" + inner(M::Grassmann, p, X, Y) + +Compute the inner product for two tangent vectors `X`, `Y` from the tangent space +of `p` on the [`Grassmann`](@ref) manifold `M`. The formula reads ````math -g_x(v,w) = \operatorname{trace}(v^{\mathrm{H}}w), +g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}Y), ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ -inner(::Grassmann, x, v, w) = dot(v, w) +inner(::Grassmann, p, X, Y) = dot(X, Y) -@doc doc""" - inverse_retract(M::Grassmann, x, y, ::PolarInverseRetraction) +@doc raw""" + inverse_retract(M::Grassmann, p, q, ::PolarInverseRetraction) Compute the inverse retraction for the [`PolarRetraction`](@ref), on the -[`Grassmann`](@ref), i.e., +[`Grassmann`](@ref) manifold `M`, i.e., ````math -\operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, +\operatorname{retr}_p^{-1}q = q*(p^\mathrm{H}q)^{-1} - p, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ -inverse_retract(M::Grassmann, ::Any, ::Any, ::PolarInverseRetraction) +inverse_retract(::Grassmann, ::Any, ::Any, ::PolarInverseRetraction) -function inverse_retract!(::Grassmann, v, x, y, ::PolarInverseRetraction) - return copyto!(v, y / (x' * y) - x) +function inverse_retract!(::Grassmann, X, p, q, ::PolarInverseRetraction) + return copyto!(X, q / (p' * q) - p) end -@doc doc""" - inverse_retract(M, x, y, ::QRInverseRetraction) +@doc raw""" + inverse_retract(M, p, q, ::QRInverseRetraction) -Compute the inverse retraction valid of the [`QRRetraction`](@ref) +Compute the inverse retraction for the [`QRRetraction`](@ref), on the +[`Grassmann`](@ref) manifold `M`, i.e., ````math -\operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, +\operatorname{retr}_p^{-1}q = q*(p^\mathrm{H}q)^{-1} - p, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inverse_retract(::Grassmann, ::Any, ::Any, ::QRInverseRetraction) -inverse_retract!(::Grassmann, v, x, y, ::QRInverseRetraction) = copyto!(v, y / (x' * y) - x) +inverse_retract!(::Grassmann, X, p, q, ::QRInverseRetraction) = copyto!(X, q / (p' * q) - p) -function isapprox(M::Grassmann, x, v, w; kwargs...) - return isapprox(sqrt(inner(M, x, zero_tangent_vector(M, x), v - w)), 0; kwargs...) +function isapprox(M::Grassmann, p, X, Y; kwargs...) + return isapprox(sqrt(inner(M, p, zero_tangent_vector(M, p), X - Y)), 0; kwargs...) end -isapprox(M::Grassmann, x, y; kwargs...) = isapprox(distance(M, x, y), 0.0; kwargs...) +isapprox(M::Grassmann, p, q; kwargs...) = isapprox(distance(M, p, q), 0.0; kwargs...) -@doc doc""" - log(M::Grassmann, x, y) +@doc raw""" + log(M::Grassmann, p, q) Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = \mathcal M=\mathrm{Gr}(n,k)$, -i.e. the tangent vector `v` whose corresponding [`geodesic`](@ref) starting from `x` -reaches `y` after time 1 on `M`. The formula reads +i.e. the tangent vector `X` whose corresponding [`geodesic`](@ref) starting from `p` +reaches `q` after time 1 on `M`. The formula reads ````math -\log_xy = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, +\log_p q = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. -$U$ and $V$ are the unitary matrices, and $S$ is a diagonal matrix containing -the singular values of the SVD-decomposition of +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 = (y^\mathrm{H}x)^{-1} ( y^\mathrm{H} - y^\mathrm{H}xx^\mathrm{H} ). +USV = (q^\mathrm{H}p)^{-1} ( q^\mathrm{H} - q^\mathrm{H}pp^\mathrm{H}). ```` + In this formula the $\operatorname{atan}$ is meant elementwise. """ log(::Grassmann, ::Any...) -function log!(M::Grassmann, v, x, y) - z = y' * x - At = y' - z * x' +function log!(M::Grassmann, X, p, q) + z = q' * p + At = q' - z * p' Bt = z \ At d = svd(Bt') - return copyto!(v, d.U * Diagonal(atan.(d.S)) * d.Vt) + return copyto!(X, d.U * Diagonal(atan.(d.S)) * d.Vt) end -@doc doc""" +@doc raw""" manifold_dimension(M::Grassmann) Return the dimension of the [`Grassmann(n,k,𝔽)`](@ref) manifold `M`, i.e. @@ -278,7 +288,7 @@ Return the dimension of the [`Grassmann(n,k,𝔽)`](@ref) manifold `M`, i.e. where $\dim_ℝ 𝔽$ is the [`real_dimension`](@ref) of `𝔽`. """ -manifold_dimension(M::Grassmann{n,k,𝔽}) where {n,k,𝔽} = k * (n - k) * real_dimension(𝔽) +manifold_dimension(::Grassmann{n,k,𝔽}) where {n,k,𝔽} = k * (n - k) * real_dimension(𝔽) """ mean( @@ -296,22 +306,22 @@ mean(::Grassmann{n,k,ℝ} where {n,k}, ::Any...) function mean!( M::Grassmann{n,k,ℝ}, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {n,k} - return mean!(M, y, x, w, GeodesicInterpolationWithinRadius(π / 4); kwargs...) + return mean!(M, p, x, w, GeodesicInterpolationWithinRadius(π / 4); kwargs...) end -@doc doc""" - project_tangent(M::Grassmann, x, w) +@doc raw""" + project_tangent(M::Grassmann, p, X) -Project the `n`-by-`k` `w` onto the tangent space of `x` on the [`Grassmann`](@ref) `M`, +Project the `n`-by-`k` `X` onto the tangent space of `p` on the [`Grassmann`](@ref) `M`, which is computed by ````math -\operatorname{proj_x}(w) = w - xx^{\mathrm{H}}w, +\operatorname{proj_p}(X) = X - pp^{\mathrm{H}}X, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. @@ -320,7 +330,7 @@ project_tangent(::Grassmann, ::Any...) project_tangent!(M::Grassmann, v, x, w) = copyto!(v, w - x * x' * w) -@doc doc""" +@doc raw""" representation_size(M::Grassmann{n,k,F}) Return the represenation size or matrix dimension of a point on the [`Grassmann`](@ref) @@ -328,52 +338,55 @@ Return the represenation size or matrix dimension of a point on the [`Grassmann` """ @generated representation_size(::Grassmann{n,k}) where {n,k} = (n, k) -@doc doc""" - retract(M::Grassmann, x, v, ::PolarRetraction) +@doc raw""" + retract(M::Grassmann, p, X, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](@ref) on the -[`Grassmann`](@ref) `M`. With $USV = x + v$ the retraction reads +[`Grassmann`](@ref) `M`. With $USV = p + X$ the retraction reads ````math -\operatorname{retr}_x v = UV^\mathrm{H}, +\operatorname{retr}_p X = UV^\mathrm{H}, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +""" +retract(::Grassmann, ::Any, ::Any, ::PolarRetraction) - retract(M::Grassmann, x, v, ::QRRetraction ) +@doc raw""" + retract(M::Grassmann, p, X, ::QRRetraction ) Compute the QR-based retraction [`QRRetraction`](@ref) on the -[`Grassmann`](@ref) `M`. With $QR = x + v$ the retraction reads +[`Grassmann`](@ref) `M`. With $QR = p + X$ the retraction reads ````math -\operatorname{retr}_xv = QD, +\operatorname{retr}_p X = QD, ```` -where D is a $m\times n$ matrix with +where D is a $m × n$ matrix with ````math D = \operatorname{diag}( \operatorname{sgn}(R_{ii}+0,5)_{i=1}^n ). ```` """ -retract(::Grassmann, ::Any...) +retract(::Grassmann, ::Any, ::Any, ::QRRetraction) -function retract!(::Grassmann, y, x, v, ::PolarRetraction) - s = svd(x + v) - return mul!(y, s.U, s.Vt) +function retract!(::Grassmann, q, p, X, ::PolarRetraction) + s = svd(p + X) + return mul!(q, s.U, s.Vt) end -function retract!(::Grassmann{N,K}, y, x, v, ::QRRetraction) where {N,K} - qrfac = qr(x + v) +function retract!(::Grassmann{N,K}, q, p, X, ::QRRetraction) where {N,K} + qrfac = qr(p + X) d = diag(qrfac.R) D = Diagonal(sign.(sign.(d .+ 0.5))) - y .= zeros(N, K) - y[1:K, 1:K] .= D - return copyto!(y, Array(qrfac.Q) * D) + q .= zeros(N, K) + q[1:K, 1:K] .= D + return copyto!(q, Array(qrfac.Q) * D) end show(io::IO, ::Grassmann{n,k,F}) where {n,k,F} = print(io, "Grassmann($(n), $(k), $(F))") -@doc doc""" - zero_tangent_vector(M::Grassmann, x) +@doc raw""" + zero_tangent_vector(M::Grassmann, p) -Return the zero tangent vector from the tangent space at `x` on the [`Grassmann`](@ref) `M`, -which is given by a zero matrix the same size as `x`. +Return the zero tangent vector from the tangent space at `p` on the [`Grassmann`](@ref) `M`, +which is given by a zero matrix the same size as `p`. """ zero_tangent_vector(::Grassmann, ::Any...) -zero_tangent_vector!(::Grassmann, v, x) = fill!(v, 0) +zero_tangent_vector!(::Grassmann, X, p) = fill!(X, 0) diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index d6508e4a1f..b21f68e003 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -1,43 +1,42 @@ -@doc doc""" +@doc raw""" Hyperbolic{N} <: Manifold -The hyperbolic space $\mathbb H^n$ represented by $n+1$-Tuples, i.e. in by -vectors in $\mathbb R^{n+1}$ using the Minkowsi metric, i.e. +The hyperbolic space $ℍ^n$ represented by $n+1$-Tuples, i.e. in by +vectors in $ℝ^{n+1}$ using the Minkowsi metric, i.e. ```math -\mathbb H^n = \Bigl\{x\in\mathbb R^{n+1} -\ \Big|\ \langle x,x \rangle_{\mathrm{M}}= -x_{n+1}^2 -+ \displaystyle\sum_{k=1}^n x_k^2 = -1, x_{n+1} > 0\Bigr\}, +ℍ^n = \Bigl\{p ∈ ℝ^{n+1} : ⟨p,p⟩_{\mathrm{M}}= -p_{n+1}^2 + + \displaystyle\sum_{k=1}^n p_k^2 = -1, p_{n+1} > 0\Bigr\}, ``` -where $\langle\cdot,\cdot\rangle_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) -is Minkowski inner product. The tangent space $T_x\mathbb H^n$ is given by +where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) +is Minkowski inner product. The tangent space $T_p ℍ^n$ is given by ````math -T_x\mathbb H^n \coloneqq \bigl\{ -v \in \mathbb R^{n+1} \ \bigl |\ \langle x,v\rangle_{\mathrm{M}} = 0 +T_p ℍ^n := \bigl\{ +X ∈ ℝ^{n+1} : ⟨p,X⟩_{\mathrm{M}} = 0 \bigr\}. ```` The Minkowski inner product inntroduces the [`MinkowskiMetric`](@ref), which is -a Riemannian metric on the tangent bundle $T\mathbb H^n$. +a Riemannian metric on the tangent bundle $T ℍ^n$. # Constructor Hyperbolic(n) -Generate the $\mathbb H^{n}\subset \mathbb R^{n+1}$ +Generate the $ℍ^{n} ⊂ ℝ^{n+1}$ """ struct Hyperbolic{N} <: Manifold end Hyperbolic(n::Int) = Hyperbolic{n}() -@doc doc""" +@doc raw""" MinkowskiMetric <: LorentzMetric The Minkowski metric is a [`LorentzMetric`](@ref) with, i.e. ````math -\langle a,b\rangle_{\mathrm{M}} = -a_{n+1}b_{n+1} + +⟨a,b⟩_{\mathrm{M}} = -a_{n+1}b_{n+1} + \displaystyle\sum_{k=1}^n a_kb_k. ```` It is also the default metric e.g. for the [`Hyperbolic`](@ref) space. @@ -45,155 +44,157 @@ It is also the default metric e.g. for the [`Hyperbolic`](@ref) space. !!! note While the `MinkowskiMetric` itself is not positive definite in the whole embedded space, it is positive definite when restricted to a tangent space $T_x\mathcal M$, - $x\in\mathcal M$, of the [`Hyperbolic`](@ref) space $\mathcal M$. + $x ∈ \mathcal M$, of the [`Hyperbolic`](@ref) space $\mathcal M$. """ struct MinkowskiMetric <: LorentzMetric end """ check_manifold_point(M::Hyperbolic, x; kwargs...) -Check whether `x` is a valid point on the [`Hyperbolic`](@ref) `M`, i.e. is a vector with +Check whether `p` is a valid point on the [`Hyperbolic`](@ref) `M`, i.e. is a vector with [`minkowski_dot`](@ref) -1. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::Hyperbolic, x; kwargs...) - if size(x) != representation_size(M) +function check_manifold_point(M::Hyperbolic, p; kwargs...) + if size(p) != representation_size(M) return DomainError( - size(x), - "The point $(x) does not lie on $(M), since its size is not $(representation_size(M)).", + size(p), + "The point $(p) does not lie on $(M), since its size is not $(representation_size(M)).", ) end - if !isapprox(minkowski_dot(x, x), -1.0; kwargs...) + if !isapprox(minkowski_dot(p, p), -1.0; kwargs...) return DomainError( - minkowski_dot(x, x), - "The point $(x) does not lie on $(M) since its Minkowski inner product is not -1.", + minkowski_dot(p, p), + "The point $(p) does not lie on $(M) since its Minkowski inner product is not -1.", ) end return nothing end """ - check_tangent_vector(M::Hyperbolic, x, v; kwargs... ) + check_tangent_vector(M::Hyperbolic, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` on the [`Hyperbolic`](@ref) `M`, i.e. -after [`check_manifold_point`](@ref)`(M,x)`, `v` has to be of same dimension as `x` -and orthogonal to `x` with respect to [`minkowski_dot`](@ref). +Check whether `X` is a tangent vector to `p` on the [`Hyperbolic`](@ref) `M`, i.e. +after [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p` +and orthogonal to `p` with respect to [`minkowski_dot`](@ref). The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::Hyperbolic, x, v; kwargs...) - perr = check_manifold_point(M, x) +function check_tangent_vector(M::Hyperbolic, p, X; kwargs...) + perr = check_manifold_point(M, p) perr === nothing || return perr - if size(v) != representation_size(M) + if size(X) != representation_size(M) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $M since its size does not match $(representation_size(M)).", + size(X), + "The vector $(X) is not a tangent to a point on $M since its size does not match $(representation_size(M)).", ) end - if !isapprox(minkowski_dot(x, v), 0.0; kwargs...) + if !isapprox(minkowski_dot(p, X), 0.0; kwargs...) return DomainError( - abs(minkowski_dot(x, v)), - "The vector $(v) is not a tangent vector to $(x) on $(M), since it is not orthogonal (with respect to the Minkowski inner product) in the embedding.", + abs(minkowski_dot(p, X)), + "The vector $(X) is not a tangent vector to $(p) on $(M), since it is not orthogonal (with respect to the Minkowski inner product) in the embedding.", ) end return nothing end -@doc doc""" - distance(M::Hyperbolic, x, y) +@doc raw""" + distance(M::Hyperbolic, p, q) Compute the distance on the [`Hyperbolic`](@ref) `M`, which reads ````math -d_{\mathbb H^n}(x,y) = \operatorname{acosh}( - \langle x, y \rangle_{\mathrm{M}}), +d_{ℍ^n}(p,q) = \operatorname{acosh}( - ⟨p, q⟩_{\mathrm{M}}), ```` -where $\langle\cdot,\cdot\rangle_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). +where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ -distance(M::Hyperbolic, x, y) = acosh(max(-minkowski_dot(x, y), 1.0)) +distance(M::Hyperbolic, p, q) = acosh(max(-minkowski_dot(p, q), 1.0)) -@doc doc""" - exp(M::Hyperbolic, x, v) +@doc raw""" + exp(M::Hyperbolic, p, X) -Compute the exponential map on the [`Hyperbolic`](@ref) space $\mathbb H^n$ eminating -from `x` towards `v`, which is optionally scaled by `t`. The formula reads +Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ emanating +from `p` towards `X`. The formula reads ````math -\exp_x v = \cosh(\sqrt{\langle v,v\rangle_{\mathrm{M}}})x -+ \sinh(\sqrt{\langle v,v\rangle_{\mathrm{M}}})\frac{v}{\sqrt{\langle v,v\rangle_{\mathrm{M}}}}, +\exp_p X = \cosh(\sqrt{⟨X,X⟩_{\mathrm{M}}})p ++ \sinh(\sqrt{⟨X,X⟩_{\mathrm{M}}})\frac{X}{\sqrt{⟨X,X⟩_{\mathrm{M}}}}, ```` -where $\langle\cdot,\cdot\rangle_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). +where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ exp(::Hyperbolic, ::Any...) -function exp!(M::Hyperbolic, y, x, v) - vn = sqrt(max(minkowski_dot(v, v), 0.0)) - vn < eps(eltype(x)) && return copyto!(y, x) - return copyto!(y, cosh(vn) * x + sinh(vn) / vn * v) +function exp!(M::Hyperbolic, q, p, X) + vn = sqrt(max(minkowski_dot(X, X), 0.0)) + vn < eps(eltype(p)) && return copyto!(q, p) + return copyto!(q, cosh(vn) * p + sinh(vn) / vn * X) end -flat!(M::Hyperbolic, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(M::Hyperbolic, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) -@doc doc""" - injectivity_radius(M::Hyperbolic[, x]) +@doc raw""" + injectivity_radius(M::Hyperbolic) + injectivity_radius(M::Hyperbolic, p) -Return the injectivity radius on the [`Hyperbolic`](@ref), which is always $\infty$. +Return the injectivity radius on the [`Hyperbolic`](@ref), which is $∞$. """ -injectivity_radius(H::Hyperbolic, args...) = Inf +injectivity_radius(H::Hyperbolic, ::Any...) = Inf -@doc doc""" - inner(M::Hyperbolic, x, v, w) +@doc raw""" + inner(M::Hyperbolic, p, X, Y) -Compute the Riemannian inner product for two tangent vectors `v` and `w` -from $T_x\mathbb H^n$ of the [`Hyperbolic`](@ref) space $\mathbb H^n$ given by -$\langle w, v \rangle_{\mathrm{M}}$ the [`minkowski_dot`](@ref) Minkowski -inner product on $\mathbb R^{n+1}$. +Compute the Riemannian inner product for two tangent vectors `X` and `Y` +from $T_p ℍ^n$ of the [`Hyperbolic`](@ref) space $ℍ^n$ given by +$⟨X, Y⟩_{\mathrm{M}}$, the [`minkowski_dot`](@ref) Minkowski +inner product on $ℝ^{n+1}$. """ -@inline inner(M::Hyperbolic, x, w, v) = minkowski_dot(w, v) +@inline inner(M::Hyperbolic, p, X, Y) = minkowski_dot(X, Y) is_default_metric(::Hyperbolic, ::MinkowskiMetric) = Val(true) -@doc doc""" - log(M::Hyperbolic, x, y) +@doc raw""" + log(M::Hyperbolic, p, q) -Compute the logarithmic map on the [`Hyperbolic`](@ref) space $\mathbb H^n$, the tangent -vector representing the [`geodesic`](@ref) starting from `x` -reaches `y` after time 1 on the [`Hyperbolic`](@ref) space `M`. -The formula reads for $x\neq y$ +Compute the logarithmic map on the [`Hyperbolic`](@ref) space $ℍ^n$, the tangent +vector representing the [`geodesic`](@ref) starting from `p` +reaches `q` after time 1. The formula reads for $x ≠ y$ ```math -\log_x y = d_{\mathbb H^n}(x,y) -\frac{y-\langle x,y\rangle_{\mathrm{M}} x}{\lVert y-\langle x,y\rangle_{\mathrm{M}} x \rVert_2} +\log_p q = d_{ℍ^n}(p,q) +\frac{q-⟨p,q⟩_{\mathrm{M}} p}{\lVert q-⟨p,q⟩_{\mathrm{M}} p \rVert_2} ``` + and is zero otherwise. """ log(::Hyperbolic, ::Any...) -function log!(M::Hyperbolic, v, x, y) - scp = minkowski_dot(x, y) - w = y + scp * x +function log!(M::Hyperbolic, X, p, q) + scp = minkowski_dot(p, q) + w = q + scp * p wn = sqrt(max(scp .^ 2 - 1, 0.0)) - wn < eps(eltype(x)) && return zero_tangent_vector!(M, v, x) - v .= acosh(max(1.0, -scp)) / wn .* w - return v + wn < eps(eltype(p)) && return zero_tangent_vector!(M, X, p) + X .= acosh(max(1.0, -scp)) / wn .* w + return X end -@doc doc""" +@doc raw""" minkowski_dot(a,b) + Compute the Minkowski inner product of two Vectors `a` and `b` of same length `n+1`, i.e. ````math -\langle a,b\rangle_{\mathrm{M}} = -a_{n+1}b_{n+1} + \displaystyle\sum_{k=1}^n a_kb_k. +⟨a,b⟩_{\mathrm{M}} = -a_{n+1}b_{n+1} + \displaystyle\sum_{k=1}^n a_kb_k. ```` """ function minkowski_dot(a::AbstractVector, b::AbstractVector) return -a[end] * b[end] + sum(a[1:end-1] .* b[1:end-1]) end -@doc doc""" +@doc raw""" manifold_dimension(H::Hyperbolic) -Return the dimension of the hyperbolic space manifold $\mathbb H^n$, i.e. $n$. +Return the dimension of the hyperbolic space manifold $ℍ^n$, i.e. $\dim(ℍ^n) = n$. """ manifold_dimension(::Hyperbolic{N}) where {N} = N @@ -211,28 +212,28 @@ Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` on the """ mean(::Hyperbolic, ::Any...) -function mean!(M::Hyperbolic, y, x::AbstractVector, w::AbstractVector; kwargs...) - return mean!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) +function mean!(M::Hyperbolic, p, x::AbstractVector, w::AbstractVector; kwargs...) + return mean!(M, p, x, w, CyclicProximalPointEstimation(); kwargs...) end -@doc doc""" - project_tangent(M::Hyperbolic, x, v) +@doc raw""" + project_tangent(M::Hyperbolic, p, X) -Perform an orthogonal projection with respect to the Minkowski inner product of `v` onto -the tangent space at `x` of the [`Hyperbolic`](@ref) space `M`. +Perform an orthogonal projection with respect to the Minkowski inner product of `X` onto +the tangent space at `p` of the [`Hyperbolic`](@ref) space `M`. The formula reads ````math -w = v + \langle x,v\rangle_{\mathrm{M}} x, +Y = X + ⟨p,X⟩_{\mathrm{M}} p, ```` -where $\langle \cdot, \cdot \rangle_{\mathrm{M}}$ denotes the Minkowski inner +where $⟨\cdot, \cdot⟩_{\mathrm{M}}$ denotes the Minkowski inner product in the embedding, see [`minkowski_dot`](@ref). """ project_tangent(::Hyperbolic, ::Any...) -project_tangent!(::Hyperbolic, w, x, v) = (w .= v .+ minkowski_dot(x, v) .* x) +project_tangent!(::Hyperbolic, Y, p, X) = (Y .= X .+ minkowski_dot(p, X) .* p) -@doc doc""" +@doc raw""" representation_size(M::Hyperbolic) Return the representation size on the [`Hyperbolic`](@ref), i.e. for the `n`-diomensional @@ -240,36 +241,36 @@ hyperbolic manifold the dimention of the embedding, i.e. `n+1`. """ @generated representation_size(::Hyperbolic{N}) where {N} = (N + 1,) -sharp!(M::Hyperbolic, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::Hyperbolic, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) show(io::IO, ::Hyperbolic{N}) where {N} = print(io, "Hyperbolic($(N))") -@doc doc""" - vector_transport_to(M::Hyperbolic, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::Hyperbolic, p, X, q, ::ParallelTransport) -Compute the paralllel transport of the `v` from the tangent space at `x` on the -[`Hyperbolic`](@ref) space $\mathbb H^n$ to the tangent at `y` along the [`geodesic`](@ref) -connecting `x` and `y`. The formula reads +Compute the paralllel transport of the `X` from the tangent space at `p` on the +[`Hyperbolic`](@ref) space $ℍ^n$ to the tangent at `q` along the [`geodesic`](@ref) +connecting `p` and `q`. The formula reads ````math -P_{y\gets x}(v) = v - \frac{\langle \log_xy,v\rangle_x}{d^2_{\mathbb H^n}(x,y)} -\bigl(\log_xy + \log_yx \bigr). +\mathcal P_{q←p}X = X - \frac{⟨\log_p q,X⟩_x}{d^2_{ℍ^n}(p,q)} +\bigl(\log_p q + \log_qp \bigr). ```` """ vector_transport_to(::Hyperbolic, ::Any, ::Any, ::Any, ::ParallelTransport) -function vector_transport_to!(M::Hyperbolic, vto, x, v, y, ::ParallelTransport) - w = log(M, x, y) - wn = norm(M, x, w) - wn < eps(eltype(x + y)) && return copyto!(vto, v) - return copyto!(vto, v - (inner(M, x, w, v) * (w + log(M, y, x)) / wn^2)) +function vector_transport_to!(M::Hyperbolic, Y, p, X, q, ::ParallelTransport) + w = log(M, p, q) + wn = norm(M, p, w) + wn < eps(eltype(p + q)) && return copyto!(Y, X) + return copyto!(Y, X - (inner(M, p, w, X) * (w + log(M, q, p)) / wn^2)) end -@doc doc""" - zero_tangent_vector(M::Hyperbolic, x) +@doc raw""" + zero_tangent_vector(M::Hyperbolic, p) -Return the zero vector from the tangent space at `x` of the [`Hyperbolic`](@ref) `M`. +Return the zero vector from the tangent space at `p` of the [`Hyperbolic`](@ref) `M`. """ zero_tangent_vector(::HybridArray, ::Any...) -zero_tangent_vector!(M::Hyperbolic, v, x) = fill!(v, 0) +zero_tangent_vector!(M::Hyperbolic, X, p) = fill!(X, 0) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 3b1260c076..c41b439843 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Metric Abstract type for the pseudo-Riemannian metric tensor $g$, a family of smoothly @@ -28,7 +28,7 @@ struct MetricManifold{M<:Manifold,G<:Metric} <: Manifold metric::G end -@doc doc""" +@doc raw""" LorentzMetric <: Metric Abstract type for Lorentz metrics, which have a single time dimension. These @@ -37,75 +37,75 @@ giving the signature $(++...+-)$. """ abstract type LorentzMetric <: Metric end -@doc doc""" +@doc raw""" RiemannianMetric <: Metric Abstract type for Riemannian metrics, a family of positive definite inner -products. The positive definite property means that for $v \in T_x M$, the -inner product $g(v, v) > 0$ whenever $v$ is not the zero vector. +products. The positive definite property means that for $X ∈ T_p \mathcal M$, the +inner product $g(X, X) > 0$ whenever $X$ is not the zero vector. """ abstract type RiemannianMetric <: Metric end -function check_manifold_point(M::MetricManifold, x; kwargs...) - return check_manifold_point(M.manifold, x; kwargs...) +function check_manifold_point(M::MetricManifold, p; kwargs...) + return check_manifold_point(M.manifold, p; kwargs...) end -function check_tangent_vector(M::MetricManifold, x, v; kwargs...) - return check_tangent_vector(M.manifold, x, v; kwargs...) +function check_tangent_vector(M::MetricManifold, p, X; kwargs...) + return check_tangent_vector(M.manifold, p, X; kwargs...) end -@doc doc""" - christoffel_symbols_first(M::MetricManifold, x; backend=:default) +@doc raw""" + christoffel_symbols_first(M::MetricManifold, p; backend=:default) Compute the Christoffel symbols of the first kind in local coordinates. The Christoffel symbols are (in Einstein summation convention) -$\Gamma_{ijk} = \frac{1}{2} \left[g_{kj,i} + g_{ik,j} - g_{ij,k}\right],$ +$Γ_{ijk} = \frac{1}{2} \Bigl[g_{kj,i} + g_{ik,j} - g_{ij,k}\Bigr],$ -where $g_{ij,k}=\frac{\partial}{\partial x^k} g_{ij}$ is the coordinate +where $g_{ij,k}=\frac{∂}{∂ p^k} g_{ij}$ is the coordinate derivative of the local representation of the metric tensor. The dimensions of the resulting multi-dimensional array are ordered $(i,j,k)$. """ -function christoffel_symbols_first(M::MetricManifold, x; backend = :default) - ∂g = local_metric_jacobian(M, x; backend = backend) +function christoffel_symbols_first(M::MetricManifold, p; backend = :default) + ∂g = local_metric_jacobian(M, p; backend = backend) n = size(∂g, 1) Γ = allocate(∂g, Size(n, n, n)) @einsum Γ[i, j, k] = 1 / 2 * (∂g[k, j, i] + ∂g[i, k, j] - ∂g[i, j, k]) return Γ end -@doc doc""" +@doc raw""" christoffel_symbols_second(M::MetricManifold, x; backend=:default) Compute the Christoffel symbols of the second kind in local coordinates. The Christoffel symbols are (in Einstein summation convention) -$\Gamma^{l}_{ij} = g^{kl} \Gamma_{ijk},$ +$Γ^{l}_{ij} = g^{kl} Γ_{ijk},$ -where $\Gamma_{ijk}$ are the Christoffel symbols of the first kind, and +where $Γ_{ijk}$ are the Christoffel symbols of the first kind, and $g^{kl}$ is the inverse of the local representation of the metric tensor. The dimensions of the resulting multi-dimensional array are ordered $(l,i,j)$. """ -function christoffel_symbols_second(M::MetricManifold, x; backend = :default) - ginv = inverse_local_metric(M, x) - Γ₁ = christoffel_symbols_first(M, x; backend = backend) +function christoffel_symbols_second(M::MetricManifold, p; backend = :default) + ginv = inverse_local_metric(M, p) + Γ₁ = christoffel_symbols_first(M, p; backend = backend) Γ₂ = allocate(Γ₁) @einsum Γ₂[l, i, j] = ginv[k, l] * Γ₁[i, j, k] return Γ₂ end -@doc doc""" - christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :default) +@doc raw""" + christoffel_symbols_second_jacobian(M::MetricManifold, p; backend = :default) Get partial derivatives of the Christoffel symbols of the second kind -for manifold `M` at `x` with respect to the coordinates of `x`, -$\frac{\partial}{\partial x^l} \Gamma^{k}_{ij} = \Gamma^{k}_{ij,l}.$ +for manifold `M` at `p` with respect to the coordinates of `p`, +$\frac{∂}{∂ p^l} Γ^{k}_{ij} = Γ^{k}_{ij,l}.$ The dimensions of the resulting multi-dimensional array are ordered $(i,j,k,l)$. """ -function christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :default) - n = size(x, 1) +function christoffel_symbols_second_jacobian(M::MetricManifold, p; backend = :default) + n = size(p, 1) ∂Γ = reshape( - _jacobian(x -> christoffel_symbols_second(M, x; backend = backend), x, backend), + _jacobian(q -> christoffel_symbols_second(M, q; backend = backend), p, backend), n, n, n, @@ -114,34 +114,34 @@ function christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :de return ∂Γ end -@doc doc""" - det_local_metric(M::MetricManifold, x) +@doc raw""" + det_local_metric(M::MetricManifold, p) Return the determinant of local matrix representation of the metric tensor $g$. """ -det_local_metric(M::MetricManifold, x) = det(local_metric(M, x)) +det_local_metric(M::MetricManifold, p) = det(local_metric(M, p)) """ - einstein_tensor(M::MetricManifold, x; backend = :default) + einstein_tensor(M::MetricManifold, p; backend = :default) -Compute the Einstein tensor of the manifold `M` at the point `x`. +Compute the Einstein tensor of the manifold `M` at the point `p`. """ -function einstein_tensor(M::MetricManifold, x; backend = :default) - Ric = ricci_tensor(M, x; backend = backend) - g = local_metric(M, x) - ginv = inverse_local_metric(M, x) +function einstein_tensor(M::MetricManifold, p; backend = :default) + Ric = ricci_tensor(M, p; backend = backend) + g = local_metric(M, p) + ginv = inverse_local_metric(M, p) S = sum(ginv .* Ric) G = Ric - g .* S / 2 return G end -@doc doc""" - exp(N::MetricManifold{M,G}, x, v) +@doc raw""" + exp(N::MetricManifold{M,G}, p, X) Copute the exponential map on the [`Manifold`](@ref) `M` equipped with the [`Metric`](@ref) `G`. If the metric was declared the default metric using [`is_default_metric`](@ref), this method -falls back to `exp(M,x,v)`. +falls back to `exp(M,p,X)`. Otherwise it numerically integrates the underlying ODE, see [`solve_exp_ode`](@ref). Currently, the numerical integration is only accurate when using a single @@ -149,59 +149,36 @@ coordinate chart that covers the entire manifold. This excludes coordinates in an embedded space. """ exp(::MetricManifold, ::Any...) -function exp(M::MMT, x, v, T::AbstractVector{T} where {T}) where {MMT<:MetricManifold} - return exp(M, is_default_metric(M), x, v, T) -end -function exp( - M::MMT, - ::Val{true}, - x, - v, - T::AbstractVector{T} where {T}, -) where {MMT<:MetricManifold} - return exp(base_manifold(M), x, v, T) -end -function exp( - M::MMT, - ::Val{false}, - x, - v, - T::AbstractVector{T} where {T}, -) where {MMT<:MetricManifold} - sol = solve_exp_ode(M, x, v, extrema(T); dense = false, saveat = T) - n = length(x) - return map(i -> sol.u[i][n+1:end], 1:length(T)) -end -exp!(M::MMT, y, x, v) where {MMT<:MetricManifold} = exp!(M, is_default_metric(M), y, x, v) -function exp!(M::MMT, ::Val{true}, y, x, v) where {MMT<:MetricManifold} - return exp!(base_manifold(M), y, x, v) +exp!(M::MMT, q, p, X) where {MMT<:MetricManifold} = exp!(M, is_default_metric(M), q, p, X) +function exp!(M::MMT, ::Val{true}, q, p, X) where {MMT<:MetricManifold} + return exp!(base_manifold(M), q, p, X) end -function exp!(M::MMT, ::Val{false}, y, x, v) where {MMT<:MetricManifold} +function exp!(M::MMT, ::Val{false}, q, p, X) where {MMT<:MetricManifold} tspan = (0.0, 1.0) - sol = solve_exp_ode(M, x, v, tspan; dense = false, saveat = [1.0]) - n = length(x) - return copyto!(y, sol.u[1][n+1:end]) + sol = solve_exp_ode(M, p, X, tspan; dense = false, saveat = [1.0]) + n = length(p) + return copyto!(q, sol.u[1][n+1:end]) end -@doc doc""" - flat(N::MetricManifold{M,G}, x, w::FVector{TangentSpaceType}) +@doc raw""" + flat(N::MetricManifold{M,G}, p, X::FVector{TangentSpaceType}) -Compute the musical isomorphism to transform the tangent vector `w` from the +Compute the musical isomorphism to transform the tangent vector `X` from the [`Manifold`](@ref) `M` equipped with [`Metric`](@ref) `G` to a cotangent by computing ````math -w^\flat = G_xw, +X^♭= G_p X, ```` -where $G_x$ is the local matrix representation of `G`, see [`local_metric`](@ref) +where $G_p$ is the local matrix representation of `G`, see [`local_metric`](@ref) """ flat(::MetricManifold, ::Any...) -function flat!(M::MMT, v::CoTFVector, x, w::TFVector) where {MMT<:MetricManifold} - g = local_metric(M, x) - copyto!(v.data, g * w.data) - return v +function flat!(M::MMT, ξ::CoTFVector, p, X::TFVector) where {MMT<:MetricManifold} + g = local_metric(M, p) + copyto!(ξ.data, g * X.data) + return ξ end """ @@ -209,32 +186,32 @@ end Compute the Gaussian curvature of the manifold `M` at the point `x`. """ -gaussian_curvature(M::MetricManifold, x; kwargs...) = ricci_curvature(M, x; kwargs...) / 2 +gaussian_curvature(M::MetricManifold, p; kwargs...) = ricci_curvature(M, p; kwargs...) / 2 -function get_basis(M::MMT, x, B::ArbitraryOrthonormalBasis) where {MMT<:MetricManifold} - return invoke(get_basis, Tuple{MMT,Any,AbstractBasis}, M, x, B) +function get_basis(M::MMT, p, B::ArbitraryOrthonormalBasis) where {MMT<:MetricManifold} + return invoke(get_basis, Tuple{MMT,Any,AbstractBasis}, M, p, B) end -function get_basis(M::MMT, x, B::AbstractBasis) where {MMT<:MetricManifold} - return get_basis(M, is_default_metric(M), x, B) +function get_basis(M::MMT, p, B::AbstractBasis) where {MMT<:MetricManifold} + return get_basis(M, is_default_metric(M), p, B) end -function get_basis(M::MMT, ::Val{true}, x, B::AbstractBasis) where {MMT<:MetricManifold} - return get_basis(base_manifold(M), x, B) +function get_basis(M::MMT, ::Val{true}, p, B::AbstractBasis) where {MMT<:MetricManifold} + return get_basis(base_manifold(M), p, B) end -function get_basis(M::MMT, ::Val{false}, x, B::AbstractBasis) where {MMT<:MetricManifold} - error("tangent_orthogonal_basis not implemented on $(typeof(M)) for point $(typeof(x)) and basis type $(typeof(B)).") +function get_basis(M::MMT, ::Val{false}, p, B::AbstractBasis) where {MMT<:MetricManifold} + error("tangent_orthogonal_basis not implemented on $(typeof(M)) for point $(typeof(p)) and basis type $(typeof(B)).") end function injectivity_radius(M::MMT, args...) where {MMT<:MetricManifold} return injectivity_radius(base_manifold(M), args...) end -@doc doc""" - inverse_local_metric(M::MetricManifold, x) +@doc raw""" + inverse_local_metric(M::MetricManifold, p) Return the local matrix representation of the inverse metric (cometric) tensor, usually written $g^{ij}$. """ -inverse_local_metric(M::MetricManifold, x) = inv(local_metric(M, x)) +inverse_local_metric(M::MetricManifold, p) = inv(local_metric(M, p)) is_decorator_manifold(M::MMT) where {MMT<:MetricManifold} = Val(true) @@ -254,10 +231,10 @@ is_default_metric(M::Manifold, G::Metric) = Val(false) is_default_metric(MM) Indicate whether the [`Metric`](@ref) `MM.G` is the default metric for -the [`Manifold`](@ref) `MM.M` within the [`MetricManifold`](@ref) `MM`. +the [`Manifold`](@ref) `MM.manifold,` within the [`MetricManifold`](@ref) `MM`. This means that any occurence of -[`MetricManifold`](@ref)`(MM.M,MM.G)` where `typeof(is_default_metric(MM.M,MM.G)) = Val{true}` -falls back to just be called with `MM.MM` such that the [`Manifold`](@ref) `MM.M` +[`MetricManifold`](@ref)`(MM.manifold,MM.G)` where `typeof(is_default_metric(MM.manifold,MM.G)) = Val{true}` +falls back to just be called with `MM.manifold,` such that the [`Manifold`](@ref) `MM.manifold` implicitly has the metric `MM.G`, for example if this was the first one implemented or is the one most commonly assumed to be used. """ @@ -276,182 +253,193 @@ function _convert_with_default(M::MT, T::Type{<:Metric}, ::Val{false}) where {MT error("Can not convert $(M) to a MetricManifold{$(MT),$(T)}, since $(T) is not the default metric.") end -@doc doc""" - inner(N::MetricManifold{M,G}, x, v, w) +@doc raw""" + inner(N::MetricManifold{M,G}, p, X, Y) -Compute the inner product of `v`, `w` from the tangent space at `x` on the +Compute the inner product of `X` and `Y` from the tangent space at `p` on the [`Manifold`](@ref) `M` using the [`Metric`](@ref) `G`. If `G` is the default -metric (see [`is_default_metric`](@ref)) this is done using `log(M, x, v, w)`, -otherwise the [`local_metric`](@ref)`(M, x)` is employed as +metric (see [`is_default_metric`](@ref)) this is done using `inner(M, p, X, Y)`, +otherwise the [`local_metric`](@ref)`(M, p)` is employed as ````math -g_x(v,w) = \langle v, G_x w\rangle, +g_p(X, Y) = ⟨X, G_p Y⟩, ```` -where $G_x$ is the local matrix representation of the [`Metric`](@ref) `G`. +where $G_p$ is the local matrix representation of the [`Metric`](@ref) `G`. """ -inner(M::MMT, x, v, w) where {MMT<:MetricManifold} = inner(M, is_default_metric(M), x, v, w) -function inner(M::MMT, ::Val{false}, x, v, w) where {MMT<:MetricManifold} - return dot(v, local_metric(M, x) * w) +inner(M::MMT, p, X, Y) where {MMT<:MetricManifold} = inner(M, is_default_metric(M), p, X, Y) +function inner(M::MMT, ::Val{false}, p, X, Y) where {MMT<:MetricManifold} + return dot(X, local_metric(M, p) * Y) end -function inner(M::MMT, ::Val{true}, x, v, w) where {MMT<:MetricManifold} - return inner(base_manifold(M), x, v, w) +function inner(M::MMT, ::Val{true}, p, X, Y) where {MMT<:MetricManifold} + return inner(base_manifold(M), p, X, Y) end function inner( B::VectorBundleFibers{<:CotangentSpaceType,MMT}, - x, - v, - w, + p, + X, + Y, ) where {MMT<:MetricManifold} - ginv = inverse_local_metric(B.M, x) - return dot(v, ginv * w) + ginv = inverse_local_metric(B.manifold, p) + return dot(X, ginv * Y) end -@doc doc""" - local_metric(M::MetricManifold, x) +@doc raw""" + local_metric(M::MetricManifold, p) -Return the local matrix representation at the point `x` of the metric tensor $g$ on the -[`Manifold`](@ref) `M`, usually written $g_{ij}$. The matrix has the property that -$g(v, w)=v^T [g_{ij}] w = g_{ij} v^i w^j$, where the latter expression uses -Einstein summation convention. +Return the local matrix representation at the point `p` of the metric +tensor $g$ on the [`Manifold`](@ref) `M`, usually written $g_{ij}$. +The matrix has the property that $g(v, w)=v^T [g_{ij}] w = g_{ij} v^i w^j$, +where the latter expression uses Einstein summation convention. """ -function local_metric(M::MetricManifold, x) - error("Local metric not implemented on $(typeof(M)) for point $(typeof(x))") +function local_metric(M::MetricManifold, p) + error("Local metric not implemented on $(typeof(M)) for point $(typeof(p))") end -@doc doc""" - local_metric_jacobian(M::MetricManifold, x; backend=:default) +@doc raw""" + local_metric_jacobian(M::MetricManifold, p; backend=:default) -Get partial derivatives of the local metric of `M` at `x` with respect to the -coordinates of `x`, $\frac{\partial}{\partial x^k} g_{ij} = g_{ij,k}$. The +Get partial derivatives of the local metric of `M` at `p` with respect to the +coordinates of `p`, $\frac{∂}{∂ p^k} g_{ij} = g_{ij,k}$. The dimensions of the resulting multi-dimensional array are ordered $(i,j,k)$. """ -function local_metric_jacobian(M, x; backend = :default) - n = size(x, 1) - ∂g = reshape(_jacobian(x -> local_metric(M, x), x, backend), n, n, n) +function local_metric_jacobian(M, p; backend = :default) + n = size(p, 1) + ∂g = reshape(_jacobian(q -> local_metric(M, q), p, backend), n, n, n) return ∂g end -log!(M::MMT, w, x, y) where {MMT<:MetricManifold} = log!(M, is_default_metric(M), w, x, y) -function log!(M::MMT, ::Val{true}, w, x, y) where {MMT<:MetricManifold} - return log!(base_manifold(M), w, x, y) +@doc raw""" + log(N::MetricManifold{M,G}, p, q) + +Copute the logarithmic map on the [`Manifold`](@ref) `M` equipped with the [`Metric`](@ref) `G`. + +If the metric was declared the default metric using [`is_default_metric`](@ref), this method +falls back to `log(M,p,q)`. Otherwise, you have to provide an implementation for the non-default +[`Metric`](@ref) `G` metric within its [`MetricManifold`](@ref)`{M,G}`. +""" +log(::MetricManifold, ::Any...) + +log!(M::MMT, X, p, q) where {MMT<:MetricManifold} = log!(M, is_default_metric(M), X, p, q) +function log!(M::MMT, ::Val{true}, X, p, q) where {MMT<:MetricManifold} + return log!(base_manifold(M), X, p, q) end -function log!(M::MMT, ::Val{false}, w, x, y) where {MMT<:MetricManifold} - error("Logarithmic map not implemented on $(typeof(M)) for points $(typeof(x)) and $(typeof(y)).") +function log!(M::MMT, ::Val{false}, X, p, q) where {MMT<:MetricManifold} + error("Logarithmic map not implemented on $(typeof(M)) for points $(typeof(p)) and $(typeof(q)).") end -@doc doc""" - log_local_metric_density(M::MetricManifold, x) +@doc raw""" + log_local_metric_density(M::MetricManifold, p) -Return the natural logarithm of the metric density $\rho$ of `M` at `x`, which +Return the natural logarithm of the metric density $ρ$ of `M` at `p`, which is given by $\rho=\log \sqrt{|\det [g_{ij}]|}$. """ -log_local_metric_density(M::MetricManifold, x) = log(abs(det_local_metric(M, x))) / 2 +log_local_metric_density(M::MetricManifold, p) = log(abs(det_local_metric(M, p))) / 2 function mean!( M::MMT, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return mean!(M, is_default_metric(M), y, x, w; kwargs...) + return mean!(M, is_default_metric(M), p, x, w; kwargs...) end function mean!( M::MMT, ::Val{true}, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return mean!(base_manifold(M), y, x, w; kwargs...) + return mean!(base_manifold(M), p, x, w; kwargs...) end function mean!( M::MMT, ::Val{false}, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return mean!(M, y, x, w, GradientDescentEstimation(); kwargs...) + return mean!(M, p, x, w, GradientDescentEstimation(); kwargs...) end function median!( M::MMT, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return median!(M, is_default_metric(M), y, x, w; kwargs...) + return median!(M, is_default_metric(M), p, x, w; kwargs...) end function median!( M::MMT, ::Val{true}, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return median!(base_manifold(M), y, x, w; kwargs...) + return median!(base_manifold(M), p, x, w; kwargs...) end function median!( M::MMT, ::Val{false}, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) where {MMT<:MetricManifold} - return median!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) + return median!(M, p, x, w, CyclicProximalPointEstimation(); kwargs...) end -@doc doc""" +@doc raw""" metric(M::MetricManifold) Get the metric $g$ of the manifold `M`. """ metric(M::MetricManifold) = M.metric -function normal_tvector_distribution(M::MMT, x, σ) where {MMT<:MetricManifold} - return normal_tvector_distribution(M, is_default_metric(M), x, σ) +function normal_tvector_distribution(M::MMT, p, σ) where {MMT<:MetricManifold} + return normal_tvector_distribution(M, is_default_metric(M), p, σ) end -function normal_tvector_distribution(M::MMT, ::Val{true}, x, σ) where {MMT<:MetricManifold} - return normal_tvector_distribution(base_manifold(M), x, σ) +function normal_tvector_distribution(M::MMT, ::Val{true}, p, σ) where {MMT<:MetricManifold} + return normal_tvector_distribution(base_manifold(M), p, σ) end -function normal_tvector_distribution(M::MMT, ::Val{false}, x, σ) where {MMT<:MetricManifold} - error("normal_tvector_distribution not implemented for a $(typeof(M)) at point $(typeof(x)) with standard deviation $(typeof(σ)).") +function normal_tvector_distribution(M::MMT, ::Val{false}, p, σ) where {MMT<:MetricManifold} + error("normal_tvector_distribution not implemented for a $(typeof(M)) at point $(typeof(p)) with standard deviation $(typeof(σ)).") end -function project_point!(M::MMT, y, x) where {MMT<:MetricManifold} - return project_point!(M, is_default_metric(M), y, x) +function project_point!(M::MMT, q, p) where {MMT<:MetricManifold} + return project_point!(M, is_default_metric(M), q, p) end -function project_point!(M::MMT, ::Val{true}, y, x) where {MMT<:MetricManifold} - return project_point!(base_manifold(M), y, x) +function project_point!(M::MMT, ::Val{true}, q, p) where {MMT<:MetricManifold} + return project_point!(base_manifold(M), q, p) end -function project_point!(M::MMT, ::Val{false}, y, x) where {MMT<:MetricManifold} - error("project_point! not implemented on $(typeof(M)) for point $(typeof(x))") +function project_point!(M::MMT, ::Val{false}, q, p) where {MMT<:MetricManifold} + error("project_point! not implemented on $(typeof(M)) for point $(typeof(p))") end -function project_tangent!(M::MMT, w, x, v) where {MMT<:MetricManifold} - return project_tangent!(M, is_default_metric(M), w, x, v) +function project_tangent!(M::MMT, Y, p, X) where {MMT<:MetricManifold} + return project_tangent!(M, is_default_metric(M), Y, p, X) end -function project_tangent!(M::MMT, ::Val{true}, w, x, v) where {MMT<:MetricManifold} - return project_tangent!(base_manifold(M), w, x, v) +function project_tangent!(M::MMT, ::Val{true}, Y, p, X) where {MMT<:MetricManifold} + return project_tangent!(base_manifold(M), Y, p, X) end -function project_tangent!(M::MMT, ::Val{false}, w, x, v) where {MMT<:MetricManifold} - error("project_tangent! not implemented for a $(typeof(M)) and tangent $(typeof(v)) at point $(typeof(x)).") +function project_tangent!(M::MMT, ::Val{false}, Y, p, X) where {MMT<:MetricManifold} + error("project_tangent! not implemented for a $(typeof(M)) and tangent $(typeof(X)) at point $(typeof(p)).") end -function projected_distribution(M::MMT, d, x) where {MMT<:MetricManifold} - return projected_distribution(M, is_default_metric(M), d, x) +function projected_distribution(M::MMT, d, p) where {MMT<:MetricManifold} + return projected_distribution(M, is_default_metric(M), d, p) end -function projected_distribution(M::MMT, ::Val{true}, d, x) where {MMT<:MetricManifold} - return projected_distribution(base_manifold(M), d, x) +function projected_distribution(M::MMT, ::Val{true}, d, p) where {MMT<:MetricManifold} + return projected_distribution(base_manifold(M), d, p) end -function projected_distribution(M::MMT, ::Val{false}, d, x) where {MMT<:MetricManifold} - error("projected_distribution not implemented for a $(typeof(M)) and with $(typeof(d)) at point $(typeof(x)).") +function projected_distribution(M::MMT, ::Val{false}, d, p) where {MMT<:MetricManifold} + error("projected_distribution not implemented for a $(typeof(M)) and with $(typeof(d)) at point $(typeof(p)).") end function projected_distribution(M::MMT, d) where {MMT<:MetricManifold} return projected_distribution(M, is_default_metric(M), d) @@ -464,76 +452,76 @@ function projected_distribution(M::MMT, ::Val{false}, d) where {MMT<:MetricManif end """ - ricci_curvature(M::MetricManifold, x; backend = :default) + ricci_curvature(M::MetricManifold, p; backend = :default) -Compute the Ricci scalar curvature of the manifold `M` at the point `x`. +Compute the Ricci scalar curvature of the manifold `M` at the point `p`. """ -function ricci_curvature(M::MetricManifold, x; backend = :default) - ginv = inverse_local_metric(M, x) - Ric = ricci_tensor(M, x; backend = backend) +function ricci_curvature(M::MetricManifold, p; backend = :default) + ginv = inverse_local_metric(M, p) + Ric = ricci_tensor(M, p; backend = backend) S = sum(ginv .* Ric) return S end """ - ricci_tensor(M::MetricManifold, x; backend = :default) + ricci_tensor(M::MetricManifold, p; backend = :default) Compute the Ricci tensor, also known as the Ricci curvature tensor, -of the manifold `M` at the point `x`. +of the manifold `M` at the point `p`. """ -function ricci_tensor(M::MetricManifold, x; kwargs...) - R = riemann_tensor(M, x; kwargs...) +function ricci_tensor(M::MetricManifold, p; kwargs...) + R = riemann_tensor(M, p; kwargs...) n = size(R, 1) Ric = allocate(R, Size(n, n)) @einsum Ric[i, j] = R[l, i, l, j] return Ric end -@doc doc""" - riemann_tensor(M::MetricManifold, x) +@doc raw""" + riemann_tensor(M::MetricManifold, p) Compute the Riemann tensor $R^l_{ijk}$, also known as the Riemann curvature -tensor, at the point `x`. The dimensions of the resulting multi-dimensional +tensor, at the point `p`. The dimensions of the resulting multi-dimensional array are ordered $(l,i,j,k)$. """ -function riemann_tensor(M::MetricManifold, x; backend = :default) - n = size(x, 1) - Γ = christoffel_symbols_second(M, x; backend = backend) - ∂Γ = christoffel_symbols_second_jacobian(M, x; backend = backend) ./ n +function riemann_tensor(M::MetricManifold, p; backend = :default) + n = size(p, 1) + Γ = christoffel_symbols_second(M, p; backend = backend) + ∂Γ = christoffel_symbols_second_jacobian(M, p; backend = backend) ./ n R = allocate(∂Γ, Size(n, n, n, n)) @einsum R[l, i, j, k] = ∂Γ[l, i, k, j] - ∂Γ[l, i, j, k] + Γ[s, i, k] * Γ[l, s, j] - Γ[s, i, j] * Γ[l, s, k] return R end -@doc doc""" - sharp(N::MetricManifold{M,G}, x, w::FVector{CotangentSpaceType}) +@doc raw""" + sharp(N::MetricManifold{M,G}, p, ξ::FVector{CotangentSpaceType}) -Compute the musical isomorphism to transform the cotangent vector `w` from the +Compute the musical isomorphism to transform the cotangent vector `ξ` from the [`Manifold`](@ref) `M` equipped with [`Metric`](@ref) `G` to a tangent by computing ````math -w^\sharp = G_x^{-1}w, +ξ^♯ = G_p^{-1} ξ, ```` -where $G_x$ is the local matrix representation of `G`, i.e. one employs -[`inverse_local_metric`](@ref) here to obtain $G_x^{-1}$. +where $G_p$ is the local matrix representation of `G`, i.e. one employs +[`inverse_local_metric`](@ref) here to obtain $G_p^{-1}$. """ sharp(::MetricManifold, ::Any) -function sharp!(M::N, v::TFVector, x, w::CoTFVector) where {N<:MetricManifold} - ginv = inverse_local_metric(M, x) - copyto!(v.data, ginv * w.data) - return v +function sharp!(M::N, X::TFVector, p, ξ::CoTFVector) where {N<:MetricManifold} + ginv = inverse_local_metric(M, p) + copyto!(X.data, ginv * ξ.data) + return X end show(io::IO, M::MetricManifold) = print(io, "MetricManifold($(M.manifold), $(M.metric))") -@doc doc""" +@doc raw""" solve_exp_ode( M::MetricManifold, - x, - v, + p, + X, tspan; backend = :default, solver = AutoVern9(Rodas5()), @@ -544,9 +532,9 @@ Approximate the exponential map on the manifold over the provided timespan assuming the Levi-Civita connection by solving the ordinary differential equation -$\frac{d^2}{dt^2} x^k + \Gamma^k_{ij} \frac{d}{dt} x_i \frac{d}{dt} x_j = 0,$ +$\frac{d^2}{dt^2} p^k + Γ^k_{ij} \frac{d}{dt} p_i \frac{d}{dt} p_j = 0,$ -where $\Gamma^k_{ij}$ are the Christoffel symbols of the second kind, and +where $Γ^k_{ij}$ are the Christoffel symbols of the second kind, and the Einstein summation convention is assumed. The arguments `tspan` and `solver` follow the `OrdinaryDiffEq` conventions. `kwargs...` specify keyword arguments that will be passed to `OrdinaryDiffEq.solve`. @@ -562,43 +550,43 @@ in an embedded space. using OrdinaryDiffEq ``` """ -function solve_exp_ode(M, x, v, tspan; kwargs...) - error("solve_exp_ode not implemented on $(typeof(M)) for point $(typeof(x)), vector $(typeof(v)), and timespan $(typeof(tspan)). For a suitable default, enter `using OrdinaryDiffEq` on Julia 1.1 or greater.") +function solve_exp_ode(M, p, X, tspan; kwargs...) + error("solve_exp_ode not implemented on $(typeof(M)) for point $(typeof(p)), vector $(typeof(X)), and timespan $(typeof(tspan)). For a suitable default, enter `using OrdinaryDiffEq` on Julia 1.1 or greater.") end function vector_transport_to!( M::MMT, - vto, - x, - v, - y, + Y, + p, + X, + q, m::AbstractVectorTransportMethod, ) where {MMT<:MetricManifold} - return vector_transport_to!(M, is_default_metric(M), vto, x, v, y, m) + return vector_transport_to!(M, is_default_metric(M), Y, p, X, q, m) end function vector_transport_to!( M::MMT, ::Val{true}, - vto, - x, - v, - y, + Y, + p, + X, + q, m, ) where {MMT<:MetricManifold} - return vector_transport_to!(base_manifold(M), vto, x, v, y, m) + return vector_transport_to!(base_manifold(M), Y, p, X, q, m) end function vector_transport_to!( M::MMT, ::Val{false}, - vto, - x, - v, - y, + Y, + p, + X, + q, m, ) where {MMT<:MetricManifold} - error("vector transport from a point of type $(typeof(x)) to a type $(typeof(y)) on a $(typeof(M)) for a vector of type $(v) and the $(typeof(m)) not yet implemented.") + error("vector transport from a point of type $(typeof(p)) to a type $(typeof(q)) on a $(typeof(M)) for a vector of type $(X) and the $(typeof(m)) not yet implemented.") end -function zero_tangent_vector!(M::MMT, v, x) where {MMT<:MetricManifold} - return zero_tangent_vector!(M.manifold, v, x) +function zero_tangent_vector!(M::MMT, X, p) where {MMT<:MetricManifold} + return zero_tangent_vector!(M.manifold, X, p) end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 90bd50a178..2409dfbff5 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -28,7 +28,7 @@ Each element of such array stores a single point or tangent vector. """ struct NestedPowerRepresentation <: AbstractPowerRepresentation end -@doc doc""" +@doc raw""" AbstractPowerManifold{M,TPR} <: Manifold An abstract [`Manifold`](@ref) to represent manifolds that are build as powers @@ -38,10 +38,10 @@ of another [`Manifold`](@ref) `M` with representation type `TPR`, a subtype of abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation} <: Manifold end -@doc doc""" +@doc raw""" PowerManifold{TM<:Manifold, TSize<:Tuple, TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{TM} -The power manifold $\mathcal M^{n_1 \times n_2 \times \dots \times n_d}$ with power geometry +The power manifold $\mathcal M^{n_1× n_2 × … × n_d}$ with power geometry `TSize` statically defines the number of elements along each axis. For example, a manifold-valued time series would be represented by a power manifold with @@ -58,7 +58,7 @@ power manifolds might be faster if they are represented as [`ProductManifold`](@ PowerManifold(M, N_1, N_2, ..., N_n) PowerManifold(M, NestedPowerRepresentation(), N_1, N_2, ..., N_n) -Generate the power manifold $M^{N_1 \times N_2 \times \dots \times N_n}$. +Generate the power manifold $M^{N_1 × N_2 × … × N_n}$. By default, the [`MultidimentionalArrayPowerRepresentation`](@ref) of points and tangent vectors is used, although a different one, for example [`NestedPowerRepresentation`](@ref), can be given as the second argument to the @@ -82,7 +82,7 @@ function PowerManifold( PowerManifold{typeof(M),Tuple{size...},TPR}(M) end -@doc doc""" +@doc raw""" PowerMetric <: Metric Represent the [`Metric`](@ref) on an [`AbstractPowerManifold`](@ref), i.e. the inner @@ -119,16 +119,16 @@ struct PowerPointDistribution{TM<:AbstractPowerManifold,TD<:MPointDistribution,T MPointDistribution{TM} manifold::TM distribution::TD - x::TX + point::TX end """ PowerFVectorDistribution([type::VectorBundleFibers], [x], distr) -Generates a random vector at point `x` from vector space (a fiber of a tangent +Generates a random vector at a `point` from vector space (a fiber of a tangent bundle) of type `type` using the power distribution of `distr`. -Vector space type and `x` can be automatically inferred from distribution `distr`. +Vector space type and `point` can be automatically inferred from distribution `distr`. """ struct PowerFVectorDistribution{ TSpace<:VectorBundleFibers{<:VectorSpaceType,<:AbstractPowerManifold}, @@ -136,7 +136,7 @@ struct PowerFVectorDistribution{ TX, } <: FVectorDistribution{TSpace,TX} type::TSpace - x::TX + point::TX distribution::TD end @@ -170,57 +170,57 @@ const PowerManifoldMultidimensional = const PowerManifoldNested = AbstractPowerManifold{<:Manifold,NestedPowerRepresentation} where {TSize} -function basis(M::AbstractPowerManifold, x, B::AbstractBasis) +function basis(M::AbstractPowerManifold, p, B::AbstractBasis) rep_size = representation_size(M.manifold) - vs = [basis(M.manifold, _read(M, rep_size, x, i), B) for i in get_iterator(M)] + vs = [basis(M.manifold, _read(M, rep_size, p, i), B) for i in get_iterator(M)] return PrecomputedPowerOrthonormalBasis(vs) end ^(M::Manifold, n) = PowerManifold(M, n...) -function basis(M::AbstractPowerManifold, x, B::ArbitraryOrthonormalBasis) - return invoke(basis, Tuple{PowerManifold,Any,AbstractBasis}, M, x, B) +function basis(M::AbstractPowerManifold, p, B::ArbitraryOrthonormalBasis) + return invoke(basis, Tuple{PowerManifold,Any,AbstractBasis}, M, p, B) end -function basis(M::AbstractPowerManifold, x, B::DiagonalizingOrthonormalBasis) - return invoke(basis, Tuple{PowerManifold,Any,AbstractBasis}, M, x, B) +function basis(M::AbstractPowerManifold, p, B::DiagonalizingOrthonormalBasis) + return invoke(basis, Tuple{PowerManifold,Any,AbstractBasis}, M, p, B) end """ - check_manifold_point(M::AbstractProductManifold, x; kwargs...) + check_manifold_point(M::AbstractProductManifold, p; kwargs...) -Check whether `x` is a valid point on an [`AbstractPowerManifold`](@ref) `M`, i.e. -each element of `x` has to be a valid point on the base manifold. +Check whether `p` is a valid point on an [`AbstractPowerManifold`](@ref) `M`, +i.e. each element of `p` has to be a valid point on the base manifold. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::AbstractPowerManifold, x; kwargs...) +function check_manifold_point(M::AbstractPowerManifold, p; kwargs...) rep_size = representation_size(M.manifold) for i in get_iterator(M) - imp = check_manifold_point(M.manifold, _read(M, rep_size, x, i); kwargs...) + imp = check_manifold_point(M.manifold, _read(M, rep_size, p, i); kwargs...) imp === nothing || return imp end return nothing end """ - check_tangent_vector(M::AbstractPowerManifold, x, v; kwargs... ) + check_tangent_vector(M::AbstractPowerManifold, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` an the [`AbstractPowerManifold`](@ref) -`M`, i.e. atfer [`check_manifold_point`](@ref)`(M, x)`, and all projections to +Check whether `X` is a tangent vector to `p` an the [`AbstractPowerManifold`](@ref) +`M`, i.e. atfer [`check_manifold_point`](@ref)`(M, p)`, and all projections to base manifolds must be respective tangent vectors. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::AbstractPowerManifold, x, v; kwargs...) - mpe = check_manifold_point(M, x) +function check_tangent_vector(M::AbstractPowerManifold, p, X; kwargs...) + mpe = check_manifold_point(M, p) mpe === nothing || return mpe rep_size = representation_size(M.manifold) for i in get_iterator(M) imp = check_tangent_vector( M.manifold, - _read(M, rep_size, x, i), - _read(M, rep_size, v, i); + _read(M, rep_size, p, i), + _read(M, rep_size, X, i); kwargs..., ) imp === nothing || return imp @@ -230,107 +230,107 @@ end function det_local_metric( M::MetricManifold{<:AbstractPowerManifold,PowerMetric}, - x::AbstractArray, + p::AbstractArray, ) - result = one(number_eltype(x)) + result = one(number_eltype(p)) rep_size = representation_size(M.manifold) for i in get_iterator(M) - result *= det_local_metric(M.manifold, _read(M, rep_size, x, i)) + result *= det_local_metric(M.manifold, _read(M, rep_size, p, i)) end return result end -@doc doc""" - distance(M::AbstractPowerManifold, x, y) +@doc raw""" + distance(M::AbstractPowerManifold, p, q) -Compute the distance between `x` and `y` on an [`AbstractPowerManifold`](@ref), +Compute the distance between `q` and `p` on an [`AbstractPowerManifold`](@ref), i.e. from the element wise distances the Forbenius norm is computed. """ -function distance(M::AbstractPowerManifold, x, y) - sum_squares = zero(number_eltype(x)) +function distance(M::AbstractPowerManifold, p, q) + sum_squares = zero(number_eltype(p)) rep_size = representation_size(M.manifold) for i in get_iterator(M) sum_squares += - distance(M.manifold, _read(M, rep_size, x, i), _read(M, rep_size, y, i))^2 + distance(M.manifold, _read(M, rep_size, p, i), _read(M, rep_size, q, i))^2 end return sqrt(sum_squares) end -@doc doc""" - exp(M::AbstractPowerManifold, x, v) +@doc raw""" + exp(M::AbstractPowerManifold, p, X) -Compute the exponential map from `x` in direction `v` on the [`AbstractPowerManifold`](@ref) `M`, +Compute the exponential map from `p` in direction `X` on the [`AbstractPowerManifold`](@ref) `M`, which can be computed using the base manifolds exponential map elementwise. """ exp(::AbstractPowerManifold, ::Any...) -function exp!(M::AbstractPowerManifold, y, x, v) +function exp!(M::AbstractPowerManifold, q, p, X) rep_size = representation_size(M.manifold) for i in get_iterator(M) exp!( M.manifold, - _write(M, rep_size, y, i), - _read(M, rep_size, x, i), - _read(M, rep_size, v, i), + _write(M, rep_size, q, i), + _read(M, rep_size, p, i), + _read(M, rep_size, X, i), ) end - return y + return q end -@doc doc""" - flat(M::AbstractPowerManifold, x, w::FVector{TangentSpaceType}) +@doc raw""" + flat(M::AbstractPowerManifold, p, X::FVector{TangentSpaceType}) -use the musical isomorphism to transform the tangent vector `w` from the tangent space at -`x` on an [`AbstractPowerManifold`](@ref) `M` to a cotangent vector. -This can be done elementwise, so r every entry of `w` (and `x`) sparately +use the musical isomorphism to transform the tangent vector `X` from the tangent space at +`p` on an [`AbstractPowerManifold`](@ref) `M` to a cotangent vector. +This can be done elementwise for each entry of `X` (and `p`). """ flat(::AbstractPowerManifold, ::Any...) -function flat!(M::AbstractPowerManifold, v::CoTFVector, x, w::TFVector) +function flat!(M::AbstractPowerManifold, ξ::CoTFVector, p, X::TFVector) rep_size = representation_size(M.manifold) for i in get_iterator(M) flat!( M.manifold, - FVector(CotangentSpace, _write(M, rep_size, v.data, i)), - _read(M, rep_size, x, i), - FVector(TangentSpace, _read(M, rep_size, w.data, i)), + FVector(CotangentSpace, _write(M, rep_size, ξ.data, i)), + _read(M, rep_size, p, i), + FVector(TangentSpace, _read(M, rep_size, X.data, i)), ) end - return v + return ξ end -function get_basis(M::AbstractPowerManifold, x, B::AbstractBasis) +function get_basis(M::AbstractPowerManifold, p, B::AbstractBasis) rep_size = representation_size(M.manifold) - vs = [get_basis(M.manifold, _read(M, rep_size, x, i), B) for i in get_iterator(M)] + vs = [get_basis(M.manifold, _read(M, rep_size, p, i), B) for i in get_iterator(M)] return PrecomputedPowerOrthonormalBasis(vs) end -function get_basis(M::AbstractPowerManifold, x, B::ArbitraryOrthonormalBasis) - return invoke(get_basis, Tuple{PowerManifold,Any,AbstractBasis}, M, x, B) +function get_basis(M::AbstractPowerManifold, p, B::ArbitraryOrthonormalBasis) + return invoke(get_basis, Tuple{PowerManifold,Any,AbstractBasis}, M, p, B) end -function get_basis(M::AbstractPowerManifold, x, B::DiagonalizingOrthonormalBasis) - return invoke(get_basis, Tuple{PowerManifold,Any,AbstractBasis}, M, x, B) +function get_basis(M::AbstractPowerManifold, p, B::DiagonalizingOrthonormalBasis) + return invoke(get_basis, Tuple{PowerManifold,Any,AbstractBasis}, M, p, B) end -function get_coordinates(M::AbstractPowerManifold, x, v, B::ArbitraryOrthonormalBasis) +function get_coordinates(M::AbstractPowerManifold, p, X, B::ArbitraryOrthonormalBasis) rep_size = representation_size(M.manifold) vs = [ - get_coordinates(M.manifold, _read(M, rep_size, x, i), _read(M, rep_size, v, i), B) + get_coordinates(M.manifold, _read(M, rep_size, p, i), _read(M, rep_size, X, i), B) for i in get_iterator(M) ] return reduce(vcat, reshape(vs, length(vs))) end function get_coordinates( M::AbstractPowerManifold, - x, - v, + p, + X, B::PrecomputedPowerOrthonormalBasis, ) rep_size = representation_size(M.manifold) vs = [ get_coordinates( M.manifold, - _read(M, rep_size, x, i), - _read(M, rep_size, v, i), + _read(M, rep_size, p, i), + _read(M, rep_size, X, i), B.bases[i...], ) for i in get_iterator(M) @@ -344,19 +344,18 @@ get_iterator(M::PowerManifold{<:Manifold,Tuple{N}}) where {N} = 1:N return Base.product(map(Base.OneTo, size_tuple)...) end -function get_vector(M::PowerManifold, x, v, B::PrecomputedPowerOrthonormalBasis) +function get_vector(M::PowerManifold, p, X, B::PrecomputedPowerOrthonormalBasis) dim = manifold_dimension(M.manifold) - rep_size = representation_size(M.manifold) - v_out = allocate(x) + v_out = allocate(p) v_iter = 1 for i in get_iterator(M) copyto!( _write(M, rep_size, v_out, i), get_vector( M.manifold, - _read(M, rep_size, x, i), - v[v_iter:v_iter+dim-1], + _read(M, rep_size, p, i), + X[v_iter:v_iter+dim-1], B.bases[i...], ), ) @@ -364,35 +363,34 @@ function get_vector(M::PowerManifold, x, v, B::PrecomputedPowerOrthonormalBasis) end return v_out end -function get_vector(M::AbstractPowerManifold, x, v, B::ArbitraryOrthonormalBasis) +function get_vector(M::AbstractPowerManifold, p, X, B::ArbitraryOrthonormalBasis) dim = manifold_dimension(M.manifold) - rep_size = representation_size(M.manifold) - v_out = allocate(x) + v_out = allocate(p) v_iter = 1 for i in get_iterator(M) copyto!( _write(M, rep_size, v_out, i), - get_vector(M.manifold, _read(M, rep_size, x, i), v[v_iter:v_iter+dim-1], B), + get_vector(M.manifold, _read(M, rep_size, p, i), X[v_iter:v_iter+dim-1], B), ) v_iter += dim end return v_out end -@doc doc""" - injectivity_radius(M::AbstractPowerManifold[, x]) +@doc raw""" + injectivity_radius(M::AbstractPowerManifold[, p]) the injectivity radius on an [`AbstractPowerManifold`](@ref) is for the global case -equal to the one of its base manifold. For a given point `x` it's equal to the +equal to the one of its base manifold. For a given point `p` it's equal to the minimum of all radii in the array entries. """ -function injectivity_radius(M::AbstractPowerManifold, x) +function injectivity_radius(M::AbstractPowerManifold, p) radius = 0.0 initialized = false rep_size = representation_size(M.manifold) for i in get_iterator(M) - cur_rad = injectivity_radius(M.manifold, _read(M, rep_size, x, i)) + cur_rad = injectivity_radius(M.manifold, _read(M, rep_size, p, i)) if initialized radius = min(cur_rad, radius) else @@ -404,48 +402,48 @@ function injectivity_radius(M::AbstractPowerManifold, x) end injectivity_radius(M::AbstractPowerManifold) = injectivity_radius(M.manifold) -@doc doc""" - inverse_retract(M::AbstractPowerManifold, x, y, m::InversePowerRetraction) +@doc raw""" + inverse_retract(M::AbstractPowerManifold, p, q, m::InversePowerRetraction) -Compute the inverse retraction from `x` with respect to `y` on an [`AbstractPowerManifold`](@ref) `M` +Compute the inverse retraction from `p` with respect to `q` on an [`AbstractPowerManifold`](@ref) `M` using an [`InversePowerRetraction`](@ref), which by default encapsulates a inverse retraction of the base manifold. Then this method is performed elementwise, so the encapsulated inverse retraction method has to be one that is available on the base [`Manifold`](@ref). """ inverse_retract(::AbstractPowerManifold, ::Any...) -function inverse_retract!(M::AbstractPowerManifold, v, x, y, method::InversePowerRetraction) +function inverse_retract!(M::AbstractPowerManifold, X, p, q, method::InversePowerRetraction) rep_size = representation_size(M.manifold) for i in get_iterator(M) inverse_retract!( M.manifold, - _write(M, rep_size, v, i), - _read(M, rep_size, x, i), - _read(M, rep_size, y, i), + _write(M, rep_size, X, i), + _read(M, rep_size, p, i), + _read(M, rep_size, q, i), method.inverse_retraction, ) end - return v + return X end -@doc doc""" - inner(M::AbstractPowerManifold, x, v, w) +@doc raw""" + inner(M::AbstractPowerManifold, p, X, Y) -Compute the inner product of `v` and `w` from the tangent space at `x` on an +Compute the inner product of `X` and `Y` from the tangent space at `p` on an [`AbstractPowerManifold`](@ref) `M`, i.e. for each arrays entry the tangent -vector entries from `v` and `w` are in the tangent space of the corresponding -element from `x`. +vector entries from `X` and `Y` are in the tangent space of the corresponding +element from `p`. The inner product is then the sum of the elementwise inner products. """ -function inner(M::AbstractPowerManifold, x, v, w) - result = zero(number_eltype(v)) +function inner(M::AbstractPowerManifold, p, X, Y) + result = zero(number_eltype(X)) rep_size = representation_size(M.manifold) for i in get_iterator(M) result += inner( M.manifold, - _read(M, rep_size, x, i), - _read(M, rep_size, v, i), - _read(M, rep_size, w, i), + _read(M, rep_size, p, i), + _read(M, rep_size, X, i), + _read(M, rep_size, Y, i), ) end return result @@ -453,105 +451,105 @@ end is_default_metric(::AbstractPowerManifold, ::PowerMetric) = Val(true) -function isapprox(M::AbstractPowerManifold, x, y; kwargs...) +function isapprox(M::AbstractPowerManifold, p, q; kwargs...) result = true rep_size = representation_size(M.manifold) for i in get_iterator(M) result &= isapprox( M.manifold, - _read(M, rep_size, x, i), - _read(M, rep_size, y, i); + _read(M, rep_size, p, i), + _read(M, rep_size, q, i); kwargs..., ) end return result end -function isapprox(M::AbstractPowerManifold, x, v, w; kwargs...) +function isapprox(M::AbstractPowerManifold, p, X, Y; kwargs...) result = true rep_size = representation_size(M.manifold) for i in get_iterator(M) result &= isapprox( M.manifold, - _read(M, rep_size, x, i), - _read(M, rep_size, v, i), - _read(M, rep_size, w, i); + _read(M, rep_size, p, i), + _read(M, rep_size, X, i), + _read(M, rep_size, Y, i); kwargs..., ) end return result end -@doc doc""" - log(M::AbstractPowerManifold, x, y) +@doc raw""" + log(M::AbstractPowerManifold, p, q) -Compute the logarithmic map from `x` to `y` on the [`AbstractPowerManifold`](@ref) `M`, +Compute the logarithmic map from `p` to `q` on the [`AbstractPowerManifold`](@ref) `M`, which can be computed using the base manifolds logarithmic map elementwise. """ log(::AbstractPowerManifold, ::Any...) -function log!(M::AbstractPowerManifold, v, x, y) +function log!(M::AbstractPowerManifold, X, p, q) rep_size = representation_size(M.manifold) for i in get_iterator(M) log!( M.manifold, - _write(M, rep_size, v, i), - _read(M, rep_size, x, i), - _read(M, rep_size, y, i), + _write(M, rep_size, X, i), + _read(M, rep_size, p, i), + _read(M, rep_size, q, i), ) end - return v + return X end -@doc doc""" +@doc raw""" manifold_dimension(M::PowerManifold) Returns the manifold-dimension of an [`PowerManifold`](@ref) `M` -$=\mathcal N = (\mathcal M)^{n_1,\ldots,n_d}, i.e. with $n=(n_1,\ldots,n_d)$ the array +$=\mathcal N = (\mathcal M)^{n_1,…,n_d}$, i.e. with $n=(n_1,…,n_d)$ the array size of the power manifold and $d_{\mathcal M}$ the dimension of the base manifold $\mathcal M$, the manifold is of dimension ````math -d_{\mathcal N} = d_{\mathcal M}\prod_{i=1}^d n_i = n_1n_2\cdot\ldots\cdot n_d d_{\mathcal M}. +\dim(\mathcal N) = \dim(\mathcal M)\prod_{i=1}^d n_i = n_1n_2\cdot…\cdot n_d \dim(\mathcal M). ```` """ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} return manifold_dimension(M.manifold) * prod(size_to_tuple(TSize)) end -@doc doc""" - norm(M::AbstractPowerManifold, x, v) +@doc raw""" + norm(M::AbstractPowerManifold, p, X) -Compute the norm of `v` from the tangent space of `x` on an +Compute the norm of `X` from the tangent space of `p` on an [`AbstractPowerManifold`](@ref) `M`, i.e. from the element wise norms the Frobenius norm is computed. """ -function norm(M::AbstractPowerManifold, x, v) - sum_squares = zero(number_eltype(v)) +function norm(M::AbstractPowerManifold, p, X) + sum_squares = zero(number_eltype(X)) rep_size = representation_size(M.manifold) for i in get_iterator(M) sum_squares += - norm(M.manifold, _read(M, rep_size, x, i), _read(M, rep_size, v, i))^2 + norm(M.manifold, _read(M, rep_size, p, i), _read(M, rep_size, X, i))^2 end return sqrt(sum_squares) end function rand(rng::AbstractRNG, d::PowerFVectorDistribution) - fv = zero_vector(d.type, d.x) + fv = zero_vector(d.type, d.point) _rand!(rng, d, fv) return fv end function rand(rng::AbstractRNG, d::PowerPointDistribution) - x = allocate_result(d.manifold, rand, d.x) + x = allocate_result(d.manifold, rand, d.point) _rand!(rng, d, x) return x end function _rand!(rng::AbstractRNG, d::PowerFVectorDistribution, v::AbstractArray) - PM = d.type.M + PM = d.type.manifold rep_size = representation_size(PM.manifold) - for i in get_iterator(d.type.M) - copyto!(d.distribution.x, _read(PM, rep_size, d.x, i)) + for i in get_iterator(d.type.manifold) + copyto!(d.distribution.point, _read(PM, rep_size, d.point, i)) _rand!(rng, d.distribution, _read(PM, rep_size, v, i)) end return v @@ -597,7 +595,7 @@ function representation_size(M::PowerManifold{<:Manifold,TSize}) where {TSize} return (representation_size(M.manifold)..., size_to_tuple(TSize)...) end -@doc doc""" +@doc raw""" retract(M::AbstractPowerManifold, x, v, m::PowerRetraction) Compute the retraction from `x` with tangent vector `v` on an [`AbstractPowerManifold`](@ref) `M` @@ -607,40 +605,40 @@ method has to be one that is available on the base [`Manifold`](@ref). """ retract(::AbstractPowerManifold, ::Any...) -function retract!(M::AbstractPowerManifold, y, x, v, method::PowerRetraction) +function retract!(M::AbstractPowerManifold, q, p, X, method::PowerRetraction) rep_size = representation_size(M.manifold) for i in get_iterator(M) retract!( M.manifold, - _write(M, rep_size, y, i), - _read(M, rep_size, x, i), - _read(M, rep_size, v, i), + _write(M, rep_size, q, i), + _read(M, rep_size, p, i), + _read(M, rep_size, X, i), method.retraction, ) end - return y + return q end -@doc doc""" - sharp(M::AbstractPowerManifold, x, w::FVector{CotangentSpaceType}) +@doc raw""" + sharp(M::AbstractPowerManifold, p, ξ::FVector{CotangentSpaceType}) -Use the musical isomorphism to transform the cotangent vector `w` from the tangent space at -`x` on an [`AbstractPowerManifold`](@ref) `M` to a tangent vector. -This can be done elementwise, so for every entry of `w` (and `x`) sparately +Use the musical isomorphism to transform the cotangent vector `ξ` from the tangent space at +`p` on an [`AbstractPowerManifold`](@ref) `M` to a tangent vector. +This can be done elementwise for every entry of `ξ` (and `p`). """ sharp(::AbstractPowerManifold, ::Any...) -function sharp!(M::AbstractPowerManifold, v::TFVector, x, w::CoTFVector) +function sharp!(M::AbstractPowerManifold, X::TFVector, p, ξ::CoTFVector) rep_size = representation_size(M.manifold) for i in get_iterator(M) sharp!( M.manifold, - FVector(TangentSpace, _write(M, rep_size, v.data, i)), - _read(M, rep_size, x, i), - FVector(CotangentSpace, _read(M, rep_size, w.data, i)), + FVector(TangentSpace, _write(M, rep_size, X.data, i)), + _read(M, rep_size, p, i), + FVector(CotangentSpace, _read(M, rep_size, ξ.data, i)), ) end - return v + return X end function show( @@ -665,7 +663,7 @@ function allocate_result(M::PowerManifoldNested, f::typeof(sharp), w::CoTFVector return FVector(TangentSpace, alloc) end -support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type, tvd.x) +support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type, tvd.point) support(d::PowerPointDistribution) = MPointSupport(d.manifold) @inline function _write(M::AbstractPowerManifold, rep_size::Tuple, x::AbstractArray, i::Int) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 412e42d075..7ce47c05a2 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,7 +1,7 @@ -@doc doc""" +@doc raw""" ProductManifold{TM<:Tuple, TRanges<:Tuple, TSizes<:Tuple} <: Manifold -Product manifold $M_1 \times M_2 \times \dots \times M_n$ with product geometry. +Product manifold $M_1 × M_2 × … × M_n$ with product geometry. `TRanges` and `TSizes` statically define the relationship between representation of the product manifold and representations of point, tangent vectors and cotangent vectors of respective manifolds. @@ -10,7 +10,7 @@ and cotangent vectors of respective manifolds. ProductManifold(M_1, M_2, ..., M_n) -generates the product manifold $M_1 \times M_2 \times \dots \times M_n$. +generates the product manifold $M_1 × M_2 × … × M_n$. Alternatively, the same manifold can be contructed using the `×` operator: `M_1 × M_2 × M_3`. """ @@ -103,22 +103,22 @@ function PrecomputedProductOrthonormalBasis( end """ - check_manifold_point(M::ProductManifold, x; kwargs...) + check_manifold_point(M::ProductManifold, p; kwargs...) -Check whether `x` is a valid point on the [`ProductManifold`](@ref) `M`. +Check whether `p` is a valid point on the [`ProductManifold`](@ref) `M`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::ProductManifold, x::ProductRepr; kwargs...) - for t ∈ ziptuples(M.manifolds, submanifold_components(M, x)) +function check_manifold_point(M::ProductManifold, p::ProductRepr; kwargs...) + for t ∈ ziptuples(M.manifolds, submanifold_components(M, p)) err = check_manifold_point(t...; kwargs...) err === nothing || return err end return nothing end -function check_manifold_point(M::ProductManifold, x::ProductArray; kwargs...) - for t ∈ ziptuples(M.manifolds, submanifold_components(M, x)) +function check_manifold_point(M::ProductManifold, p::ProductArray; kwargs...) + for t ∈ ziptuples(M.manifolds, submanifold_components(M, p)) err = check_manifold_point(t...; kwargs...) err === nothing || return err end @@ -126,18 +126,18 @@ function check_manifold_point(M::ProductManifold, x::ProductArray; kwargs...) end """ - check_tangent_vector(M::ProductManifold, x, v; kwargs... ) + check_tangent_vector(M::ProductManifold, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` on the [`ProductManifold`](@ref) -`M`, i.e. atfer [`check_manifold_point`](@ref)`(M, x)`, and all projections to +Check whether `X` is a tangent vector to `p` on the [`ProductManifold`](@ref) +`M`, i.e. after [`check_manifold_point`](@ref)`(M, p)`, and all projections to base manifolds must be respective tangent vectors. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::ProductManifold, x::ProductRepr, v::ProductRepr; kwargs...) - perr = check_manifold_point(M, x) +function check_tangent_vector(M::ProductManifold, p::ProductRepr, X::ProductRepr; kwargs...) + perr = check_manifold_point(M, p) perr === nothing || return perr - ts = ziptuples(M.manifolds, submanifold_components(M, x), submanifold_components(M, v)) + ts = ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, X)) for t ∈ ts err = check_tangent_vector(t...; kwargs...) err === nothing || return err @@ -146,13 +146,13 @@ function check_tangent_vector(M::ProductManifold, x::ProductRepr, v::ProductRepr end function check_tangent_vector( M::ProductManifold, - x::ProductArray, - v::ProductArray; + p::ProductArray, + X::ProductArray; kwargs..., ) - perr = check_manifold_point(M, x) + perr = check_manifold_point(M, p) perr === nothing || return perr - ts = ziptuples(M.manifolds, submanifold_components(M, x), submanifold_components(M, v)) + ts = ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, X)) for t ∈ ts err = check_tangent_vector(t...; kwargs...) err === nothing || return err @@ -160,7 +160,7 @@ function check_tangent_vector( return nothing end -@doc doc""" +@doc raw""" cross(M,N) cross(M1, M2, M3,...) @@ -181,112 +181,113 @@ function cross(M1::ProductManifold, M2::ProductManifold) return ProductManifold(M1.manifolds..., M2.manifolds...) end -function det_local_metric(M::MetricManifold{ProductManifold,ProductMetric}, x::ProductArray) - dets = map(det_local_metric, M.manifolds, submanifold_components(M, x)) +function det_local_metric(M::MetricManifold{ProductManifold,ProductMetric}, p::ProductArray) + dets = map(det_local_metric, M.manifolds, submanifold_components(M, p)) return prod(dets) end -@doc doc""" - distance(M::ProductManifold, x, y) +@doc raw""" + distance(M::ProductManifold, p, q) -compute the distance two points `x` and `y` on the [`ProductManifold`](@ref) `M`, which is +Compute the distance between two points `p` and `q` on the [`ProductManifold`](@ref) `M`, which is the 2-norm of the elementwise distances on the internal manifolds that build `M`. """ -function distance(M::ProductManifold, x, y) +function distance(M::ProductManifold, p, q) return sqrt(sum( map( distance, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), ) .^ 2, )) end -@doc doc""" - exp(M::ProductManifold, x, v) +@doc raw""" + exp(M::ProductManifold, p, X) -compute the exponential map from `x` towards `v` on the [`ProductManifold`](@ref) `M`, +compute the exponential map from `p` in the direction of `X` on the [`ProductManifold`](@ref) `M`, which is the elementwise exponential map on the internal manifolds that build `M`. """ exp(::ProductManifold, ::Any...) -function exp(M::ProductManifold, x::ProductRepr, v::ProductRepr) +function exp(M::ProductManifold, p::ProductRepr, X::ProductRepr) return ProductRepr(map( exp, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, v), + submanifold_components(M, p), + submanifold_components(M, X), )...) end -function exp!(M::ProductManifold, y, x, v) +function exp!(M::ProductManifold, q, p, X) map( exp!, M.manifolds, - submanifold_components(M, y), - submanifold_components(M, x), - submanifold_components(M, v), + submanifold_components(M, q), + submanifold_components(M, p), + submanifold_components(M, X), ) - return y + return q end -@doc doc""" - flat(M::ProductManifold, x, w::FVector{TangentSpaceType}) +@doc raw""" + flat(M::ProductManifold, p, X::FVector{TangentSpaceType}) -use the musical isomorphism to transform the tangent vector `w` from the tangent space at -`x` on the [`ProductManifold`](@ref) `M` to a cotangent vector. -This can be done elementwise, so for every entry of `w` (and `x`) sparately +use the musical isomorphism to transform the tangent vector `X` from the tangent space at +`p` on the [`ProductManifold`](@ref) `M` to a cotangent vector. +This can be done elementwise for every entry of `X` (with respect to the corresponding +entry in `p`) separately. """ flat(::ProductManifold, ::Any...) -function flat!(M::ProductManifold, v::CoTFVector, x, w::TFVector) - vfs = map(u -> FVector(CotangentSpace, u), submanifold_components(v)) - wfs = map(u -> FVector(TangentSpace, u), submanifold_components(w)) - map(flat!, M.manifolds, vfs, submanifold_components(M, x), wfs) - return v +function flat!(M::ProductManifold, ξ::CoTFVector, p, X::TFVector) + vfs = map(u -> FVector(CotangentSpace, u), submanifold_components(ξ)) + wfs = map(u -> FVector(TangentSpace, u), submanifold_components(X)) + map(flat!, M.manifolds, vfs, submanifold_components(M, p), wfs) + return ξ end -function get_basis(M::ProductManifold, x, B::AbstractBasis) - parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(x))) +function get_basis(M::ProductManifold, p, B::AbstractBasis) + parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(p))) return PrecomputedProductOrthonormalBasis(parts) end -function get_basis(M::ProductManifold, x, B::DiagonalizingOrthonormalBasis) +function get_basis(M::ProductManifold, p, B::DiagonalizingOrthonormalBasis) vs = map(ziptuples( M.manifolds, - submanifold_components(x), - submanifold_components(B.v), + submanifold_components(p), + submanifold_components(B.frame_direction), )) do t return get_basis(t[1], t[2], DiagonalizingOrthonormalBasis(t[3])) end return PrecomputedProductOrthonormalBasis(vs) end -function get_basis(M::ProductManifold, x, B::ArbitraryOrthonormalBasis) - parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(x))) +function get_basis(M::ProductManifold, p, B::ArbitraryOrthonormalBasis) + parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(p))) return PrecomputedProductOrthonormalBasis(parts) end -function get_coordinates(M::ProductManifold, x, v, B::PrecomputedProductOrthonormalBasis) +function get_coordinates(M::ProductManifold, p, X, B::PrecomputedProductOrthonormalBasis) reps = map( get_coordinates, M.manifolds, - submanifold_components(x), - submanifold_components(v), + submanifold_components(p), + submanifold_components(X), B.parts, ) return vcat(reps...) end -function get_coordinates(M::ProductManifold, x, v, B::ArbitraryOrthonormalBasis) +function get_coordinates(M::ProductManifold, p, X, B::ArbitraryOrthonormalBasis) reps = map( t -> get_coordinates(t..., B), - ziptuples(M.manifolds, submanifold_components(x), submanifold_components(v)), + ziptuples(M.manifolds, submanifold_components(p), submanifold_components(X)), ) return vcat(reps...) end function get_vector( M::ProductManifold{<:NTuple{N,Any}}, - x::ProductRepr, - v, + p::ProductRepr, + X, B::PrecomputedProductOrthonormalBasis, ) where {N} dims = map(manifold_dimension, M.manifolds) @@ -294,8 +295,8 @@ function get_vector( parts = ntuple(N) do i get_vector( M.manifolds[i], - submanifold_component(x, i), - v[dims_acc[i]:dims_acc[i]+dims[i]-1], + submanifold_component(p, i), + X[dims_acc[i]:dims_acc[i]+dims[i]-1], B.parts[i], ) end @@ -303,8 +304,8 @@ function get_vector( end function get_vector( M::ProductManifold{<:NTuple{N,Any}}, - x::ProductRepr, - v, + p::ProductRepr, + X, B::ArbitraryOrthonormalBasis, ) where {N} dims = map(manifold_dimension, M.manifolds) @@ -312,8 +313,8 @@ function get_vector( parts = ntuple(N) do i get_vector( M.manifolds[i], - submanifold_component(x, i), - v[dims_acc[i]:dims_acc[i]+dims[i]-1], + submanifold_component(p, i), + X[dims_acc[i]:dims_acc[i]+dims[i]-1], B, ) end @@ -322,10 +323,10 @@ end function get_vectors( M::ProductManifold{<:NTuple{N,Manifold}}, - x::ProductRepr, + p::ProductRepr, B::PrecomputedProductOrthonormalBasis, ) where {N} - xparts = submanifold_components(x) + xparts = submanifold_components(p) BVs = map(t -> get_vectors(t...), ziptuples(M.manifolds, xparts, B.parts)) zero_tvs = map(t -> zero_tangent_vector(t...), ziptuples(M.manifolds, xparts)) vs = typeof(ProductRepr(zero_tvs...))[] @@ -335,120 +336,121 @@ function get_vectors( return vs end -function hat!(M::ProductManifold, v, x, vⁱ) +function hat!(M::ProductManifold, X, p, Xⁱ) dim = manifold_dimension(M) - @assert length(vⁱ) == dim + @assert length(Xⁱ) == dim i = one(dim) - ts = ziptuples(M.manifolds, submanifold_components(M, v), submanifold_components(M, x)) + ts = ziptuples(M.manifolds, submanifold_components(M, X), submanifold_components(M, p)) for t ∈ ts dim = manifold_dimension(first(t)) - tvⁱ = @inbounds view(vⁱ, i:(i+dim-1)) - hat!(t..., tvⁱ) + tXⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) + hat!(t..., tXⁱ) i += dim end - return v + return X end -@doc doc""" - injectivity_radius(M::ProductManifold[, x]) +@doc raw""" + injectivity_radius(M::ProductManifold) + injectivity_radius(M::ProductManifold, x) Compute the injectivity radius on the [`ProductManifold`](@ref), which is the minimum of the factor manifolds. """ injectivity_radius(::ProductManifold, ::Any...) -function injectivity_radius(M::ProductManifold, x) - return min(map(injectivity_radius, M.manifolds, submanifold_components(M, x))...) +function injectivity_radius(M::ProductManifold, p) + return min(map(injectivity_radius, M.manifolds, submanifold_components(M, p))...) end injectivity_radius(M::ProductManifold) = min(map(injectivity_radius, M.manifolds)...) -@doc doc""" - inner(M::ProductManifold, x, v, w) +@doc raw""" + inner(M::ProductManifold, p, X, Y) -compute the inner product of two tangent vectors `v`, `w` from the tangent space -at `x` on the [`ProductManifold`](@ref) `M`, which is just the sum of the +compute the inner product of two tangent vectors `X`, `Y` from the tangent space +at `p` on the [`ProductManifold`](@ref) `M`, which is just the sum of the internal manifolds that build `M`. """ -function inner(M::ProductManifold, x, v, w) +function inner(M::ProductManifold, p, X, Y) subproducts = map( inner, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, v), - submanifold_components(M, w), + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, Y), ) return sum(subproducts) end -@doc doc""" - inverse_retract(M::ProductManifold, x, y, m::InverseProductRetraction) +@doc raw""" + inverse_retract(M::ProductManifold, p, q, m::InverseProductRetraction) -Compute the inverse retraction from `x` with respect to `y` on the [`ProductManifold`](@ref) +Compute the inverse retraction from `p` with respect to `q` on the [`ProductManifold`](@ref) `M` using an [`InverseProductRetraction`](@ref), which by default encapsulates a inverse retraction for each manifold of the product. Then this method is performed elementwise, so the encapsulated inverse retraction methods have to be available per factor. """ inverse_retract(::ProductManifold, ::Any, ::Any, ::Any, ::InverseProductRetraction) -function inverse_retract!(M::ProductManifold, v, x, y, method::InverseProductRetraction) +function inverse_retract!(M::ProductManifold, X, p, q, method::InverseProductRetraction) map( inverse_retract!, M.manifolds, - submanifold_components(M, v), - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, X), + submanifold_components(M, p), + submanifold_components(M, q), method.inverse_retractions, ) - return v + return X end is_default_metric(::ProductManifold, ::ProductMetric) = Val(true) -function isapprox(M::ProductManifold, x, y; kwargs...) +function isapprox(M::ProductManifold, p, q; kwargs...) return all( t -> isapprox(t...; kwargs...), - ziptuples(M.manifolds, submanifold_components(M, x), submanifold_components(M, y)), + ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, q)), ) end -function isapprox(M::ProductManifold, x, v, w; kwargs...) +function isapprox(M::ProductManifold, p, X, Y; kwargs...) return all( t -> isapprox(t...; kwargs...), ziptuples( M.manifolds, - submanifold_components(M, x), - submanifold_components(M, v), - submanifold_components(M, w), + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, Y), ), ) end -@doc doc""" - log(M::ProductManifold, x, y) +@doc raw""" + log(M::ProductManifold, p, q) -Compute the logarithmic map from `x` to `y` on the [`ProductManifold`](@ref) `M`, +Compute the logarithmic map from `p` to `q` on the [`ProductManifold`](@ref) `M`, which can be computed using the logarithmic maps of the manifolds elementwise. """ log(::ProductManifold, ::Any...) -function log(M::ProductManifold, x::ProductRepr, y::ProductRepr) +function log(M::ProductManifold, p::ProductRepr, q::ProductRepr) return ProductRepr(map( log, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, p), + submanifold_components(M, q), )...) end -function log!(M::ProductManifold, v, x, y) +function log!(M::ProductManifold, X, p, q) map( log!, M.manifolds, - submanifold_components(M, v), - submanifold_components(M, x), - submanifold_components(M, y), + submanifold_components(M, X), + submanifold_components(M, p), + submanifold_components(M, q), ) - return v + return X end -@doc doc""" +@doc raw""" manifold_dimension(M::ProductManifold) Return the manifold dimension of the [`ProductManifold`](@ref), which is the sum of the @@ -456,19 +458,19 @@ manifold dimensions the product is made of. """ manifold_dimension(M::ProductManifold) = mapreduce(manifold_dimension, +, M.manifolds) -@doc doc""" - norm(M::PowerManifold, x, v) +@doc raw""" + norm(M::PowerManifold, p, X) -Compute the norm of `v` from the tangent space of `x` on the [`ProductManifold`](@ref), +Compute the norm of `X` from the tangent space of `p` on the [`ProductManifold`](@ref), i.e. from the element wise norms the 2-norm is computed. """ -function norm(M::ProductManifold, x, v) +function norm(M::ProductManifold, p, X) norms_squared = ( map( norm, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, v), + submanifold_components(M, p), + submanifold_components(M, X), ) .^ 2 ) return sqrt(sum(norms_squared)) @@ -476,12 +478,12 @@ end function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - x::Union{AbstractArray,MPoint,ProductRepr}, + p::Union{AbstractArray,MPoint,ProductRepr}, distributions::FVectorDistribution..., ) - return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(x)}( + return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( type, - x, + p, distributions, ) end @@ -489,18 +491,18 @@ function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, distributions::FVectorDistribution..., ) - x = ProductRepr(map(d -> support(d).x, distributions)) - return ProductFVectorDistribution(type, x, distributions...) + p = ProductRepr(map(d -> support(d).point, distributions)) + return ProductFVectorDistribution(type, p, distributions...) end function ProductFVectorDistribution(distributions::FVectorDistribution...) - M = ProductManifold(map(d -> support(d).space.M, distributions)...) - VS = support(distributions[1]).space.VS - if !all(d -> support(d).space.VS == VS, distributions) + M = ProductManifold(map(d -> support(d).space.manifold, distributions)...) + fiber = support(distributions[1]).space.fiber + if !all(d -> support(d).space.fiber == fiber, distributions) error("Not all distributions have support in vector spaces of the same type, which is currently not supported") end # Probably worth considering sum spaces in the future? - x = ProductRepr(map(d -> support(d).x, distributions)...) - return ProductFVectorDistribution(VectorBundleFibers(VS, M), x, distributions...) + x = ProductRepr(map(d -> support(d).point, distributions)...) + return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) end function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) @@ -521,62 +523,62 @@ end function _rand!(rng::AbstractRNG, d::ProductPointDistribution, x::AbstractArray{<:Number}) return copyto!(x, rand(rng, d)) end -function _rand!(rng::AbstractRNG, d::ProductPointDistribution, x::ProductRepr) +function _rand!(rng::AbstractRNG, d::ProductPointDistribution, p::ProductRepr) map( t -> _rand!(rng, t[1], t[2]), d.distributions, - submanifold_components(d.manifold, x), + submanifold_components(d.manifold, p), ) - return x + return p end function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, v::AbstractArray{<:Number}) return copyto!(v, rand(rng, d)) end -function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, v::ProductRepr) - map(t -> _rand!(rng, t[1], t[2]), d.distributions, submanifold_components(d.space.M, v)) - return v +function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, X::ProductRepr) + map(t -> _rand!(rng, t[1], t[2]), d.distributions, submanifold_components(d.space.manifold, X)) + return X end -@doc doc""" - retract(M::ProductManifold, x, v, m::ProductRetraction) +@doc raw""" + retract(M::ProductManifold, p, X, m::ProductRetraction) -Compute the retraction from `x` with tangent vector `v` on the [`ProductManifold`](@ref) `M` +Compute the retraction from `p` with tangent vector `X` on the [`ProductManifold`](@ref) `M` using an [`ProductRetraction`](@ref), which by default encapsulates retractions of the base manifolds. Then this method is performed elementwise, so the encapsulated retractions method has to be one that is available on the manifolds. """ retract(::ProductManifold, ::Any...) -function retract!(M::ProductManifold, y, x, v, method::ProductRetraction) +function retract!(M::ProductManifold, q, p, X, method::ProductRetraction) map( retract!, M.manifolds, - submanifold_components(M, y), - submanifold_components(M, x), - submanifold_components(M, v), + submanifold_components(M, q), + submanifold_components(M, p), + submanifold_components(M, X), method.retractions, ) - return y + return q end function representation_size(M::ProductManifold) return (mapreduce(m -> prod(representation_size(m)), +, M.manifolds),) end -@doc doc""" - sharp(M::ProductManifold, x, w::FVector{CotangentSpaceType}) +@doc raw""" + sharp(M::ProductManifold, p, ξ::FVector{CotangentSpaceType}) -Use the musical isomorphism to transform the cotangent vector `w` from the tangent space at -`x` on the [`ProductManifold`](@ref) `M` to a tangent vector. -This can be done elementwise, so vor every entry of `w` (and `x`) sparately +Use the musical isomorphism to transform the cotangent vector `ξ` from the tangent space at +`p` on the [`ProductManifold`](@ref) `M` to a tangent vector. +This can be done elementwise for every entry of `ξ` (and `p`) separately """ sharp(::ProductManifold, ::Any...) -function sharp!(M::ProductManifold, v::TFVector, x, w::CoTFVector) - vfs = map(u -> FVector(TangentSpace, u), submanifold_components(v)) - wfs = map(u -> FVector(CotangentSpace, u), submanifold_components(w)) - map(sharp!, M.manifolds, vfs, submanifold_components(M, x), wfs) - return v +function sharp!(M::ProductManifold, X::TFVector, p, ξ::CoTFVector) + vfs = map(u -> FVector(TangentSpace, u), submanifold_components(X)) + wfs = map(u -> FVector(CotangentSpace, u), submanifold_components(ξ)) + map(sharp!, M.manifolds, vfs, submanifold_components(M, p), wfs) + return X end function _show_submanifold(io::IO, M::Manifold; pre = "") @@ -655,31 +657,31 @@ support(d::ProductPointDistribution) = MPointSupport(d.manifold) function support(tvd::ProductFVectorDistribution) return FVectorSupport( tvd.type, - ProductRepr(map(d -> support(d).x, tvd.distributions)...), + ProductRepr(map(d -> support(d).point, tvd.distributions)...), ) end -function vee!(M::ProductManifold, vⁱ, x, v) +function vee!(M::ProductManifold, Xⁱ, p, X) dim = manifold_dimension(M) - @assert length(vⁱ) == dim + @assert length(Xⁱ) == dim i = one(dim) - ts = ziptuples(M.manifolds, submanifold_components(M, x), submanifold_components(M, v)) + ts = ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, X)) for t ∈ ts SM = first(t) dim = manifold_dimension(SM) - tvⁱ = @inbounds view(vⁱ, i:(i+dim-1)) - vee!(SM, tvⁱ, Base.tail(t)...) + tXⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) + vee!(SM, tXⁱ, Base.tail(t)...) i += dim end - return vⁱ + return Xⁱ end -function zero_tangent_vector!(M::ProductManifold, v, x) +function zero_tangent_vector!(M::ProductManifold, X, p) map( zero_tangent_vector!, M.manifolds, - submanifold_components(M, v), - submanifold_components(M, x), + submanifold_components(M, X), + submanifold_components(M, p), ) - return v + return X end diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index fd10a0bed7..3f035e5523 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,14 +1,15 @@ -@doc doc""" +@doc raw""" Rotations{N} <: Manifold -Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n\times n$ -real-valued orthogonal matrices with determinant $+1$. +The special orthogonal manifold $\mathrm{SO}(n)$ represented by $n × n$ +real-valued orthogonal matrices with determinant $+1$ is the manifold of `Rotations`, +since these matrices represent all rotations of points in $ℝ^n$. # Constructor Rotations(n) -Generate the $\mathrm{SO}(n) \subset \mathbb R^{n\times n}$ +Generate the $\mathrm{SO}(n) \subset ℝ^{n × n}$ """ struct Rotations{N} <: Manifold end @@ -37,12 +38,12 @@ function NormalRotationDistribution( return NormalRotationDistribution{TResult,typeof(M),typeof(d)}(M, d) end -@doc doc""" +@doc raw""" angles_4d_skew_sym_matrix(A) -The Lie algebra of $\mathrm{SO}(4)$ consists of 4x4 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 +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`. By convention, the returned values are sorted in decreasing order @@ -60,56 +61,56 @@ function angles_4d_skew_sym_matrix(A) end """ - check_manifold_point(M,x; kwargs...) + check_manifold_point(M, p; kwargs...) -Check whether `x` is a valid point on the [`Rotations`](@ref) `M`, +Check whether `p` is a valid point on the [`Rotations`](@ref) `M`, i.e. is an array of size [`manifold_dimension`](@ref)`(M)` and represents a valid rotation. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::Rotations{N}, x; kwargs...) where {N} - if size(x) != (N, N) +function check_manifold_point(M::Rotations{N}, p; kwargs...) where {N} + if size(p) != (N, N) return DomainError( - size(x), - "The point $(x) does not lie on $M, since its size is not $((N, N)).", + size(p), + "The point $(p) does not lie on $M, since its size is not $((N, N)).", ) end - if !isapprox(det(x), 1; kwargs...) - return DomainError(det(x), "The determinant of $x has to be +1 but it is $(det(x))") + if !isapprox(det(p), 1; kwargs...) + return DomainError(det(p), "The determinant of $p has to be +1 but it is $(det(p))") end - if !isapprox(transpose(x) * x, one(x); kwargs...) - return DomainError(norm(x), "$x has to be orthogonal but it's not") + if !isapprox(transpose(p) * p, one(p); kwargs...) + return DomainError(norm(p), "$p has to be orthogonal but it's not") end return nothing end """ - check_tangent_vector(M,x,v; kwargs... ) + check_tangent_vector(M, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` on the [`Rotations`](@ref) -space `M`, i.e. after [`check_manifold_point`](@ref)`(M,x)`, `v` has to be of same -dimension as `x` and orthogonal to `x`. +Check whether `X` is a tangent vector to `p` on the [`Rotations`](@ref) +space `M`, i.e. after [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same +dimension and orthogonal to `p`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::Rotations{N}, x, v; kwargs...) where {N} - perr = check_manifold_point(M, x) +function check_tangent_vector(M::Rotations{N}, p, X; kwargs...) where {N} + perr = check_manifold_point(M, p) perr === nothing || return perr - if size(v) != (N, N) + if size(X) != (N, N) return DomainError( - size(v), - "The array $(v) is not a tangent to a point on $M since its size does not match $((N, N)).", + size(X), + "The array $(X) is not a tangent to a point on $M since its size does not match $((N, N)).", ) end - if !isapprox(transpose(v) + v, zero(v); kwargs...) + if !isapprox(transpose(X) + X, zero(X); kwargs...) return DomainError( - size(v), - "The array $(v) is not a tangent to a point on $M since it is not skew-symmetric.", + size(X), + "The array $(X) is not a tangent to a point on $M since it is not skew-symmetric.", ) end return nothing end -@doc doc""" +@doc raw""" cos_angles_4d_rotation_matrix(R) 4D rotations can be described by two orthogonal planes that are unchanged by @@ -127,8 +128,7 @@ matrix. This function computes these more efficiently by solving the system ``` By convention, the returned values are sorted in increasing order. See -[`angles_4d_skew_sym_matrix`](@ref). For derivation of the above, see -[[Gallier, 2013]](#Gallier2003). +[`angles_4d_skew_sym_matrix`](@ref). """ function cos_angles_4d_rotation_matrix(R) trR = tr(R) @@ -137,54 +137,56 @@ function cos_angles_4d_rotation_matrix(R) return (a + b, a - b) end -@doc doc""" - exp(M::Rotations, x, v) +@doc raw""" + exp(M::Rotations, p, X) + +Compute the exponential map on the [`Rotations`](@ref) from `p` into direction +`X`, i.e. -Compute the exponential map on the [`Rotations`](@ref) from `x` into direction -`v`, i.e. ````math -\exp_xv = x \operatorname{Exp}(v), +\exp_p X = p \operatorname{Exp}(X), ```` -where $\operatorname{Exp}(v)$ denotes the matrix exponential of $v$. +where $\operatorname{Exp}(X)$ denotes the matrix exponential of $X$. - exp(M::Rotations{4}, x, v) + exp(M::Rotations{4}, p, X) -Compute the exponential map of tangent vector `v` at point `x` from $\mathrm{SO}(4)$ +Compute the exponential map of tangent vector `X` at point `p` from $\mathrm{SO}(4)$ manifold `M`. The algorithm used is a more numerically stable form of those proposed in [^Gallier2002] and [^Andrica2013]. [^Gallier2002]: -> Gallier J.; Xu D.; Computing exponentials of skew-symmetric matrices -> and logarithms of orthogonal matrices. -> International Journal of Robotics and Automation (2002), 17(4), pp. 1-11. -> [pdf](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.3205). + > Gallier J.; Xu D.; Computing exponentials of skew-symmetric matrices + > and logarithms of orthogonal matrices. + > International Journal of Robotics and Automation (2002), 17(4), pp. 1-11. + > [pdf](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.3205). + [^Andrica2013]: -> Andrica D.; Rohan R.-A.; Computing the Rodrigues coefficients of the -> exponential map of the Lie groups of matrices. -> Balkan Journal of Geometry and Its Applications (2013), 18(2), pp. 1-2. -> [pdf](https://www.emis.de/journals/BJGA/v18n2/B18-2-an.pdf). + > Andrica D.; Rohan R.-A.; Computing the Rodrigues coefficients of the + > exponential map of the Lie groups of matrices. + > Balkan Journal of Geometry and Its Applications (2013), 18(2), pp. 1-2. + > [pdf](https://www.emis.de/journals/BJGA/v18n2/B18-2-an.pdf). """ exp(::Rotations, ::Any...) -exp!(M::Rotations, y, x, v) = copyto!(y, x * exp(v)) -function exp!(M::Rotations{2}, y, x, v) - θ = vee(M, x, v)[1] - @assert size(y) == (2, 2) - @assert size(x) == (2, 2) +exp!(M::Rotations, q, p, X) = copyto!(q, p * exp(X)) +function exp!(M::Rotations{2}, q, p, X) + θ = vee(M, p, X)[1] + @assert size(q) == (2, 2) + @assert size(p) == (2, 2) @inbounds begin sinθ, cosθ = sincos(θ) - y[1] = x[1] * cosθ + x[3] * sinθ - y[2] = x[2] * cosθ + x[4] * sinθ - y[3] = x[3] * cosθ - x[1] * sinθ - y[4] = x[4] * cosθ - x[2] * sinθ + q[1] = p[1] * cosθ + p[3] * sinθ + q[2] = p[2] * cosθ + p[4] * sinθ + q[3] = p[3] * cosθ - p[1] * sinθ + q[4] = p[4] * cosθ - p[2] * sinθ end - return y + return q end -function exp!(M::Rotations{3}, y, x, v) - θ = norm(M, x, v) / sqrt(2) +function exp!(M::Rotations{3}, q, p, X) + θ = norm(M, p, X) / sqrt(2) if θ ≈ 0 a = 1 - θ^2 / 6 b = θ / 2 @@ -192,12 +194,12 @@ function exp!(M::Rotations{3}, y, x, v) a = sin(θ) / θ b = (1 - cos(θ)) / θ^2 end - y .= x .+ x * (a .* v .+ b .* (v^2)) - return y + q .= p .+ p * (a .* X .+ b .* (X^2)) + return q end -function exp!(M::Rotations{4}, y, x, v) - T = eltype(v) - α, β = angles_4d_skew_sym_matrix(v) +function exp!(M::Rotations{4}, q, p, X) + T = eltype(X) + α, β = angles_4d_skew_sym_matrix(X) sinα, cosα = sincos(α) sinβ, cosβ = sincos(β) α² = α^2 @@ -230,135 +232,138 @@ function exp!(M::Rotations{4}, y, x, v) a₂ = (sincα - (1 - r) / 2 * cosα) * c a₃ = (e + (1 - r) * (e - sincα / 2)) * c end - - v² = v * v - y .= a₀ .* x .+ x * (a₁ .* v .+ a₂ .* v² .+ a₃ .* (v² * v)) - return y + X² = X * X + q .= a₀ .* p .+ p * (a₁ .* X .+ a₂ .* X² .+ a₃ .* (X² * X)) + return q end -flat!(M::Rotations, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(M::Rotations, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) -function get_coordinates(M::Rotations, x, v, B::ArbitraryOrthonormalBasis) where {N} - T = Base.promote_eltype(x, v) - return vee(M, x, v) .* sqrt(T(2)) +function get_coordinates(M::Rotations, p, X, B::ArbitraryOrthonormalBasis) where {N} + T = Base.promote_eltype(p, X) + return vee(M, p, X) .* sqrt(T(2)) end -function get_vector(M::Rotations, x, v, B::ArbitraryOrthonormalBasis) where {N} - T = Base.promote_eltype(x, v) - return hat(M, x, v) ./ sqrt(T(2)) -end - -function hat!(M::Rotations{2}, Ω, x, θ::Real) - @assert length(Ω) == 4 - @inbounds begin - Ω[1] = 0 - Ω[3] = -θ - Ω[2] = θ - Ω[4] = 0 - end - return Ω +function get_vector(M::Rotations, p, X, B::ArbitraryOrthonormalBasis) where {N} + T = Base.promote_eltype(p, X) + return hat(M, p, X) ./ sqrt(T(2)) end -hat!(M::Rotations{2}, Ω, x, ω) = hat!(M, Ω, x, ω[1]) -@doc doc""" - hat(M::Rotations, x, ω) +@doc raw""" + hat(M::Rotations, p, Xⁱ) -Convert the unique tangent vector components $\omega$ at point $x$ on rotations +Convert the unique tangent vector components `Xⁱ` at point `p` on [`Rotations`](@ref) group $\mathrm{SO}(n)$ to the matrix representation $\Omega$ of the tangent vector. See [`vee`](@ref) for the conventions used. """ -hat(M::Rotations, ::Any...) +hat(::Rotations, ::Any...) -function hat!(M::Rotations{N}, Ω, x, ω) where {N} - @assert size(Ω) == (N, N) - @assert length(ω) == manifold_dimension(M) +function hat!(M::Rotations{2}, X, p, Xⁱ::Real) + @assert length(X) == 4 @inbounds begin - Ω[1, 1] = 0 - Ω[1, 2] = -ω[3] - Ω[1, 3] = ω[2] - Ω[2, 1] = ω[3] - Ω[2, 2] = 0 - Ω[2, 3] = -ω[1] - Ω[3, 1] = -ω[2] - Ω[3, 2] = ω[1] - Ω[3, 3] = 0 + X[1] = 0 + X[3] = -Xⁱ + X[2] = Xⁱ + X[4] = 0 + end + return X +end +hat!(M::Rotations{2}, X, p, Xⁱ) = hat!(M, X, p, Xⁱ[1]) +function hat!(M::Rotations{N}, X, p, Xⁱ) where {N} + @assert size(X) == (N, N) + @assert length(Xⁱ) == manifold_dimension(M) + @inbounds begin + X[1, 1] = 0 + X[1, 2] = -Xⁱ[3] + X[1, 3] = Xⁱ[2] + X[2, 1] = Xⁱ[3] + X[2, 2] = 0 + X[2, 3] = -Xⁱ[1] + X[3, 1] = -Xⁱ[2] + X[3, 2] = Xⁱ[1] + X[3, 3] = 0 k = 4 for i = 4:N for j = 1:i-1 - Ω[i, j] = ω[k] - Ω[j, i] = -ω[k] + X[i, j] = Xⁱ[k] + X[j, i] = -Xⁱ[k] k += 1 end - Ω[i, i] = 0 + X[i, i] = 0 end end - return Ω + return X end -@doc doc""" +@doc raw""" injectivity_radius(M::Rotations) - injectivity_radius(M::Rotations, x) + injectivity_radius(M::Rotations, p) Return the injectivity radius on the [`Rotations`](@ref) `M`, which is globally ````math - \operatorname{inj}_{\mathrm{SO}(n)}(x) = \pi\sqrt{2}. + \operatorname{inj}_{\mathrm{SO}(n)}(x) = π\sqrt{2}. ```` - injectivity_radius(M::Rotations, x, ::PolarRetraction) + injectivity_radius(M::Rotations, p, ::PolarRetraction) Return the radius of injectivity for the [`PolarRetraction`](@ref) on the -[`Rotations`](@ref) `M` which is $\frac{\pi}{\sqrt{2}}$. +[`Rotations`](@ref) `M` which is $\frac{π}{\sqrt{2}}$. """ injectivity_radius(::Rotations) = π * sqrt(2.0) -injectivity_radius(::Rotations, x, ::PolarRetraction) = π / sqrt(2.0) +injectivity_radius(::Rotations, p, ::PolarRetraction) = π / sqrt(2.0) -@doc doc""" - inner(M::Rotations, x, w, v) +@doc raw""" + inner(M::Rotations, p, X, Y) -Compute the inner product of the two tangent vectors `w, v` from the tangent -plane at `x` on the special orthogonal space `M=`$\mathrm{SO}(n)$ using the +Compute the inner product of the two tangent vectors `X`, `Y` from the tangent +plane at `p` on the special orthogonal space `M=`$\mathrm{SO}(n)$ using the restriction of the metric from the embedding, i.e. -$(v, w)_x = \operatorname{tr}(v^T w)$. +````math +g_p(X, Y) = \operatorname{tr}(v^T w), +```` Tangent vectors are represented by matrices. """ -inner(M::Rotations, x, w, v) = dot(w, v) +inner(M::Rotations, p, X, Y) = dot(X, Y) -@doc doc""" - inverse_retract(M, x, y, ::PolarInverseRetraction) +@doc raw""" + inverse_retract(M, p, q, ::PolarInverseRetraction) -Compute a vector from the tangent space $T_x\mathrm{SO}(n)$ -of the point `x` on the [`Rotations`](@ref) manifold `M` -with which the point `y` can be reached by the -[`PolarRetraction`](@ref) from the point `x` after time 1. +Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ +of the point `p` on the [`Rotations`](@ref) manifold `M` +with which the point `q` can be reached by the +[`PolarRetraction`](@ref) from the point `p` after time 1. The formula reads ````math -\operatorname{retr}^{-1}_x(y) -= -\frac{1}{2}(x^{\mathrm{T}}ys - (x^{\mathrm{T}}ys)^{\mathrm{T}}) +\operatorname{retr}^{-1}_p(q) += -\frac{1}{2}(p^{\mathrm{T}}qs - (p^{\mathrm{T}}qs)^{\mathrm{T}}) ```` where $s$ is the solution to the Sylvester equation -$x^{\mathrm{T}}ys + s(x^{\mathrm{T}}y)^{\mathrm{T}} + 2\mathrm{I}_n = 0.$ +$p^{\mathrm{T}}qs + s(p^{\mathrm{T}}q)^{\mathrm{T}} + 2I_n = 0.$ +""" +inverse_retract(::Rotations, ::Any, ::Any, ::PolarInverseRetraction) - inverse_retract(M::Rotations, x, y, ::QRInverseRetraction) +@doc raw""" + inverse_retract(M::Rotations, p, q, ::QRInverseRetraction) -Compute a vector from the tangent space $T_x\mathrm{SO}(n)$ of the point `x` on the -[`Rotations`](@ref) manifold `M` with which the point `y` can be reached by the -[`QRRetraction`](@ref) from the point `x` after time 1. +Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ of the point `p` on the +[`Rotations`](@ref) manifold `M` with which the point `q` can be reached by the +[`QRRetraction`](@ref) from the point `q` after time 1. """ -inverse_retract(::Rotations, ::Any, ::Any, ::PolarInverseRetraction) +inverse_retract(::Rotations, ::Any, ::Any, ::QRInverseRetraction) -function inverse_retract!(M::Rotations, v, x, y, method::PolarInverseRetraction) - A = transpose(x) * y - H = 2 * one(x) +function inverse_retract!(M::Rotations, X, p, q, method::PolarInverseRetraction) + A = transpose(p) * q + H = 2 * one(p) try B = sylvester(collect(A), collect(transpose(A)), collect(H)) C = A * B - v .= (transpose(C) .- C) ./ 2 + X .= (transpose(C) .- C) ./ 2 catch e if isa(e, LinearAlgebra.LAPACKException) throw(OutOfInjectivityRadiusError()) @@ -366,11 +371,11 @@ function inverse_retract!(M::Rotations, v, x, y, method::PolarInverseRetraction) rethrow() end end - return v + return X end -function inverse_retract!(M::Rotations{N}, v, x, y, ::QRInverseRetraction) where {N} - A = transpose(x) * y - R = zero(v) +function inverse_retract!(M::Rotations{N}, X, p, q, ::QRInverseRetraction) where {N} + A = transpose(p) * q + R = zero(X) for i = 1:N b = zeros(i) b[end] = 1 @@ -378,53 +383,53 @@ function inverse_retract!(M::Rotations{N}, v, x, y, ::QRInverseRetraction) where R[1:i, i] = A[1:i, 1:i] \ b end C = A * R - v .= (C .- transpose(C)) ./ 2 - return v + X .= (C .- transpose(C)) ./ 2 + return X end -@doc doc""" - log(M::Rotations, x, y) +@doc raw""" + log(M::Rotations, p, q) Compute the logarithmic map on the [`Rotations`](@ref) manifold `M`$=\mathrm{SO}(n)$, which is given by ```math -\log_{x} y = - \frac{1}{2} \bigl(\operatorname{Log}(x^{\mathrm{T}}y) - - (\operatorname{Log} x^{\mathrm{T}}y)^{\mathrm{T}}), +\log_p q = + \frac{1}{2} \bigl(\operatorname{Log}(p^{\mathrm{T}}q) + - (\operatorname{Log} p^{\mathrm{T}}q)^{\mathrm{T}}), ``` where $\operatorname{Log}$ denotes the matrix logarithm. For antipodal rotations the function returns deterministically one of the tangent vectors -that point at `y`. +that point at `q`. """ log(::Rotations, ::Any...) -function log!(M::Rotations, v, x, y) - U = transpose(x) * y - v .= real(log_safe(U)) - return project_tangent!(M, v, x, v) +function log!(M::Rotations, X, p, q) + U = transpose(p) * q + X .= real(log_safe(U)) + return project_tangent!(M, X, p, X) end -function log!(M::Rotations{2}, v, x, y) - U = transpose(x) * y +function log!(M::Rotations{2}, X, p, q) + U = transpose(p) * q @assert size(U) == (2, 2) @inbounds θ = atan(U[2], U[1]) - return hat!(M, v, x, θ) + return hat!(M, X, p, θ) end -function log!(M::Rotations{3}, v, x, y) - U = transpose(x) * y +function log!(M::Rotations{3}, X, p, q) + U = transpose(p) * q cosθ = (tr(U) - 1) / 2 if cosθ ≈ -1 eig = eigen_safe(U) ival = findfirst(λ -> isapprox(λ, 1), eig.values) vi = SVector{3}(1:3) ax = eig.vectors[vi, ival] - return hat!(M, v, x, π * ax) + return hat!(M, X, p, π * ax) end - v .= ((U .- transpose(U)) ./ (2 * usinc_from_cos(cosθ))) - return v + X .= ((U .- transpose(U)) ./ (2 * usinc_from_cos(cosθ))) + return X end -function log!(M::Rotations{4}, v, x, y) - U = transpose(x) * y +function log!(M::Rotations{4}, X, p, q) + U = transpose(p) * q cosα, cosβ = cos_angles_4d_rotation_matrix(U) α = acos(clamp(cosα, -1, 1)) β = acos(clamp(cosβ, -1, 1)) @@ -438,17 +443,17 @@ function log!(M::Rotations{4}, v, x, y) E[2, 1] = -α E[1, 2] = α end - copyto!(v, P * E * transpose(P)) + copyto!(X, P * E * transpose(P)) else - copyto!(v, real(log_safe(U))) + copyto!(X, real(log_safe(U))) end - return project_tangent!(M, v, x, v) + return project_tangent!(M, X, p, X) end -@doc doc""" +@doc raw""" manifold_dimension(M::Rotations) -Return the dimension of the manifold $\mathrm{SO}(n)$, i.e. $\frac{n(n-1)}{2}$. +Return the dimension of the manifold $\mathrm{SO}(n)$, i.e. $\dim(\mathrm{SO}(n)) = \frac{n(n-1)}{2}$. """ manifold_dimension(M::Rotations{N}) where {N} = div(N * (N - 1), 2) @@ -466,27 +471,27 @@ Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using """ mean(::Rotations, ::Any) -function mean!(M::Rotations, y, x::AbstractVector, w::AbstractVector; kwargs...) - return mean!(M, y, x, w, GeodesicInterpolationWithinRadius(π / 2 / √2); kwargs...) +function mean!(M::Rotations, q, x::AbstractVector, w::AbstractVector; kwargs...) + return mean!(M, q, x, w, GeodesicInterpolationWithinRadius(π / 2 / √2); kwargs...) end -@doc doc""" - norm(M::Rotations, x, v) +@doc raw""" + norm(M::Rotations, p, X) -Compute the norm of a tangent vector `v` from the tangent space at `x` on the +Compute the norm of a tangent vector `X` from the tangent space at `p` on the [`Rotations`](@ref) `M`. The formula reads ````math -\lVert v \rVert_x = \lVert v \rVert, +\lVert X \rVert_x = \lVert X \rVert, ```` -i.e. the Frobenius norm of `v`, where tangent vectors are represented by -elements from the Lie group. +i.e. the Frobenius norm of `X`, where tangent vectors are represented by +elements from the Lie algebra. """ -norm(M::Rotations, x, v) = norm(v) +norm(M::Rotations, p, X) = norm(X) -@doc doc""" - normal_rotation_distribution(M::Rotations, x, σ::Real) +@doc raw""" + normal_rotation_distribution(M::Rotations, p, σ::Real) Return a random point on the manifold [`Rotations`](@ref) `M` by generating a (Gaussian) random orthogonal matrix with determinant $+1$. Let @@ -495,7 +500,7 @@ $QR = A$ be the QR decomposition of a random matrix $A$, then the formula reads -$x = QD$ +$p = QD$ where $D$ is a diagonal matrix with the signs of the diagonal entries of $R$, i.e. @@ -505,70 +510,71 @@ $D_{ij}=\begin{cases} \operatorname{sgn}(R_{ij}) & \text{if} \; i=j \\ 0 & \, \t It can happen that the matrix gets -1 as a determinant. In this case, the first and second columns are swapped. -The argument `x` is used to determine the type of returned points. +The argument `p` is used to determine the type of returned points. """ -function normal_rotation_distribution(M::Rotations{N}, x, σ::Real) where {N} +function normal_rotation_distribution(M::Rotations{N}, p, σ::Real) where {N} d = Distributions.MvNormal(zeros(N * N), σ) - return NormalRotationDistribution(M, d, x) + return NormalRotationDistribution(M, d, p) end """ - normal_tvector_distribution(M::Rotations, x, σ) + normal_tvector_distribution(M::Rotations, p, σ) Normal distribution in ambient space with standard deviation `σ` -projected to tangent space at `x`. +projected to tangent space at `p`. """ -function normal_tvector_distribution(M::Rotations, x, σ) - d = Distributions.MvNormal(reshape(zero(x), :), σ) - return ProjectedFVectorDistribution(TangentBundleFibers(M), x, d, project_vector!, x) +function normal_tvector_distribution(M::Rotations, p, σ) + d = Distributions.MvNormal(reshape(zero(p), :), σ) + return ProjectedFVectorDistribution(TangentBundleFibers(M), p, d, project_vector!, p) end -@doc doc""" - project_point(M::Rotations, x; check_det = true) +@doc raw""" + project_point(M::Rotations, p; check_det = true) -Project `x` to the nearest point on manifold `M`. +Project `p` to the nearest point on manifold `M`. -Given the singular value decomposition $x = U \Sigma V^\mathrm{T}$, with the +Given the singular value decomposition $p = U Σ V^\mathrm{T}$, with the singular values sorted in descending order, the projection is ````math -\operatorname{proj}_{\mathrm{SO}(n)}(x) = -U\operatorname{diag}\left[1,1,\dots,\det(U V^\mathrm{T})\right] V^\mathrm{T} +\operatorname{proj}_{\mathrm{SO}(n)}(p) = +U\operatorname{diag}\left[1,1,…,\det(U V^\mathrm{T})\right] V^\mathrm{T} ```` The diagonal matrix ensures that the determinant of the result is $+1$. -If `x` is expected to be almost special orthogonal, then you may avoid this +If `p` is expected to be almost special orthogonal, then you may avoid this check with `check_det = false`. """ project_point(::Rotations, ::Any...) -function project_point!(M::Rotations{N}, y, x; check_det = true) where {N} - F = svd(x) - copyto!(y, F.U * F.Vt) - if check_det && det(y) < 0 +function project_point!(M::Rotations{N}, q, p; check_det = true) where {N} + F = svd(p) + copyto!(q, F.U * F.Vt) + if check_det && det(q) < 0 d = similar(F.S) @inbounds fill!(view(d, 1:N-1), 1) @inbounds d[N] = -1 - copyto!(y, F.U * Diagonal(d) * F.Vt) + copyto!(q, F.U * Diagonal(d) * F.Vt) end - return y + return q end -@doc doc""" - project_tangent(M::Rotations, x, v) +@doc raw""" + project_tangent(M::Rotations, p, X) -Project the matrix `v` onto the tangent space by making `v` skew symmetric, +Project the matrix `X` onto the tangent space by making `X` skew symmetric, ````math -\operatorname{proj}_x(v) = \frac{v-v^{\mathrm{T}}}{2}, +\operatorname{proj}_p(X) = \frac{X-X^{\mathrm{T}}}{2}, ```` + where tangent vectors are represented by elements from the Lie group """ project_tangent(::Rotations, ::Any...) -project_tangent!(M::Rotations, w, x, v) = (w .= (v .- transpose(v)) ./ 2) +project_tangent!(M::Rotations, Y, p, X) = (Y .= (X .- transpose(X)) ./ 2) -@doc doc""" +@doc raw""" representation_size(M::Rotations) Return the `size()` of a point on the [`Rotations`](@ref) `M`, i.e. for the @@ -576,7 +582,7 @@ $\mathrm{SO}(n)$ it's `(n,n)`. """ @generated representation_size(::Rotations{N}) where {N} = (N, N) -sharp!(M::Rotations, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::Rotations, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) function rand( rng::AbstractRNG, @@ -608,94 +614,96 @@ function _fix_random_rotation(A::AbstractMatrix) return C end -@doc doc""" - retract(M, x, v) - retract(M, x, v, ::QRRetraction) - -Compute the SVD-based retraction on the [`Rotations`](@ref) `M` from `x` in direction `v` -(as an element of the Lie group) and is a first-order approximation of the exponential map. - -This is also the default retraction on the [`Rotations`](@ref) - - retract(M::Rotations, x, v, ::PolarRetraction) +@doc raw""" + retract(M::Rotations, p, X, ::PolarRetraction) -Compute the SVD-based retraction on the [`Rotations`](@ref) `M` from `x` in direction `v` +Compute the SVD-based retraction on the [`Rotations`](@ref) `M` from `p` in direction `X` (as an element of the Lie group) and is a second-order approximation of the exponential map. Let ````math -USV = x + xv +USV = p + pX ```` be the singular value decomposition, then the formula reads ````math -\operatorname{retr}_x v = UV^\mathrm{T}. +\operatorname{retr}_p X = UV^\mathrm{T}. ```` """ -retract(::Rotations, ::Any...) +retract(::Rotations, ::Any, ::Any, ::PolarRetraction) + +@doc raw""" + retract(M, p, X, ::QRRetraction) + +Compute the QR-based retraction on the [`Rotations`](@ref) `M` from `p` in direction `X` +(as an element of the Lie group), which is a first-order approximation of the exponential map. + +This is also the default retraction on the [`Rotations`](@ref) +""" +retract(::Rotations, ::Any, ::Any, ::QRRetraction) -function retract!(M::Rotations, y::AbstractArray{T}, x, v, method::QRRetraction) where {T} - A = x + x * v +function retract!(M::Rotations, q::AbstractArray{T}, p, X, method::QRRetraction) where {T} + A = p + p * X qr_decomp = qr(A) d = diag(qr_decomp.R) D = Diagonal(sign.(d .+ convert(T, 0.5))) - return copyto!(y, qr_decomp.Q * D) + return copyto!(q, qr_decomp.Q * D) end -retract!(M::Rotations, y, x, v) = retract!(M, y, x, v, QRRetraction()) -function retract!(M::Rotations, y, x, v, method::PolarRetraction) - A = x + x * v - return project_point!(M, y, A; check_det = false) +retract!(M::Rotations, q, p, X) = retract!(M, q, p, X, QRRetraction()) +function retract!(M::Rotations, q, p, X, method::PolarRetraction) + A = p + p * X + return project_point!(M, q, A; check_det = false) end show(io::IO, ::Rotations{N}) where {N} = print(io, "Rotations($(N))") -@doc doc""" - vee(M::Rotations, x, Ω) +@doc raw""" + vee(M::Rotations, p X) -Extract the unique tangent vector components $\omega$ at point $x$ on rotations -group $\mathrm{SO}(n)$ from the matrix representation $\Omega$ of the tangent +Extract the unique tangent vector components `Xⁱ` at point `p` on [`Rotations`](@ref) +$\mathrm{SO}(n)$ from the matrix representation `X` of the tangent vector. The basis on the Lie algebra $\mathfrak{so}(n)$ is chosen such that for -$\mathrm{SO}(2)$, $\omega=\theta=\Omega_{21}$ is the angle of rotation, and +$\mathrm{SO}(2)$, $X^i=\theta=X_{21}$ is the angle of rotation, and for $\mathrm{SO}(3)$, -$\omega = (\Omega_{32}, \Omega_{13}, \Omega_{21}) = \theta u$ is the +$X^i = (X_{32}, X_{13}, X_{21}) = \theta u$ is the angular velocity and axis-angle representation, where $u$ is the unit vector along the axis of rotation. For $\mathrm{SO}(n)$ where $n \ge 4$, the additional elements of $\omega$ are -$\omega_{i (i - 3)/2 + j + 1} = \Omega_{ij}$, for $i \in [4, n], j \in [1,i)$. +$X^i_{j (j - 3)/2 + k + 1} = X_{jk}$, for $j ∈ [4, n], k ∈ [1,j)$. """ vee(::Rotations, ::Any...) -function vee!(M::Rotations{N}, ω, x, Ω) where {N} - @assert size(Ω) == (N, N) - @assert length(ω) == manifold_dimension(M) +function vee!(M::Rotations{N}, Xⁱ, p, X) where {N} + @assert size(X) == (N, N) + @assert length(Xⁱ) == manifold_dimension(M) @inbounds begin - ω[1] = Ω[3, 2] - ω[2] = Ω[1, 3] - ω[3] = Ω[2, 1] + Xⁱ[1] = X[3, 2] + Xⁱ[2] = X[1, 3] + Xⁱ[3] = X[2, 1] k = 4 for i = 4:N, j = 1:i-1 - ω[k] = Ω[i, j] + Xⁱ[k] = X[i, j] k += 1 end end - return ω + return Xⁱ end -function vee!(M::Rotations{2}, ω, x, Ω) - ω[1] = Ω[2] - return ω +function vee!(M::Rotations{2}, Xⁱ, p, X) + Xⁱ[1] = X[2] + return Xⁱ end -@doc doc""" - zero_tangent_vector(M::Rotations, x) +@doc raw""" + zero_tangent_vector(M::Rotations, p) -Return the zero tangent vector from the tangent space art `x` on the [`Rotations`](@ref) +Return the zero tangent vector from the tangent space art `p` on the [`Rotations`](@ref) as an element of the Lie group, i.e. the zero matrix. """ -zero_tangent_vector(M::Rotations, x) = zero(x) +zero_tangent_vector(M::Rotations, p) = zero(p) -zero_tangent_vector!(M::Rotations, v, x) = fill!(v, 0) +zero_tangent_vector!(M::Rotations, X, p) = fill!(X, 0) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 953055472f..545ec7c957 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -1,28 +1,28 @@ -@doc doc""" +@doc raw""" Sphere{N} <: Manifold -The unit sphere manifold $\mathbb S^n$ represented by $n+1$-Tuples, i.e. in by -vectors in $\mathbb R^{n+1}$ of unit length +The unit sphere manifold $𝕊^n$ represented by $n+1$-Tuples, i.e. in by +vectors in $ℝ^{n+1}$ of unit length # Constructor Sphere(n) -Generate the $\mathbb S^{n}\subset \mathbb R^{n+1}$ +Generate $𝕊^{n} ⊂ ℝ^{n+1}$. """ struct Sphere{N} <: Manifold end Sphere(n::Int) = Sphere{n}() -function get_basis(M::Sphere{N}, x, B::DiagonalizingOrthonormalBasis) where {N} +function get_basis(M::Sphere{N}, p, B::DiagonalizingOrthonormalBasis) where {N} A = zeros(N + 1, N + 1) - A[1, :] = transpose(x) - A[2, :] = transpose(B.v) + A[1, :] = transpose(p) + A[2, :] = transpose(B.frame_direction) V = nullspace(A) κ = ones(N) - if !iszero(B.v) + if !iszero(B.frame_direction) # if we have a nonzero direction for the geodesic, add it and it gets curvature zero from the tensor - V = cat(B.v / norm(M, x, B.v), V; dims = 2) + V = cat(B.frame_direction / norm(M, p, B.frame_direction), V; dims = 2) κ[1] = 0 # no curvature along the geodesic direction, if x!=y end vecs = [V[:, i] for i = 1:N] @@ -30,174 +30,174 @@ function get_basis(M::Sphere{N}, x, B::DiagonalizingOrthonormalBasis) where {N} end """ - check_manifold_point(S, x; kwargs...) + check_manifold_point(M, p; kwargs...) -Check whether `x` is a valid point on the [`Sphere`](@ref) `S`, i.e. is a vector -of length [`manifold_dimension`](@ref)`(S)+1` (approximately) of unit length. +Check whether `p` is a valid point on the [`Sphere`](@ref) `M`, i.e. is a vector +of length [`manifold_dimension`](@ref)`(M)+1` (approximately) of unit length. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(S::Sphere{N}, x; kwargs...) where {N} - if size(x) != representation_size(S) +function check_manifold_point(M::Sphere{N}, p; kwargs...) where {N} + if size(p) != representation_size(M) return DomainError( - size(x), - "The point $(x) does not lie on $S, since its size is not $(N+1).", + size(p), + "The point $(p) does not lie on $M, since its size is not $(N+1).", ) end - if !isapprox(norm(x), 1.0; kwargs...) + if !isapprox(norm(p), 1.0; kwargs...) return DomainError( - norm(x), - "The point $(x) does not lie on the sphere $(S) since its norm is not 1.", + norm(p), + "The point $(p) does not lie on the sphere $(M) since its norm is not 1.", ) end return nothing end """ - check_tangent_vector(S, x, v; kwargs... ) + check_tangent_vector(M, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` on the [`Sphere`](@ref) `S`, i.e. -after [`check_manifold_point`](@ref)`(S,x)`, `v` has to be of same dimension as `x` -and orthogonal to `x`. +Check whether `X` is a tangent vector to `p` on the [`Sphere`](@ref) `M`, i.e. +after [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p` +and orthogonal to `p`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(S::Sphere{N}, x, v; kwargs...) where {N} - perr = check_manifold_point(S, x) +function check_tangent_vector(M::Sphere{N}, p, X; kwargs...) where {N} + perr = check_manifold_point(M, p) perr === nothing || return perr - if size(v) != representation_size(S) + if size(X) != representation_size(M) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $S since its size does not match $(N+1).", + size(X), + "The vector $(X) is not a tangent to a point on $M since its size does not match $(N+1).", ) end - if !isapprox(abs(dot(x, v)), 0.0; kwargs...) + if !isapprox(abs(dot(p, X)), 0.0; kwargs...) return DomainError( - abs(dot(x, v)), - "The vector $(v) is not a tangent vector to $(x) on $(S), since it is not orthogonal in the embedding.", + abs(dot(p, X)), + "The vector $(X) is not a tangent vector to $(p) on $(M), since it is not orthogonal in the embedding.", ) end return nothing end -@doc doc""" - distance(M::Sphere, x, y) +@doc raw""" + distance(M::Sphere, p, q) -Compute the geodesic distance betweeen `x` and `y` on the [`Sphere`](@ref) `M`. +Compute the geodesic distance betweeen `p` and `q` on the [`Sphere`](@ref) `M`. The formula is given by the (shorter) great arc length on the (or a) great circle -both `x` and `y` lie on. +both `p` and `q` lie on. ````math -d_{\mathbb S^n}(x,y) = \operatorname{acos}(\langle x, y\rangle). +d_{𝕊^n}(p,q) = \arccos(⟨p,q⟩). ```` """ -distance(S::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) +distance(::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) -@doc doc""" - exp(M::Sphere, x, v) +@doc raw""" + exp(M::Sphere, p, X) -Compute the exponential map from `x` into the tangent direction `v` on the [`Sphere`](@ref) -`M` by following the great arc eminating from `x` in direction `v` along with length of `v`. +Compute the exponential map from `p` in the tangent direction `X` on the [`Sphere`](@ref) +`M` by following the great arc eminating from `p` in direction `X`. ````math -\exp_x v = \cos(\lVert v \rVert_x)x + \sin(\lVert v \rVert_x)\frac{v}{\lVert v \rVert_x}, +\exp_p X = \cos(\lVert X \rVert_p)p + \sin(\lVert X \rVert_p)\frac{X}{\lVert X \rVert_p}, ```` -where $\lVert v \rVert_x$ is the [`norm`](@ref norm(::Sphere,x,v)) on the +where $\lVert X \rVert_p$ is the [`norm`](@ref norm(::Sphere,p,X)) on the [`Sphere`](@ref) `M`. """ exp(::Sphere, ::Any...) -function exp!(M::Sphere, y, x, v) - θ = norm(M, x, v) - y .= cos(θ) .* x .+ usinc(θ) .* v - return y +function exp!(M::Sphere, q, p, X) + θ = norm(M, p, X) + q .= cos(θ) .* p .+ usinc(θ) .* X + return q end -flat!(M::Sphere, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(M::Sphere, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) -@doc doc""" - injectivity_radius(M::Sphere[, x]) +@doc raw""" + injectivity_radius(M::Sphere[, p]) -Return the injectivity radius for the [`Sphere`](@ref) `M`, which is globally $\pi$. +Return the injectivity radius for the [`Sphere`](@ref) `M`, which is globally $π$. injectivity_radius(M::Sphere, x, ::ProjectionRetraction) Return the injectivity radius for the [`ProjectionRetraction`](@ref) on the -[`Sphere`](@ref), which is globally $\frac{\pi}{2}$. +[`Sphere`](@ref), which is globally $\frac{π}{2}$. """ injectivity_radius(::Sphere, ::Any...) = π injectivity_radius(::Sphere, ::Any, ::ProjectionRetraction) = π / 2 -@doc doc""" - inner(S::Sphere, x, w, v) +@doc raw""" + inner(M::Sphere, p, X, Y) -Compute the inner product of the two tangent vectors `w,v` from the tangent -plane at `x` on the sphere `S=`$\mathbb S^n$ using the restriction of the -metric from the embedding, i.e. $ (v,w)_x = v^\mathrm{T}w $. +Compute the inner product of the two tangent vectors `X`, `Y` from the tangent +space at `p` on the [`Sphere`](@ref) `M` using the restriction of the +metric from the embedding, i.e. $ g_p(X,Y) = X^\mathrm{T}Y$. """ -@inline inner(S::Sphere, x, w, v) = dot(w, v) +@inline inner(S::Sphere, p, X, Y) = dot(X, Y) -function get_vector(M::Sphere{N}, x, v, B::ArbitraryOrthonormalBasis) where {N} - x[1] ≈ 1 && return vcat(0, v) - xp1 = x .+ ntuple(i -> ifelse(i == 1, 1, 0), N + 1) - v0 = vcat(0, v) - return 2 * xp1 * dot(xp1, v0) / dot(xp1, xp1) - v0 +function get_vector(M::Sphere{N}, p, X, B::ArbitraryOrthonormalBasis) where {N} + p[1] ≈ 1 && return vcat(0, X) + xp1 = p .+ ntuple(i -> ifelse(i == 1, 1, 0), N + 1) + X0 = vcat(0, X) + return 2 * xp1 * dot(xp1, X0) / dot(xp1, xp1) - X0 end -@doc doc""" - inverse_retract(M::Sphere, x, y, ::ProjectionInverseRetraction) +@doc raw""" + inverse_retract(M::Sphere, p, q, ::ProjectionInverseRetraction) Compute the inverse of the projection based retraction on the [`Sphere`](@ref), -i.e. rearranging $x+v = y\lVert x+d \rVert_2$ yields -since $\langle x,v\rangle = 0$ and when $d_{\mathbb S^2}(x,y) \leq \frac{\pi}{2}$ that +i.e. rearranging $p+X = q\lVert p+X\rVert_2$ yields +since $⟨p,X⟩ = 0$ and when $d_{𝕊^2}(p,q) \leq \frac{π}{2}$ that ````math -\operatorname{retr}_x^{-1}(y) = \frac{y}{\langle x, y \rangle} - x. +\operatorname{retr}_p^{-1}(q) = \frac{q}{⟨p, q⟩} - p. ```` """ inverse_retract(::Sphere, ::Any, ::Any, ::ProjectionInverseRetraction) -function inverse_retract!(::Sphere, v, x, y, ::ProjectionInverseRetraction) - return (v .= y ./ dot(x, y) .- x) +function inverse_retract!(::Sphere, X, p, q, ::ProjectionInverseRetraction) + return (X .= q ./ dot(p, q) .- p) end -@doc doc""" - log(M::Sphere, x, y) +@doc raw""" + log(M::Sphere, p, q) -Compute the logarithmic map on the [`Sphere`](@ref) `M`, i.e. the tangent vector, whose -geodesic starting from `x` reaches `y` after time 1. -The formula reads for $x\neq -y$ +Compute the logarithmic map on the [`Sphere`](@ref) `M`, i.e. the tangent vector, +whose geodesic starting from `p` reaches `q` after time 1. +The formula reads for $x ≠ -y$ ````math -\log_x y = d_{\mathbb S^n}(x,y) -\frac{y-\langle x,y\rangle x}{\lVert y-\langle x,y\rangle x \rVert_2}, +\log_p q = d_{𝕊^n}(p,q) \frac{q-⟨p,q⟩ p}{\lVert q-⟨p,q⟩ p \rVert_2}, ```` + and a deterministic choice from the set of tangent vectors is returned if $x=-y$, i.e. for opposite points. """ log(::Sphere, ::Any...) -function log!(S::Sphere, v, x, y) - cosθ = dot(x, y) +function log!(S::Sphere, X, p, q) + cosθ = dot(p, q) if cosθ ≈ -1 # appr. opposing points, return deterministic choice from set-valued log - fill!(v, 0) - if x[1] ≈ 1 - v[2] = 1 + fill!(X, 0) + if p[1] ≈ 1 + X[2] = 1 else - v[1] = 1 + X[1] = 1 end - copyto!(v, v .- dot(x, v) .* x) - v .*= π / norm(v) + copyto!(X, X .- dot(p, X) .* p) + X .*= π / norm(X) else cosθ = cosθ > 1 ? one(cosθ) : cosθ θ = acos(cosθ) - v .= (y .- cosθ .* x) ./ usinc(θ) + X .= (q .- cosθ .* p) ./ usinc(θ) end - return project_tangent!(S, v, x, v) + return project_tangent!(S, X, p, X) end -@doc doc""" - manifold_dimension(S::Sphere) +@doc raw""" + manifold_dimension(M::Sphere) -Return the dimension of the manifold $\mathbb S^n$, i.e. $n$. +Return the dimension of the [`Sphere`](@ref)`(n) `M`, i.e. $𝕊^n$, which is $\dim(𝕊^n) = n$. """ manifold_dimension(S::Sphere{N}) where {N} = N @@ -215,75 +215,75 @@ Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using """ mean(::Sphere, ::Any...) -function mean!(S::Sphere, y, x::AbstractVector, w::AbstractVector; kwargs...) - return mean!(S, y, x, w, GeodesicInterpolationWithinRadius(π / 2); kwargs...) +function mean!(S::Sphere, p, x::AbstractVector, w::AbstractVector; kwargs...) + return mean!(S, p, x, w, GeodesicInterpolationWithinRadius(π / 2); kwargs...) end -@doc doc""" - norm(M::Sphere, x, v) +@doc raw""" + norm(M::Sphere, p, X) -Compute the length of the tangent vector `v` from the tangent space at `x` on the +Compute the length of the tangent vector `v` from the tangent space at `p` on the [`Sphere`](@ref) `M`, which is the norm in the embedding, i.e. ````math -\lVert v \rVert_x = \lVert v \rVert_2. +\lVert X \rVert_p = \lVert X \rVert_2. ```` """ -norm(M::Sphere, x, v) = norm(v) +norm(M::Sphere, p, X) = norm(X) """ - normal_tvector_distribution(S::Sphere, x, σ) + normal_tvector_distribution(S::Sphere, p, σ) Normal distribution in ambient space with standard deviation `σ` -projected to tangent space at `x`. +projected to tangent space at `p`. """ -function normal_tvector_distribution(S::Sphere, x, σ) - d = Distributions.MvNormal(zero(x), σ) - return ProjectedFVectorDistribution(TangentBundleFibers(S), x, d, project_vector!, x) +function normal_tvector_distribution(S::Sphere, p, σ) + d = Distributions.MvNormal(zero(p), σ) + return ProjectedFVectorDistribution(TangentBundleFibers(S), p, d, project_vector!, p) end -@doc doc""" - project_point(M::Sphere, x) +@doc raw""" + project_point(M::Sphere, p) -Project the point `x` from the embedding onto the [`Sphere`](@ref) `M`. +Project the point `p` from the embedding onto the [`Sphere`](@ref) `M`. ````math -\operatorname{proj}_{\mathbb S^n}(x) = \frac{x}{\lVert x \rVert_2}. +\operatorname{proj}_{𝕊^n}(p) = \frac{p}{\lVert p \rVert_2}. ```` """ project_point(::Sphere, ::Any...) -project_point!(S::Sphere, x) = (x ./= norm(x)) +project_point!(S::Sphere, p) = (p ./= norm(p)) -@doc doc""" - project_tangent(M::Sphere, x, v) +@doc raw""" + project_tangent(M::Sphere, p, X) -Project the point `v` onto the tangent space at `x` on the [`Sphere`](@ref) `M`. +Project the point `X` onto the tangent space at `p` on the [`Sphere`](@ref) `M`. ````math -\operatorname{proj}_{x}(v) = v - \langle x, v \rangle_x +\operatorname{proj}_{p}(X) = X - ⟨p, X⟩p ```` """ project_tangent(::Sphere, ::Any...) -project_tangent!(S::Sphere, w, x, v) = (w .= v .- dot(x, v) .* x) +project_tangent!(S::Sphere, Y, p, X) = (Y .= X .- dot(p, X) .* p) -@doc doc""" - get_coordinates(M::Sphere, x, v, B::ArbitraryOrthonormalBasis) +@doc raw""" + get_coordinates(M::Sphere, p, X, B::ArbitraryOrthonormalBasis) -Represent the tangent vector `v` at point `x` from a sphere `M` in -an orthonormal basis by rotating the vector `v` using rotation matrix -$2\frac{x_p x_p^\mathrm{T}}{x_p^\mathrm{T} x_p} - I$ where $x_p = x + (1, 0, \dots, 0)$. +Represent the tangent vector `X` at point `p` from the [`Sphere`](@ref) `M` in +an orthonormal basis by rotating the vector `X` using the rotation matrix +$2\frac{q q^\mathrm{T}}{q^\mathrm{T} q} - I$ where $q = p + (1, 0, …, 0)$. """ -function get_coordinates(M::Sphere{N}, x, v, B::ArbitraryOrthonormalBasis) where {N} - if isapprox(x[1], 1) - return v[2:end] +function get_coordinates(M::Sphere{N}, p, X, B::ArbitraryOrthonormalBasis) where {N} + if isapprox(p[1], 1) + return X[2:end] else - xp1 = x .+ ntuple(i -> ifelse(i == 1, 1, 0), N + 1) - return (2*xp1*dot(xp1, v)/dot(xp1, xp1)-v)[2:end] + xp1 = p .+ ntuple(i -> ifelse(i == 1, 1, 0), N + 1) + return (2*xp1*dot(xp1, X)/dot(xp1, xp1)-X)[2:end] end end -@doc doc""" +@doc raw""" representation_size(M::Sphere) Return the size points on the [`Sphere`](@ref) `M` are represented as, i.e. @@ -291,66 +291,66 @@ for the `n`-dimensional [`Sphere`](@ref) it is vectors of size `(n+1,)`. """ @generated representation_size(::Sphere{N}) where {N} = (N + 1,) -@doc doc""" - retract(M::Sphere, x, y, ::ProjectionRetraction) +@doc raw""" + retract(M::Sphere, p, X, ::ProjectionRetraction) Compute the retraction that is based on projection, i.e. ````math -\operatorname{retr}_x(v) = \frac{x+v}{\lVert x+v \rVert_2} +\operatorname{retr}_p(X) = \frac{p+X}{\lVert p+X \rVert_2} ```` """ retract(::Sphere, ::Any, ::Any, ::ProjectionRetraction) -function retract!(M::Sphere, y, x, v, ::ProjectionRetraction) - y .= x .+ v - return project_point!(M, y) +function retract!(M::Sphere, q, p, X, ::ProjectionRetraction) + q .= p .+ X + return project_point!(M, q) end -sharp!(M::Sphere, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::Sphere, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) show(io::IO, ::Sphere{N}) where {N} = print(io, "Sphere($(N))") """ - uniform_distribution(S::Sphere, x) + uniform_distribution(M::Sphere, p) -Uniform distribution on given sphere. Generated points will be of similar -type to `x`. +Uniform distribution on given [`Sphere`](@ref) `M`. Generated points will be of +similar type as `p`. """ -function uniform_distribution(S::Sphere, x) - d = Distributions.MvNormal(zero(x), 1.0) - return ProjectedPointDistribution(S, d, project_point!, x) +function uniform_distribution(M::Sphere, p) + d = Distributions.MvNormal(zero(p), 1.0) + return ProjectedPointDistribution(M, d, project_point!, p) end -@doc doc""" - vector_transport_to(M::Sphere, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::Sphere, p, X, q, ::ParallelTransport) Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by ````math -P_{y\gets x}(v) = v - \frac{\langle \log_xy,v\rangle_x}{d^2_{\mathbb S^n}(x,y)} -\bigl(\log_xy + \log_yx \bigr). +\mathcal P_{q←p}(X) = X - \frac{⟨\log_p q,X⟩_p}{d^2_{𝕊^n}(p,q)} +\bigl(\log_p q + \log_qp \bigr). ```` """ vector_transport_to(::Sphere, ::Any, ::Any, ::Any, ::ParallelTransport) -function vector_transport_to!(M::Sphere, vto, x, v, y, ::ParallelTransport) - v_xy = log(M, x, y) - vl = norm(M, x, v_xy) - copyto!(vto, v) +function vector_transport_to!(M::Sphere, Y, p, X, q, ::ParallelTransport) + v_xy = log(M, p, q) + vl = norm(M, p, v_xy) + copyto!(Y, X) if vl > 0 - factor = 2 * dot(v, y) / (norm(x + y)^2) - vto .-= factor .* (x .+ y) + factor = 2 * dot(X, q) / (norm(p + q)^2) + Y .-= factor .* (p .+ q) end - return vto + return Y end -@doc doc""" - zero_tangent_vector(M::Sphere, x) +@doc raw""" + zero_tangent_vector(M::Sphere, p) -Return the zero tangent vector from the tangent space at `x` on the [`Sphere`](@ref) `M`, +Return the zero tangent vector from the tangent space at `p` on the [`Sphere`](@ref) `M`, which is the zero vector in the embedding. """ zero_tangent_vector(::Sphere, ::Any...) -zero_tangent_vector!(S::Sphere, v, x) = fill!(v, 0) +zero_tangent_vector!(::Sphere, X, p) = fill!(X, 0) diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 499acd989a..1db65d9aca 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,27 +1,27 @@ -@doc doc""" - Stiefel{n,k,T} <: Manifold +@doc raw""" + Stiefel{n,k,F} <: Manifold -The Stiefel manifold consists of all $n\times k$, $n\geq k$ orthonormal matrices, i.e. +The Stiefel manifold consists of all $n × k$, $n\geq k$ unitary matrices, i.e. ````math -\mathcal M = \{ x \in \mathbb F^{n\times k} : x^{\mathrm{H}}x = I_k \}, +\{ p ∈ 𝔽^{n × k} : p^{\mathrm{H}}p = I_k \}, ```` -where $\mathbb F \in \{\mathbb R, \mathbb C\}$, +where `F`$=𝔽 ∈ \{ℝ, ℂ\}$, $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and -$I_n \in \mathbb R^{n\times n}$ denotes the $k \times k$ identity matrix. +$I_n ∈ ℝ^{n× n}$ denotes the $k × k$ identity matrix. -The tangent space at a point $x\in\mathcal M$ is given by +The tangent space at a point $p ∈ \mathcal M$ is given by ````math -T_x\mathcal M = \{ v \in \mathbb{F}^{n\times k} : x^{\mathrm{H}}v + v^{\mathrm{H}}x=0_n\}, +T_p \mathcal M = \{ X ∈ 𝔽^{n × k} : p^{\mathrm{H}}X + X^{\mathrm{H}}p=0_n\}, ```` -where $0_n$ is the $k\times k$ zero matrix. +where $0_n$ is the $k × k$ zero matrix. -The metric is either inherited from $\mathbb R^{n,k}$ for the real-valued case +The metric is either inherited from $ℝ^{n,k}$ for the real-valued case or the one inherited from interpreting the complex valued entries in the Gaussian -plane $\mathbb R^2$ and then over all entries as before, i.e. the latter +plane $ℝ^2$ and then over all entries as before, i.e. the latter may be called an Hermitian metric in the complex-valued matrices. The manifold is named after @@ -30,158 +30,166 @@ The manifold is named after # Constructor Stiefel(n,k,F=ℝ) -Generate the (real-valued) Stiefel manifold of $n\times k$ dimensional orthonormal matrices. +Generate the (real-valued) Stiefel manifold of $n × k$ dimensional orthonormal matrices. """ struct Stiefel{n,k,F} <: Manifold end Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() -@doc doc""" - check_manifold_point(M::Stiefel, x; kwargs...) +@doc raw""" + check_manifold_point(M::Stiefel, p; kwargs...) -Check whether `x` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, -i.e. that it has the right [`AbstractNumbers`](@ref) type and $x^{\mathrm{H}}x$ -is (approximatly) the identity, where $\cdot^{\mathrm{H}}$ is the complex conjugate -transpose. The settings for approximately can be set with `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`](@ref) type and $p^{\mathrm{H}}p$ is (approximately) the identity, where $\cdot^{\mathrm{H}}$ is the +complex conjugate transpose. The settings for approximately can be set with `kwargs...`. """ -function check_manifold_point(M::Stiefel{n,k,T}, x; kwargs...) where {n,k,T} - if (T === ℝ) && !(eltype(x) <: Real) +function check_manifold_point(M::Stiefel{n,k,F}, p; kwargs...) where {n,k,F} + if (F === ℝ) && !(eltype(p) <: Real) return DomainError( - eltype(x), - "The matrix $(x) is not a real-valued matrix, so it does noe lie on the Stiefel manifold of dimension ($(n),$(k)).", + eltype(p), + "The matrix $(p) is not a real-valued matrix, so it does noe lie on the Stiefel manifold of dimension ($(n),$(k)).", ) end - if (T === ℂ) && !(eltype(x) <: Real) && !(eltype(x) <: Complex) + if (F === ℂ) && !(eltype(p) <: Real) && !(eltype(p) <: Complex) return DomainError( - eltype(x), - "The matrix $(x) is neiter real- nor complex-valued matrix, so it does noe lie on the complex Stiefel manifold of dimension ($(n),$(k)).", + eltype(p), + "The matrix $(p) is neiter real- nor complex-valued matrix, so it does noe lie on the complex Stiefel manifold of dimension ($(n),$(k)).", ) end - if any(size(x) != representation_size(M)) + if any(size(p) != representation_size(M)) return DomainError( - size(x), - "The matrix $(x) is does not lie on the Stiefel manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + size(p), + "The matrix $(p) is does not lie on the Stiefel manifold of dimension ($(n),$(k)), since its dimensions are wrong.", ) end - c = x' * x + c = p' * p if !isapprox(c, one(c); kwargs...) return DomainError( norm(c - one(c)), - "The point $(x) does not lie on the Stiefel manifold of dimension ($(n),$(k)), because x'x is not the unit matrix.", + "The point $(p) does not lie on the Stiefel manifold of dimension ($(n),$(k)), because x'x is not the unit matrix.", ) end end -@doc doc""" - check_tangent_vector(M::Stiefel, x, v; kwargs...) +@doc raw""" + check_tangent_vector(M::Stiefel, p, X; kwargs...) -Check whether `v` is a valid tangent vector at `x` on the [`Stiefel`](@ref) +Checks whether `X` is a valid tangent vector at `p` on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. the [`AbstractNumbers`](@ref) fits and -it (approximtly) holds that $x^{\mathrm{H}}v + v^{\mathrm{H}}x = 0$, where -`kwargs...` is passed to the `isapprox`. +it (approximately) holds that $p^{\mathrm{H}}X + X^{\mathrm{H}}p = 0$. +The settings for approximately can be set with `kwargs...`. """ -function check_tangent_vector(M::Stiefel{n,k,T}, x, v; kwargs...) where {n,k,T} - mpe = check_manifold_point(M, x) +function check_tangent_vector(M::Stiefel{n,k,F}, p, X; kwargs...) where {n,k,F} + mpe = check_manifold_point(M, p) mpe === nothing || return mpe - if (T === ℝ) && !(eltype(v) <: Real) + if (F === ℝ) && !(eltype(X) <: Real) return DomainError( - eltype(v), - "The matrix $(v) is not a real-valued matrix, so it can not be a tangent vector to the Stiefel manifold of dimension ($(n),$(k)).", + eltype(X), + "The matrix $(X) is not a real-valued matrix, so it can not be a tangent vector to the Stiefel manifold of dimension ($(n),$(k)).", ) end - if (T === ℂ) && !(eltype(v) <: Real) && !(eltype(v) <: Complex) + if (F === ℂ) && !(eltype(X) <: Real) && !(eltype(X) <: Complex) return DomainError( - eltype(v), - "The matrix $(v) is neiter real- nor complex-valued matrix, so it can not bea tangent vectorto the complex Stiefel manifold of dimension ($(n),$(k)).", + eltype(X), + "The matrix $(X) is neiter real- nor complex-valued matrix, so it can not bea tangent vectorto the complex Stiefel manifold of dimension ($(n),$(k)).", ) end - if any(size(v) != representation_size(M)) + if any(size(X) != representation_size(M)) return DomainError( - size(v), - "The matrix $(v) is does not lie in the tangent space of $(x) on the Stiefel manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + size(X), + "The matrix $(X) is does not lie in the tangent space of $(p) on the Stiefel manifold of dimension ($(n),$(k)), since its dimensions are wrong.", ) end - if !isapprox(x' * v + v' * x, zeros(k, k); kwargs...) + if !isapprox(p' * X + X' * p, zeros(k, k); kwargs...) return DomainError( - norm(x' * v + v' * x), - "The matrix $(v) is does not lie in the tangent space of $(x) on the Stiefel manifold of dimension ($(n),$(k)), since x'v + v'x is not the zero matrix.", + norm(p' * X + X' * p), + "The matrix $(X) is does not lie in the tangent space of $(p) on the Stiefel manifold of dimension ($(n),$(k)), since x'v + v'x is not the zero matrix.", ) end end -@doc doc""" - exp(M, x, v) +@doc raw""" + exp(M, p, X) Compute the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` -eminating from `x` into tangent direction `v`. +emanating from `p` in tangent direction `X`. -$\operatorname{exp}_{x} v = \begin{pmatrix} - x\\v +````math +\exp_p X = \begin{pmatrix} + p\\X \end{pmatrix} - \exp + \operatorname{Exp} \left( - \begin{pmatrix} x^{\mathrm{H}}v & - v^{\mathrm{H}}v\\ - I_n & x^{\mathrm{H}}v\end{pmatrix} + \begin{pmatrix} p^{\mathrm{H}}X & - X^{\mathrm{H}}X\\ + I_n & p^{\mathrm{H}}X\end{pmatrix} \right) -\begin{pmatrix} \exp( -x^{\mathrm{H}}v) \\ 0_n\end{pmatrix}$ +\begin{pmatrix} \exp( -p^{\mathrm{H}}X) \\ 0_n\end{pmatrix}, +```` -where $\exp$ denotes matrix exponential, +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 \times k$, respectively. +$0_k$ are the identity matrix and the zero matrix of dimension $k × k$, respectively. """ exp(::Stiefel, ::Any...) -function exp!(M::Stiefel{n,k}, y, x, v) where {n,k} +function exp!(M::Stiefel{n,k}, q, p, X) where {n,k} return copyto!( - y, - [x v] * - exp([x'v -v' * v; one(zeros(eltype(x), k, k)) x' * v]) * - [exp(-x'v); zeros(eltype(x), k, k)], + q, + [p X] * + exp([p'X -X' * X; one(zeros(eltype(p), k, k)) p' * X]) * + [exp(-p'X); zeros(eltype(p), k, k)], ) end -@doc doc""" - inner(M::Stiefel, x, v, w) +@doc raw""" + inner(M::Stiefel, p, X, Y) -Compute the inner product for two tangent vectors `v`, `w` from the -tangent space of `x` on the [`Stiefel`](@ref) manifold `M`. The formula reads +Compute the inner product for two tangent vectors `X`, `Y` from the +tangent space of `p` on the [`Stiefel`](@ref) manifold `M`. The formula reads ````math -(v,w)_x = \operatorname{trace}(v^{\mathrm{H}}w), +g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}Y), ```` i.e. the [`EuclideanMetric`](@ref) from the embedding restricted to the tangent space. For the complex-valued case this is the Hermitian metric, to be precise. """ -inner(::Stiefel, x, v, w) = dot(v, w) +inner(::Stiefel, p, X, Y) = dot(X, Y) -@doc doc""" - inverse_retract(M::Stiefel, x, y, ::PolarInverseRetraction) +@doc raw""" + inverse_retract(M::Stiefel, p, q, ::PolarInverseRetraction) Compute the inverse retraction based on a singular value decomposition -for two points `x`, `y` on the [`Stiefel`](@ref) manifold `M` and return -the resulting tangent vector in `v`. This follows the folloing approach: -From the Polar retraction we know that +for two points `p`, `q` on the [`Stiefel`](@ref) manifold `M`. +This follows the folloing approach: From the Polar retraction we know that ````math -\operatorname{retr}_x^{-1}(y) = ys - q +\operatorname{retr}_p^{-1}q = qs - t ```` -if such a symmetric positive definite $k\times k$ matrix exists. Since $ys-q$ is -also a tangent vector at $x$ 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 -x^{\mathrm{H}}ys + s(x^{\mathrm{H}}y)^{\mathrm{H}} + 2I_k = 0, +p^{\mathrm{H}}qs + s(p^{\mathrm{H}}q)^{\mathrm{H}} + 2I_k = 0, ```` which can either be solved by a Lyapunov approach or a continuous-time algebraic Riccati equation as described in [^KanekoFioriTanaka2013] This implementation follows the Lyapunov approach. - inverse_retract(M, x, y, ::QRInverseRetraction) +[^KanekoFioriTanaka2013]: + > T. Kaneko, S. Fiori, T. Tanaka: "Empirical Arithmetic Averaging over the + > Compact Stiefel Manifold", IEEE Transactions on Signal Processing, 2013, + > doi: [10.1109/TSP.2012.2226167](https://doi.org/10.1109/TSP.2012.2226167). +""" +inverse_retract(::Stiefel, ::Any, ::Any, ::PolarInverseRetraction) + +@doc raw""" + inverse_retract(M, p, q, ::QRInverseRetraction) Compute the inverse retraction based on a qr decomposition -for two points `x`, `y` on the [`Stiefel`](@ref) manifold `M` and return -the resulting tangent vector in `v`. The computation follows Algorithm 1 +for two points `p`, `q` on the [`Stiefel`](@ref) manifold `M` and return +the resulting tangent vector in `X`. The computation follows Algorithm 1 in [^KanekoFioriTanaka2013]. [^KanekoFioriTanaka2013]: @@ -189,104 +197,114 @@ in [^KanekoFioriTanaka2013]. > Compact Stiefel Manifold", IEEE Transactions on Signal Processing, 2013, > doi: [10.1109/TSP.2012.2226167](https://doi.org/10.1109/TSP.2012.2226167). """ -inverse_retract(::Stiefel, ::Any...) +inverse_retract(::Stiefel, ::Any, ::Any, ::QRInverseRetraction) -function inverse_retract!(::Stiefel, v, x, y, ::PolarInverseRetraction) - A = x' * y - H = -2 * one(x' * x) +function inverse_retract!(::Stiefel, X, p, q, ::PolarInverseRetraction) + A = p' * q + H = -2 * one(p' * p) B = lyap(A, H) - return copyto!(v, y * B - x) + return copyto!(X, q * B - p) end -function inverse_retract!(::Stiefel{n,k}, v, x, y, ::QRInverseRetraction) where {n,k} - A = x' * y - R = zeros(typeof(one(eltype(x)) * one(eltype(y))), k, k) +function inverse_retract!(::Stiefel{n,k}, X, p, q, ::QRInverseRetraction) where {n,k} + A = p' * q + R = zeros(typeof(one(eltype(p)) * one(eltype(q))), k, k) for i = 1:k b = zeros(i) b[i] = 1 b[1:(end-1)] = -transpose(R[1:(i-1), 1:(i-1)]) * A[i, 1:(i-1)] R[1:i, i] = A[1:i, 1:i] \ b end - return copyto!(v, y * R - x) + return copyto!(X, q * R - p) end -function isapprox(M::Stiefel, x, v, w; kwargs...) - return isapprox(sqrt(inner(M, x, zero_tangent_vector(M, x), v - w)), 0; kwargs...) +function isapprox(M::Stiefel, p, X, Y; kwargs...) + return isapprox(sqrt(inner(M, p, zero_tangent_vector(M, p), X - Y)), 0; kwargs...) end -isapprox(M::Stiefel, x, y; kwargs...) = isapprox(norm(x - y), 0; kwargs...) +isapprox(M::Stiefel, p, q; kwargs...) = isapprox(norm(p - q), 0; kwargs...) -@doc doc""" +@doc raw""" manifold_dimension(M::Stiefel) Return the dimension of the [`Stiefel`](@ref) manifold `M`=$\operatorname{St}(n,k,𝔽)$. The dimension is given by ````math -\dim \mathrm{Stiefel}(n, k, ℝ) &= nk - \frac{1}{2}k(k+1)\\ -\dim \mathrm{Stiefel}(n, k, ℂ) &= 2nk - k^2\\ -\dim \mathrm{Stiefel}(n, k, ℍ) &= 4nk - k(2k-1) +\begin{aligned} +\dim \mathrm{St}(n, k, ℝ) &= nk - \frac{1}{2}k(k+1)\\ +\dim \mathrm{St}(n, k, ℂ) &= 2nk - k^2\\ +\dim \mathrm{St}(n, k, ℍ) &= 4nk - k(2k-1) +\end{aligned} ```` """ manifold_dimension(::Stiefel{n,k,ℝ}) where {n,k} = n * k - div(k * (k + 1), 2) manifold_dimension(::Stiefel{n,k,ℂ}) where {n,k} = 2 * n * k - k * k manifold_dimension(::Stiefel{n,k,ℍ}) where {n,k} = 4 * n * k - k * (2k - 1) -@doc doc""" - project_tangent(M, x, v) +@doc raw""" + project_tangent(M, p, X) -Project `v` onto the tangent space of `x` to the [`Stiefel`](@ref) manifold `M`. +Project `X` onto the tangent space of `p` to the [`Stiefel`](@ref) manifold `M`. The formula reads ````math -\operatorname{proj}_{\mathcal M}(x,v) = v - x \operatorname{Sym}(x^{\mathrm{H}}v), +\operatorname{proj}_{\mathcal M}(p, X) = X - p \operatorname{Sym}(p^{\mathrm{H}}X), ```` -where $\operatorname{Sym}(y)$ is the symmetrization of $y$, e.g. by -$\operatorname{Sym}(y) = \frac{y^{\mathrm{H}}+y}{2}$. +where $\operatorname{Sym}(q)$ is the symmetrization of $q$, e.g. by +$\operatorname{Sym}(q) = \frac{q^{\mathrm{H}}+q}{2}$. """ project_tangent(::Stiefel, ::Any...) -project_tangent!(::Stiefel, w, x, v) = copyto!(w, v - x * Symmetric(x' * v)) +project_tangent!(::Stiefel, Y, p, X) = copyto!(Y, X - p * Symmetric(p' * X)) -@doc doc""" - retract(M, x, v, ::PolarRetraction) +@doc raw""" + retract(M, p, X, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](@ref) on the -[`Stiefel`](@ref) manifold `M`. With $USV = x + v$ the retraction reads +[`Stiefel`](@ref) manifold `M`. With $USV = p + X$ the retraction reads + ````math -\operatorname{retr}_x(v) = U\bar{V}^\mathrm{H}. +\operatorname{retr}_p X = U\bar{V}^\mathrm{H}. ```` +""" +retract(::Stiefel, ::Any, ::Any, ::PolarRetraction) - retract(M, x, v, ::QRRetraction ) +@doc raw""" + retract(M, p, X, ::QRRetraction ) Compute the QR-based retraction [`QRRetraction`](@ref) on the -[`Stiefel`](@ref) manifold `M`. With $QR = x + v$ the retraction reads +[`Stiefel`](@ref) manifold `M`. With $QR = p + X$ the retraction reads + ````math -\operatorname{retr}_x(v) = QD, +\operatorname{retr}_p X = QD, ```` -where D is a $n\times 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}(x) = \begin{cases} -1 & \text{ for } x > 0,\\ -0 & \text{ for } x = 0,\\ --1& \text{ for } x < 0. + +where $\operatorname{sgn}(p) = \begin{cases} +1 & \text{ for } p > 0,\\ +0 & \text{ for } p = 0,\\ +-1& \text{ for } p < 0. \end{cases}$ """ -retract(::Stiefel, ::Any...) +retract(::Stiefel, ::Any, ::Any, ::QRRetraction) -function retract!(::Stiefel, y, x, v, ::PolarRetraction) - s = svd(x + v) - return mul!(y, s.U, s.Vt) +function retract!(::Stiefel, q, p, X, ::PolarRetraction) + s = svd(p + X) + return mul!(q, s.U, s.Vt) end -function retract!(::Stiefel, y, x, v, ::QRRetraction) - qrfac = qr(x + v) +function retract!(::Stiefel, q, p, X, ::QRRetraction) + qrfac = qr(p + X) d = diag(qrfac.R) D = Diagonal(sign.(sign.(d .+ 0.5))) - return copyto!(y, Matrix(qrfac.Q) * D) + return copyto!(q, Matrix(qrfac.Q) * D) end -@doc doc""" +@doc raw""" representation_size(M::Stiefel) Returns the representation size of the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, @@ -296,12 +314,13 @@ i.e. `(n,k)`, which is the matrix dimensions. show(io::IO, ::Stiefel{n,k,F}) where {n,k,F} = print(io, "Stiefel($(n), $(k), $(F))") -@doc doc""" - zero_tangent_vector(M::Stiefel, x) +@doc raw""" + zero_tangent_vector(M::Stiefel, p) -Returns the zero tangent vector from the tangent space at `x` on the [`Stiefel`](@ref) -`M`=$\operatorname{St}(n,k)$, i.e. an `(n,k)` zero matrix. +Returns the zero tangent vector from the tangent space at `p` +on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, +i.e. an `(n,k)` zero matrix. """ zero_tangent_vector(::Stiefel, ::Any...) -zero_tangent_vector!(::Stiefel, v, x) = fill!(v, 0) +zero_tangent_vector!(::Stiefel, X, p) = fill!(X, 0) diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 7fd060f8d8..8179499b1a 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -1,243 +1,245 @@ -@doc doc""" +@doc raw""" SymmetricMatrices{n,F} <: Manifold The [`Manifold`](@ref) $ \operatorname{Sym} (n)$ consisting of the real- or complex-valued -symmetric matrices of size $ n\times n$, i.e. the set +symmetric matrices of size $ n× n$, i.e. the set ````math -\operatorname{Sym}(n) = \bigl\{A \in \mathbb F^{n\times n} \big| A^{\mathrm{H}} = A \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 transposed -and the field $\mathbb F \in \{ \mathbb R, \mathbb C\}$ is set by the +and the field $𝔽 ∈ \{ ℝ, ℂ\}$ is set by the [`AbstractNumbers`](@ref) `F`. -Though it is slighty redundant, usually the matrices are safed as $n\times n$ arrays. +Though it is slightly redundant, usually the matrices are stored as $n × n$ arrays. # Constructor SymmetricMatrices(n::Int, F::AbstractNumbers=ℝ) -Generate the manifold of $n\times n$ symmetric metrices. +Generate the manifold of $n × n$ symmetric metrices. """ struct SymmetricMatrices{n,F} <: Manifold end SymmetricMatrices(n::Int, F::AbstractNumbers = ℝ) = SymmetricMatrices{n,F}() -@doc doc""" - check_manifold_point(M::SymmetricMatrices{n,F}, x; kwargs...) +@doc raw""" + check_manifold_point(M::SymmetricMatrices{n,F}, p; kwargs...) -Check whether `x` is a valid manifold point on the [`SymmetricMatrices`](@ref) `M`, i.e. -whether `x` is a symmetric matrix of size `(n,n)` with values from the corresponding +Check whether `p` is a valid manifold point on the [`SymmetricMatrices`](@ref) `M`, i.e. +whether `p` is a symmetric matrix of size `(n,n)` with values from the corresponding [`AbstractNumbers`](@ref) `F`. -The tolerance for the symmetry of `x` can be set using `kwargs...`. +The tolerance for the symmetry of `p` can be set using `kwargs...`. """ -function check_manifold_point(M::SymmetricMatrices{n,F}, x; kwargs...) where {n,F} - if (F === ℝ) && !(eltype(x) <: Real) +function check_manifold_point(M::SymmetricMatrices{n,F}, p; kwargs...) where {n,F} + if (F === ℝ) && !(eltype(p) <: Real) return DomainError( - eltype(x), - "The matrix $(x) does not lie on $M, since its values are not real.", + eltype(p), + "The matrix $(p) does not lie on $M, since its values are not real.", ) end - if (F === ℂ) && !(eltype(x) <: Real) && !(eltype(x) <: Complex) + if (F === ℂ) && !(eltype(p) <: Real) && !(eltype(p) <: Complex) return DomainError( - eltype(x), - "The matrix $(x) does not lie on $M, since its values are not complex.", + eltype(p), + "The matrix $(p) does not lie on $M, since its values are not complex.", ) end - if size(x) != (n, n) + if size(p) != (n, n) return DomainError( - size(x), - "The point $(x) does not lie on $M since its size ($(size(x))) does not match the representation size ($(representation_size(M))).", + size(p), + "The point $(p) does not lie on $M since its size ($(size(p))) does not match the representation size ($(representation_size(M))).", ) end - if !isapprox(norm(x - transpose(x)), 0.0; kwargs...) + if !isapprox(norm(p - transpose(p)), 0.0; kwargs...) return DomainError( - norm(x - transpose(x)), - "The point $(x) does not lie on $M, since it is not symmetric.", + norm(p - transpose(p)), + "The point $(p) does not lie on $M, since it is not symmetric.", ) end return nothing end """ - check_tangent_vector(M::SymmetricMatrices{n,F}, x, v; kwargs... ) + check_tangent_vector(M::SymmetricMatrices{n,F}, p, X; kwargs... ) -Check whether `v` is a tangent vector to manifold point `x` on the -[`SymmetricMatrices`](@ref) `M`, i.e. `v` has to be a symmetric matrix of dimension `(n,n)` +Check whether `X` is a tangent vector to manifold point `p` on the +[`SymmetricMatrices`](@ref) `M`, i.e. `X` has to be a symmetric matrix of size `(n,n)` and its values have to be from the correct [`AbstractNumbers`](@ref). -The tolerance for the symmetry of `x` and `v` can be set using `kwargs...`. +The tolerance for the symmetry of `p` and `X` can be set using `kwargs...`. """ -function check_tangent_vector(M::SymmetricMatrices{n,F}, x, v; kwargs...) where {n,F} - t = check_manifold_point(M, x; kwargs...) +function check_tangent_vector(M::SymmetricMatrices{n,F}, p, X; kwargs...) where {n,F} + t = check_manifold_point(M, p; kwargs...) t === nothing || return t - if (F === ℝ) && !(eltype(v) <: Real) + if (F === ℝ) && !(eltype(X) <: Real) return DomainError( - eltype(v), - "The matrix $(v) is not a tangent to a point on $M, since its values are not real.", + eltype(X), + "The matrix $(X) is not a tangent to a point on $M, since its values are not real.", ) end - if (F === ℂ) && !(eltype(v) <: Real) && !(eltype(v) <: Complex) + if (F === ℂ) && !(eltype(X) <: Real) && !(eltype(X) <: Complex) return DomainError( - eltype(v), - "The matrix $(v) is not a tangent to a point on $M, since its values are not complex.", + eltype(X), + "The matrix $(X) is not a tangent to a point on $M, since its values are not complex.", ) end - if size(v) != (n, n) + if size(X) != (n, n) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $(M) since its size ($(size(v))) does not match the representation size ($(representation_size(M))).", + size(X), + "The vector $(X) is not a tangent to a point on $(M) since its size ($(size(X))) does not match the representation size ($(representation_size(M))).", ) end - if !isapprox(norm(v - transpose(v)), 0.0; kwargs...) + if !isapprox(norm(X - transpose(X)), 0.0; kwargs...) return DomainError( - norm(v - transpose(v)), - "The vector $(v) is not a tangent vector to $(x) on $(M), since it is not symmetric.", + norm(X - transpose(X)), + "The vector $(X) is not a tangent vector to $(p) on $(M), since it is not symmetric.", ) end return nothing end -@doc doc""" - distance(M::SymmetricMatrices, x, y) +@doc raw""" + distance(M::SymmetricMatrices, p, q) Compute distance using the inherited metric, i.e. taking the Frobenius-norm of the difference. """ -distance(M::SymmetricMatrices, x, y) = norm(x - y) +distance(M::SymmetricMatrices, p, q) = norm(p - q) -@doc doc""" - exp(M::SymmetricMatrices, x, v) +@doc raw""" + exp(M::SymmetricMatrices, p, X) -Compute the exponential map eminating from `x` in tangent direction `v` on the +Compute the exponential map emanating from `p` in tangent direction `X` on the [`SymmetricMatrices`](@ref) `M`, which reads ````math -\exp_xv = x + v. +\exp_p X = p + X. ```` """ exp(::SymmetricMatrices, ::Any...) -exp!(M::SymmetricMatrices, y, x, v) = (y .= x .+ v) +exp!(M::SymmetricMatrices, q, p, X) = (q .= p .+ X) -@doc doc""" - flat(M::SymmetricMatrices, x, w::FVector{TangentSpaceType}) +@doc raw""" + flat(M::SymmetricMatrices, p, X::FVector{TangentSpaceType}) -Compute the [`flat`](@ref flat(M::Manifold, x, w::FVector)) isomorphism of the -[`SymmetricMatrices`](@ref) `M` on the manifold point `x` and tangent vector `w`. +Compute the [`flat`](@ref flat(M::Manifold, p, X::FVector)) isomorphism of the +[`SymmetricMatrices`](@ref) `M` on the manifold point `p` and tangent vector `X`. -Since `M` is already a vector space over $\mathbb R$, this returns just the vector `w`. +Since `M` is already a vector space over $ℝ$, this returns just the vector `X`. """ flat(::SymmetricMatrices, ::Any...) -flat!(M::SymmetricMatrices, v::CoTFVector, x, w::TFVector) = copyto!(v, w) +flat!(M::SymmetricMatrices, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) function get_coordinates( M::SymmetricMatrices{N,ℝ}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = similar(v, dim) - @assert size(v) == (N, N) + Y = similar(X, dim) + @assert size(X) == (N, N) @assert dim == div(N * (N + 1), 2) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, sqrt(2)) - @inbounds vout[k] = v[i, j] * scale + @inbounds Y[k] = X[i, j] * scale k += 1 end - return vout + return Y end function get_coordinates( M::SymmetricMatrices{N,ℂ}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = similar(v, dim) - @assert size(v) == (N, N) + Y = similar(X, dim) + @assert size(X) == (N, N) @assert dim == N * (N + 1) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, sqrt(2)) - @inbounds vout[k] = real(v[i, j]) * scale + @inbounds Y[k] = real(X[i, j]) * scale k += 1 - @inbounds vout[k] = imag(v[i, j]) * scale + @inbounds Y[k] = imag(X[i, j]) * scale k += 1 end - return vout + return Y end function get_vector( M::SymmetricMatrices{N,ℝ}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, x) - @assert size(v) == (div(N * (N + 1), 2),) - @assert size(vout) == (N, N) + Y = allocate_result(M, get_vector, p) + @assert size(X) == (div(N * (N + 1), 2),) + @assert size(Y) == (N, N) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, 1 / sqrt(2)) - @inbounds vout[i, j] = v[k] * scale - @inbounds vout[j, i] = v[k] * scale + @inbounds Y[i, j] = X[k] * scale + @inbounds Y[j, i] = X[k] * scale k += 1 end - return vout + return Y end function get_vector( M::SymmetricMatrices{N,ℂ}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, x, x .* 1im) - @assert size(v) == (N * (N + 1),) - @assert size(vout) == (N, N) + Y = allocate_result(M, get_vector, p, p .* 1im) + @assert size(X) == (N * (N + 1),) + @assert size(Y) == (N, N) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, 1 / sqrt(2)) - @inbounds vout[i, j] = Complex(v[k], v[k+1]) * scale - @inbounds vout[j, i] = vout[i, j] + @inbounds Y[i, j] = Complex(X[k], X[k+1]) * scale + @inbounds Y[j, i] = Y[i, j] k += 2 end - return vout + return Y end -@doc doc""" - inner(M::SymmetricMatrices, x, w, v) +@doc raw""" + inner(M::SymmetricMatrices, p, X, Y) -Compute the inner product of the two tangent vectors `w`, `v` from the tangent -space at `x` on the [`SymmetricMatrices`](@ref) `M` using the restriction of the +Compute the inner product of the two tangent vectors `X`, `Y` from the tangent +space at `p` on the [`SymmetricMatrices`](@ref) `M` using the restriction of the metric from the embedding, i.e. + ````math -(v,w)_x = \operatorname{tr}(v^{\mathrm{H}}w), +g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}Y), ```` + where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ -@inline inner(M::SymmetricMatrices, x, w, v) = dot(w, v) +@inline inner(M::SymmetricMatrices, p, X, Y) = dot(X, Y) -@doc doc""" - log(M::SymmetricMatrices, x, y) -Compute the logarithmic map from `x` to `y` on the [`SymmetricMatrices`](@ref) `M`, which +@doc raw""" + log(M::SymmetricMatrices, p, q) +Compute the logarithmic map from `p` to `q` on the [`SymmetricMatrices`](@ref) `M`, which reads ````math -\log_xy = y-x. +\log_p q = q-p. ```` """ log(::SymmetricMatrices, ::Any...) -log!(M::SymmetricMatrices, v, x, y) = (v .= y .- x) +log!(M::SymmetricMatrices, X, p, q) = (X .= q .- p) -@doc doc""" +@doc raw""" manifold_dimension(M::SymmetricMatrices{n,𝔽}) Return the dimension of the [`SymmetricMatrices`](@ref) matrix `M` over the number system @@ -253,96 +255,97 @@ function manifold_dimension(::SymmetricMatrices{N,𝔽}) where {N,𝔽} return div(N * (N + 1), 2) * real_dimension(𝔽) end -@doc doc""" - norm(M::SymmetricMatrices, x, v) +@doc raw""" + norm(M::SymmetricMatrices, p, X) -Compute the norm of the tangent vector `v` from the tangent space at `x` on the +Compute the norm of the tangent vector `X` from the tangent space at `p` on the [`SymmetricMatrices`](@ref) `M`, which is the norm from the embedding, i.e. ````math -\lVert v \rVert_x = \lVert v \rVert_2 +\lVert X \rVert_p = \lVert X \rVert_2 ```` """ -norm(M::SymmetricMatrices, x, v) = norm(v) +norm(M::SymmetricMatrices, p, X) = norm(X) -@doc doc""" - project_point(M::SymmetricMatrices,x) +@doc raw""" + project_point(M::SymmetricMatrices, p) -Projects `x` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. +Projects `p` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. ````math -\operatorname{proj}_{\operatorname{Sym}(n)}(x) = \frac{1}{2} \bigl( x + x^{\mathrm{H}} \bigr), +\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. """ project_point(::SymmetricMatrices, ::Any...) -project_point!(M::SymmetricMatrices, x) = (x .= (x + transpose(x)) ./ 2) +project_point!(M::SymmetricMatrices, p) = (p .= (p + p') ./ 2) -@doc doc""" - project_tangent(M::SymmetricMatrices, x, v) +@doc raw""" + project_tangent(M::SymmetricMatrices, p, X) -Project the matrix `v` onto the tangent space at `x` on the [`SymmetricMatrices`](@ref) `M`, +Project the matrix `X` onto the tangent space at `p` on the [`SymmetricMatrices`](@ref) `M`, ````math -\operatorname{proj}_x(v) = \frac{1}{2} \bigl( v + v^{\mathrm{H}} \bigr), +\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. """ project_tangent(::SymmetricMatrices, ::Any...) -project_tangent!(M::SymmetricMatrices, w, x, v) = (w .= (v .+ transpose(v)) ./ 2) +project_tangent!(M::SymmetricMatrices, Y, p, X) = (Y .= (X .+ transpose(X)) ./ 2) -@doc doc""" +@doc raw""" representation_size(M::SymmetricMatrices) Returns the size points on the [`SymmetricMatrices`](@ref) `M` are represented as, i.e. -for the $n\times n$ it's `(n,n)`. +for the $n × n$ it's `(n,n)`. """ @generated representation_size(::SymmetricMatrices{N}) where {N} = (N, N) -@doc doc""" - sharp(M::SymmetricMatrices, x, w::FVector{CotangentSpaceType}) +@doc raw""" + sharp(M::SymmetricMatrices, p, ξ::FVector{CotangentSpaceType}) -Compute the [`sharp`](@ref sharp(M::Manifold, x, w::FVector)) isomorphism of the -[`SymmetricMatrices`](@ref) `M` on the manifold point `x` and cotangent vector `w`. +Compute the [`sharp`](@ref sharp(M::Manifold, p, ξ::FVector)) isomorphism of the +[`SymmetricMatrices`](@ref) `M` on the manifold point `p` and cotangent vector `ξ`. -Since `M` is already a vector space over $\mathbb R$, this returns just the vector `w`. +Since `M` is already a vector space over $ℝ$, this returns just the vector `ξ` as a tangent +vector. """ sharp(::SymmetricMatrices, ::Any...) -sharp!(M::SymmetricMatrices, v::TFVector, x, w::CoTFVector) = copyto!(v, w) +sharp!(M::SymmetricMatrices, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) function show(io::IO, ::SymmetricMatrices{n,F}) where {n,F} print(io, "SymmetricMatrices($(n), $(F))") end -@doc doc""" - vector_transport_to(M::SymmetricMatrices, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::SymmetricMatrices, p, X, q, ::ParallelTransport) Compute the parallel -[`vector_transport_to`](@ref vector_transport_to(M::Manifold, x, v, y, ParallelTransport())) -of `v` from the tangent space at `x` on the [`SymmetricMatrices`](@ref) `M` to `y`. +[`vector_transport_to`](@ref vector_transport_to(M::Manifold, p, X, y, ParallelTransport())) +of `X` from the tangent space at `p` on the [`SymmetricMatrices`](@ref) `M` to `q`. Since the metric is inherited from the embedding space, this is just the identity, i.e. ````math -P_{y\gets x}(v) = v. +\mathcal P_{q←p}(X) = X. ```` """ vector_transport_to(::SymmetricMatrices, ::Any...) -function vector_transport_to!(M::SymmetricMatrices, vto, x, v, y, ::ParallelTransport) - return copyto!(vto, v) +function vector_transport_to!(M::SymmetricMatrices, Y, p, X, q, ::ParallelTransport) + return copyto!(Y, X) end -@doc doc""" - zero_tangent_vector(M, x) +@doc raw""" + zero_tangent_vector(M, p) -Return the zero tangent vector for the tangent space at `x` on the +Return the zero tangent vector for the tangent space at `p` on the [`SymmetricMatrices`](@ref) `M`, i.e. the zero matrix. """ zero_tangent_vector(::SymmetricMatrices, ::Any...) -zero_tangent_vector!(M::SymmetricMatrices, v, x) = fill!(v, 0) +zero_tangent_vector!(M::SymmetricMatrices, X, p) = fill!(X, 0) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index ea30e40e9a..1260182356 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -1,97 +1,96 @@ -@doc doc""" +@doc raw""" SymmetricPositiveDefinite{N} <: Manifold The manifold of symmetric positive definite matrices, i.e. -```math +````math \mathcal P(n) = \bigl\{ -x \in \mathbb R^{n\times n} : -\xi^\mathrm{T}x\xi > 0 \text{ for all } \xi \in \mathbb R^{n}\backslash\{0\} +p ∈ ℝ^{n × n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \bigr\} -``` +```` # Constructor SymmetricPositiveDefinite(n) -generates the manifold $\mathcal P(n) \subset \mathbb R^{n\times n}$ +generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$ """ struct SymmetricPositiveDefinite{N} <: Manifold end SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}() -@doc doc""" - check_manifold_point(M::SymmetricPositiveDefinite, x; kwargs...) +@doc raw""" + check_manifold_point(M::SymmetricPositiveDefinite, p; kwargs...) -checks, whether `x` is a valid point on the [`SymmetricPositiveDefinite`](@ref) `M`, i.e. is a matrix +checks, whether `p` is a valid point on the [`SymmetricPositiveDefinite`](@ref) `M`, i.e. is a matrix of size `(N,N)`, symmetric and positive definite. The tolerance for the second to last test can be set using the `kwargs...`. """ -function check_manifold_point(M::SymmetricPositiveDefinite{N}, x; kwargs...) where {N} - if size(x) != representation_size(M) +function check_manifold_point(M::SymmetricPositiveDefinite{N}, p; kwargs...) where {N} + if size(p) != representation_size(M) return DomainError( - size(x), - "The point $(x) does not lie on $(M), since its size is not $(representation_size(M)).", + size(p), + "The point $(p) does not lie on $(M), since its size is not $(representation_size(M)).", ) end - if !isapprox(norm(x - transpose(x)), 0.0; kwargs...) + if !isapprox(norm(p - transpose(p)), 0.0; kwargs...) return DomainError( - norm(x), - "The point $(x) does not lie on $(M) since its not a symmetric matrix:", + norm(p), + "The point $(p) does not lie on $(M) since its not a symmetric matrix:", ) end - if !all(eigvals(x) .> 0) + if !all(eigvals(p) .> 0) return DomainError( - norm(x), - "The point $x does not lie on $(M) since its not a positive definite matrix.", + norm(p), + "The point $p does not lie on $(M) since its not a positive definite matrix.", ) end return nothing end """ - check_tangent_vector(M::SymmetricPositiveDefinite, x, v; kwargs... ) + check_tangent_vector(M::SymmetricPositiveDefinite, p, X; kwargs... ) -Check whether `v` is a tangent vector to `x` on the [`SymmetricPositiveDefinite`](@ref) `M`, -i.e. atfer [`check_manifold_point`](@ref)`(M,x)`, `v` has to be of same dimension as `x` +Check whether `X` is a tangent vector to `p` on the [`SymmetricPositiveDefinite`](@ref) `M`, +i.e. atfer [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p` and a symmetric matrix, i.e. this stores tangent vetors as elements of the corresponding Lie group. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::SymmetricPositiveDefinite{N}, x, v; kwargs...) where {N} - mpe = check_manifold_point(M, x) +function check_tangent_vector(M::SymmetricPositiveDefinite{N}, p, X; kwargs...) where {N} + mpe = check_manifold_point(M, p) mpe === nothing || return mpe - if size(v) != representation_size(M) + if size(X) != representation_size(M) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $(M) since its size does not match $(representation_size(M)).", + size(X), + "The vector $(X) is not a tangent to a point on $(M) since its size does not match $(representation_size(M)).", ) end - if !isapprox(norm(v - transpose(v)), 0.0; kwargs...) + if !isapprox(norm(X - transpose(X)), 0.0; kwargs...) return DomainError( - size(v), - "The vector $(v) is not a tangent to a point on $(M) (represented as an element of the Lie algebra) since its not symmetric.", + size(X), + "The vector $(X) is not a tangent to a point on $(M) (represented as an element of the Lie algebra) since its not symmetric.", ) end return nothing end -@doc doc""" - injectivity_radius(M::SymmetricPositiveDefinite[, x]) - injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}[, x]) - injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}[, x]) +@doc raw""" + injectivity_radius(M::SymmetricPositiveDefinite[, p]) + injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}[, p]) + injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}[, p]) Return the injectivity radius of the [`SymmetricPositiveDefinite`](@ref). Since `M` is a Hadamard manifold with respect to the [`LinearAffineMetric`](@ref) and the -[`LogCholeskyMetric`](@ref), the injectivity radius is globally $\infty$. +[`LogCholeskyMetric`](@ref), the injectivity radius is globally $∞$. """ injectivity_radius(M::SymmetricPositiveDefinite{N}, args...) where {N} = Inf -@doc doc""" +@doc raw""" manifold_dimension(M::SymmetricPositiveDefinite) returns the dimension of -[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n\in \mathbb N$, i.e. +[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n ∈ ℕ$, i.e. ````math \dim \mathcal P(n) = \frac{n(n+1)}{2} ```` @@ -116,19 +115,19 @@ mean(::SymmetricPositiveDefinite, ::Any) function mean!( M::SymmetricPositiveDefinite, - y, + p, x::AbstractVector, w::AbstractVector; kwargs..., ) - return mean!(M, y, x, w, GeodesicInterpolation(); kwargs...) + return mean!(M, p, x, w, GeodesicInterpolation(); kwargs...) end -@doc doc""" +@doc raw""" representation_size(M::SymmetricPositiveDefinite) Return the size of an array representing an element on the -[`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. $n\times n$, the size of such a +[`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. $n × n$, the size of such a symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) @@ -137,7 +136,7 @@ function show(io::IO, ::SymmetricPositiveDefinite{N}) where {N} print(io, "SymmetricPositiveDefinite($(N))") end -@doc doc""" +@doc raw""" zero_tangent_vector(M::SymmetricPositiveDefinite,x) returns the zero tangent vector in the tangent space of the symmetric positive diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index 5bb2cfbda6..1435c45bbd 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" LinearAffineMetric <: Metric The linear affine metric is the metric for symmetric positive definite matrices, that employs @@ -8,74 +8,74 @@ struct LinearAffineMetric <: RiemannianMetric end is_default_metric(::SymmetricPositiveDefinite, ::LinearAffineMetric) = Val(true) -@doc doc""" - distance(M::SymmetricPositiveDefinite, x, y) - distance(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}) +@doc raw""" + distance(M::SymmetricPositiveDefinite, p, q) + distance(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, p, q) -Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between `x` and `y`, +Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between `p` and `q`, as a [`MetricManifold`](@ref) with [`LinearAffineMetric`](@ref). The formula reads ```math -d_{\mathcal P(n)}(x,y) -= \lVert \operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})\rVert_{\mathrm{F}}., +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. """ -function distance(M::SymmetricPositiveDefinite{N}, x, y) where {N} - s = real.(eigvals(x, y)) +function distance(M::SymmetricPositiveDefinite{N}, p, q) where {N} + s = real.(eigvals(p, q)) return any(s .<= eps()) ? 0 : sqrt(sum(abs.(log.(s)) .^ 2)) end -@doc doc""" - exp(M::SymmetricPositiveDefinite, x, v) - exp(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, v) +@doc raw""" + exp(M::SymmetricPositiveDefinite, p, X) + exp(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, p, X) -Compute the exponential map from `x` with tangent vector `v` on the +Compute the exponential map from `p` with tangent vector `X` on the [`SymmetricPositiveDefinite`](@ref) `M` with its default [`MetricManifold`](@ref) having the [`LinearAffineMetric`](@ref). The formula reads ```math -\exp_x v = x^{\frac{1}{2}}\operatorname{Exp}(x^{-\frac{1}{2}} v x^{-\frac{1}{2}})x^{\frac{1}{2}}, +\exp_p X = p^{\frac{1}{2}}\operatorname{Exp}(p^{-\frac{1}{2}} X p^{-\frac{1}{2}})p^{\frac{1}{2}}, ``` where $\operatorname{Exp}$ denotes to the matrix exponential. """ exp(::SymmetricPositiveDefinite, ::Any...) -function exp!(M::SymmetricPositiveDefinite{N}, y, x, v) where {N} - e = eigen(Symmetric(x)) +function exp!(M::SymmetricPositiveDefinite{N}, q, p, X) where {N} + e = eigen(Symmetric(p)) U = e.vectors S = e.values Ssqrt = Diagonal(sqrt.(S)) SsqrtInv = Diagonal(1 ./ sqrt.(S)) - xSqrt = Symmetric(U * Ssqrt * transpose(U)) - xSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) - T = Symmetric(xSqrtInv * v * xSqrtInv) + pSqrt = Symmetric(U * Ssqrt * transpose(U)) + pSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) + T = Symmetric(pSqrtInv * X * pSqrtInv) eig1 = eigen(T) # numerical stabilization Se = Diagonal(exp.(eig1.values)) Ue = eig1.vectors - xue = xSqrt * Ue - return copyto!(y, xue * Se * transpose(xue)) + pUe = pSqrt * Ue + return copyto!(q, pUe * Se * transpose(pUe)) end -@doc doc""" - [Ξ,κ] = get_basis(M::SymmetricPositiveDefinite, x, B::DiagonalizingOrthonormalBasis) - [Ξ,κ] = get_basis(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, B::DiagonalizingOrthonormalBasis) +@doc raw""" + [Ξ,κ] = get_basis(M::SymmetricPositiveDefinite, p, B::DiagonalizingOrthonormalBasis) + [Ξ,κ] = get_basis(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, p, B::DiagonalizingOrthonormalBasis) Return a orthonormal basis `Ξ` as a vector of tangent vectors (of length -[`manifold_dimension`](@ref) of `M`) in the tangent space of `x` on the +[`manifold_dimension`](@ref) of `M`) in the tangent space of `p` on the [`MetricManifold`](@ref) of [`SymmetricPositiveDefinite`](@ref) manifold `M` with [`LinearAffineMetric`](@ref) that diagonalizes the curvature tensor $R(u,v)w$ -with eigenvalues `κ` and where the direction `B.v` has curvature `0`. +with eigenvalues `κ` and where the direction `B.frame_direction` has curvature `0`. """ function get_basis( M::SymmetricPositiveDefinite{N}, - x, + p, B::DiagonalizingOrthonormalBasis, ) where {N} - xSqrt = sqrt(x) - eigv = eigen(B.v) + pSqrt = sqrt(p) + eigv = eigen(B.frame_direction) V = eigv.vectors Ξ = [ (i == j ? 1 / 2 : @@ -89,159 +89,160 @@ function get_basis( end function get_basis( M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, - x, + p, B::DiagonalizingOrthonormalBasis, ) where {N} - return get_basis(base_manifold(M), x, B) + return get_basis(base_manifold(M), p, B) end function get_coordinates( M::SymmetricPositiveDefinite{N}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis, ) where {N} dim = manifold_dimension(M) - vout = similar(v, dim) - @assert size(v) == (N, N) + Y = similar(X, dim) + @assert size(X) == (N, N) @assert dim == div(N * (N + 1), 2) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, sqrt(2)) - @inbounds vout[k] = v[i, j] * scale + @inbounds Y[k] = X[i, j] * scale k += 1 end - return vout + return Y end function get_coordinates( M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis, ) where {N} - return get_coordinates(base_manifold(M), x, v, B) + return get_coordinates(base_manifold(M), p, X, B) end function get_vector( M::SymmetricPositiveDefinite{N}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, x) - @assert size(v) == (div(N * (N + 1), 2),) - @assert size(vout) == (N, N) + Y = allocate_result(M, get_vector, p) + @assert size(X) == (div(N * (N + 1), 2),) + @assert size(Y) == (N, N) k = 1 for i = 1:N, j = i:N scale = ifelse(i == j, 1, 1 / sqrt(2)) - @inbounds vout[i, j] = v[k] * scale - @inbounds vout[j, i] = v[k] * scale + @inbounds Y[i, j] = X[k] * scale + @inbounds Y[j, i] = X[k] * scale k += 1 end - return vout + return Y end function get_vector( M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, - x, - v, + p, + X, B::ArbitraryOrthonormalBasis, ) where {N} - return get_vector(base_manifold(M), x, v, B) + return get_vector(base_manifold(M), p, X, B) end -@doc doc""" - inner(M::SymmetricPositiveDefinite, x, v, w) - inner(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, w) +@doc raw""" + inner(M::SymmetricPositiveDefinite, p, X, Y) + inner(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, p, X, Y) -Compute the inner product of `v`, `w` in the tangent space of `x` on +Compute the inner product of `X`, `Y` in the tangent space of `p` on the [`SymmetricPositiveDefinite`](@ref) manifold `M`, as a [`MetricManifold`](@ref) with [`LinearAffineMetric`](@ref). The formula reads ````math -(v, w)_x = \operatorname{tr}(x^{-1} v x^{-1} w), +g_p(X,Y) = \operatorname{tr}(p^{-1} X p^{-1} Y), ```` """ -function inner(M::SymmetricPositiveDefinite, x, v, w) - F = cholesky(Symmetric(x)) - return tr((F \ Symmetric(v)) * (F \ Symmetric(w))) +function inner(M::SymmetricPositiveDefinite, p, X, Y) + F = cholesky(Symmetric(p)) + return tr((F \ Symmetric(X)) * (F \ Symmetric(Y))) end -@doc doc""" - log(M::SymmetricPositiveDefinite, x, y) - log(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, y) +@doc raw""" + log(M::SymmetricPositiveDefinite, p, q) + log(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, p, q) -Compute the logarithmic map from `x` to `y` on the [`SymmetricPositiveDefinite`](@ref) +Compute the logarithmic map from `p` to `q` on the [`SymmetricPositiveDefinite`](@ref) as a [`MetricManifold`](@ref) with [`LinearAffineMetric`](@ref). The formula reads ```math -\log_x y = -x^{\frac{1}{2}}\operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})x^{\frac{1}{2}}, +\log_p q = +p^{\frac{1}{2}}\operatorname{Log}(p^{-\frac{1}{2}}qp^{-\frac{1}{2}})p^{\frac{1}{2}}, ``` where $\operatorname{Log}$ denotes to the matrix logarithm. """ log(::SymmetricPositiveDefinite, ::Any...) -function log!(M::SymmetricPositiveDefinite{N}, v, x, y) where {N} - e = eigen(Symmetric(x)) +function log!(M::SymmetricPositiveDefinite{N}, X, p, q) where {N} + e = eigen(Symmetric(p)) U = e.vectors S = e.values Ssqrt = Diagonal(sqrt.(S)) SsqrtInv = Diagonal(1 ./ sqrt.(S)) - xSqrt = Symmetric(U * Ssqrt * transpose(U)) - xSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) - T = Symmetric(xSqrtInv * y * xSqrtInv) + pSqrt = Symmetric(U * Ssqrt * transpose(U)) + pSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) + T = Symmetric(pSqrtInv * q * pSqrtInv) e2 = eigen(T) Se = Diagonal(log.(max.(e2.values, eps()))) - xue = xSqrt * e2.vectors - return mul!(v, xue, Se * transpose(xue)) + pUe = pSqrt * e2.vectors + return mul!(X, pUe, Se * transpose(pUe)) end -@doc doc""" - vector_transport_to(M::SymmetricPositiveDefinite, x, v, y, ::ParallelTransport) - vector_transport_to(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, y, ::ParallelTransport) +@doc raw""" + vector_transport_to(M::SymmetricPositiveDefinite, p, X, q, ::ParallelTransport) + vector_transport_to(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, p, X, y, ::ParallelTransport) -Compute the parallel transport on the [`SymmetricPositiveDefinite`](@ref) as a +Compute the parallel transport of `X` from the tangent space at `p` to the +tangent space at `q` on the [`SymmetricPositiveDefinite`](@ref) as a [`MetricManifold`](@ref) with the [`LinearAffineMetric`](@ref). The formula reads ```math -P_{y\gets x}(v) = x^{\frac{1}{2}} +\mathcal P_{q←p}X = p^{\frac{1}{2}} \operatorname{Exp}\bigl( -\frac{1}{2}x^{-\frac{1}{2}}\log_x(y)x^{-\frac{1}{2}} +\frac{1}{2}p^{-\frac{1}{2}}\log_p(q)p^{-\frac{1}{2}} \bigr) -x^{-\frac{1}{2}}v x^{-\frac{1}{2}} +p^{-\frac{1}{2}}X p^{-\frac{1}{2}} \operatorname{Exp}\bigl( -\frac{1}{2}x^{-\frac{1}{2}}\log_x(y)x^{-\frac{1}{2}} +\frac{1}{2}p^{-\frac{1}{2}}\log_p(q)p^{-\frac{1}{2}} \bigr) -x^{\frac{1}{2}}, +p^{\frac{1}{2}}, ``` where $\operatorname{Exp}$ denotes the matrix exponential and `log` the logarithmic map on [`SymmetricPositiveDefinite`](@ref) -(again with respect to the metric mentioned). +(again with respect to the [`LinearAffineMetric`](@ref)). """ vector_transport_to(::SymmetricPositiveDefinite, ::Any, ::Any, ::Any, ::ParallelTransport) function vector_transport_to!( M::SymmetricPositiveDefinite{N}, - vto, - x, - v, - y, + Y, + p, + X, + q, ::ParallelTransport, ) where {N} - distance(M, x, y) < 2 * eps(eltype(x)) && copyto!(vto, v) - e = eigen(Symmetric(x)) + distance(M, p, q) < 2 * eps(eltype(p)) && copyto!(Y, X) + e = eigen(Symmetric(p)) U = e.vectors S = e.values Ssqrt = sqrt.(S) SsqrtInv = Diagonal(1 ./ Ssqrt) Ssqrt = Diagonal(Ssqrt) - xSqrt = Symmetric(U * Ssqrt * transpose(U)) - xSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) - tv = Symmetric(xSqrtInv * v * xSqrtInv) - ty = Symmetric(xSqrtInv * y * xSqrtInv) + pSqrt = Symmetric(U * Ssqrt * transpose(U)) + pSqrtInv = Symmetric(U * SsqrtInv * transpose(U)) + tv = Symmetric(pSqrtInv * X * pSqrtInv) + ty = Symmetric(pSqrtInv * q * pSqrtInv) e2 = eigen(ty) Se = Diagonal(log.(e2.values)) Ue = e2.vectors @@ -249,7 +250,7 @@ function vector_transport_to!( e3 = eigen(ty2) Sf = Diagonal(exp.(e3.values)) Uf = e3.vectors - xue = xSqrt * Uf * Sf * transpose(Uf) - vtp = Symmetric(xue * tv * transpose(xue)) - return copyto!(vto, vtp) + pUe = pSqrt * Uf * Sf * transpose(Uf) + vtp = Symmetric(pUe * tv * transpose(pUe)) + return copyto!(Y, vtp) end diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 495c05339e..8f0b06a51a 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" LogCholeskyMetric <: Metric The Log-Cholesky metric imposes a metric based on the Cholesky decomposition as @@ -10,55 +10,55 @@ introduced by [^Lin2019]. """ struct LogCholeskyMetric <: RiemannianMetric end -cholesky_to_spd(l, w) = (l * l', w * l' + l * w') +cholesky_to_spd(x, W) = (x * x', W * x' + x * W') -tangent_cholesky_to_tangent_spd!(l, w) = (w .= w * l' + l * w') +tangent_cholesky_to_tangent_spd!(x, W) = (W .= W * x' + x * W') -spd_to_cholesky(x, v) = spd_to_cholesky(x, cholesky(x).L, v) +spd_to_cholesky(p, X) = spd_to_cholesky(p, cholesky(p).L, X) -function spd_to_cholesky(x, l, v) - w = inv(l) * v * inv(transpose(l)) +function spd_to_cholesky(p, x, X) + w = inv(x) * X * inv(transpose(x)) # strictly lower triangular plus half diagonal - return (l, l * (LowerTriangular(w) - Diagonal(w) / 2)) + return (x, x * (LowerTriangular(w) - Diagonal(w) / 2)) end -@doc doc""" - distance(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) +@doc raw""" + distance(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) Compute the distance on the manifold of [`SymmetricPositiveDefinite`](@ref) -nmatrices, i.e. between two symmetric positive definite matrices `x` and `y` +nmatrices, i.e. between two symmetric positive definite matrices `p` and `q` with respect to the [`LogCholeskyMetric`](@ref). The formula reads ````math -d_{\mathcal P(n)}(x,y) = \sqrt{ - \lVert \lfloor l \rfloor - \lfloor k \rfloor \rVert_{\mathrm{F}}^2 - + \lVert \log(\operatorname{diag}(l)) - \log(\operatorname{diag}(k))\rVert_{\mathrm{F}}^2 }\ \ , +d_{\mathcal P(n)}(p,q) = \sqrt{ + \lVert ⌊ x ⌋ - ⌊ y ⌋ \rVert_{\mathrm{F}}^2 + + \lVert \log(\operatorname{diag}(x)) - \log(\operatorname{diag}(y))\rVert_{\mathrm{F}}^2 }\ \ , ```` -where $l$ and $k$ are the cholesky factors of $x$ and $y$, respectively, -$\lfloor\cdot\rfloor$ denbotes the strictly lower triangular matrix of its argument, -and $\lVert\cdot\rVert_{\mathrm{F}}$ denotes the Frobenius norm. +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. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, - x, - y, + p, + q, ) where {N} - return distance(CholeskySpace{N}(), cholesky(x).L, cholesky(y).L) + return distance(CholeskySpace{N}(), cholesky(p).L, cholesky(q).L) end -@doc doc""" - exp(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v) +@doc raw""" + exp(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, X) Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with -[`LogCholeskyMetric`](@ref) from `x` into direction `v`. The formula reads +[`LogCholeskyMetric`](@ref) from `p` into direction `X`. The formula reads ````math -\exp_x v = (\exp_l w)(\exp_l w)^\mathrm{T} +\exp_p X = (\exp_y W)(\exp_y W)^\mathrm{T} ```` -where $\exp_lw$ is the exponential map on [`CholeskySpace`](@ref), $l$ is the cholesky -decomposition of $x$, $w = l(l^{-1}vl^{-\mathrm{T}})_\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 $(\cdot)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$. """ @@ -66,87 +66,87 @@ exp(::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) function exp!( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, - y, - x, - v, + q, + p, + X, ) where {N} - (l, w) = spd_to_cholesky(x, v) - z = exp(CholeskySpace{N}(), l, w) - return copyto!(y, z * z') + (y, W) = spd_to_cholesky(p, X) + z = exp(CholeskySpace{N}(), y, W) + return copyto!(q, z * z') end -@doc doc""" - inner(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v, w) +@doc raw""" + inner(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, Y) -Compute the inner product of two matrices `v`, `w` in the tangent space of `x` +Compute the inner product of two matrices `X`, `Y` in the tangent space of `p` on the [`SymmetricPositiveDefinite`](@ref) manifold `M`, as a [`MetricManifold`](@ref) with [`LogCholeskyMetric`](@ref). The formula reads ````math - (v,w)_x = (p_l(w),p_l(v))_l, + g_p(X,Y) = ⟨a_z(X),a_z(Y)⟩_z, ```` -where the right hand side is the inner product on the [`CholeskySpace`](@ref), -$l$ is the cholesky factor of $x$, -$p_l(w) = l (l^{-1}wl^{-\mathrm{T}})_{\frac{1}{2}}$, and $(\cdot)_\frac{1}{2}$ +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}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$ """ function inner( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, - x, - v, - w, + p, + X, + Y, ) where {N} - (l, vl) = spd_to_cholesky(x, v) - (l, wl) = spd_to_cholesky(x, l, w) - return inner(CholeskySpace{N}(), l, vl, wl) + (z, Xz) = spd_to_cholesky(p, X) + (z, Yz) = spd_to_cholesky(p, z, Y) + return inner(CholeskySpace{N}(), z, Xz, Yz) end -@doc doc""" - log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) +@doc raw""" + log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) Compute the logarithmic map on [`SymmetricPositiveDefinite`](@ref) `M` with -respect to the [`LogCholeskyMetric`](@ref) eminating from `x` to `y`. +respect to the [`LogCholeskyMetric`](@ref) emanating from `p` to `q`. The formula can be adapted from the [`CholeskySpace`](@ref) as ````math -\log_x y = lw^{\mathrm{T}} + wl^{\mathrm{T}}, +\log_p q = xW^{\mathrm{T}} + Wx^{\mathrm{T}}, ```` -where $l$ is the colesky factor of $x$ and $w=\log_lk$ for $k$ the cholesky factor -of $y$ 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...) function log!( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, - v, - x, - y, + X, + p, + q, ) where {N} - l = cholesky(x).L - k = cholesky(y).L - log!(CholeskySpace{N}(), v, l, k) - return tangent_cholesky_to_tangent_spd!(l, v) + x = cholesky(p).L + y = cholesky(q).L + log!(CholeskySpace{N}(), X, x, y) + return tangent_cholesky_to_tangent_spd!(x, X) end -@doc doc""" +@doc raw""" vector_transport_to( M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, - x, - v, - y, + p, + X, + q, ::ParallelTransport, ) -Parallely transport the tangent vector `v` at `x` along the geodesic to `y` with respect to +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 $l$ and $k$ denote the cholesky factors of `x` and `y`, respectively and -$w = l(l^{-1}vl^{-\mathrm{T}})_\frac{1}{2}$, where $(\cdot)_\frac{1}{2}$ denotes the lower -triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With $u$ the parallel -transport on [`CholeskySpace`](@ref) from $l$ to $k$. 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 $(\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 ````math - \mathcal P_{y\gets x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. +\mathcal P_{q←p}X = yV^{\mathrm{T}} + Vy^{\mathrm{T}}. ```` """ vector_transport_to( @@ -159,14 +159,14 @@ vector_transport_to( function vector_transport_to!( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, - vto, - x, - v, - y, + Y, + p, + X, + q, ::ParallelTransport, ) where {N} - k = cholesky(y).L - (l, w) = spd_to_cholesky(x, v) - vector_transport_to!(CholeskySpace{N}(), vto, l, w, k, ParallelTransport()) - return tangent_cholesky_to_tangent_spd!(k, vto) + y = cholesky(q).L + (x, W) = spd_to_cholesky(p, X) + vector_transport_to!(CholeskySpace{N}(), Y, x, W, y, ParallelTransport()) + return tangent_cholesky_to_tangent_spd!(y, Y) end diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index bde46b6054..ba530fb098 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" LogEuclideanMetric <: Metric The LogEuclidean Metric consists of the Euclidean metric applied to all elements after mapping them @@ -6,15 +6,15 @@ into the Lie Algebra, i.e. performing a matrix logarithm beforehand. """ struct LogEuclideanMetric <: RiemannianMetric end -@doc doc""" - distance(M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, x, y) +@doc raw""" + distance(M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, p, q) Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between -`x` and `y` as a [`MetricManifold`](@ref) with [`LogEuclideanMetric`](@ref). +`p` and `q` as a [`MetricManifold`](@ref) with [`LogEuclideanMetric`](@ref). The formula reads ```math - d_{\mathcal P(n)}(x,y) = \lVert \Log x - \Log y \rVert_{\mathrm{F}} + d_{\mathcal P(n)}(p,q) = \lVert \Log p - \Log q \rVert_{\mathrm{F}} ``` where $\operatorname{Log}$ denotes the matrix logarithm and @@ -22,8 +22,8 @@ $\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, - x, - y, + p, + q, ) where {N} - return norm(log(Symmetric(x)) - log(Symmetric(y))) + return norm(log(Symmetric(p)) - log(Symmetric(q))) end diff --git a/src/manifolds/Torus.jl b/src/manifolds/Torus.jl index d743d73135..1c9cf0c19c 100644 --- a/src/manifolds/Torus.jl +++ b/src/manifolds/Torus.jl @@ -1,9 +1,9 @@ -@doc doc""" +@doc raw""" Torus{N} <: AbstractPowerManifold -The n-dimensionsl 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 is stored internally within `manifold`, such that all functions of +The [`Circle`](@ref) is stored internally within `M.manifold`, such that all functions of [`AbstractPowerManifold`](@ref) can be used directly. """ struct Torus{N} <: AbstractPowerManifold{Circle{ℝ},MultidimentionalArrayPowerRepresentation} @@ -14,36 +14,43 @@ Torus(n::Int) = Torus{n}(Circle()) ^(M::Circle, n::Int) = Torus{n}(M) -@doc doc""" - check_manifold_point(M::Torus{n},x) +@doc raw""" + check_manifold_point(M::Torus{n},p) -check whether `x` is a valid point on the [`GraphManifold`](@ref) +Checks whether `p` is a valid point on the [`Torus`](@ref) `M`, i.e. each of +its entries is a valid point on the [`Circle`](@ref) and the length of `x` is `n`. """ check_manifold_point(::Torus, ::Any) -function check_manifold_point(M::Torus{N}, x; kwargs...) where {N} - if length(x) != N +function check_manifold_point(M::Torus{N}, p; kwargs...) where {N} + if length(p) != N return DomainError( - length(x), - "The number of elements in `x` ($(length(x))) does not match the dimension of the torus ($(N)).", + length(p), + "The number of elements in `p` ($(length(p))) does not match the dimension of the torus ($(N)).", ) end - return check_manifold_point(PowerManifold(M.manifold, N), x; kwargs...) + return check_manifold_point(PowerManifold(M.manifold, N), p; kwargs...) end +@doc raw""" + check_tangent_vector(M::Torus{n}, p, X) -function check_tangent_vector(M::Torus{N}, x, v; kwargs...) where {N} - if length(x) != N +Checks whether `X` is a valid tangent vector to `p` on the [`Torus`](@ref) `M`. +This means, that `p` is valid, that `X` is of correct dimension and elementwise +a tangent vector to the elements of `p` on the [`Circle`](@ref). +""" +function check_tangent_vector(M::Torus{N}, p, X; kwargs...) where {N} + if length(p) != N return DomainError( - length(x), - "The number of elements in `x` ($(length(x))) does not match the dimension of the torus ($(N)).", + length(p), + "The number of elements in `x` ($(length(p))) does not match the dimension of the torus ($(N)).", ) end - if length(v) != N + if length(X) != N return DomainError( - length(v), - "The number of elements in `v` ($(length(v))) does not match the dimension of the torus ($(N)).", + length(X), + "The number of elements in `v` ($(length(X))) does not match the dimension of the torus ($(N)).", ) end - return check_tangent_vector(PowerManifold(M.manifold, N), x, v; kwargs...) + return check_tangent_vector(PowerManifold(M.manifold, N), p, X; kwargs...) end get_iterator(M::Torus{N}) where {N} = 1:N diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 63b0c3ffe3..fd50031619 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -4,15 +4,15 @@ Abstract type for tangent spaces, cotangent spaces, their tensor products, exterior products, etc. -Every vector space `VS` is supposed to provide: +Every vector space `fiber` is supposed to provide: * a method of constructing vectors, * basic operations: addition, subtraction, multiplication by a scalar and negation (unary minus), -* [`zero_vector!(VS, v, x)`](@ref) to construct zero vectors at point `x`, -* `allocate(v)` and `allocate(v, T)` for vector `v` and type `T`, -* `copyto!(v, w)` for vectors `v` and `w`, +* [`zero_vector!(fiber, X, p)`](@ref) to construct zero vectors at point `p`, +* `allocate(X)` and `allocate(X, T)` for vector `X` and type `T`, +* `copyto!(X, Y)` for vectors `X` and `Y`, * `number_eltype(v)` for vector `v`, -* [`vector_space_dimension(::VectorBundleFibers{<:typeof(VS)}) where VS`](@ref). +* [`vector_space_dimension(::VectorBundleFibers{<:typeof(fiber)}) where fiber`](@ref). Optionally: * inner product via `inner` (used to provide Riemannian metric on vector @@ -47,10 +47,10 @@ end TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) """ - VectorBundleFibers(VS::VectorSpaceType, M::Manifold) + VectorBundleFibers(fiber::VectorSpaceType, M::Manifold) Type representing a family of vector spaces (fibers) of a vector bundle over `M` -with vector spaces of type `VS`. In contrast with `VectorBundle`, operations +with vector spaces of type `fiber`. In contrast with `VectorBundle`, operations on `VectorBundleFibers` expect point-like and vector-like parts to be passed separately instead of being bundled together. It can be thought of as a representation of vector spaces from a vector bundle but without @@ -58,8 +58,8 @@ storing the point at which a vector space is attached (which is specified separately in various functions). """ struct VectorBundleFibers{TVS<:VectorSpaceType,TM<:Manifold} - VS::TVS - M::TM + fiber::TVS + manifold::TM end const TangentBundleFibers{M} = VectorBundleFibers{TangentSpaceType,M} where {M<:Manifold} @@ -72,31 +72,31 @@ const CotangentBundleFibers{M} = CotangentBundleFibers(M::Manifold) = VectorBundleFibers(CotangentSpace, M) """ - VectorSpaceAtPoint(fiber::VectorBundleFibers, x) + VectorSpaceAtPoint(fiber::VectorBundleFibers, p) -A vector space (fiber type `fiber` of a vector bundle) at point `x` from -the manifold `fiber.M`. +A vector space (fiber type `fiber` of a vector bundle) at point `p` from +the manifold `fiber.manifold`. """ struct VectorSpaceAtPoint{TFiber<:VectorBundleFibers,TX} fiber::TFiber - x::TX + point::TX end """ - TangentSpaceAtPoint(M::Manifold, x) + TangentSpaceAtPoint(M::Manifold, p) Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent -space at `x`. +space at `p`. """ -TangentSpaceAtPoint(M::Manifold, x) = VectorSpaceAtPoint(TangentBundleFibers(M), x) +TangentSpaceAtPoint(M::Manifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) """ - CotangentSpaceAtPoint(M::Manifold, x) + CotangentSpaceAtPoint(M::Manifold, p) Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent -space at `x`. +space at `p`. """ -CotangentSpaceAtPoint(M::Manifold, x) = VectorSpaceAtPoint(CotangentBundleFibers(M), x) +CotangentSpaceAtPoint(M::Manifold, p) = VectorSpaceAtPoint(CotangentBundleFibers(M), p) """ VectorBundle(M::Manifold, type::VectorSpaceType) @@ -105,12 +105,12 @@ Vector bundle on manifold `M` of type `type`. """ struct VectorBundle{TVS<:VectorSpaceType,TM<:Manifold} <: Manifold type::TVS - M::TM - VS::VectorBundleFibers{TVS,TM} + manifold::TM + fiber::VectorBundleFibers{TVS,TM} end -function VectorBundle(VS::TVS, M::TM) where {TVS<:VectorSpaceType,TM<:Manifold} - return VectorBundle{TVS,TM}(VS, M, VectorBundleFibers(VS, M)) +function VectorBundle(fiber::TVS, M::TM) where {TVS<:VectorSpaceType,TM<:Manifold} + return VectorBundle{TVS,TM}(fiber, M, VectorBundleFibers(fiber, M)) end const TangentBundle{M} = VectorBundle{TangentSpaceType,M} where {M<:Manifold} @@ -144,66 +144,66 @@ struct PrecomputedVectorBundleOrthonormalBasis{ vec_basis::TVec end -(+)(v1::FVector, v2::FVector) = FVector(v1.type, v1.data + v2.data) +(+)(X::FVector, Y::FVector) = FVector(X.type, X.data + Y.data) -(-)(v1::FVector, v2::FVector) = FVector(v1.type, v1.data - v2.data) -(-)(v::FVector) = FVector(v.type, -v.data) +(-)(X::FVector, Y::FVector) = FVector(X.type, X.data - Y.data) +(-)(X::FVector) = FVector(X.type, -X.data) -(*)(a::Number, v::FVector) = FVector(v.type, a * v.data) +(*)(a::Number, X::FVector) = FVector(X.type, a * X.data) -function copyto!(y::FVector, x::FVector) - copyto!(y.data, x.data) - return y +function copyto!(X::FVector, Y::FVector) + copyto!(X.data, Y.data) + return X end -base_manifold(B::VectorBundleFibers) = base_manifold(B.M) +base_manifold(B::VectorBundleFibers) = base_manifold(B.manifold) base_manifold(B::VectorSpaceAtPoint) = base_manifold(B.fiber) -base_manifold(B::VectorBundle) = base_manifold(B.M) +base_manifold(B::VectorBundle) = base_manifold(B.manifold) """ bundle_projection(B::VectorBundle, x::ProductRepr) -Projection of point `x` from the bundle `M` to the base manifold. -Returns the point on the base manifold `B.M` at which the vector part -of `x` is attached. +Projection of point `p` from the bundle `M` to the base manifold. +Returns the point on the base manifold `B.manifold` at which the vector part +of `p` is attached. """ -bundle_projection(B::VectorBundle, x) = submanifold_component(B.M, x, Val(1)) +bundle_projection(B::VectorBundle, p) = submanifold_component(B.manifold, p, Val(1)) """ - distance(B::VectorBundleFibers, x, v, w) + distance(B::VectorBundleFibers, p, X, Y) -Distance between vectors `v` and `w` from the vector space at point `x` -from the manifold `M.M`, that is the base manifold of `M`. +Distance between vectors `X` and `Y` from the vector space at point `p` +from the manifold `B.manifold`, that is the base manifold of `M`. """ -distance(B::VectorBundleFibers, x, v, w) = norm(B, x, v - w) -@doc doc""" - distance(B::VectorBundle, x, y) +distance(B::VectorBundleFibers, p, X, Y) = norm(B, p, X - Y) +@doc raw""" + distance(B::VectorBundle, p, q) Distance between points $x$ and $y$ from the -vector bundle `B` over manifold `B.VS` (denoted $M$). +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, \xi_y)$. + Similarly, $q = (x_q, V_q)$. The distance is calculated as -$d_B(x, y) = \sqrt{d_M(p_x, p_y)^2 + d_F(\xi_x, \xi_{y\to x})^2}$ +$d_B(x, y) = \sqrt{d_M(x_p, x_q)^2 + d_F(V_p, V_{q←p})^2}$ -where $d_M$ is the distance on manifold $M$, $d_F$ is the distance -between two vectors from the fiber $F$ and $\xi_{y\to x}$ is the result -of parallel transport of vector $\xi_y$ to point $p_x$. The default +where $d_\mathcal M$ is the distance on manifold $\mathcal M$, $d_F$ is the distance +between two vectors from the fiber $F$ and $V_{q←p}$ is the result +of parallel transport of vector $V_q$ to point $x_p$. The default behavior of [`vector_transport_to`](@ref) is used to compute the vector transport. """ -function distance(B::VectorBundle, x, y) - px, ξx = submanifold_components(B.M, x) - py, ξy = submanifold_components(B.M, y) - dist_man = distance(B.M, px, py) - vy_x = vector_transport_to(B.M, py, ξy, px) - dist_vec = distance(B.VS, px, ξx, vy_x) +function distance(B::VectorBundle, p, q) + xp, Vp = submanifold_components(B.manifold, p) + xq, Vq = submanifold_components(B.manifold, q) + dist_man = distance(B.manifold, xp, xq) + vy_x = vector_transport_to(B.manifold, xq, Vq, xp) + dist_vec = distance(B.fiber, xp, Vp, vy_x) return sqrt(dist_man^2 + dist_vec^2) end @@ -212,134 +212,134 @@ function number_eltype(::Type{FVector{TType,TData}}) where {TType<:VectorSpaceTy end number_eltype(v::FVector) = number_eltype(v.data) -@doc doc""" - exp(B::VectorBundle, x, v) +@doc raw""" + exp(B::VectorBundle, p, X) -Exponential map of tangent vector $v$ at point $x$ from -vector bundle `B` over manifold `B.VS` (denoted $M$). +Exponential map of tangent vector $X$ at point $p$ from +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (\xi_{v,M}, \xi_{v,F}) \in T_{x}B$ where - $\xi_{v,M}$ is a tangent vector from the tangent space $T_{p_x}M$ and - $\xi_{v,F}$ is a tangent vector from the tangent space $T_{\xi_x}F$ (isomorphic to $F$). + * The tangent vector $X = (V_{X,M}, V_{X,F}) ∈ T_pB$ where + $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and + $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). The exponential map is calculated as -$\exp_{x}(v) = (\exp_{p_x}(\xi_{v,M}), \xi_{\exp})$ +$\exp_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp})$ -where $\xi_{\exp}$ is the result of vector transport of $\xi_x + \xi_{v,F}$ -to the point $\exp_{p_x}(\xi_{v,M})$. -The sum $\xi_x + \xi_{v,F}$ corresponds to the exponential map in the vector space $F$. +where $V_{\exp}$ is the result of vector transport of $V_p + V_{X,F}$ +to the point $\exp_{x_p}(V_{X,M})$. +The sum $V_p + V_{X,F}$ corresponds to the exponential map in the vector space $F$. """ exp(::VectorBundle, ::Any) -function exp!(B::VectorBundle, y, x, v) - px, ξx = submanifold_components(B.M, x) - py, ξy = submanifold_components(B.M, y) - ξvM, ξvF = submanifold_components(B.M, v) - exp!(B.M, py, px, ξvM) - vector_transport_to!(B.M, ξy, px, ξx + ξvF, py) - return y +function exp!(B::VectorBundle, q, p, X) + xp, Xp = submanifold_components(B.manifold, p) + xq, Xq = submanifold_components(B.manifold, q) + VXM, VXF = submanifold_components(B.manifold, X) + exp!(B.manifold, xq, xp, VXM) + vector_transport_to!(B.manifold, Xq, xp, Xp + VXF, xq) + return q end -@doc doc""" - flat(M::Manifold, x, w::FVector) +@doc raw""" + flat(M::Manifold, p, X::FVector) -Compute the flat isomorphism (one of the musical isomorphisms) of tangent vector `w` -from the vector space of type `M` at point `x` from the underlying [`Manifold`](@ref). +Compute the flat isomorphism (one of the musical isomorphisms) of tangent vector `X` +from the vector space of type `M` at point `p` from the underlying [`Manifold`](@ref). The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$\flat \colon T\mathcal M \to T^{*}\mathcal M$ +$♭ : T\mathcal M → T^{*}\mathcal M$ """ -function flat(M::Manifold, x, w::FVector) - v = allocate_result(M, flat, w, x) - return flat!(M, v, x, w) +function flat(M::Manifold, p, X::FVector) + ξ = allocate_result(M, flat, X, p) + return flat!(M, ξ, p, X) end -function flat!(M::Manifold, v::FVector, x, w::FVector) +function flat!(M::Manifold, ξ::FVector, p, X::FVector) error( "flat! not implemented for vector bundle fibers space " * - "of type $(typeof(M)), vector of type $(typeof(v)), point of " * - "type $(typeof(x)) and vector of type $(typeof(w)).", + "of type $(typeof(M)), vector of type $(typeof(ξ)), point of " * + "type $(typeof(p)) and vector of type $(typeof(X)).", ) end -function get_basis(M::VectorBundle, x, B::DiagonalizingOrthonormalBasis) - xp1 = submanifold_component(x, Val(1)) - bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(1))) - b1 = get_basis(M.M, xp1, bv1) - bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(2))) - b2 = get_basis(M.VS, xp1, bv2) +function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) + xp1 = submanifold_component(p, Val(1)) + bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) + b1 = get_basis(M.manifold, xp1, bv1) + bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) + b2 = get_basis(M.fiber, xp1, bv2) return PrecomputedVectorBundleOrthonormalBasis(b1, b2) end -function get_basis(M::TangentBundleFibers, x, B::DiagonalizingOrthonormalBasis) - return get_basis(M.M, x, B) +function get_basis(M::TangentBundleFibers, p, B::DiagonalizingOrthonormalBasis) + return get_basis(M.manifold, p, B) end -function get_coordinates(M::VectorBundle, x, v, B::ArbitraryOrthonormalBasis) where {N} - px, ξx = submanifold_components(M.M, x) - ξvM, ξvF = submanifold_components(M.M, v) - coord1 = get_coordinates(M.M, px, ξvM, B) - coord2 = get_coordinates(M.VS, px, ξvF, B) +function get_coordinates(M::VectorBundle, p, X, B::ArbitraryOrthonormalBasis) where {N} + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + coord1 = get_coordinates(M.manifold, px, VXM, B) + coord2 = get_coordinates(M.fiber, px, VXF, B) return vcat(coord1, coord2) end function get_coordinates( M::VectorBundle, - x, - v, + p, + X, B::PrecomputedVectorBundleOrthonormalBasis, ) where {N} - px, ξx = submanifold_components(M.M, x) - ξvM, ξvF = submanifold_components(M.M, v) - coord1 = get_coordinates(M.M, px, ξvM, B.base_basis) - coord2 = get_coordinates(M.VS, px, ξvF, B.vec_basis) + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + coord1 = get_coordinates(M.manifold, px, VXM, B.base_basis) + coord2 = get_coordinates(M.fiber, px, VXF, B.vec_basis) return vcat(coord1, coord2) end -function get_coordinates(M::TangentBundleFibers, x, v, B::AbstractBasis) where {N} - return get_coordinates(M.M, x, v, B) +function get_coordinates(M::TangentBundleFibers, p, X, B::AbstractBasis) where {N} + return get_coordinates(M.manifold, p, X, B) end -function get_vector(M::VectorBundle, x, v, B::ArbitraryOrthonormalBasis) where {N} - mdim = manifold_dimension(M.M) - xp1 = submanifold_component(x, Val(1)) - v1 = get_vector(M.M, xp1, v[1:mdim], B) - v2 = get_vector(M.VS, xp1, v[mdim+1:end], B) +function get_vector(M::VectorBundle, p, X, B::ArbitraryOrthonormalBasis) where {N} + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + v1 = get_vector(M.manifold, xp1, X[1:n], B) + v2 = get_vector(M.fiber, xp1, X[n+1:end], B) return ProductRepr(v1, v2) end function get_vector( M::VectorBundle, - x, - v, + p, + X, B::PrecomputedVectorBundleOrthonormalBasis, ) where {N} - mdim = manifold_dimension(M.M) - xp1 = submanifold_component(x, Val(1)) - v1 = get_vector(M.M, xp1, v[1:mdim], B.base_basis) - v2 = get_vector(M.VS, xp1, v[mdim+1:end], B.vec_basis) + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + v1 = get_vector(M.manifold, xp1, X[1:n], B.base_basis) + v2 = get_vector(M.fiber, xp1, X[n+1:end], B.vec_basis) return ProductRepr(v1, v2) end -function get_vector(M::TangentBundleFibers, x, v, B::AbstractBasis) where {N} - return get_vector(M.M, x, v, B) +function get_vector(M::TangentBundleFibers, p, X, B::AbstractBasis) where {N} + return get_vector(M.manifold, p, X, B) end -function get_vectors(M::VectorBundle, x, B::PrecomputedVectorBundleOrthonormalBasis) - xp1 = submanifold_component(x, Val(1)) - zero_m = zero_tangent_vector(M.M, xp1) - zero_f = zero_vector(M.VS, xp1) +function get_vectors(M::VectorBundle, p, B::PrecomputedVectorBundleOrthonormalBasis) + xp1 = submanifold_component(p, Val(1)) + zero_m = zero_tangent_vector(M.manifold, xp1) + zero_f = zero_vector(M.fiber, xp1) vs = typeof(ProductRepr(zero_m, zero_f))[] - for bv in get_vectors(M.M, xp1, B.base_basis) + for bv in get_vectors(M.manifold, xp1, B.base_basis) push!(vs, ProductRepr(bv, zero_f)) end - for bv in get_vectors(M.VS, xp1, B.vec_basis) + for bv in get_vectors(M.fiber, xp1, B.vec_basis) push!(vs, ProductRepr(zero_m, bv)) end return vs end -get_vectors(::VectorBundleFibers, x, B::PrecomputedOrthonormalBasis) = B.vectors +get_vectors(::VectorBundleFibers, p, B::PrecomputedOrthonormalBasis) = B.vectors function get_vectors(::VectorBundleFibers, x, B::PrecomputedDiagonalizingOrthonormalBasis) return B.vectors end @@ -347,207 +347,206 @@ end Base.@propagate_inbounds getindex(x::FVector, i) = getindex(x.data, i) """ - inner(B::VectorBundleFibers, x, v, w) + inner(B::VectorBundleFibers, p, X, Y) -Inner product of vectors `v` and `w` from the vector space of type `B.VS` -at point `x` from manifold `B.M`. +Inner product of vectors `X` and `Y` from the vector space of type `B.fiber` +at point `p` from manifold `B.manifold`. """ -function inner(B::VectorBundleFibers, x, v, w) +function inner(B::VectorBundleFibers, p, X, Y) error( "inner not defined for vector space family of type $(typeof(B)), " * - "point of type $(typeof(x)) and " * - "vectors of types $(typeof(v)) and $(typeof(w)).", + "point of type $(typeof(p)) and " * + "vectors of types $(typeof(X)) and $(typeof(Y)).", ) end -inner(B::VectorBundleFibers{<:TangentSpaceType}, x, v, w) = inner(B.M, x, v, w) -function inner(B::VectorBundleFibers{<:CotangentSpaceType}, x, v, w) +inner(B::VectorBundleFibers{<:TangentSpaceType}, p, X, Y) = inner(B.manifold, p, X, Y) +function inner(B::VectorBundleFibers{<:CotangentSpaceType}, p, X, Y) return inner( - B.M, - x, - sharp(B.M, x, FVector(CotangentSpace, v)).data, - sharp(B.M, x, FVector(CotangentSpace, w)).data, + B.manifold, + p, + sharp(B.manifold, p, FVector(CotangentSpace, X)).data, + sharp(B.manifold, p, FVector(CotangentSpace, Y)).data, ) end -@doc doc""" - inner(B::VectorBundle, x, v, w) +@doc raw""" + inner(B::VectorBundle, p, X, Y) -Inner product of tangent vectors `v` and `w` at point `x` from the -vector bundle `B` over manifold `B.VS` (denoted $M$). +Inner product of tangent vectors `X` and `Y` at point `p` from the +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (\xi_{v,M}, \xi_{v,F}) \in T_{x}B$ where - $\xi_{v,M}$ is a tangent vector from the tangent space $T_{p_x}M$ and - $\xi_{v,F}$ is a tangent vector from the tangent space $T_{\xi_x}F$ (isomorphic to $F$). - Similarly for the other tangent vector $w = (\xi_{w,M}, \xi_{w,F}) \in T_{x}B$. + * The tangent vector $v = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where + $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and + $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). + Similarly for the other tangent vector $w = (V_{Y,M}, V_{Y,F}) ∈ T_{x}B$. The inner product is calculated as -$\langle v, w \rangle_{B} = \langle \xi_{v,M}, \xi_{w,M} \rangle_{M} + \langle \xi_{v,F}, \xi_{w,F} \rangle_{F}.$ +$⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ """ -function inner(B::VectorBundle, x, v, w) - px, ξx = submanifold_components(B.M, x) - ξvM, ξvF = submanifold_components(B.M, v) - ξwM, ξwF = submanifold_components(B.M, w) - return inner(B.M, px, ξvM, ξwM) + inner(B.VS, ξx, ξvF, ξwF) +function inner(B::VectorBundle, p, X, Y) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + return inner(B.manifold, px, VXM, VYM) + inner(B.fiber, Vx, VXF, VYF) end -function isapprox(B::VectorBundle, x, y; kwargs...) - px, ξx = submanifold_components(B.M, x) - py, ξy = submanifold_components(B.M, y) - return isapprox(B.M, px, py; kwargs...) && isapprox(ξx, ξy; kwargs...) +function isapprox(B::VectorBundle, p, q; kwargs...) + xp, Vp = submanifold_components(B.manifold, p) + xq, Vq = submanifold_components(B.manifold, q) + return isapprox(B.manifold, xp, xq; kwargs...) && isapprox(Vp, Vq; kwargs...) end -function isapprox(B::VectorBundle, x, v, w; kwargs...) - px, ξx = submanifold_components(B.M, x) - ξvM, ξvF = submanifold_components(B.M, v) - ξwM, ξwF = submanifold_components(B.M, w) - return isapprox(B.M, ξvM, ξwM; kwargs...) && isapprox(B.M, px, ξvF, ξwF; kwargs...) +function isapprox(B::VectorBundle, p, X, Y; kwargs...) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + return isapprox(B.manifold, VXM, VYM; kwargs...) && isapprox(B.manifold, px, VXF, VYF; kwargs...) end -@doc doc""" - log(B::VectorBundle, x, y) +@doc raw""" + log(B::VectorBundle, p, q) -Logarithmic map of the point $y$ at point $x$ from -vector bundle `B` over manifold `B.VS` (denoted $M$). +Logarithmic map of the point `y` at point `p` from +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, \xi_y)$. + Similarly, $q = (x_q, V_q)$. The logarithmic map is calculated as -$\log_{x}(y) = (\log_{p_x}(p_y), \xi_{\log} - \xi_x)$ +$\log_p q = (\log_{x_p}(x_q), V_{\log} - V_p)$ -where $\xi_{\log}$ is the result of vector transport of $\xi_y$ -to the point $p_x$. -The difference $\xi_{\log} - \xi_x$ corresponds to the logarithmic map in the vector space $F$. +where $V_{\log}$ is the result of vector transport of $V_q$ to the point $x_p$. +The difference $V_{\log} - V_p$ corresponds to the logarithmic map in the vector space $F$. """ log(::VectorBundle, ::Any...) -function log!(B::VectorBundle, v, x, y) - px, ξx = submanifold_components(B.M, x) - py, ξy = submanifold_components(B.M, y) - ξvM, ξvF = submanifold_components(B.M, v) - log!(B.M, ξvM, px, py) - vector_transport_to!(B.M, ξvF, py, ξy, px) - copyto!(ξvF, ξvF - ξx) - return v +function log!(B::VectorBundle, X, p, q) + px, Vx = submanifold_components(B.manifold, p) + py, Vy = submanifold_components(B.manifold, q) + VXM, VXF = submanifold_components(B.manifold, X) + log!(B.manifold, VXM, px, py) + vector_transport_to!(B.manifold, VXF, py, Vy, px) + copyto!(VXF, VXF - Vx) + return X end -manifold_dimension(B::VectorBundle) = manifold_dimension(B.M) + vector_space_dimension(B.VS) +manifold_dimension(B::VectorBundle) = manifold_dimension(B.manifold) + vector_space_dimension(B.fiber) """ - norm(B::VectorBundleFibers, x, v) + norm(B::VectorBundleFibers, p, q) -Norm of the vector `v` from the vector space of type `B.VS` -at point `x` from manifold `B.M`. +Norm of the vector `X` from the vector space of type `B.fiber` +at point `p` from manifold `B.manifold`. """ -norm(B::VectorBundleFibers, x, v) = sqrt(inner(B, x, v, v)) -norm(B::VectorBundleFibers{<:TangentSpaceType}, x, v) = norm(B.M, x, v) +norm(B::VectorBundleFibers, p, X) = sqrt(inner(B, p, X, X)) +norm(B::VectorBundleFibers{<:TangentSpaceType}, p, X) = norm(B.manifold, p, X) -@doc doc""" - project_point(B::VectorBundle, x) +@doc raw""" + project_point(B::VectorBundle, p) -Project the point $x$ from the ambient space of the vector bundle `B` -over manifold `B.VS` (denoted $M$) to the vector bundle. +Project the point `p` from the ambient space of the vector bundle `B` +over manifold `B.fiber` (denoted $\mathcal M$) to the vector bundle. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x$ belongs to the ambient space of $M$ - and $\xi_x$ belongs to the ambient space of the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p$ belongs to the ambient space of $\mathcal M$ + and $V_p$ belongs to the ambient space of the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. -The projection is calculated by projecting the point $p_x$ to the manifold $M$ -and then projecting the vector $\xi_x$ to the tangent space $T_{p_x}M$. +The projection is calculated by projecting the point $x_p$ to the manifold $\mathcal M$ +and then projecting the vector $V_p$ to the tangent space $T_{x_p}\mathcal M$. """ project_point(::VectorBundle, ::Any...) -function project_point!(B::VectorBundle, x) - px, ξx = submanifold_components(B.M, x) - project_point!(B.M, px) - project_tangent!(B.M, ξx, px, ξx) - return x +function project_point!(B::VectorBundle, p) + px, Vx = submanifold_components(B.manifold, p) + project_point!(B.manifold, px) + project_tangent!(B.manifold, Vx, px, Vx) + return p end -@doc doc""" - project_tangent(B::VectorBundle, x, v) +@doc raw""" + project_tangent(B::VectorBundle, p, X) -Project the element $v$ of the ambient space of the tangent space $T_x B$ -to the tangent space $T_x B$. +Project the element `X` of the ambient space of the tangent space $T_p B$ +to the tangent space $T_p B$. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. - * The vector $x = (\xi_{v,M}, \xi_{v,F})$ where $p_x$ belongs to the ambient space of $T_{p_x}M$ - and $\xi_{v,F}$ belongs to the ambient space of the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The vector $x = (V_{X,M}, V_{X,F})$ where $x_p$ belongs to the ambient space of $T_{x_p}\mathcal M$ + and $V_{X,F}$ belongs to the ambient space of the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. -The projection is calculated by projecting $\xi_{v,M}$ to tangent space $T_{p_x}M$ -and then projecting the vector $\xi_{v,F}$ to the fiber $F$. +The projection is calculated by projecting $V_{X,M}$ to tangent space $T_{x_p}\mathcal M$ +and then projecting the vector $V_{X,F}$ to the fiber $F$. """ project_tangent(::VectorBundle, ::Any...) -function project_tangent!(B::VectorBundle, w, x, v) - px, ξx = submanifold_components(B.M, x) - ξvM, ξvF = submanifold_components(B.M, v) - ξwM, ξwF = submanifold_components(B.M, w) - project_tangent!(B.M, ξwM, px, ξvM) - project_tangent!(B.M, ξwF, px, ξvF) - return w +function project_tangent!(B::VectorBundle, Y, p, X) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + project_tangent!(B.manifold, VYM, px, VXM) + project_tangent!(B.manifold, VYF, px, VXF) + return Y end """ - project_vector(B::VectorBundleFibers, x, w) + project_vector(B::VectorBundleFibers, p, X) -Project vector `w` from the vector space of type `B.VS` at point `x`. +Project vector `X` from the vector space of type `B.fiber` at point `p`. """ -function project_vector(B::VectorBundleFibers, x, w) - v = allocate_result(B, project_vector, x, w) - return project_vector!(B, v, x, w) +function project_vector(B::VectorBundleFibers, p, X) + Y = allocate_result(B, project_vector, p, X) + return project_vector!(B, Y, p, X) end -function project_vector!(B::VectorBundleFibers{<:TangentSpaceType}, v, x, w) - return project_tangent!(B.M, v, x, w) +function project_vector!(B::VectorBundleFibers{<:TangentSpaceType}, Y, p, X) + return project_tangent!(B.manifold, Y, p, X) end -function project_vector!(B::VectorBundleFibers, v, x, w) - error("project_vector! not implemented for vector space family of type $(typeof(B)), output vector of type $(typeof(v)) and input vector at point $(typeof(x)) with type of w $(typeof(w)).") +function project_vector!(B::VectorBundleFibers, Y, p, X) + error("project_vector! not implemented for vector space family of type $(typeof(B)), output vector of type $(typeof(Y)) and input vector at point $(typeof(p)) with type of w $(typeof(X)).") end Base.@propagate_inbounds setindex!(x::FVector, val, i) = setindex!(x.data, val, i) -representation_size(B::VectorBundleFibers{<:TCoTSpaceType}) = representation_size(B.M) +representation_size(B::VectorBundleFibers{<:TCoTSpaceType}) = representation_size(B.manifold) function representation_size(B::VectorBundle) - len_manifold = prod(representation_size(B.M)) - len_vs = prod(representation_size(B.VS)) + len_manifold = prod(representation_size(B.manifold)) + len_vs = prod(representation_size(B.fiber)) return (len_manifold + len_vs,) end -@doc doc""" - sharp(M::Manifold, x, w::FVector) +@doc raw""" + sharp(M::Manifold, p, ξ::FVector) -Compute the sharp isomorphism (one of the musical isomorphisms) of vector `w` -from the vector space `M` at point `x` from the underlying [`Manifold`](@ref). +Compute the sharp isomorphism (one of the musical isomorphisms) of vector `ξ` +from the vector space `M` at point `p` from the underlying [`Manifold`](@ref). The function can be used for example to transform vectors from the cotangent bundle to vectors from the tangent bundle -$\sharp \colon T^{*}\mathcal M \to T\mathcal M$ +$♯ : T^{*}\mathcal M → T\mathcal M$ """ -function sharp(M::Manifold, x, w::FVector) - v = allocate_result(M, sharp, w, x) - return sharp!(M, v, x, w) +function sharp(M::Manifold, p, ξ::FVector) + X = allocate_result(M, sharp, ξ, p) + return sharp!(M, X, p, ξ) end -function sharp!(M::Manifold, v::FVector, x, w::FVector) +function sharp!(M::Manifold, X::FVector, p, ξ::FVector) error( "sharp! not implemented for vector bundle fibers space " * - "of type $(typeof(M)), vector of type $(typeof(v)), point of " * - "type $(typeof(x)) and vector of type $(typeof(w)).", + "of type $(typeof(M)), vector of type $(typeof(X)), point of " * + "type $(typeof(p)) and vector of type $(typeof(ξ)).", ) end @@ -557,7 +556,7 @@ function show(io::IO, tpt::TensorProductType) print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") end function show(io::IO, fiber::VectorBundleFibers) - print(io, "VectorBundleFibers($(fiber.VS), $(fiber.M))") + print(io, "VectorBundleFibers($(fiber.fiber), $(fiber.manifold))") end function show(io::IO, mime::MIME"text/plain", vs::VectorSpaceAtPoint) summary(io, vs) @@ -567,13 +566,13 @@ function show(io::IO, mime::MIME"text/plain", vs::VectorSpaceAtPoint) sf = replace(sf, '\n' => "\n$(pre)") println(io, pre, sf) println(io, "Base point:") - sp = sprint(show, "text/plain", vs.x; context = io, sizehint = 0) + sp = sprint(show, "text/plain", vs.point; context = io, sizehint = 0) sp = replace(sp, '\n' => "\n$(pre)") print(io, pre, sp) end -show(io::IO, vb::VectorBundle) = print(io, "VectorBundle($(vb.type), $(vb.M))") -show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.M))") -show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.M))") +show(io::IO, vb::VectorBundle) = print(io, "VectorBundle($(vb.type), $(vb.manifold))") +show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") +show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.manifold))") allocate(x::FVector) = FVector(x.type, allocate(x.data)) allocate(x::FVector, ::Type{T}) where {T} = FVector(x.type, allocate(x.data, T)) @@ -582,7 +581,7 @@ allocate(x::FVector, ::Type{T}) where {T} = FVector(x.type, allocate(x.data, T)) allocate_result(B::VectorBundleFibers, f, x...) Allocates an array for the result of function `f` that is -an element of the vector space of type `B.VS` on manifold `B.M` +an element of the vector space of type `B.fiber` on manifold `B.manifold` and arguments `x...` for implementing the non-modifying operation using the modifying operation. """ @@ -601,7 +600,7 @@ end allocate_result_type(B::VectorBundleFibers, f, args::NTuple{N,Any}) where N Returns type of element of the array that will represent the result of -function `f` for representing an operation with result in the vector space `VS` +function `f` for representing an operation with result in the vector space `fiber` for manifold `M` on given arguments (passed at a tuple). """ function allocate_result_type(B::VectorBundleFibers, f, args::NTuple{N,Any}) where {N} @@ -627,63 +626,63 @@ Dimension of the vector space of type `B`. function vector_space_dimension(B::VectorBundleFibers) error("vector_space_dimension not implemented for vector space family $(typeof(B)).") end -vector_space_dimension(B::VectorBundleFibers{<:TCoTSpaceType}) = manifold_dimension(B.M) +vector_space_dimension(B::VectorBundleFibers{<:TCoTSpaceType}) = manifold_dimension(B.manifold) function vector_space_dimension(B::VectorBundleFibers{<:TensorProductType}) dim = 1 - for space in B.VS.spaces - dim *= vector_space_dimension(VectorBundleFibers(space, B.M)) + for space in B.fiber.spaces + dim *= vector_space_dimension(VectorBundleFibers(space, B.manifold)) end return dim end """ - zero_vector(B::VectorBundleFibers, x) + zero_vector(B::VectorBundleFibers, p) -Compute the zero vector from the vector space of type `B.VS` at point `x` -from manifold `B.M`. +Compute the zero vector from the vector space of type `B.fiber` at point `p` +from manifold `B.manifold`. """ -function zero_vector(B::VectorBundleFibers, x) - v = allocate_result(B, zero_vector, x) - return zero_vector!(B, v, x) +function zero_vector(B::VectorBundleFibers, p) + X = allocate_result(B, zero_vector, p) + return zero_vector!(B, X, p) end """ - zero_vector!(B::VectorBundleFibers, v, x) + zero_vector!(B::VectorBundleFibers, X, p) -Save the zero vector from the vector space of type `B.VS` at point `x` -from manifold `B.M` to `v`. +Save the zero vector from the vector space of type `B.fiber` at point `p` +from manifold `B.manifold` to `X`. """ -function zero_vector!(B::VectorBundleFibers, v, x) +function zero_vector!(B::VectorBundleFibers, X, p) error("zero_vector! not implemented for vector space family of type $(typeof(B)).") end -function zero_vector!(B::VectorBundleFibers{<:TangentSpaceType}, v, x) - return zero_tangent_vector!(B.M, v, x) +function zero_vector!(B::VectorBundleFibers{<:TangentSpaceType}, X, p) + return zero_tangent_vector!(B.manifold, X, p) end -@doc doc""" - zero_tangent_vector(B::VectorBundle, x) +@doc raw""" + zero_tangent_vector(B::VectorBundle, p) -Zero tangent vector at point $x$ from the vector bundle `B` -over manifold `B.VS` (denoted $M$). The zero vector belongs to the space $T_{x}B$ +Zero tangent vector at point `p` from the vector bundle `B` +over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the - fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. The zero vector is calculated as -$\mathbf{0}_{x} = (\mathbf{0}_{p_x}, \mathbf{0}_F)$ +$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ -where $\mathbf{0}_{p_x}$ is the zero tangent vector from $T_{p_x}M$ and +where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and $\mathbf{0}_F$ is the zero element of the vector space $F$. """ zero_tangent_vector(::VectorBundle, ::Any...) -function zero_tangent_vector!(B::VectorBundle, v, x) - px, ξx = submanifold_components(B.M, x) - ξvM, ξvF = submanifold_components(B.M, v) - zero_tangent_vector!(B.M, ξvM, px) - zero_vector!(B.VS, ξvF, ξx) - return v +function zero_tangent_vector!(B::VectorBundle, X, p) + xp, Vp = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + zero_tangent_vector!(B.manifold, VXM, xp) + zero_vector!(B.fiber, VXF, Vp) + return X end diff --git a/src/numbers.jl b/src/numbers.jl index dc9b7ebca2..8bc9d079ef 100644 --- a/src/numbers.jl +++ b/src/numbers.jl @@ -39,7 +39,7 @@ Base.show(io::IO, ::QuaternionNumbers) = print(io, "ℍ") ^(𝔽::AbstractNumbers, n) = Euclidean(n...; field = 𝔽) -@doc doc""" +@doc raw""" real_dimension(𝔽::AbstractNumbers) Return the real dimension $\dim_ℝ 𝔽$ of the [`AbstractNumbers`] system `𝔽`. diff --git a/src/orthonormal_bases.jl b/src/orthonormal_bases.jl index 9c6647b8d1..1a5c8acc48 100644 --- a/src/orthonormal_bases.jl +++ b/src/orthonormal_bases.jl @@ -70,21 +70,21 @@ function ProjectedOrthonormalBasis(method::Symbol, F::AbstractNumbers = ℝ) return ProjectedOrthonormalBasis{method,F}() end -@doc doc""" - DiagonalizingOrthonormalBasis(v, F::AbstractNumbers = ℝ) +@doc raw""" + DiagonalizingOrthonormalBasis(frame_direction, F::AbstractNumbers = ℝ) An orthonormal basis `Ξ` as a vector of tangent vectors (of length determined by [`manifold_dimension`](@ref)) in the tangent space that diagonalizes the curvature -tensor $R(u,v)w$ and where the direction `v` has curvature `0`. +tensor $R(u,v)w$ and where the direction `frame_direction` $v$ has curvature `0`. The type parameter `F` denotes the [`AbstractNumbers`](@ref) that will be used as scalars. """ struct DiagonalizingOrthonormalBasis{TV,F} <: AbstractOrthonormalBasis{F} - v::TV + frame_direction::TV end -function DiagonalizingOrthonormalBasis(v, F::AbstractNumbers = ℝ) - return DiagonalizingOrthonormalBasis{typeof(v),F}(v) +function DiagonalizingOrthonormalBasis(X, F::AbstractNumbers = ℝ) + return DiagonalizingOrthonormalBasis{typeof(X),F}(X) end const ArbitraryOrDiagonalizingBasis = @@ -106,7 +106,7 @@ function PrecomputedOrthonormalBasis(vectors::AbstractVector, F::AbstractNumbers return PrecomputedOrthonormalBasis{typeof(vectors),F}(vectors) end -@doc doc""" +@doc raw""" DiagonalizingOrthonormalBasis(vectors, kappas, F::AbstractNumbers = ℝ) A precomputed orthonormal basis `Ξ` as a vector of tangent vectors (of length determined @@ -172,25 +172,25 @@ end function get_vector(M::Manifold, x, v, B::AbstractPrecomputedOrthonormalBasis) # quite convoluted but: # 1) preserves the correct `eltype` - # 2) guarantees a reasonable array type `vout` + # 2) guarantees a reasonable array type `Y` # (for example scalar * `SizedArray` is an `SArray`) bvectors = get_vectors(M, x, B) if isa(bvectors[1], ProductRepr) vt = v[1] * bvectors[1] - vout = allocate(bvectors[1], eltype(vt)) - copyto!(vout, vt) + Y = allocate(bvectors[1], eltype(vt)) + copyto!(Y, vt) for i = 2:length(v) - vout += v[i] * bvectors[i] + Y += v[i] * bvectors[i] end - return vout + return Y else vt = v[1] .* bvectors[1] - vout = allocate(bvectors[1], eltype(vt)) - copyto!(vout, vt) + Y = allocate(bvectors[1], eltype(vt)) + copyto!(Y, vt) for i = 2:length(v) - vout .+= v[i] .* bvectors[i] + Y .+= v[i] .* bvectors[i] end - return vout + return Y end end @@ -380,7 +380,7 @@ function show(io::IO, mime::MIME"text/plain", onb::DiagonalizingOrthonormalBasis io, "DiagonalizingOrthonormalBasis with coordinates in $(number_system(onb)) and eigenvalue 0 in direction:", ) - sk = sprint(show, "text/plain", onb.v, context = io, sizehint = 0) + sk = sprint(show, "text/plain", onb.frame_direction, context = io, sizehint = 0) sk = replace(sk, '\n' => "\n ") print(io, sk) end diff --git a/src/product_representations.jl b/src/product_representations.jl index d3f77bb4ba..758c680706 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -157,14 +157,14 @@ function ProductArray( end ProductArray(M::ShapeSpecification, data) = ProductArray(typeof(M), data, M.reshapers) -@doc doc""" +@doc raw""" prod_point(M::ShapeSpecification, pts...) Construct a product point from product manifold `M` based on point `pts` represented by [`ProductArray`](@ref). # Example -To construct a point on the product manifold $S^2 \times \mathbb{R}^2$ +To construct a point on the product manifold $S^2 × ℝ^2$ from points on the sphere and in the euclidean space represented by, respectively, `[1.0, 0.0, 0.0]` and `[-3.0, 2.0]` you need to construct shape specification first. It describes how linear storage of `ProductArray` @@ -187,7 +187,7 @@ function prod_point(M::ShapeSpecification, pts...) return ProductArray(M, Array(data)) end -@doc doc""" +@doc raw""" submanifold_component(M::Manifold, x, i::Integer) submanifold_component(M::Manifold, x, ::Val(i)) where {i} submanifold_component(x, i::Integer) @@ -201,7 +201,7 @@ submanifold_component(M::Manifold, x, i::Val) = submanifold_component(x, i) submanifold_component(x, ::Val{I}) where {I} = x.parts[I] submanifold_component(x, i::Integer) = submanifold_component(x, Val(i)) -@doc doc""" +@doc raw""" submanifold_components(M::Manifold, x) submanifold_components(x) diff --git a/src/projected_distribution.jl b/src/projected_distribution.jl index 640c807283..501b62c843 100644 --- a/src/projected_distribution.jl +++ b/src/projected_distribution.jl @@ -8,7 +8,7 @@ specified by providing the `x` argument. struct ProjectedPointDistribution{TResult,TM<:Manifold,TD<:Distribution,TProj} <: MPointDistribution{TM} manifold::TM - d::TD + distribution::TD proj!::TProj end @@ -26,41 +26,41 @@ function ProjectedPointDistribution( end function rand(rng::AbstractRNG, d::ProjectedPointDistribution{TResult}) where {TResult} - x = convert(TResult, rand(rng, d.d)) + x = convert(TResult, rand(rng, d.distribution)) return d.proj!(d.manifold, x) end function _rand!(rng::AbstractRNG, d::ProjectedPointDistribution, x::AbstractArray{<:Number}) - _rand!(rng, d.d, x) + _rand!(rng, d.distribution, x) return d.proj!(d.manifold, x) end support(d::ProjectedPointDistribution) = MPointSupport(d.manifold) """ - ProjectedFVectorDistribution(type::VectorBundleFibers, x, d, project_vector!) + ProjectedFVectorDistribution(type::VectorBundleFibers, p, d, project_vector!) Generates a random vector from ambient space of manifold `type.manifold` -at point `x` and projects it to vector space of type `type` using function +at point `p` and projects it to vector space of type `type` using function `project_vector!`, see [`project_vector`](@ref) for documentation. Generated arrays are of type `TResult`. """ struct ProjectedFVectorDistribution{ TResult, TSpace<:VectorBundleFibers, - TX, + ManifoldPoint, TD<:Distribution, TProj, -} <: FVectorDistribution{TSpace,TX} +} <: FVectorDistribution{TSpace,ManifoldPoint} type::TSpace - x::TX - d::TD + point::ManifoldPoint + distribution::TD project_vector!::TProj end function ProjectedFVectorDistribution( type::VectorBundleFibers, - x, + p, d::Distribution, project_vector!, xt::TResult, @@ -68,20 +68,20 @@ function ProjectedFVectorDistribution( return ProjectedFVectorDistribution{ TResult, typeof(type), - typeof(x), + typeof(p), typeof(d), typeof(project_vector!), }( type, - x, + p, d, project_vector!, ) end function rand(rng::AbstractRNG, d::ProjectedFVectorDistribution{TResult}) where {TResult} - v = convert(TResult, reshape(rand(rng, d.d), size(d.x))) - return d.project_vector!(d.type, v, d.x, v) + X = convert(TResult, reshape(rand(rng, d.distribution), size(d.point))) + return d.project_vector!(d.type, X, d.point, X) end function _rand!( @@ -93,4 +93,4 @@ function _rand!( return copyto!(v, rand(rng, d)) end -support(tvd::ProjectedFVectorDistribution) = FVectorSupport(tvd.type, tvd.x) +support(tvd::ProjectedFVectorDistribution) = FVectorSupport(tvd.type, tvd.point) diff --git a/src/statistics.jl b/src/statistics.jl index 6fc5d2871a..a5e1075dd7 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -14,7 +14,7 @@ struct CyclicProximalPointEstimation <: AbstractEstimationMethod end _unit_weights(n::Int) = ProbabilityWeights(ones(n), n) -@doc doc""" +@doc raw""" GeodesicInterpolation <: AbstractEstimationMethod Repeated weighted geodesic interpolation method for estimating the Riemannian @@ -24,16 +24,16 @@ The algorithm proceeds with the following simple online update: ```math \begin{aligned} -\mu_1 &= x_1\\ +μ_1 &= x_1\\ t_k &= \frac{w_k}{\sum_{i=1}^k w_i}\\ -\mu_{k} &= \gamma_{\mu_{k-1}}(x_k; t_k), +μ_{k} &= \gamma_{μ_{k-1}}(x_k; t_k), \end{aligned} ``` -where $x_k$ are points, $w_k$ are weights, $\mu_k$ is the $k$th estimate of the +where $x_k$ are points, $w_k$ are weights, $μ_k$ is the $k$th estimate of the mean, and $\gamma_x(y; t)$ is the point at time $t$ along the [`shortest_geodesic`](@ref shortest_geodesic(::Manifold, ::Any, ::Any, ::Real)) -between points $x,y \in \mathcal M$. The algorithm +between points $x,y ∈ \mathcal M$. The algorithm terminates when all $x_k$ have been considered. In the [`Euclidean`](@ref) case, this exactly computes the weighted mean. @@ -43,13 +43,13 @@ points are in an open geodesic ball about the mean with corresponding radius (see [`GeodesicInterpolationWithinRadius`](@ref)): * All simply connected complete Riemannian manifolds with non-positive sectional - curvature at radius $\infty$ [^Cheng2016], in particular: + curvature at radius $∞$ [^Cheng2016], in particular: + [`Euclidean`](@ref) + [`SymmetricPositiveDefinite`](@ref) [^Ho2013] * Other manifolds: - + [`Sphere`](@ref): $\frac{\pi}{2}$ [^Salehian2015] - + [`Grassmann`](@ref): $\frac{\pi}{4}$ [^Chakraborty2015] - + [`Stiefel`](@ref)/[`Rotations`](@ref): $\frac{\pi}{2 \sqrt 2}$ [^Chakraborty2019] + + [`Sphere`](@ref): $\frac{π}{2}$ [^Salehian2015] + + [`Grassmann`](@ref): $\frac{π}{4}$ [^Chakraborty2015] + + [`Stiefel`](@ref)/[`Rotations`](@ref): $\frac{π}{2 \sqrt 2}$ [^Chakraborty2019] For online variance computation, the algorithm additionally uses an analogous recursion to the weighted Welford algorithm [^West1979]. @@ -116,14 +116,14 @@ function show(io::IO, method::GeodesicInterpolationWithinRadius) print(io, "GeodesicInterpolationWithinRadius($(method.radius))") end -@doc doc""" +@doc raw""" mean(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) Compute the (optionally weighted) Riemannian center of mass also known as Karcher mean of the vector `x` of points on the [`Manifold`](@ref) `M`, defined as the point that satisfies the minimizer ````math -\argmin_{y\in\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), +\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). @@ -145,7 +145,7 @@ Compute the mean using the specified `method`. x::AbstractVector, [w::AbstractWeights,] method::GradientDescentEstimation; - x0=x[1], + p0=x[1], stop_iter=100, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -154,7 +154,7 @@ Compute the mean using the specified `method`. Compute the mean using the gradient descent scheme [`GradientDescentEstimation`](@ref). -Optionally, provide `x0`, the starting point (by default set to the first data +Optionally, provide `p0`, the starting point (by default set to the first data point). `stop_iter` denotes the maximal number of iterations to perform and the `kwargs...` are passed to [`isapprox`](@ref) to stop, when the minimal change between two iterates is small. For more stopping criteria check the @@ -193,7 +193,7 @@ function mean( return mean!(M, y, x, w, method...; kwargs...) end -@doc doc""" +@doc raw""" mean!(M::Manifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) mean!( M::Manifold, @@ -226,7 +226,7 @@ function mean!( x::AbstractVector, w::AbstractVector, ::GradientDescentEstimation; - x0 = x[1], + p0 = x[1], stop_iter = 100, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -236,7 +236,7 @@ function mean!( if length(w) != n throw(DimensionMismatch("The number of weights ($(length(w))) does not match the number of points for the mean ($(n)).")) end - copyto!(y, x0) + copyto!(y, p0) yold = allocate_result(M, mean, y) v = zero_tangent_vector(M, y) vtmp = copy(v) @@ -281,7 +281,7 @@ mean(::Manifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation) function mean!( M::Manifold, - y, + q, x::AbstractVector, w::AbstractVector, ::GeodesicInterpolation; @@ -298,19 +298,19 @@ function mean!( @inbounds begin j = order[1] s = w[j] - copyto!(y, x[j]) + copyto!(q, x[j]) end - v = zero_tangent_vector(M, y) - ytmp = allocate_result(M, mean, y) + v = zero_tangent_vector(M, q) + ytmp = allocate_result(M, mean, q) @inbounds for i = 2:n j = order[i] s += w[j] t = w[j] / s - inverse_retract!(M, v, y, x[j], inverse_retraction) - retract!(M, ytmp, y, v, t, retraction) - copyto!(y, ytmp) + inverse_retract!(M, v, q, x[j], inverse_retraction) + retract!(M, ytmp, q, v, t, retraction) + copyto!(q, ytmp) end - return y + return q end """ @@ -332,30 +332,30 @@ mean(::Manifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolationWith function mean!( M::Manifold, - y, + q, x::AbstractVector, w::AbstractVector, method::GeodesicInterpolationWithinRadius; shuffle_rng = nothing, kwargs..., ) - mean!(M, y, x, w, GeodesicInterpolation(); shuffle_rng = shuffle_rng, kwargs...) + mean!(M, q, x, w, GeodesicInterpolation(); shuffle_rng = shuffle_rng, kwargs...) radius = method.radius - injectivity_radius(M, y) ≤ radius && return y + injectivity_radius(M, q) ≤ radius && return q for i in eachindex(x) - @inbounds if distance(M, y, x[i]) ≥ radius - return mean!(M, y, x, w, GradientDescentEstimation(); x0 = y, kwargs...) + @inbounds if distance(M, q, x[i]) ≥ radius + return mean!(M, q, x, w, GradientDescentEstimation(); p0 = q, kwargs...) end end - return y + return q end function mean!( M::Manifold, - y, + q, x::AbstractVector, w::AbstractVector, ::CyclicProximalPointEstimation; - x0 = x[1], + p0 = x[1], stop_iter = 1000000, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -365,32 +365,32 @@ function mean!( if length(w) != n throw(DimensionMismatch("The number of weights ($(length(w))) does not match the number of points for the median ($(n)).")) end - copyto!(y, x0) - yold = allocate_result(M, median, y) + copyto!(q, p0) + yold = allocate_result(M, median, q) ytmp = copy(yold) - v = zero_tangent_vector(M, y) + X = zero_tangent_vector(M, q) wv = convert(Vector, w) ./ sum(w) for i = 1:stop_iter λ = 0.5 / i - copyto!(yold, y) + copyto!(yold, q) for j = 1:n @inbounds t = (2 * λ * wv[j]) / (1 + 2 * λ * wv[j]) - @inbounds inverse_retract!(M, v, y, x[j], inverse_retraction) - retract!(M, ytmp, y, v, t, retraction) - copyto!(y, ytmp) + @inbounds inverse_retract!(M, X, q, x[j], inverse_retraction) + retract!(M, ytmp, q, X, t, retraction) + copyto!(q, ytmp) end - isapprox(M, y, yold; kwargs...) && break + isapprox(M, q, yold; kwargs...) && break end - return y + return q end -@doc doc""" +@doc raw""" median(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) Compute the (optionally weighted) Riemannian median of the vector `x` of points on the [`Manifold`](@ref) `M`, defined as the point that satisfies the minimizer ````math -\argmin_{y\in\mathcal M} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{\mathcal M}(y,x_i), +\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). This function is nonsmooth (i.e nondifferentiable). @@ -413,7 +413,7 @@ Compute the median using the specified `method`. x::AbstractVector, [w::AbstractWeights,] method::CyclicProximalPointEstimation; - x0=x[1], + p0=x[1], stop_iter=1000000, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -422,7 +422,7 @@ Compute the median using the specified `method`. Compute the median using [`CyclicProximalPointEstimation`](@ref). -Optionally, provide `x0`, the starting point (by default set to the first +Optionally, provide `p0`, the starting point (by default set to the first data point). `stop_iter` denotes the maximal number of iterations to perform and the `kwargs...` are passed to [`isapprox`](@ref) to stop, when the minimal change between two iterates is small. For more stopping criteria check the @@ -460,7 +460,7 @@ function median( return median!(M, y, x, w, method...; kwargs...) end -@doc doc""" +@doc raw""" median!(M::Manifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) median!( M::Manifold, @@ -476,24 +476,24 @@ computes the [`median`](@ref) in-place in `y`. median!(::Manifold, ::Any...) function median!( M::Manifold, - y, + q, x::AbstractVector, method::AbstractEstimationMethod...; kwargs..., ) w = _unit_weights(length(x)) - return median!(M, y, x, w, method...; kwargs...) + return median!(M, q, x, w, method...; kwargs...) end function median!(M::Manifold, y, x::AbstractVector, w::AbstractVector; kwargs...) return median!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) end function median!( M::Manifold, - y, + q, x::AbstractVector, w::AbstractVector, ::CyclicProximalPointEstimation; - x0 = x[1], + p0 = x[1], stop_iter = 1000000, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -503,26 +503,26 @@ function median!( if length(w) != n throw(DimensionMismatch("The number of weights ($(length(w))) does not match the number of points for the median ($(n)).")) end - copyto!(y, x0) - yold = allocate_result(M, median, y) + copyto!(q, p0) + yold = allocate_result(M, median, q) ytmp = copy(yold) - v = zero_tangent_vector(M, y) + v = zero_tangent_vector(M, q) wv = convert(Vector, w) ./ sum(w) for i = 1:stop_iter λ = 0.5 / i - copyto!(yold, y) + copyto!(yold, q) for j = 1:n - @inbounds t = min(λ * wv[j] / distance(M, y, x[j]), 1.0) - @inbounds inverse_retract!(M, v, y, x[j], inverse_retraction) - retract!(M, ytmp, y, v, t, retraction) - copyto!(y, ytmp) + @inbounds t = min(λ * wv[j] / distance(M, q, x[j]), 1.0) + @inbounds inverse_retract!(M, v, q, x[j], inverse_retraction) + retract!(M, ytmp, q, v, t, retraction) + copyto!(q, ytmp) end - isapprox(M, y, yold; kwargs...) && break + isapprox(M, q, yold; kwargs...) && break end - return y + return q end -@doc doc""" +@doc raw""" var(M, x, m=mean(M, x); corrected=true, kwargs...) var(M, x, w::AbstractWeights, m=mean(M, x, w); corrected=false, kwargs...) @@ -557,7 +557,7 @@ function var(M::Manifold, x::AbstractVector, w::AbstractWeights; kwargs...) end var(M::Manifold, x::AbstractVector; kwargs...) = mean_and_var(M, x; kwargs...)[2] -@doc doc""" +@doc raw""" std(M, x, m=mean(M, x); corrected=true, kwargs...) std(M, x, w::AbstractWeights, m=mean(M, x, w); corrected=false, kwargs...) @@ -574,7 +574,7 @@ can be activated by setting `corrected=true`. """ std(M::Manifold, args...; kwargs...) = sqrt(var(M, args...; kwargs...)) -@doc doc""" +@doc raw""" mean_and_var(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, var) Compute the [`mean`](@ref mean(::Manifold, args...)) and the [`var`](@ref)iance @@ -617,7 +617,7 @@ function mean_and_var( return mean_and_var(M, x, w, method...; corrected = corrected, kwargs...) end -@doc doc""" +@doc raw""" mean_and_var( M::Manifold, x::AbstractVector @@ -721,7 +721,7 @@ function mean_and_var( injectivity_radius(M, y) ≤ radius && return y, v for i in eachindex(x) @inbounds if distance(M, y, x[i]) ≥ radius - mean!(M, y, x, w, GradientDescentEstimation(); x0 = y, kwargs...) + mean!(M, y, x, w, GradientDescentEstimation(); p0 = y, kwargs...) v = var(M, x, w, y; corrected = corrected) return y, v end @@ -729,7 +729,7 @@ function mean_and_var( return y, v end -@doc doc""" +@doc raw""" mean_and_std(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, std) Compute the [`mean`](@ref mean(::Manifold, args...)) and the standard deviation diff --git a/src/utils.jl b/src/utils.jl index e13b98c183..e50d7c5522 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" usinc(θ::Real) Unnormalized version of `sinc` function, i.e. @@ -7,7 +7,7 @@ equivalent to `sinc(θ/π)`. """ @inline usinc(θ::Real) = θ == 0 ? one(θ) : isinf(θ) ? zero(θ) : sin(θ) / θ -@doc doc""" +@doc raw""" usinc_from_cos(x::Real) Unnormalized version of `sinc` function, i.e. @@ -24,10 +24,10 @@ $x = cos(\theta)$. end end -allocate(x, s::Size{S}) where {S} = similar(x, S...) -allocate(x::StaticArray, s::Size{S}) where {S} = similar(x, maybesize(s)) -allocate(x, ::Type{T}, s::Size{S}) where {S,T} = similar(x, T, S...) -allocate(x::StaticArray, ::Type{T}, s::Size{S}) where {S,T} = similar(x, T, maybesize(s)) +allocate(p, s::Size{S}) where {S} = similar(p, S...) +allocate(p::StaticArray, s::Size{S}) where {S} = similar(p, maybesize(s)) +allocate(p, ::Type{T}, s::Size{S}) where {S,T} = similar(p, T, S...) +allocate(p::StaticArray, ::Type{T}, s::Size{S}) where {S,T} = similar(p, T, maybesize(s)) """ eigen_safe(x) diff --git a/test/circle.jl b/test/circle.jl index 818ff2e703..4c61f219df 100644 --- a/test/circle.jl +++ b/test/circle.jl @@ -29,6 +29,7 @@ include("utils.jl") x .+= 2*π project_point!(M,x) @test x == MVector(0.0) + @test project_tangent(M,0.0,1.) == 1. end types = [Float64, Float32] diff --git a/test/groups/array_manifold.jl b/test/groups/array_manifold.jl index c620a20200..13ee57f875 100644 --- a/test/groups/array_manifold.jl +++ b/test/groups/array_manifold.jl @@ -7,82 +7,82 @@ eg = Matrix{Float64}(I, 3, 3) ω = [[1.0, 2.0, 3.0], [3.0, 2.0, 1.0]] - x, y = [exp(M, eg, hat(M, eg, ωi)) for ωi in ω] - v = hat(M, eg, [-1.0, 2.0, 0.5]) + p, q = [exp(M, eg, hat(M, eg, ωi)) for ωi in ω] + X = hat(M, eg, [-1.0, 2.0, 0.5]) e = Identity(AG) @test e === Identity(G) - x2, y2 = ArrayMPoint(x), ArrayMPoint(y) - v2 = ArrayTVector(v) + p2, q2 = ArrayMPoint(p), ArrayMPoint(q) + X2 = ArrayTVector(X) - @test identity(AG, x2) isa ArrayMPoint - @test isapprox(G, identity(AG, x2).value, identity(G, x)) + @test identity(AG, p2) isa ArrayMPoint + @test isapprox(G, identity(AG, p2).value, identity(G, p)) @test identity(AG, e) === e @test_throws DomainError identity(AG, Identity(TranslationGroup(3))) - eg = similar(x2) - identity!(AG, eg, x2) - @test isapprox(G, eg.value, identity(G, x)) - eg = similar(x2) + eg = similar(p2) + identity!(AG, eg, p2) + @test isapprox(G, eg.value, identity(G, p)) + eg = similar(p2) identity!(AG, eg, e) - @test isapprox(G, eg.value, identity(G, x)) + @test isapprox(G, eg.value, identity(G, p)) - @test inv(AG, x2) isa ArrayMPoint - @test isapprox(G, inv(AG, x2).value, inv(G, x)) + @test inv(AG, p2) isa ArrayMPoint + @test isapprox(G, inv(AG, p2).value, inv(G, p)) @test inv(AG, e) === e @test_throws DomainError inv(AG, Identity(TranslationGroup(3))) - xinv = similar(x2) - inv!(AG, xinv, x2) - @test isapprox(G, xinv.value, inv(G, x)) - eg = similar(x2) + xinv = similar(p2) + inv!(AG, xinv, p2) + @test isapprox(G, xinv.value, inv(G, p)) + eg = similar(p2) inv!(AG, eg, e) @test isapprox(G, eg.value, e) - @test compose(AG, x2, y2) isa ArrayMPoint - @test isapprox(G, compose(AG, x2, y2).value, compose(G, x, y)) - @test compose(AG, x2, e) === x2 - @test compose(AG, e, x2) === x2 - - xy = similar(x2) - compose!(AG, xy, x2, y2) - @test isapprox(G, xy.value, compose(G, x, y)) - xy = similar(x2) - compose!(AG, xy, e, y2) - @test isapprox(G, xy.value, compose(G, e, y)) - xy = similar(x2) - compose!(AG, xy, x2, e) - @test isapprox(G, xy.value, compose(G, x, e)) + @test compose(AG, p2, q2) isa ArrayMPoint + @test isapprox(G, compose(AG, p2, q2).value, compose(G, p, q)) + @test compose(AG, p2, e) === p2 + @test compose(AG, e, p2) === p2 + + pq = similar(p2) + compose!(AG, pq, p2, q2) + @test isapprox(G, pq.value, compose(G, p, q)) + pq = similar(p2) + compose!(AG, pq, e, q2) + @test isapprox(G, pq.value, compose(G, e, q)) + pq = similar(p2) + compose!(AG, pq, p2, e) + @test isapprox(G, pq.value, compose(G, p, e)) for conv in (LeftAction(), RightAction()) - @test translate(AG, x2, y2, conv) isa ArrayMPoint - @test isapprox(G, translate(AG, x2, y2, conv).value, translate(G, x, y, conv)) + @test translate(AG, p2, q2, conv) isa ArrayMPoint + @test isapprox(G, translate(AG, p2, q2, conv).value, translate(G, p, q, conv)) - xy = similar(x2) - translate!(AG, xy, x2, y2, conv) - @test isapprox(G, xy.value, translate(G, x, y, conv)) + pq = similar(p2) + translate!(AG, pq, p2, q2, conv) + @test isapprox(G, pq.value, translate(G, p, q, conv)) - @test inverse_translate(AG, x2, y2, conv) isa ArrayMPoint - @test isapprox(G, inverse_translate(AG, x2, y2, conv).value, inverse_translate(G, x, y, conv)) + @test inverse_translate(AG, p2, q2, conv) isa ArrayMPoint + @test isapprox(G, inverse_translate(AG, p2, q2, conv).value, inverse_translate(G, p, q, conv)) - xinvy = similar(x2) - inverse_translate!(AG, xinvy, x2, y2, conv) - @test isapprox(G, xinvy.value, inverse_translate(G, x, y, conv)) + pinvq = similar(p2) + inverse_translate!(AG, pinvq, p2, q2, conv) + @test isapprox(G, pinvq.value, inverse_translate(G, p, q, conv)) end for conv in (LeftAction(), RightAction()) - @test translate_diff(AG, y2, x2, v2, conv; atol = 1e-10) isa ArrayTVector - @test isapprox(G, translate_diff(AG, y2, x2, v2, conv; atol = 1e-10).value, translate_diff(G, y, x, v, conv)) + @test translate_diff(AG, q2, p2, X2, conv; atol = 1e-10) isa ArrayTVector + @test isapprox(G, translate_diff(AG, q2, p2, X2, conv; atol = 1e-10).value, translate_diff(G, q, p, X, conv)) - vout = similar(v2) - translate_diff!(AG, vout, y2, x2, v2, conv; atol = 1e-10) - @test isapprox(vout.value, translate_diff(G, y, x, v, conv)) + Y = similar(X2) + translate_diff!(AG, Y, q2, p2, X2, conv; atol = 1e-10) + @test isapprox(Y.value, translate_diff(G, q, p, X, conv)) - @test inverse_translate_diff(AG, y2, x2, v2, conv; atol = 1e-10) isa ArrayTVector - @test isapprox(G, inverse_translate_diff(AG, y2, x2, v2, conv; atol = 1e-10).value, inverse_translate_diff(G, y, x, v, conv)) + @test inverse_translate_diff(AG, q2, p2, X2, conv; atol = 1e-10) isa ArrayTVector + @test isapprox(G, inverse_translate_diff(AG, q2, p2, X2, conv; atol = 1e-10).value, inverse_translate_diff(G, q, p, X, conv)) - vout = similar(v2) - inverse_translate_diff!(AG, vout, y2, x2, v2, conv; atol = 1e-10) - @test isapprox(vout.value, inverse_translate_diff(G, y, x, v, conv)) + Y = similar(X2) + inverse_translate_diff!(AG, Y, q2, p2, X2, conv; atol = 1e-10) + @test isapprox(Y.value, inverse_translate_diff(G, q, p, X, conv)) end end diff --git a/test/groups/group_utils.jl b/test/groups/group_utils.jl index 6684d081f6..650d86aba7 100644 --- a/test/groups/group_utils.jl +++ b/test/groups/group_utils.jl @@ -152,37 +152,37 @@ function test_group( end test_diff && @testset "translation differential" begin - v = v_pts[1] + X = v_pts[1] g21 = compose(G, g_pts[2], g_pts[1]) g12 = compose(G, g_pts[1], g_pts[2]) - @test isapprox(G, g12, translate_diff(G, g_pts[2], g_pts[1], v), translate_diff(G, g_pts[2], g_pts[1], v, LeftAction()); atol = atol) - @test is_tangent_vector(G, g12, translate_diff(G, g_pts[2], g_pts[1], v, LeftAction()), true; atol = atol) - RightAction() in diff_convs && @test is_tangent_vector(G, g21, translate_diff(G, g_pts[2], g_pts[1], v, RightAction()), true; atol = atol) + @test isapprox(G, g12, translate_diff(G, g_pts[2], g_pts[1], X), translate_diff(G, g_pts[2], g_pts[1], X, LeftAction()); atol = atol) + @test is_tangent_vector(G, g12, translate_diff(G, g_pts[2], g_pts[1], X, LeftAction()), true; atol = atol) + RightAction() in diff_convs && @test is_tangent_vector(G, g21, translate_diff(G, g_pts[2], g_pts[1], X, RightAction()), true; atol = atol) for conv in diff_convs g2g1 = translate(G, g_pts[2], g_pts[1], conv...) g2invg1 = inverse_translate(G, g_pts[2], g_pts[1], conv...) - @test isapprox(G, g_pts[1], inverse_translate_diff(G, g_pts[2], g2g1, translate_diff(G, g_pts[2], g_pts[1], v, conv...), conv...), v; atol = atol) - @test isapprox(G, g_pts[1], translate_diff(G, g_pts[2], g2invg1, inverse_translate_diff(G, g_pts[2], g_pts[1], v, conv...), conv...), v; atol = atol) + @test isapprox(G, g_pts[1], inverse_translate_diff(G, g_pts[2], g2g1, translate_diff(G, g_pts[2], g_pts[1], X, conv...), conv...), X; atol = atol) + @test isapprox(G, g_pts[1], translate_diff(G, g_pts[2], g2invg1, inverse_translate_diff(G, g_pts[2], g_pts[1], X, conv...), conv...), X; atol = atol) end test_mutating && @testset "mutating" begin for conv in diff_convs g2g1 = translate(G, g_pts[2], g_pts[1], conv...) g2invg1 = inverse_translate(G, g_pts[2], g_pts[1], conv...) - vout = allocate(v) - @test translate_diff!(G, vout, g_pts[2], g_pts[1], v, conv...) === vout - @test isapprox(G, g2g1, vout, translate_diff(G, g_pts[2], g_pts[1], v, conv...); atol = atol) - - vout = translate_diff(G, g_pts[2], g_pts[1], v, conv...) - vout2 = allocate(vout) - @test inverse_translate_diff!(G, vout2, g_pts[2], g2g1, vout, conv...) === vout2 - @test isapprox(G, g_pts[1], vout2, v; atol = atol) - - vout = inverse_translate_diff(G, g_pts[2], g_pts[1], v, conv...) - vout2 = allocate(vout) - @test translate_diff!(G, vout2, g_pts[2], g2invg1, vout, conv...) === vout2 - @test isapprox(G, g_pts[1], vout2, v; atol = atol) + Y = allocate(X) + @test translate_diff!(G, Y, g_pts[2], g_pts[1], X, conv...) === Y + @test isapprox(G, g2g1, Y, translate_diff(G, g_pts[2], g_pts[1], X, conv...); atol = atol) + + Y = translate_diff(G, g_pts[2], g_pts[1], X, conv...) + Z = allocate(Y) + @test inverse_translate_diff!(G, Z, g_pts[2], g2g1, Y, conv...) === Z + @test isapprox(G, g_pts[1], Z, X; atol = atol) + + Y = inverse_translate_diff(G, g_pts[2], g_pts[1], X, conv...) + Z = allocate(Y) + @test translate_diff!(G, Z, g_pts[2], g2invg1, Y, conv...) === Z + @test isapprox(G, g_pts[1], Z, X; atol = atol) end end end diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index 9f67fc3596..52c0cbb5b4 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -4,7 +4,6 @@ include("group_utils.jl") @testset "Semidirect product group" begin M1 = TranslationGroup(2) A = TranslationAction(M1, M1) - G = SemidirectProductGroup(M1, M1, A) @test G === GroupManifold( TranslationGroup(2) × TranslationGroup(2), diff --git a/test/metric.jl b/test/metric.jl index c86aad5fb0..f8364e871a 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -282,7 +282,9 @@ end @test norm(MM2, x, v) === norm(M, x, v) @test distance(MM2, x, y) === distance(M, x, y) @test exp!(MM2, y, x, v) === exp!(M, y, x, v) + @test exp(MM2, x, v) == exp(M, x, v) @test log!(MM2, v, x, y) === log!(M, v, x, y) + @test log(MM2, x, y) == log(M, x, y) @test retract!(MM2, y, x, v) === retract!(M, y, x, v) @test retract!(MM2, y, x, v, 1) === retract!(M, y, x, v, 1) @@ -297,8 +299,8 @@ end a = Manifolds.projected_distribution(M, Distributions.MvNormal(zero(zeros(3)), 1.0)) b = Manifolds.projected_distribution(MM2, Distributions.MvNormal(zero(zeros(3)), 1.0)) - @test isapprox(Matrix(a.d.Σ), Matrix(b.d.Σ)) - @test isapprox(a.d.μ, b.d.μ) + @test isapprox(Matrix(a.distribution.Σ), Matrix(b.distribution.Σ)) + @test isapprox(a.distribution.μ, b.distribution.μ) @test get_basis(M,x,ArbitraryOrthonormalBasis()) == get_basis(MM2,x,ArbitraryOrthonormalBasis()) @test_throws ErrorException get_basis(MM,x,ArbitraryOrthonormalBasis()) cov = flat(M, x, FVector(TangentSpace, v)) diff --git a/test/runtests.jl b/test/runtests.jl index f7b3c52e46..405c2ea7d2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,10 +12,10 @@ include("euclidean.jl") include("fixed_rank.jl") include("grassmann.jl") include("hyperbolic.jl") +include("rotations.jl") include("sphere.jl") include("stiefel.jl") include("symmetric.jl") -include("rotations.jl") include("symmetric_positive_definite.jl") include("torus.jl") diff --git a/test/statistics.jl b/test/statistics.jl index ff94db891c..6665cfe8b9 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -461,7 +461,7 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes S = Sphere(2) x = [[1.0, 0, 0], [0, 1.0, 0]] m = mean(S, x, GeodesicInterpolation()) - mg = mean(S, x, GradientDescentEstimation(); x0 = m) + mg = mean(S, x, GradientDescentEstimation(); p0 = m) vg = var(S, x, mg) @test mean(S, x, GeodesicInterpolationWithinRadius(Inf)) == m @@ -482,8 +482,8 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes P1 = SymmetricPositiveDefinite(3) P2 = MetricManifold(P1, LinearAffineMetric()) @testset "$P" for P in [P1, P2] - x0 = collect(exp(Symmetric(randn(rng, 3, 3) * 0.1))) - x = [exp(P, x0, Symmetric(randn(rng, 3, 3) * 0.1)) for _=1:10] + p0 = collect(exp(Symmetric(randn(rng, 3, 3) * 0.1))) + x = [exp(P, p0, Symmetric(randn(rng, 3, 3) * 0.1)) for _=1:10] w = pweights([rand(rng) for _ = 1:length(x)]) m = mean(P, x, w) mg = mean(P, x, w, GeodesicInterpolation()) @@ -496,13 +496,13 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes @testset "Sphere default" begin rng = MersenneTwister(47) S = Sphere(2) - x0 = [1.0, 0, 0] + p0 = [1.0, 0, 0] x = [normalize(randn(rng, 3)) for _=1:10] x = [x; -x] w = pweights([rand(rng) for _ = 1:length(x)]) m = mean(S, x, w) mg = mean(S, x, w, GeodesicInterpolation()) - mf = mean(S, x, w, GradientDescentEstimation(); x0 = mg) + mf = mean(S, x, w, GradientDescentEstimation(); p0 = mg) @test m != mg @test m == mf @@ -517,14 +517,14 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes @testset "Rotations default" begin rng = MersenneTwister(47) R = Manifolds.Rotations(3) - x0 = collect(Diagonal(ones(3))) - v1 = hat(R, x0, [1.0, 0.0, 0.0]) - v2 = hat(R, x0, [0.0, 1.0, 0.0]) - x = [exp(R, x0, v1, π/2*(1:4)); exp(R, x0, v2, π/2*(1:4))] + p0 = collect(Diagonal(ones(3))) + v1 = hat(R, p0, [1.0, 0.0, 0.0]) + v2 = hat(R, p0, [0.0, 1.0, 0.0]) + x = [exp(R, p0, v1, π/2*(1:4)); exp(R, p0, v2, π/2*(1:4))] w = pweights([rand(rng) for _ = 1:length(x)]) m = mean(R, x, w) mg = mean(R, x, w, GeodesicInterpolation()) - mf = mean(R, x, w, GradientDescentEstimation(); x0 = mg) + mf = mean(R, x, w, GradientDescentEstimation(); p0 = mg) @test m != mg @test m == mf @@ -540,16 +540,16 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes @testset "Grassmann default" begin rng = MersenneTwister(47) G = Manifolds.Grassmann(3, 2) - x0 = [1.0 0.0; 0.0 1.0; 0.0 0.0] - x = [exp(G, x0, project_tangent(G, x0, randn(rng, 3, 2) * 10)) for _= 1:10] + p0 = [1.0 0.0; 0.0 1.0; 0.0 0.0] + x = [exp(G, p0, project_tangent(G, p0, randn(rng, 3, 2) * 10)) for _= 1:10] w = pweights([rand(rng) for _ = 1:length(x)]) m = mean(G, x, w) mg = mean(G, x, w, GeodesicInterpolation()) - mf = mean(G, x, w, GradientDescentEstimation(); x0 = mg) + mf = mean(G, x, w, GradientDescentEstimation(); p0 = mg) @test m != mg @test m == mf - x = [exp(G, x0, project_tangent(G, x0, randn(rng, 3, 2) * 0.01)) for _= 1:10] + x = [exp(G, p0, project_tangent(G, p0, randn(rng, 3, 2) * 0.01)) for _= 1:10] w = pweights([rand(rng) for _ = 1:length(x)]) m = mean(G, x, w) mg = mean(G, x, w, GeodesicInterpolation()) diff --git a/test/symmetric_positive_definite.jl b/test/symmetric_positive_definite.jl index 5d66da2b28..7e3536cf1b 100644 --- a/test/symmetric_positive_definite.jl +++ b/test/symmetric_positive_definite.jl @@ -94,5 +94,8 @@ include("utils.jl") @test isapprox(0.0, inner(M2,x,X[i],X[j])) end end + d2onb = get_basis(M2, x, DiagonalizingOrthonormalBasis(v)) + @test donb.kappas == d2onb.kappas + @test donb.vectors==d2onb.vectors end end diff --git a/test/utils.jl b/test/utils.jl index 92f2ceb22f..337d00117f 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -110,8 +110,8 @@ function test_manifold(M::Manifold, pts::AbstractVector; end test_repr(Manifolds.representation_size(M)) - for VS ∈ (Manifolds.TangentSpace, Manifolds.CotangentSpace) - test_repr(Manifolds.representation_size(Manifolds.VectorBundleFibers(VS, M))) + for fiber ∈ (Manifolds.TangentSpace, Manifolds.CotangentSpace) + test_repr(Manifolds.representation_size(Manifolds.VectorBundleFibers(fiber, M))) end end @@ -434,7 +434,7 @@ function test_manifold(M::Manifold, pts::AbstractVector; for _ in 1:10 randtv = rand(tvd) atol = rand_tvector_atol_multiplier * find_eps(randtv) - @test is_tangent_vector(M, supp.x, randtv; atol = atol) + @test is_tangent_vector(M, supp.point, randtv; atol = atol) end end end diff --git a/test/vector_bundle.jl b/test/vector_bundle.jl index 0a24542554..aa8745fcd5 100644 --- a/test/vector_bundle.jl +++ b/test/vector_bundle.jl @@ -26,6 +26,9 @@ struct TestVectorSpaceType <: VectorSpaceType end @test (2*fv1).type == TangentSpace PM = ProductManifold(Sphere(2), Euclidean(2)) + @test_throws ErrorException flat(PM,ProductRepr([0.0,],[0.0]),FVector(CotangentSpace, ProductRepr([0.0],[0.0]))) + @test_throws ErrorException sharp(PM,ProductRepr([0.0,],[0.0]),FVector(TangentSpace, ProductRepr([0.0],[0.0]))) + fv2 = FVector(TangentSpace, ProductRepr([1.0, 0.0, 0.0], [1.0, 2.0])) @test submanifold_component(fv2, 1) == [1, 0, 0] @test submanifold_component(fv2, 2) == [1, 2] @@ -98,12 +101,12 @@ struct TestVectorSpaceType <: VectorSpaceType end 0.0""" @test base_manifold(t_x) == M @test base_manifold(ct_x) == M - @test t_x.fiber.M == M - @test ct_x.fiber.M == M - @test t_x.fiber.VS == TangentSpace - @test ct_x.fiber.VS == CotangentSpace - @test t_x.x == x - @test ct_x.x == x + @test t_x.fiber.manifold== M + @test ct_x.fiber.manifold== M + @test t_x.fiber.fiber == TangentSpace + @test ct_x.fiber.fiber == CotangentSpace + @test t_x.point == x + @test ct_x.point == x end @testset "tensor product" begin