From 436eb296d4d2b1a24e77e6dd24b29664eb318695 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 19:44:03 +0100 Subject: [PATCH 01/74] update documentation to use more utf8, especially in math. --- src/groups/group_action.jl | 10 +-- src/groups/special_euclidean.jl | 2 +- src/manifolds/CholeskySpace.jl | 8 +-- src/manifolds/Circle.jl | 16 ++--- src/manifolds/Euclidean.jl | 16 ++--- src/manifolds/FixedRankMatrices.jl | 28 ++++----- src/manifolds/GraphManifold.jl | 4 +- src/manifolds/Grassmann.jl | 48 +++++++------- src/manifolds/Hyperbolic.jl | 62 +++++++++---------- src/manifolds/MetricManifold.jl | 2 +- src/manifolds/PowerManifold.jl | 12 ++-- src/manifolds/ProductManifold.jl | 4 +- src/manifolds/Rotations.jl | 4 +- src/manifolds/Sphere.jl | 26 ++++---- src/manifolds/Stiefel.jl | 30 ++++----- src/manifolds/Symmetric.jl | 18 +++--- src/manifolds/SymmetricPositiveDefinite.jl | 10 +-- .../SymmetricPositiveDefiniteLinearAffine.jl | 4 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 12 ++-- .../SymmetricPositiveDefiniteLogEuclidean.jl | 2 +- src/manifolds/VectorBundle.jl | 24 +++---- src/product_representations.jl | 2 +- src/statistics.jl | 16 ++--- 23 files changed, 180 insertions(+), 180 deletions(-) diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 2067480fd1..10d40d9fbe 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -89,8 +89,8 @@ 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)_x (v) &: T_x ℳ → T_{a ⋅ x} ℳ\\ +(\mathrm{d}R_a)_x (v) &: T_x ℳ → T_{x ⋅ a} ℳ \end{aligned} ```` """ @@ -112,8 +112,8 @@ 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)_x^{-1} (v) &: T_x M → T_{a^{-1} ⋅ x} ℳ\\ +(\mathrm{d}R_a)_x^{-1} (v) &: T_x M → T_{x ⋅ a^{-1}} ℳ \end{aligned} ```` """ @@ -139,7 +139,7 @@ the element closest to `x2` in the metric of the G-manifold: ```math \arg\min_{g ∈ G} d_M(g ⋅ x_1, x_2) ``` -where $G$ is the group that acts on the G-manifold $M$. +where $G$ is the group that acts on the G-manifold $ℳ$. """ function optimal_alignment(A::AbstractGroupAction, x1, x2) error("optimal_alignment not implemented for $(typeof(A)) and points $(typeof(x1)) and $(typeof(x2)).") diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 007812ec1e..b679556de9 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -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},ℝ}, diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index dd782c40d8..6e0e6cb4a7 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -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 @@ -83,7 +83,7 @@ matrices `x`, `y` 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 + +d_{ℳ}(x,y) = \sqrt{\sum_{i>j} (x_{ij}-y_{ij})^2 + \sum_{j=1}^m (\log x_{jj} - \log y_{jj})^2 } ```` @@ -191,11 +191,11 @@ Parallely transport the tangent vector `v` at `x` along the geodesic to `y` on to the [`CholeskySpace`](@ref) manifold `M`. The formula reads ````math -\mathcal P_{y\gets x}(v) = \lfloor v \rfloor +\mathcal 𝒫_{y\gets x}(v) = \lfloor v \rfloor + \operatorname{diag}(y)\operatorname{diag}(x)^{-1}\operatorname{diag}(v), ```` -where $\lfloor\cdot\rfloor$ denotes the strictly lower triangular matrix, +where $\lfloor·\rfloor$ denotes the strictly lower triangular matrix, and $\operatorname{diag}$ extracts the diagonal matrix. """ vector_transport_to(::CholeskySpace, ::Any, ::Any, ::Any, ::ParallelTransport) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 482a4ec31a..279dd74c2f 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -1,8 +1,8 @@ @doc doc""" 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$ as a manifold ere manifold represented by +real-valued data in $[-\pi,\pi)$ or complex-valued data $z\in ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor @@ -21,7 +21,7 @@ Circle(f::AbstractNumbers = ℝ) = Circle{f}() 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$. +for the complex-valued case its a unit number, $x \in ℂ$ with $\lvert x \rvert = 1$. """ check_manifold_point(::Circle, ::Any...) @@ -100,7 +100,7 @@ Compute the exponential map on the [`Circle`](@ref). ````math \exp_xv = (x+v)_{2\pi}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, +where $(·)$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. For the complex-valued case the formula is the same as for the [`Sphere`](@ref) @@ -203,7 +203,7 @@ Compute the logarithmic map on the [`Circle`](@ref) `M`. ````math \exp_xv = (y,x)_{2\pi}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, +where $(·)$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. For the complex-valued case the formula is the same as for the [`Sphere`](@ref) @@ -244,7 +244,7 @@ end manifold_dimension(M::Circle) Return the dimension of the [`Circle`](@ref) `M`, -i.e. $\operatorname{dim}(\mathbb S^1) = 1$. +i.e. $\operatorname{dim}(𝕊^1) = 1$. """ manifold_dimension(::Circle) = 1 @@ -252,7 +252,7 @@ manifold_dimension(::Circle) = 1 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 +[`Circle`](@ref) $𝕊^1$ by the wrapped mean, i.e. the remainder of the mean modulo 2π. """ mean(::Circle, ::Any) @@ -320,7 +320,7 @@ 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)} +𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℂ}(x,y)} \bigl(\log_xy + \log_yx \bigr), ```` where [`log`](@ref) denotes the logarithmic map on `M`. diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 0a431dc281..d529933851 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -1,19 +1,19 @@ @doc doc""" 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·… 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 @@ -128,19 +128,19 @@ injectivity_radius(::Euclidean) = Inf 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}, ```` -where $I$ is the set of integer vectors $k\in\mathbb N^i$, such that for all +where $I$ is the set of integer vectors $k\in ℕ^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, ```` -where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ inner(::Euclidean, ::Any...) @inline inner(::Euclidean, x, v, w) = dot(v, w) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 2e3df2a24c..1792366996 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,34 +1,34 @@ @doc doc""" FixedRankMatrices{m,n,k,T} <: 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 (complex-valued) matrices of fixed rank $k$, i.e. ````math -\mathcal M = \{ x \in \mathbb R^{m\times n} : \operatorname{rank}(x) = k \}. +ℳ = \{ x \in ℝ^{m ⨉ n} : \operatorname{rank}(x) = k \}. ```` # 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 +A point $x\in ℳ$ can be stored using orthonormal matrices +$U\in ℝ^{m ⨉ k}$, $V\in ℝ^{n ⨉ 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 -The tangent space $T_x\mathcal M$ at a point $x\in\mathcal M$ with $x=USV^\mathrm{T}$ +The tangent space $T_x ℳ$ at a point $x\in ℳ$ with $x=USV^\mathrm{T}$ 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_xℳ = \bigl\{ UMV^\mathrm{T} + U_xV^\mathrm{T} + UV_x^\mathrm{T} : + M \in ℝ^{k ⨉ k}, + U_x \in ℝ^{m ⨉ k}, + V_x \in ℝ^{n ⨉ k} \text{ s.t. } U_x^\mathrm{T}U = 0_k, V_x^\mathrm{T}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. This implementation follows[^Vandereycken2013]. # Constructor FixedRankMatrics(m,n,k,t=ℝ) @@ -86,7 +86,7 @@ together with its base point, see for example [`FixedRankMatrices`](@ref) # 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{T}$, $M\in ℝ^{k ⨉ k}$ """ struct UMVTVector{TU<:AbstractMatrix,TM<:AbstractMatrix,TVt<:AbstractMatrix} <: TVector U::TU @@ -241,7 +241,7 @@ end 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 +Project the matrix $A\in ℝ^{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]. @@ -293,7 +293,7 @@ Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by comput y = U_kS_kV_k^\mathrm{T}, ```` 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 +in the sense that $S_k$ is the diagonal matrix of size $k ⨉ k$ with the $k$ largest singular values and $U$ and $V$ are shortened accordingly. """ retract(::FixedRankMatrices, ::Any, ::Any, ::PolarRetraction) diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index 1f49dbed5a..c3b2c272c3 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -190,7 +190,7 @@ end 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}. +d_{𝒩} = \lvert V \rVert d_ℳ. ```` """ function manifold_dimension(M::VertexGraphManifold) @@ -202,7 +202,7 @@ end 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}. +d_{𝒩} = \lvert E \rVert d_ℳ. ```` """ function manifold_dimension(M::EdgeGraphManifold) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index f9d3801be1..a44a4a2874 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -2,20 +2,20 @@ 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. +by $k$ linear independent vectors $𝔽^n$, where +$𝔽 \in \{ℝ, ℂ\}$ 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\}, +: x \in 𝔽^{n ⨉ k}, \bar{x}^\mathrm{T}x = 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$ +where ${\bar·}^{\mathrm{T}}$ denotes the complex conjugate transpose and +$I_k$ is the $k ⨉ k$ identity matrix. This means, that the columns of $x$ form an orthonormal 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. @@ -26,17 +26,17 @@ 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} : +v \in 𝔽^{n ⨉ k} : {\bar v}^{\mathrm{T}}x + {\bar x}^{\mathrm{T}}v = 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 @@ -51,7 +51,7 @@ 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 @@ -101,8 +101,8 @@ Check whether `v` is a tangent vector in the tangent space of `x` on the [`Grass x^{\mathrm{H}}v + v^{\mathrm{H}}x = 0_k, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ -denotes the $k\times k$ zero natrix. +where $·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ +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) @@ -139,7 +139,7 @@ end 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 +$x^\mathrm{H}y$, where $·^{\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)). @@ -166,7 +166,7 @@ Then the exponential map is written using z = x V\cos(S)V^\mathrm{H} + U\sin(S)V^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\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 @@ -197,7 +197,7 @@ The formula reads g_x(v,w) = \operatorname{trace}(v^{\mathrm{H}}w), ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inner(::Grassmann, x, v, w) = dot(v, w) @@ -211,7 +211,7 @@ Compute the inverse retraction for the [`PolarRetraction`](@ref), on the \operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inverse_retract(M::Grassmann, ::Any, ::Any, ::PolarInverseRetraction) @@ -227,7 +227,7 @@ Compute the inverse retraction valid of the [`QRRetraction`](@ref) ````math \operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inverse_retract(::Grassmann, ::Any, ::Any, ::QRInverseRetraction) @@ -241,15 +241,15 @@ isapprox(M::Grassmann, x, y; kwargs...) = isapprox(distance(M, x, y), 0.0; kwarg @doc doc""" log(M::Grassmann, x, y) -Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = \mathcal M=\mathrm{Gr}(n,k)$, +Compute the logarithmic map on the [`Grassmann`](@ref) `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 ````math -\log_xy = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, +\log_xy = V· \operatorname{atan}(S) · U^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\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 ````math @@ -314,7 +314,7 @@ which is computed by \operatorname{proj_x}(w) = w - xx^{\mathrm{H}}w, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ project_tangent(::Grassmann, ::Any...) @@ -337,7 +337,7 @@ Compute the SVD-based retraction [`PolarRetraction`](@ref) on the \operatorname{retr}_x v = UV^\mathrm{H}, ```` -where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. retract(M::Grassmann, x, v, ::QRRetraction ) @@ -346,7 +346,7 @@ Compute the QR-based retraction [`QRRetraction`](@ref) on the ````math \operatorname{retr}_xv = 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 ). ```` diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 9302f5b002..8e6fd8861e 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -1,31 +1,31 @@ @doc doc""" 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 +ℍ^n = \Bigl\{x\in ℝ^{n+1} +\ \Big|\ ⟨x,x⟩_{\mathrm{M}}= -x_{n+1}^2 + \displaystyle\sum_{k=1}^n x_k^2 = -1, x_{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 $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) +is Minkowski inner product. The tangent space $T_x ℍ^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_x ℍ^n \coloneqq \bigl\{ +v \in ℝ^{n+1} \ \bigl |\ ⟨x,v⟩_{\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}\subset ℝ^{n+1}$ """ struct Hyperbolic{N} <: Manifold end @@ -37,15 +37,15 @@ Hyperbolic(n::Int) = Hyperbolic{n}() 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. !!! 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$. + it is positive definite when restricted to a tangent space $T_xℳ$, + $x\in ℳ$, of the [`Hyperbolic`](@ref) space $ℳ$. """ struct MinkowskiMetric <: LorentzMetric end @@ -103,25 +103,25 @@ end 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}(x,y) = \operatorname{acosh}( - ⟨x, y⟩_{\mathrm{M}}), ```` -where $\langle\cdot,\cdot\rangle_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). +where $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ distance(M::Hyperbolic, x, y) = acosh(max(-minkowski_dot(x, y), 1.0)) @doc doc""" exp(M::Hyperbolic, x, v) -Compute the exponential map on the [`Hyperbolic`](@ref) space $\mathbb H^n$ eminating +Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ eminating from `x` towards `v`, which is optionally scaled by `t`. 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_x v = \cosh(\sqrt{⟨v,v⟩_{\mathrm{M}}})x ++ \sinh(\sqrt{⟨v,v⟩_{\mathrm{M}}})\frac{v}{\sqrt{⟨v,v⟩_{\mathrm{M}}}}, ```` -where $\langle\cdot,\cdot\rangle_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). +where $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ exp(::Hyperbolic, ::Any...) @@ -144,9 +144,9 @@ injectivity_radius(H::Hyperbolic, args...) = Inf inner(M::Hyperbolic, x, v, w) 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}$. +from $T_x ℍ^n$ of the [`Hyperbolic`](@ref) space $ℍ^n$ given by +$⟨w, v⟩_{\mathrm{M}}$ the [`minkowski_dot`](@ref) Minkowski +inner product on $ℝ^{n+1}$. """ @inline inner(M::Hyperbolic, x, w, v) = minkowski_dot(w, v) @@ -155,14 +155,14 @@ is_default_metric(::Hyperbolic, ::MinkowskiMetric) = Val(true) @doc doc""" log(M::Hyperbolic, x, y) -Compute the logarithmic map on the [`Hyperbolic`](@ref) space $\mathbb H^n$, the tangent +Compute the logarithmic map on the [`Hyperbolic`](@ref) space $ℍ^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$ ```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_x y = d_{ℍ^n}(x,y) +\frac{y-⟨x,y⟩_{\mathrm{M}} x}{\lVert y-⟨x,y⟩_{\mathrm{M}} x \rVert_2} ``` and is zero otherwise. """ @@ -183,7 +183,7 @@ 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) @@ -193,7 +193,7 @@ end @doc doc""" 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. $n$. """ manifold_dimension(::Hyperbolic{N}) where {N} = N @@ -223,9 +223,9 @@ the tangent space at `x` of the [`Hyperbolic`](@ref) space `M`. The formula reads ````math -w = v + \langle x,v\rangle_{\mathrm{M}} x, +w = v + ⟨x,v⟩_{\mathrm{M}} x, ```` -where $\langle \cdot, \cdot \rangle_{\mathrm{M}}$ denotes the Minkowski inner +where $⟨·, ·⟩_{\mathrm{M}}$ denotes the Minkowski inner product in the embedding, see [`minkowski_dot`](@ref). """ project_tangent(::Hyperbolic, ::Any...) @@ -246,11 +246,11 @@ sharp!(M::Hyperbolic, v::TFVector, x, w::CoTFVector) = copyto!(v, w) vector_transport_to(M::Hyperbolic, x, v, y, ::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) +[`Hyperbolic`](@ref) space $ℍ^n$ to the tangent at `y` along the [`geodesic`](@ref) connecting `x` and `y`. The formula reads ````math -P_{y\gets x}(v) = v - \frac{\langle \log_xy,v\rangle_x}{d^2_{\mathbb H^n}(x,y)} +𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℍ^n}(x,y)} \bigl(\log_xy + \log_yx \bigr). ```` """ diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 39bf64b5f9..d55502feb8 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -285,7 +285,7 @@ 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 ````math -g_x(v,w) = \langle v, G_x w\rangle, +g_x(v,w) = ⟨v, G_x w⟩, ```` where $G_x$ is the local matrix representation of the [`Metric`](@ref) `G`. """ diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 8c6f6eb7fd..bfa47ba810 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -41,7 +41,7 @@ abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation @doc doc""" 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 $ℳ^{n_1⨉ n_2 ⨉ \dots ⨉ 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 ⨉ \dots ⨉ 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 @@ -496,12 +496,12 @@ end 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 -size of the power manifold and $d_{\mathcal M}$ the dimension of the base manifold -$\mathcal M$, the manifold is of dimension +$=𝒩 = (ℳ)^{n_1,…,n_d}, i.e. with $n=(n_1,…,n_d)$ the array +size of the power manifold and $d_{ℳ}$ the dimension of the base manifold +$ℳ$, 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}. +d_{𝒩} = d_{ℳ}\prod_{i=1}^d n_i = n_1n_2·…· n_d d_{ℳ}. ```` """ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index b99d6a9414..3c5c8bba24 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,7 +1,7 @@ @doc doc""" 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 ⨉ \dots ⨉ 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 ⨉ \dots ⨉ M_n$. Alternatively, the same manifold can be contructed using the `×` operator: `M_1 × M_2 × M_3`. """ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index ada4a2a8be..49a5d960cb 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,14 +1,14 @@ @doc doc""" Rotations{N} <: Manifold -Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n\times n$ +Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n ⨉ n$ real-valued orthogonal matrices with determinant $+1$. # 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 diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 86dfff0b81..be837d963c 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -1,14 +1,14 @@ @doc doc""" 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 the $𝕊^{n}\subset ℝ^{n+1}$ """ struct Sphere{N} <: Manifold end @@ -86,7 +86,7 @@ The formula is given by the (shorter) great arc length on the (or a) great circl both `x` and `y` lie on. ````math -d_{\mathbb S^n}(x,y) = \operatorname{acos}(\langle x, y\rangle). +d_{𝕊^n}(x,y) = \operatorname{acos}(⟨x, y⟩). ```` """ distance(S::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) @@ -130,7 +130,7 @@ injectivity_radius(::Sphere, ::Any, ::ProjectionRetraction) = π / 2 inner(S::Sphere, x, w, v) 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 +plane at `x` on the sphere `S=`$𝕊^n$ using the restriction of the metric from the embedding, i.e. $ (v,w)_x = v^\mathrm{T}w $. """ @inline inner(S::Sphere, x, w, v) = dot(w, v) @@ -147,10 +147,10 @@ end 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 +since $⟨x,v⟩ = 0$ and when $d_{𝕊^2}(x,y) \leq \frac{\pi}{2}$ that ````math -\operatorname{retr}_x^{-1}(y) = \frac{y}{\langle x, y \rangle} - x. +\operatorname{retr}_x^{-1}(y) = \frac{y}{⟨x, y⟩} - x. ```` """ inverse_retract(::Sphere, ::Any, ::Any, ::ProjectionInverseRetraction) @@ -167,8 +167,8 @@ geodesic starting from `x` reaches `y` after time 1. The formula reads for $x\neq -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_x y = d_{𝕊^n}(x,y) +\frac{y-⟨x,y⟩ x}{\lVert y-⟨x,y⟩ x \rVert_2}, ```` and a deterministic choice from the set of tangent vectors is returned if $x=-y$, i.e. for opposite points. @@ -197,7 +197,7 @@ end @doc doc""" manifold_dimension(S::Sphere) -Return the dimension of the manifold $\mathbb S^n$, i.e. $n$. +Return the dimension of the manifold $𝕊^n$, i.e. $n$. """ manifold_dimension(S::Sphere{N}) where {N} = N @@ -247,7 +247,7 @@ end Project the point `x` from the embedding onto the [`Sphere`](@ref) `M`. ````math -\operatorname{proj}_{\mathbb S^n}(x) = \frac{x}{\lVert x \rVert_2}. +\operatorname{proj}_{𝕊^n}(x) = \frac{x}{\lVert x \rVert_2}. ```` """ project_point(::Sphere, ::Any...) @@ -260,7 +260,7 @@ project_point!(S::Sphere, x) = (x ./= norm(x)) Project the point `v` onto the tangent space at `x` on the [`Sphere`](@ref) `M`. ````math -\operatorname{proj}_{x}(v) = v - \langle x, v \rangle_x +\operatorname{proj}_{x}(v) = v - ⟨x, v⟩_x ```` """ project_tangent(::Sphere, ::Any...) @@ -326,7 +326,7 @@ end 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)} +𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{𝕊^n}(x,y)} \bigl(\log_xy + \log_yx \bigr). ```` """ diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index b4839d4daf..7306ac1c5f 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,27 +1,27 @@ @doc doc""" Stiefel{n,k,T} <: 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$ orthonormal matrices, i.e. ````math -\mathcal M = \{ x \in \mathbb F^{n\times k} : x^{\mathrm{H}}x = I_k \}, +ℳ = \{ x \in 𝔽^{n ⨉ k} : x^{\mathrm{H}}x = I_k \}, ```` -where $\mathbb F \in \{\mathbb R, \mathbb C\}$, -$\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. +where $𝔽 \in \{ℝ, ℂ\}$, +$·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and +$I_n \in ℝ^{n\times n}$ denotes the $k \times k$ identity matrix. -The tangent space at a point $x\in\mathcal M$ is given by +The tangent space at a point $x\in ℳ$ 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_x ℳ = \{ v \in 𝔽^{n\times k} : x^{\mathrm{H}}v + v^{\mathrm{H}}x=0_n\}, ```` where $0_n$ is the $k\times 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 @@ -41,7 +41,7 @@ Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() 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 +is (approximatly) the identity, where $·^{\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} @@ -126,7 +126,7 @@ $\operatorname{exp}_{x} v = \begin{pmatrix} \begin{pmatrix} \exp( -x^{\mathrm{H}}v) \\ 0_n\end{pmatrix}$ where $\exp$ denotes matrix exponential, -$\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_k$ and +$·^{\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. """ exp(::Stiefel, ::Any...) @@ -221,9 +221,9 @@ Return the dimension of the [`Stiefel`](@ref) manifold `M`=$\operatorname{St}(n, 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) +\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) ```` """ manifold_dimension(::Stiefel{n,k,ℝ}) where {n,k} = n * k - div(k * (k + 1), 2) @@ -237,7 +237,7 @@ Project `v` onto the tangent space of `x` 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}_{ℳ}(x,v) = v - x \operatorname{Sym}(x^{\mathrm{H}}v), ```` where $\operatorname{Sym}(y)$ is the symmetrization of $y$, e.g. by diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index cd0ae5dff9..2996cde9fd 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -5,10 +5,10 @@ The [`Manifold`](@ref) $ \operatorname{Sym} (n)$ consisting of the real- or comp symmetric matrices of size $ n\times 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\{A \in 𝔽^{n\times n} \big| A^{\mathrm{H}} = A \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 +where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed +and the field $𝔽 \in \{ ℝ, ℂ\}$ is set by the [`AbstractNumbers`](@ref) `F`. Though it is slighty redundant, usually the matrices are safed as $n\times n$ arrays. @@ -127,7 +127,7 @@ exp!(M::SymmetricMatrices, y, x, v) = (y .= x .+ v) 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`. -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 `w`. """ flat(::SymmetricMatrices, ::Any...) @@ -220,7 +220,7 @@ metric from the embedding, i.e. ````math (v,w)_x = \operatorname{tr}(v^{\mathrm{H}}w), ```` -where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ @inline inner(M::SymmetricMatrices, x, w, v) = dot(w, v) @@ -274,7 +274,7 @@ Projects `x` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. \operatorname{proj}_{\operatorname{Sym}(n)}(x) = \frac{1}{2} \bigl( x + x^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ project_point(::SymmetricMatrices, ::Any...) @@ -289,7 +289,7 @@ Project the matrix `v` onto the tangent space at `x` on the [`SymmetricMatrices` \operatorname{proj}_x(v) = \frac{1}{2} \bigl( v + v^{\mathrm{H}} \bigr), ```` -where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ project_tangent(::SymmetricMatrices, ::Any...) @@ -309,7 +309,7 @@ for the $n\times n$ it's `(n,n)`. 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`. -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 `w`. """ sharp(::SymmetricMatrices, ::Any...) @@ -324,7 +324,7 @@ of `v` from the tangent space at `x` on the [`SymmetricMatrices`](@ref) `M` to ` Since the metric is inherited from the embedding space, this is just the identity, i.e. ````math -P_{y\gets x}(v) = v. +𝒫_{y\gets x}(v) = v. ```` """ vector_transport_to(::SymmetricMatrices, ::Any...) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 2a18a4383e..536ff8b30b 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -6,8 +6,8 @@ The manifold of symmetric positive definite matrices, i.e. ```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\} +x \in ℝ^{n\times n} : +\xi^\mathrm{T}x\xi > 0 \text{ for all } \xi \in ℝ^{n}\backslash\{0\} \bigr\} ``` @@ -15,7 +15,7 @@ x \in \mathbb R^{n\times n} : SymmetricPositiveDefinite(n) -generates the manifold $\mathcal P(n) \subset \mathbb R^{n\times n}$ +generates the manifold $\mathcal P(n) \subset ℝ^{n\times n}$ """ struct SymmetricPositiveDefinite{N} <: Manifold end @@ -91,7 +91,7 @@ injectivity_radius(M::SymmetricPositiveDefinite{N}, args...) where {N} = Inf 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\in ℕ$, i.e. ````math \dim \mathcal P(n) = \frac{n(n+1)}{2} ```` @@ -129,7 +129,7 @@ end 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 -symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. +symmetric positive definite matrix on $ℳ = \mathcal P(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index 5bb2cfbda6..fe057464b7 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -20,7 +20,7 @@ d_{\mathcal P(n)}(x,y) = \lVert \operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})\rVert_{\mathrm{F}}., ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert·\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance(M::SymmetricPositiveDefinite{N}, x, y) where {N} s = real.(eigvals(x, y)) @@ -206,7 +206,7 @@ Compute the parallel transport on the [`SymmetricPositiveDefinite`](@ref) as a The formula reads ```math -P_{y\gets x}(v) = x^{\frac{1}{2}} +𝒫_{y\gets x}(v) = x^{\frac{1}{2}} \operatorname{Exp}\bigl( \frac{1}{2}x^{-\frac{1}{2}}\log_x(y)x^{-\frac{1}{2}} \bigr) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 495c05339e..48c8585936 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -36,8 +36,8 @@ d_{\mathcal P(n)}(x,y) = \sqrt{ ```` 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. +$\lfloor·\rfloor$ denbotes the strictly lower triangular matrix of its argument, +and $\lVert·\rVert_{\mathrm{F}}$ denotes the Frobenius norm. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, @@ -59,7 +59,7 @@ Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with 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}$, -and $(\cdot)_\frac{1}{2}$ +and $(·)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$. """ exp(::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) @@ -88,7 +88,7 @@ a [`MetricManifold`](@ref) with [`LogCholeskyMetric`](@ref). The formula reads 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}$ +$p_l(w) = l (l^{-1}wl^{-\mathrm{T}})_{\frac{1}{2}}$, and $(·)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$ """ function inner( @@ -141,12 +141,12 @@ Parallely transport the tangent vector `v` at `x` along the geodesic to `y` with 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 +$w = l(l^{-1}vl^{-\mathrm{T}})_\frac{1}{2}$, where $(·)_\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 ````math - \mathcal P_{y\gets x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. + \mathcal 𝒫_{y\gets x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. ```` """ vector_transport_to( diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index bde46b6054..261282d82a 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -18,7 +18,7 @@ The formula reads ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert·\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 8a0aef41ef..61dcb215aa 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -179,7 +179,7 @@ distance(B::VectorBundleFibers, x, v, w) = norm(B, x, v - w) distance(B::VectorBundle, x, y) Distance between points $x$ and $y$ from the -vector bundle `B` over manifold `B.VS` (denoted $M$). +vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the @@ -191,7 +191,7 @@ 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}$ -where $d_M$ is the distance on manifold $M$, $d_F$ is the distance +where $d_ℳ$ is the distance on manifold $ℳ$, $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 behavior of [`vector_transport_to`](@ref) is used to compute the vector @@ -213,7 +213,7 @@ number_eltype(v::FVector) = number_eltype(v.data) exp(B::VectorBundle, x, v) Exponential map of tangent vector $v$ at point $x$ from -vector bundle `B` over manifold `B.VS` (denoted $M$). +vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the @@ -250,7 +250,7 @@ from the vector space of type `M` at point `x` from the underlying [`Manifold`]( 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$ +$\flat \colon Tℳ \to T^{*}ℳ$ """ function flat(M::Manifold, x, w::FVector) v = allocate_result(M, flat, w, x) @@ -369,7 +369,7 @@ end inner(B::VectorBundle, x, v, w) Inner product of tangent vectors `v` and `w` at point `x` from the -vector bundle `B` over manifold `B.VS` (denoted $M$). +vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the @@ -382,7 +382,7 @@ Notation: 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}.$ +$⟨v, w⟩_{B} = ⟨\xi_{v,M}, \xi_{w,M}⟩_{M} + ⟨\xi_{v,F}, \xi_{w,F}⟩_{F}.$ """ function inner(B::VectorBundle, x, v, w) px, ξx = submanifold_components(B.M, x) @@ -407,7 +407,7 @@ end log(B::VectorBundle, x, y) Logarithmic map of the point $y$ at point $x$ from -vector bundle `B` over manifold `B.VS` (denoted $M$). +vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the @@ -450,15 +450,15 @@ norm(B::VectorBundleFibers{<:TangentSpaceType}, x, v) = norm(B.M, x, v) project_point(B::VectorBundle, x) Project the point $x$ from the ambient space of the vector bundle `B` -over manifold `B.VS` (denoted $M$) to the vector bundle. +over manifold `B.VS` (denoted $ℳ$) to the vector bundle. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x$ belongs to the ambient space of $M$ + * The point $x = (p_x, \xi_x)$ where $p_x$ belongs to the ambient space of $ℳ$ 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 canonical projection of that vector bundle $B$. -The projection is calculated by projecting the point $p_x$ to the manifold $M$ +The projection is calculated by projecting the point $p_x$ to the manifold $ℳ$ and then projecting the vector $\xi_x$ to the tangent space $T_{p_x}M$. """ project_point(::VectorBundle, ::Any...) @@ -533,7 +533,7 @@ from the vector space `M` at point `x` 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$ +$\sharp \colon T^{*}ℳ \to Tℳ$ """ function sharp(M::Manifold, x, w::FVector) v = allocate_result(M, sharp, w, x) @@ -637,7 +637,7 @@ end zero_tangent_vector(B::VectorBundle, x) 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$ +over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{x}B$ Notation: * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the diff --git a/src/product_representations.jl b/src/product_representations.jl index 98c033b755..3741c37bd0 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -164,7 +164,7 @@ 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 \times ℝ^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` diff --git a/src/statistics.jl b/src/statistics.jl index a30f4f7923..a22059ee79 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -33,7 +33,7 @@ t_k &= \frac{w_k}{\sum_{i=1}^k w_i}\\ where $x_k$ are points, $w_k$ are weights, $\mu_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 \in ℳ$. The algorithm terminates when all $x_k$ have been considered. In the [`Euclidean`](@ref) case, this exactly computes the weighted mean. @@ -119,9 +119,9 @@ 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\in ℳ} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}^2(y,x_i), ```` -where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). +where $\mathrm{d}_{ℳ}$ denotes the Riemannian [`distance`](@ref). In the general case, the [`GradientDescentEstimation`](@ref) is used to compute the mean. However, this default may be overloaded for specific manifolds. @@ -386,9 +386,9 @@ end 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\in ℳ} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}(y,x_i), ```` -where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). +where $\mathrm{d}_{ℳ}$ denotes the Riemannian [`distance`](@ref). This function is nonsmooth (i.e nondifferentiable). In the general case, the [`CyclicProximalPointEstimation`](@ref) is used to compute the @@ -526,7 +526,7 @@ compute the (optionally weighted) variance of a `Vector` `x` of `n` data points on the [`Manifold`](@ref) `M`, i.e. ````math -\frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m), +\frac{1}{c} \sum_{i=1}^n w_i d_{ℳ}^2 (x_i,m), ```` where `c` is a correction term, see [Statistics.var](https://juliastats.org/StatsBase.jl/stable/scalarstats/#Statistics.var). @@ -560,8 +560,8 @@ var(M::Manifold, x::AbstractVector; kwargs...) = mean_and_var(M, x; kwargs...)[2 compute the optionally weighted standard deviation of a `Vector` `x` of `n` data points on the [`Manifold`](@ref) `M`, i.e. -````math -\sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m)}, +````math𝒩 +\sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{ℳ}^2 (x_i,m)}, ```` where `c` is a correction term, see [Statistics.std](https://juliastats.org/StatsBase.jl/stable/scalarstats/#Statistics.std). From fade359fbf903546b6f7bb57df6b9a629c5e3445 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 19:44:46 +0100 Subject: [PATCH 02/74] update documentation, where this might not be that useful. --- docs/src/manifolds/euclidean.md | 2 +- docs/src/manifolds/graph.md | 4 ++-- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/rotations.md | 4 ++-- docs/src/manifolds/symmetricpositivedefinite.md | 2 +- docs/src/manifolds/torus.md | 6 +++--- docs/src/manifolds/vector_bundle.md | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) 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..7838513d56 100644 --- a/docs/src/manifolds/graph.md +++ b/docs/src/manifolds/graph.md @@ -1,11 +1,11 @@ # Graph manifold For a given graph $G(V,E)$ implemented using [`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. +i.e., it's either a $ℳ^{\lvert V \rvert}$ for the case of a vertex manifold or a $ℳ^{\lvert E \rvert}$ for the case of a edge manifold. ## Example: -To make a graph manifold over $\mathbb{R}^2$ with three vertices and two edges, one can use +To make a graph manifold over $ℝ^2$ with three vertices and two edges, one can use ```@example using Manifolds using LightGraphs diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 4265bdb1ba..f3490c7e55 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,6 +1,6 @@ # Product manifold -Product manifold $M = M_1 \times M_2 \times \dots M_n$ of manifolds $M_1, M_2, \dots, M_n$. +Product manifold $M = M_1 ⨉ M_2 ⨉ \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). ```@autodocs diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index ffff05dddb..2a668168dc 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -1,8 +1,8 @@ # 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}} = +$\mathrm{SO}(n) = \bigl\{R \in ℝ^{n⨉ n} \big| RR^{\mathrm{T}} = R^{\mathrm{T}}R = \mathrm{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. diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 12a4f478d2..deb5909025 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 \in ℝ^{n⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0\neq x \inℝ^n \bigr\} ``` ```@docs diff --git a/docs/src/manifolds/torus.md b/docs/src/manifolds/torus.md index 5ea42ed038..686d614607 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 \equiv [-π,π)^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\inℝ^{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..ff6c3a5475 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -1,6 +1,6 @@ # Vector bundles -Vector bundle $E$ is a manifold that is built on top of another manifold $M$ (base space). +Vector bundle $E$ is a manifold that is built on top of another manifold $ℳ$ (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. These vector spaces are called fibers. Bundle projection can be performed using function [`bundle_projection`](@ref). From 8fca61fb73c820cabf0e1e8ccc0c65e8ae6b2880 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 19:45:00 +0100 Subject: [PATCH 03/74] Adds a notation page. --- docs/make.jl | 1 + docs/src/notation.md | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 docs/src/notation.md diff --git a/docs/make.jl b/docs/make.jl index cd76e470f3..5d0c19209a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -38,6 +38,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/notation.md b/docs/src/notation.md new file mode 100644 index 0000000000..b229b686ec --- /dev/null +++ b/docs/src/notation.md @@ -0,0 +1,24 @@ +# 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 by terminology. Thy might be used in a plain form within the code or +when referring to that code. This is for example the case the caligraphic symbols. + +Within the documented functions the utf8 symbols are used whenever possible, +as long as that renders still in TeX within this documentation. + +A + +| Symbol | Description | Also used | Comment | +|:--:|:-------------- |:--:|:--- | +| ``T^*_x ℳ`` | The cotangent space at ``x`` | | | +| ``ξ`` | A cotangent vector from ``T^*_x ℳ`` | ``ξ_1,ξ_2,…,η,𝛇`` | | +| ``F`` | A fiber | | +| ``⟨·,·⟩`` | inner product (in ``T^*_x ℳ``) | ``⟨·,·⟩_x, g_x(·,·)`` | +| ``ℳ`` | A manifold | ``ℳ_1, ℳ_2,…,𝒩`` | | +| ``𝒫_{y\gets x}X`` | parallel Transport | +| ``x`` | A point on ℳ | ``x_1,x_2,…,y,z`` | | +| ``T_x ℳ`` | The tangent space at ``x`` | | | +| ``X`` | A tangent vector from ``T_x ℳ`` | ``X_1,X_2,…,Y,Z`` | sometimes with the base point in the index, ``X_x``. | +| ``B`` | The Vector bundle | | From ed6a6f1b4e1879713ce8704a132ca484c5f64803 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 19:56:17 +0100 Subject: [PATCH 04/74] =?UTF-8?q?\circ=20->=20=E2=9A=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/groups/group.jl | 32 +++++++++++++------------- src/groups/semidirect_product_group.jl | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 6d1840bbee..7a5c77faa5 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -237,7 +237,7 @@ end inv(G::AbstractGroupManifold, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x \circ x^{-1} = x^{-1} \circ x = e ∈ G$. +$x ⚬ x^{-1} = x^{-1} ⚬ x = e ∈ G$. """ inv(M::Manifold, x) = inv(M, x, is_decorator_manifold(M)) inv(M::Manifold, x, ::Val{true}) = inv(M.manifold, x) @@ -253,7 +253,7 @@ end 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$. +$x ⚬ x^{-1} = x^{-1} ⚬ x = e ∈ G$. The result is saved to `y`. """ inv!(M::Manifold, y, x) = inv!(M, y, x, is_decorator_manifold(M)) @@ -265,7 +265,7 @@ end @doc doc""" identity(G::AbstractGroupManifold, x) -Identity element $e ∈ G$, such that for any element $x ∈ G$, $x \circ e = e \circ x = x$. +Identity element $e ∈ G$, such that for any element $x ∈ G$, $x ⚬ e = e ⚬ x = x$. The returned element is of a similar type to `x`. """ identity(M::Manifold, x) = identity(M, x, is_decorator_manifold(M)) @@ -315,7 +315,7 @@ isapprox(::GT, ::E, ::E; kwargs...) where {GT<:GroupManifold,E<:Identity{GT}} = @doc doc""" compose(G::AbstractGroupManifold, x, y) -Compose elements $x,y ∈ G$ using the group operation $x \circ y$. +Compose elements $x,y ∈ G$ using the group operation $x ⚬ 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) @@ -343,8 +343,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x \circ y\\ -R_x &: y ↦ y \circ x. +L_x &: y ↦ x ⚬ y\\ +R_x &: y ↦ y ⚬ x. \end{aligned} ``` """ @@ -369,8 +369,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x \circ y\\ -R_x &: y ↦ y \circ x. +L_x &: y ↦ x ⚬ y\\ +R_x &: y ↦ y ⚬ x. \end{aligned} ``` Result of the operation is saved in `z`. @@ -396,8 +396,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified 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}. +L_x^{-1} &: y ↦ x^{-1} ⚬ y\\ +R_x^{-1} &: y ↦ y ⚬ x^{-1}. \end{aligned} ``` """ @@ -422,8 +422,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified 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}. +L_x^{-1} &: y ↦ x^{-1} ⚬ y\\ +R_x^{-1} &: y ↦ y ⚬ x^{-1}. \end{aligned} ``` Result is saved in `z`. @@ -450,8 +450,8 @@ differential of the translation by $x$ on $v$, written as $(\mathrm{d}τ_x)_y (v 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_x)_y (v) &: T_y G → T_{x ⚬ y} G\\ +(\mathrm{d}R_x)_y (v) &: T_y G → T_{y ⚬ x} G\\ \end{aligned} ``` """ @@ -494,8 +494,8 @@ $((\mathrm{d}τ_x)_y)^{-1} (v) = (\mathrm{d}τ_{x^{-1}})_y (v)$, with the specif 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_x)_y)^{-1} (v) &: T_y G → T_{x^{-1} ⚬ y} G\\ +((\mathrm{d}R_x)_y)^{-1} (v) &: T_y G → T_{y ⚬ x^{-1}} G\\ \end{aligned} ``` """ diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 0f578696fc..e1918fd5ff 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -24,7 +24,7 @@ $G = N ⋊_θ H$, where $θ: H × N \to N$ is an automorphism action of $H$ on $N$. The group $G$ has the composition rule ````math -g \circ g' = (n, h) \circ (n', h') = (n \circ θ_h(n'), h \circ h') +g ⚬ g' = (n, h) ⚬ (n', h') = (n ⚬ θ_h(n'), h ⚬ h') ```` and the inverse From 5f00666fdb4ca163d2cdbd915ddc27d3bb088c6e Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 19:59:50 +0100 Subject: [PATCH 05/74] fixes circ, since I mistakenly took the wrong circ. --- src/groups/group.jl | 32 +++++++++++++------------- src/groups/semidirect_product_group.jl | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 7a5c77faa5..7d011131f1 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -237,7 +237,7 @@ end inv(G::AbstractGroupManifold, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x ⚬ x^{-1} = x^{-1} ⚬ x = e ∈ G$. +$x ◦ x^{-1} = x^{-1} ◦ x = e ∈ G$. """ inv(M::Manifold, x) = inv(M, x, is_decorator_manifold(M)) inv(M::Manifold, x, ::Val{true}) = inv(M.manifold, x) @@ -253,7 +253,7 @@ end inv!(G::AbstractGroupManifold, y, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x ⚬ x^{-1} = x^{-1} ⚬ x = e ∈ G$. +$x ◦ x^{-1} = x^{-1} ◦ x = e ∈ G$. The result is saved to `y`. """ inv!(M::Manifold, y, x) = inv!(M, y, x, is_decorator_manifold(M)) @@ -265,7 +265,7 @@ end @doc doc""" identity(G::AbstractGroupManifold, x) -Identity element $e ∈ G$, such that for any element $x ∈ G$, $x ⚬ e = e ⚬ x = x$. +Identity element $e ∈ G$, such that for any element $x ∈ G$, $x ◦ e = e ◦ x = x$. The returned element is of a similar type to `x`. """ identity(M::Manifold, x) = identity(M, x, is_decorator_manifold(M)) @@ -315,7 +315,7 @@ isapprox(::GT, ::E, ::E; kwargs...) where {GT<:GroupManifold,E<:Identity{GT}} = @doc doc""" compose(G::AbstractGroupManifold, x, y) -Compose elements $x,y ∈ G$ using the group operation $x ⚬ y$. +Compose elements $x,y ∈ G$ using the group operation $x ◦ 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) @@ -343,8 +343,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x ⚬ y\\ -R_x &: y ↦ y ⚬ x. +L_x &: y ↦ x ◦ y\\ +R_x &: y ↦ y ◦ x. \end{aligned} ``` """ @@ -369,8 +369,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x ⚬ y\\ -R_x &: y ↦ y ⚬ x. +L_x &: y ↦ x ◦ y\\ +R_x &: y ↦ y ◦ x. \end{aligned} ``` Result of the operation is saved in `z`. @@ -396,8 +396,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified either left $L_x^{-1}$ or right $R_x^{-1}$, defined as ```math \begin{aligned} -L_x^{-1} &: y ↦ x^{-1} ⚬ y\\ -R_x^{-1} &: y ↦ y ⚬ x^{-1}. +L_x^{-1} &: y ↦ x^{-1} ◦ y\\ +R_x^{-1} &: y ↦ y ◦ x^{-1}. \end{aligned} ``` """ @@ -422,8 +422,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified either left $L_x^{-1}$ or right $R_x^{-1}$, defined as ```math \begin{aligned} -L_x^{-1} &: y ↦ x^{-1} ⚬ y\\ -R_x^{-1} &: y ↦ y ⚬ x^{-1}. +L_x^{-1} &: y ↦ x^{-1} ◦ y\\ +R_x^{-1} &: y ↦ y ◦ x^{-1}. \end{aligned} ``` Result is saved in `z`. @@ -450,8 +450,8 @@ differential of the translation by $x$ on $v$, written as $(\mathrm{d}τ_x)_y (v specified left or right convention. The differential transports vectors: ```math \begin{aligned} -(\mathrm{d}L_x)_y (v) &: T_y G → T_{x ⚬ y} G\\ -(\mathrm{d}R_x)_y (v) &: T_y G → T_{y ⚬ x} G\\ +(\mathrm{d}L_x)_y (v) &: T_y G → T_{x ◦ y} G\\ +(\mathrm{d}R_x)_y (v) &: T_y G → T_{y ◦ x} G\\ \end{aligned} ``` """ @@ -494,8 +494,8 @@ $((\mathrm{d}τ_x)_y)^{-1} (v) = (\mathrm{d}τ_{x^{-1}})_y (v)$, with the specif right convention. The differential transports vectors: ```math \begin{aligned} -((\mathrm{d}L_x)_y)^{-1} (v) &: T_y G → T_{x^{-1} ⚬ y} G\\ -((\mathrm{d}R_x)_y)^{-1} (v) &: T_y G → T_{y ⚬ x^{-1}} G\\ +((\mathrm{d}L_x)_y)^{-1} (v) &: T_y G → T_{x^{-1} ◦ y} G\\ +((\mathrm{d}R_x)_y)^{-1} (v) &: T_y G → T_{y ◦ x^{-1}} G\\ \end{aligned} ``` """ diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index e1918fd5ff..4e6ea643a8 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -24,7 +24,7 @@ $G = N ⋊_θ H$, where $θ: H × N \to N$ is an automorphism action of $H$ on $N$. The group $G$ has the composition rule ````math -g ⚬ g' = (n, h) ⚬ (n', h') = (n ⚬ θ_h(n'), h ⚬ h') +g ◦ g' = (n, h) ◦ (n', h') = (n ◦ θ_h(n'), h ◦ h') ```` and the inverse From 8ab09285bbea56a52a11dfd50e08f2ba1bf60743 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 25 Jan 2020 20:04:12 +0100 Subject: [PATCH 06/74] refactors a few further M. --- docs/src/manifolds/vector_bundle.md | 2 +- docs/src/orthonormal_bases.md | 4 ++-- src/groups/group_action.jl | 10 +++++----- src/manifolds/MetricManifold.jl | 2 +- src/manifolds/VectorBundle.jl | 24 ++++++++++++------------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index ff6c3a5475..eb468306e4 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 $ℳ$ (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. +It is characterized by a continuous function $\Pi \colon E \to ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $\Pi$, $\Pi^{-1}(\{x\})$, 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/orthonormal_bases.md b/docs/src/orthonormal_bases.md index 748bec43d1..e4105ff0de 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 ℳ$ 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 ℳ$ 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))$. 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/groups/group_action.jl b/src/groups/group_action.jl index 10d40d9fbe..2b6aaa06af 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -33,7 +33,7 @@ direction(::AbstractGroupAction{AD}) where {AD} = AD() Apply action `a` to the point `x`. 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 $x ∈ ℳ$ and action element $a$, the right action is ````math x ⋅ a ≐ a^{-1} ⋅ x. @@ -82,7 +82,7 @@ end @doc doc""" apply_diff(A::AbstractGroupAction, a, x, v) -For group point $x ∈ M$ and tangent vector $v ∈ T_x M$, compute the action of the +For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, 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 transports vectors @@ -105,15 +105,15 @@ end @doc doc""" inverse_apply_diff(A::AbstractGroupAction, a, x, v) -For group point $x ∈ M$ and tangent vector $v ∈ T_x M$, compute the action of the +For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, 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 differential transports vectors ````math \begin{aligned} -(\mathrm{d}L_a)_x^{-1} (v) &: T_x M → T_{a^{-1} ⋅ x} ℳ\\ -(\mathrm{d}R_a)_x^{-1} (v) &: T_x M → T_{x ⋅ a^{-1}} ℳ +(\mathrm{d}L_a)_x^{-1} (v) &: T_x ℳ → T_{a^{-1} ⋅ x} ℳ\\ +(\mathrm{d}R_a)_x^{-1} (v) &: T_x ℳ → T_{x ⋅ a^{-1}} ℳ \end{aligned} ```` """ diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index d55502feb8..f1e9c17f8b 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -41,7 +41,7 @@ abstract type LorentzMetric <: Metric end 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 +products. The positive definite property means that for $v \in T_x ℳ$, the inner product $g(v, v) > 0$ whenever $v$ is not the zero vector. """ abstract type RiemannianMetric <: Metric end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 61dcb215aa..1b51256a74 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -182,7 +182,7 @@ Distance between points $x$ and $y$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. Similarly, $y = (p_y, \xi_y)$. @@ -216,11 +216,11 @@ Exponential map of tangent vector $v$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and $\xi_{v,F}$ is a tangent vector from the tangent space $T_{\xi_x}F$ (isomorphic to $F$). The exponential map is calculated as @@ -372,11 +372,11 @@ Inner product of tangent vectors `v` and `w` at point `x` from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ 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$. @@ -410,7 +410,7 @@ Logarithmic map of the point $y$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. Similarly, $y = (p_y, \xi_y)$. @@ -459,7 +459,7 @@ Notation: canonical projection of that vector bundle $B$. The projection is calculated by projecting the point $p_x$ to the manifold $ℳ$ -and then projecting the vector $\xi_x$ to the tangent space $T_{p_x}M$. +and then projecting the vector $\xi_x$ to the tangent space $T_{p_x}ℳ$. """ project_point(::VectorBundle, ::Any...) @@ -477,15 +477,15 @@ Project the element $v$ of the ambient space of the tangent space $T_x B$ to the tangent space $T_x B$. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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$ + * The vector $x = (\xi_{v,M}, \xi_{v,F})$ where $p_x$ belongs to the ambient space of $T_{p_x}ℳ$ 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 canonical projection of that vector bundle $B$. -The projection is calculated by projecting $\xi_{v,M}$ to tangent space $T_{p_x}M$ +The projection is calculated by projecting $\xi_{v,M}$ to tangent space $T_{p_x}ℳ$ and then projecting the vector $\xi_{v,F}$ to the fiber $F$. """ project_tangent(::VectorBundle, ::Any...) @@ -640,7 +640,7 @@ Zero tangent vector at point $x$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{x}B$ Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in M$ and $\xi_x$ belongs to the + * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. @@ -648,7 +648,7 @@ The zero vector is calculated as $\mathbf{0}_{x} = (\mathbf{0}_{p_x}, \mathbf{0}_F)$ -where $\mathbf{0}_{p_x}$ is the zero tangent vector from $T_{p_x}M$ and +where $\mathbf{0}_{p_x}$ is the zero tangent vector from $T_{p_x}ℳ$ and $\mathbf{0}_F$ is the zero element of the vector space $F$. """ zero_tangent_vector(::VectorBundle, ::Any...) From ed30878a128bb2215460e4c7f85e71e4c7a61c27 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sun, 26 Jan 2020 06:58:45 +0100 Subject: [PATCH 07/74] =?UTF-8?q?removes=20a=20spurious=20=F0=9D=92=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/statistics.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statistics.jl b/src/statistics.jl index a22059ee79..874b93e890 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -560,7 +560,7 @@ var(M::Manifold, x::AbstractVector; kwargs...) = mean_and_var(M, x; kwargs...)[2 compute the optionally weighted standard deviation of a `Vector` `x` of `n` data points on the [`Manifold`](@ref) `M`, i.e. -````math𝒩 +````math \sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{ℳ}^2 (x_i,m)}, ```` where `c` is a correction term, see From 103f7734aadeabab474c0650507fa14be9ede4ca Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 05:49:01 +0100 Subject: [PATCH 08/74] Update docs/src/notation.md Co-Authored-By: Seth Axen --- docs/src/notation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index b229b686ec..03f7d037e7 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -6,7 +6,7 @@ The order by terminology. Thy might be used in a plain form within the code or when referring to that code. This is for example the case the caligraphic symbols. Within the documented functions the utf8 symbols are used whenever possible, -as long as that renders still in TeX within this documentation. +as long as that renders still in $\TeX$ within this documentation. A From 4bbd3da04bfefa3e0714aa33f754ace4b69a3a51 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 05:50:02 +0100 Subject: [PATCH 09/74] Update src/manifolds/Hyperbolic.jl Co-Authored-By: Seth Axen --- src/manifolds/Hyperbolic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 8e6fd8861e..7e3e387b8f 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -25,7 +25,7 @@ a Riemannian metric on the tangent bundle $T ℍ^n$. Hyperbolic(n) -Generate the $ℍ^{n}\subset ℝ^{n+1}$ +Generate the $ℍ^{n} ⊂ ℝ^{n+1}$ """ struct Hyperbolic{N} <: Manifold end From ffdcb3562857a93f70329fc67925b5749627e971 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:23:29 +0100 Subject: [PATCH 10/74] =?UTF-8?q?replaces=20\gets=20with=20=E2=86=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/assets/data.csv | 501 ++++++++++++++++++ docs/src/assets/distgen.jl | 6 + docs/src/assets/logo.tex | 53 ++ docs/src/assets/logo2-3.png | Bin 0 -> 26183 bytes docs/src/assets/logo2.tex | 70 +++ docs/src/assets/logo2start.png | Bin 0 -> 30097 bytes docs/src/assets/logo2start2.png | Bin 0 -> 43569 bytes docs/src/assets/logo_b.png | Bin 0 -> 22647 bytes docs/src/assets/logo_d.png | Bin 0 -> 22839 bytes docs/src/assets/scatter.tex | 9 + docs/src/notation.md | 2 +- src/manifolds/CholeskySpace.jl | 2 +- src/manifolds/Circle.jl | 2 +- src/manifolds/Hyperbolic.jl | 2 +- src/manifolds/Sphere.jl | 2 +- src/manifolds/Symmetric.jl | 2 +- .../SymmetricPositiveDefiniteLinearAffine.jl | 2 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 2 +- 18 files changed, 647 insertions(+), 8 deletions(-) create mode 100644 docs/src/assets/data.csv create mode 100644 docs/src/assets/distgen.jl create mode 100644 docs/src/assets/logo.tex create mode 100644 docs/src/assets/logo2-3.png create mode 100644 docs/src/assets/logo2.tex create mode 100644 docs/src/assets/logo2start.png create mode 100644 docs/src/assets/logo2start2.png create mode 100644 docs/src/assets/logo_b.png create mode 100644 docs/src/assets/logo_d.png create mode 100644 docs/src/assets/scatter.tex diff --git a/docs/src/assets/data.csv b/docs/src/assets/data.csv new file mode 100644 index 0000000000..cb3f3c85a0 --- /dev/null +++ b/docs/src/assets/data.csv @@ -0,0 +1,501 @@ +Column1,Column2 +0.20925327738754995,-0.8198152103661166 +-1.2375159316247513,0.2780649524278085 +1.304704806666583,-0.48802888909159314 +-1.014055767982855,1.6702031348499604 +-0.8249339638660002,0.7869135342806515 +-0.42443914696236124,1.2868384293974038 +-0.5069378224589008,0.7517522095304663 +0.021607745981637224,-0.6957781678549813 +-0.6752089361520257,-0.32690317331943547 +0.13352705143786522,-0.2699751190321647 +0.44003756846744985,-0.6490843041541213 +-1.2633002887557327,0.733370809300122 +-0.812343841131522,0.41107030441625664 +0.040527398378345056,0.6006505684416966 +-0.3696441558151978,1.1997633196198099 +1.6190199681164392,1.1669211091231826 +-0.7933753479813779,1.8439006366085011 +1.4818190108864036,0.6681715126786485 +0.39227771229115194,-0.7299409703238529 +2.1535806948171894,-0.819278387567473 +0.5402137976890716,1.4393175803452996 +0.1524847929018447,-0.12202799879353918 +-0.5169311437487083,-0.656550597213316 +-1.3296522881490533,1.1853610124760732 +-0.5830844544294357,0.49635692659984215 +1.0549906797161193,1.535480046982853 +0.19961348871136966,0.5473394356376905 +-2.0317268803227067,0.6204805495129485 +-1.1846323324805526,2.0233109820616204 +-0.8383067244402802,-0.002960080034038997 +-0.19719961853083573,0.01162432089656421 +-0.876732156125276,0.5134611034072128 +1.4444382840997283,-1.5480047307621652 +-0.21765453432903134,-1.3600143580881323 +0.5097790315186881,2.3315092839007834 +-1.2000240116398087,-0.5454096176740909 +-0.5543908443359303,-1.361026975616243 +-0.42145784009541387,1.1287385508544776 +-0.4162438472130716,-0.6693415016331465 +0.5815859596211345,-0.6364102071983105 +1.6865178028342303,0.39682831446483735 +-2.1485589150755855,0.3789264001099479 +1.4633839272953002,-0.1570896576049031 +-0.6142425305276377,-1.6144157008660154 +-0.6920142319146345,-1.2635962956082882 +0.5297117265245924,1.2885415502464046 +-0.5783882075322441,0.21036324163085332 +0.8537007367718846,-1.3004166169553726 +0.2387354994460789,-1.141056672808649 +0.44721942133987835,-0.013700992812876588 +0.8486606652743779,1.4578922253239885 +-0.06465700826069337,-1.2856198177338611 +-0.0256329281019438,1.90848170940853 +0.1304860743216497,-0.21668290789297612 +0.5088784437842744,-2.471689670168774 +0.9189970198413899,-1.2666717783403252 +2.1517826072689767,-0.5721125239908472 +0.7565289243993455,0.771590662525266 +-0.33792912549181486,-1.9835754280726403 +-0.5228609666669066,1.103076476972974 +1.0485066785805952,-0.10353217108228314 +0.21493934084311667,-1.5483679040062617 +0.021795604688018003,0.8152892437251155 +1.6000944585083674,1.6001367742104688 +1.9225049856984067,-0.7503233372929577 +0.4856957960493018,-0.9539650279306797 +2.2324720780889575,-0.14826737071230928 +1.2907003483480892,0.07023454791668922 +1.5026877879990928,-0.37703334287349816 +-0.004597043741592526,0.6268780132220775 +1.7070547713227697,-0.9853665740323286 +-0.6591872853968587,0.7718903637092069 +-0.27829968158936097,1.1875305610828857 +0.22413504624288524,-0.16322262511759422 +-3.1367752651691463,-0.54948675106928 +1.4222672599740964,-0.6401898029871066 +0.29153094130253604,1.173863959748359 +-0.07970845084719652,0.10374547723068062 +1.1752636222734072,0.043498218355799874 +-1.2603757079650668,0.9188171812655966 +-1.763262176293205,-0.7211190308095813 +-0.8425550698438031,0.5932725294479552 +-0.5804225135839073,-0.19106259241394635 +0.2609231790700467,2.2688403524084153 +0.7717347991884914,-0.15120662460284198 +0.03717803949457743,-0.22351744405399038 +0.41013559338739775,-0.22485805629103348 +-0.12579185096357087,-1.5059598908747502 +1.49291142898796,0.7543479963935693 +0.7751774719693498,-0.05352258363806766 +-0.10324996801731351,0.4470669456912923 +0.9034382387634113,-1.1390543572402494 +-0.3908357830067875,0.19124155895988032 +-0.21932536028106456,-1.537948292586809 +-0.6457068469039726,-0.595992730963507 +1.0421104551995974,-0.8621436956775052 +-0.16602083130725678,-0.1414973031950705 +0.23386253086626543,-1.9766242963665435 +-1.6309324668384733,0.440535497717788 +0.08720419144571004,-0.15353124779651434 +0.6806103201689528,0.26099302801614593 +0.7396500767429188,0.2201436106363608 +-1.4031839397423733,-0.29745169957078255 +-0.6473699639285514,-1.2216927986547592 +1.537490234146802,1.006462705697397 +0.22088899686952984,0.05964396839218332 +-0.8771392377373487,0.38136795446031413 +-1.1069605346268045,0.3284131333384977 +-1.61889858413774,-0.3665534698058741 +-0.4810507242335761,0.8961772329815396 +-0.9740471656769839,0.3662284856191772 +-0.08605763364522674,0.3675251466821569 +0.7931476515990055,-2.0891713194419546 +-0.6909177552749688,0.42814393382034643 +-1.615228358304979,1.133443929237911 +-0.6723715321222826,-2.783685752268319 +2.017517152027973,0.16540380353678152 +0.5790923158925938,1.3699532544986102 +-2.2048235305084836,1.7237354387670187 +-0.6058544375395607,0.2859501013504142 +0.7538458428689907,0.18043439723595692 +1.5987551875336228,-0.31900574056038905 +-0.9615487099678645,-0.42310951199869723 +-0.28540666589661795,-1.4453301777166825 +-0.8233415746224023,0.19501174692274206 +-0.38480757368389484,-1.534282189555801 +0.6535232271506434,0.03608725535016844 +-1.0652947178602814,-1.5117635477575704 +0.29045261377270337,-0.6776286820916985 +-0.4117802448609643,-0.9858432613174591 +-0.9745995453928522,0.5340406418015569 +0.5190038790642223,-0.9052337808820757 +0.16195471501505157,0.1802787312006402 +0.4765814681658343,1.4156611379693895 +-1.2120555164420173,0.8151011519509629 +-1.4049866420488497,-1.6506316060858535 +1.7614565810823388,0.8064244699560507 +0.5783005929235394,-0.0355582099350019 +1.0207157683268422,0.5755632690975645 +-2.188256883531687,0.23433179137069388 +1.1630547334296317,-1.143249674500201 +-0.3931256515376709,-0.11137912575615719 +0.7965005173379419,0.4235478439240067 +-0.6184801703633964,0.9352224359361652 +0.876618139190272,0.7166556917221233 +-0.5561293738292874,-0.7718126672657308 +-0.08918399792515726,0.3875676248737453 +-1.2425099198509577,0.43965580056858516 +-0.9148002112811672,-0.6585252572632969 +2.338330755443459,1.0984610783517668 +-0.6688223533227194,-1.375522618739578 +2.193814549454824,-0.005511122117891882 +-1.9980439807478227,-0.7431188268434156 +-0.6249027944488548,-0.7946546898984421 +-0.07280246634346817,1.6353094552743106 +0.8901161451643341,0.12434583767200093 +-0.5840336822792623,0.4752680397301709 +0.5098922574562021,-0.6341822369799789 +2.920776034979655,1.1141729332606483 +-1.1065324191867454,-2.075590096153505 +-0.1486735971881187,0.23150245628627533 +1.1975755597395987,0.517826284162693 +-0.7341948944016199,-0.4235540395300665 +0.10737541917825233,0.07112491597623669 +0.8683313082256237,-0.8207567861031164 +0.08471829454336718,0.15920423305531078 +0.25529513899306266,1.0667090935065797 +1.4195808972844797,0.11683437906931887 +1.2978679827821356,-1.1007432300412743 +-1.36253729779608,0.2471096794379451 +-0.74530941407898,0.7786742523748338 +0.17894322742608113,0.33084023772932386 +-0.23858363624255882,-0.02878690589206565 +-0.36400809328542516,0.23608952314532497 +0.2785842506453619,-0.6836247466038713 +0.04570786528874005,1.1674946396655035 +0.12300326113166653,-1.6509696247354544 +0.9902578346586426,-0.4304789643062097 +0.7396581278949144,-1.2147550662303785 +-0.4854282152927687,-0.889589364634145 +-1.1823552203208014,-1.217946204010757 +-0.5823813131159803,0.5747103526987678 +0.2795240808414551,-2.030318635676795 +0.4095483033457007,0.35903719168595116 +1.1358137961687802,-1.839413715008768 +0.9136573000176265,0.4596709320611816 +-0.7582209842350823,-0.2164098425700527 +1.4853905059499748,0.6024985095168356 +-1.1348310931312249,1.7004073096812582 +-0.039614290696515765,1.596867378809741 +-1.01023726959206,-2.0125571075517614 +0.4341334181689187,-0.4704507055457917 +-0.4395362349213977,-2.1828081189770785 +1.3386828148917396,0.9279191428214413 +-1.2759874746613895,-0.9939846221695626 +0.9408242272225353,0.5783381896447548 +-1.9370399932267697,-0.4358988150333681 +2.206962275372279,-2.235003644548357 +1.8734196852547227,0.6948886100052856 +-0.5874618471965549,-1.7655860831953067 +0.05969511541624782,0.6843758327465203 +-0.08971273020548921,-1.2807757417391585 +-0.6760223602432806,-0.21164660376511943 +1.7175414320911377,-0.4787328780455168 +0.47279206116353184,0.3708241004727506 +-1.6107202153392217,0.12878632324114822 +0.3407438473927114,0.24877489443667297 +1.9473102145674297,0.8577223738782214 +1.4000239354264268,-1.4332261232470174 +-1.6009731148630897,0.4109530439774122 +-0.03302730584748071,-0.5953924869417049 +0.8784260193173496,-0.17688563530099977 +-1.3700547715745968,0.33879243592374125 +-0.08425258650003066,-0.26225235198739655 +-1.2223159270173345,0.4161779453839043 +0.1261284348399706,-0.46112667714961336 +1.6713054833929968,-0.050594593772474994 +1.2133052158890176,-0.8809453083423139 +0.7838013271880482,1.295347704039081 +-0.2606004327647152,-0.9445325323220984 +0.42991364630851137,1.4877227836246028 +-0.18391551957496874,0.5928519404759451 +-0.5171531256363292,-0.31126965968768167 +0.8974439100178079,-0.24620190095548614 +-0.7612619866268598,-1.3816739004575191 +2.120298578006751,0.3866425020677111 +-1.002574174670511,1.0489768194057139 +1.24071439701067,0.34683277672072504 +0.9424466477319209,-1.1901592554979785 +-0.21274600588203385,-0.5980088931297092 +-0.6662146743219123,-0.12303565163319465 +-0.10839318140516134,-0.341876930205938 +0.7240273701268818,-0.33903582536431 +-1.4165715715950367,1.366101058065914 +1.889022098413358,2.2021992400359025 +-1.809452348093021,0.756108396435671 +-0.5963801759889581,1.2296048220423788 +-1.2590615174547624,-1.622775827508899 +-0.23207266116181025,1.04362146601353 +0.9662222128461432,-0.8353636669604038 +0.7709449279358161,-1.4061220536645491 +0.5435608533459653,0.6851641267029456 +-0.124195088013306,-0.010956241144021432 +1.02362042736966,-0.09354620797538536 +-0.8867283865262188,-0.03293397265795115 +1.1871341694617725,1.076583895509326 +1.5008175087834852,1.1919084379841514 +-0.10171389509048433,-0.20725147533465663 +-0.30617403788514586,0.593750053021037 +0.16593969549340595,0.07085183039057677 +1.6949783757618488,-0.38539437706155233 +1.1594682147714586,-1.8185767799090713 +-0.12380277009651296,0.036404231295301105 +0.8753325042402745,1.7691717568515994 +-0.6738461808200675,0.6250863296407262 +-0.3920641987212776,-0.03893165117029216 +-0.06038965220490704,1.977285403350687 +0.22327641647265678,-0.25183479845192946 +0.797314938454692,-0.1423496142092209 +1.018338195516711,-0.7952365481025468 +-0.3882877901023438,1.6979599646756065 +1.6350394709628604,-1.8704975836151914 +0.7225521289199064,0.22986902922360838 +-0.41095041464161325,-0.04289951839157107 +0.8216272789907563,-1.245910847706707 +1.1482269815450588,-1.409055968110682 +0.3679455692316837,-0.41641995912572244 +0.6775141081529522,-0.16603421904221488 +-0.17953656708395252,-0.40731399845064603 +0.45934412509766176,0.5258948413155806 +-1.6073338245545745,0.6174662801596772 +0.3795079260987492,-0.2265315311338671 +0.37053963630106046,-0.36306861273417745 +-1.4064790458848673,0.4316367650582259 +-0.9239223859546113,0.8514374556346487 +-0.19813473286691904,1.0915577588356662 +0.47805411504436124,-1.9796529268008705 +0.9096356845761988,1.9504075166939363 +1.1121864071351912,0.8169446641997076 +-0.76392035916547,-1.9996658573924553 +0.9116095691451693,1.374745664802311 +-1.3073444452855485,-0.7973928806480879 +-0.009518698942546155,-1.1346364242274858 +-0.4162256085972741,0.9497397553266617 +0.49139190846461417,1.5998889664554186 +-0.9709171930549572,0.04593985446294173 +1.005784325023403,-0.2649783606949649 +0.13263856992440676,0.7074688738981193 +0.9423669144267327,-1.6572325168991833 +-0.04873899341087811,1.2629660504844864 +-1.1529445903174524,1.845424423263187 +-0.7571962042748505,-0.6099026413644854 +0.10076587651728887,-1.1575773748041835 +-0.23246085296994107,1.0444055354400077 +-1.3904752640829892,1.935067519210738 +-0.8275051574603665,1.1165826177908906 +-0.3532977368791006,0.9762276770047948 +0.04555514713055206,1.1010339442745045 +-0.4798494095416221,-0.5846501693381377 +0.07600913731545283,0.6452937030142104 +0.4005441433340665,-0.8198942641155922 +0.3281976746952413,1.8795273434110265 +-0.11638950438046095,-0.8508859026531531 +0.9464520007826062,-0.7963791676029883 +-0.40924349996092974,0.18323106084622415 +0.0457878718431123,0.8490769298808012 +-1.629864129257769,0.7132218805686016 +0.818570882630934,-0.36879077667051957 +-0.4825470271238746,-0.39399469376924207 +-0.4066698075512619,-0.7295142941575049 +-2.4166127392162524,1.4361676897364186 +-1.7128550397184765,0.9393046156428141 +0.2969233033198962,-0.008761620775262807 +0.09475997102957479,0.13291709320131737 +-1.102822384805198,1.3749718618841524 +-0.1991124924253596,1.1187991965646242 +1.3079620695641467,-2.794306320777006 +-1.1289393623664903,-0.7528044133289846 +-0.8352574037747552,2.8346581300743963 +0.048236775166861104,0.18380732752201018 +-0.8047753262686002,1.1947441034029387 +0.2008406604651372,-0.626240601577856 +-0.12442551827381988,2.291686808407615 +1.195862288989434,-0.7773083689695514 +-1.415364003563011,2.6355257886035535 +-0.6689472928937514,1.3028183693261257 +-0.1972508510566795,0.38147110643902826 +0.2926560251284588,0.9058815725778023 +-0.1433314671848272,0.3137048556384926 +0.8462921722716078,-2.4802590132298854 +-1.009391574040696,-0.6151053193514879 +1.9465016279634086,-0.9273074198790414 +1.0680830606775091,-1.447904986110108 +0.49973926959112563,1.4341388578535592 +-1.1650854448734136,-0.16388896295184432 +0.9803533861459229,-0.22621649742808633 +2.4752915271452482,-2.0040862331421416 +-1.3708395925805268,-1.1600679095400648 +0.4751740582157677,-1.5370049438849613 +-0.648577580900969,2.322571175843582 +-0.7344456972327366,-0.27949247723809817 +0.033314832029100995,0.7647039926196152 +0.40502498786658897,-0.11499821285080047 +1.6907705466366305,0.19629802609880695 +-0.16027970104034442,0.22417370619866644 +1.7082909119570338,0.44893137069158623 +-1.5879657476595934,0.485201193984783 +-0.11448381425488455,-0.45700981488683956 +-0.9392115278739103,0.016844435943600834 +0.14491239321542976,0.4569708291605997 +-0.23040928643973008,0.4303178072552902 +-0.8414329611503186,0.25542999522229093 +0.2680790535836209,-0.21530551744328486 +1.017541776547052,0.7168812670145285 +-0.3071759697090416,0.7081708067000178 +-0.6189929768852306,0.43645547065889595 +0.6864653646281125,-0.13840049602023893 +2.0602113381869844,1.051240184879768 +1.1522489630803732,0.46350924617163364 +0.8038004091711378,-0.7442547370151313 +-1.4266860683641622,-0.2485580756738553 +1.3256691351947092,1.1606693004961317 +-0.1797608486391368,0.40472867593631834 +-0.7477437878819732,0.4287667149165427 +-0.19650439042216764,0.6123505795876122 +-0.6823174808680208,0.3677541144306763 +-0.031101730683239237,0.17008058482380087 +0.05297765052557897,-0.2863631195451015 +0.9974306444526836,-0.5699193095739777 +-0.8698140279472525,-0.8861422020525384 +-1.2710146130140438,-0.255990678882614 +-0.2563433685873366,0.3755783314755773 +1.8403282805749002,-0.018371249845875112 +-2.8198896365564425,0.18034287562734888 +-0.859736917257879,0.7109218804284065 +0.4324327934194318,0.3332988218126938 +-0.565361668477626,0.3807024266942734 +-1.4566264572618028,0.18009141014137356 +-0.8161806409460663,1.3639023182353618 +-0.7505456374513456,0.22170777405420683 +-0.4548076250769765,-1.3355975103825863 +-1.0398647789512183,0.8354775900105511 +-1.297248932390027,-0.2796818585519065 +0.1227521338249744,-1.6528638583640762 +0.18606429131956786,-2.2247215020370232 +0.5858403357681301,1.1174444250612563 +0.10550047249195406,0.6305943872716262 +1.3071625584409796,1.8402844194288526 +1.4621868422862605,0.8402399506474633 +-0.5899928532788463,0.5005475788920318 +0.9625124467075618,0.1184281575820163 +-0.5734686157994059,0.7731973345635538 +-0.44906653634942467,1.9755941856499282 +-1.3953589766233268,0.5618541928618719 +1.0424982870390762,1.4147989614168979 +1.3031824626870165,-0.9430373141532785 +-0.9709115216011411,-0.14329715791873932 +0.062148522814003515,0.8341962507578219 +0.7611678151721707,0.8057535428671398 +-0.20677165455713256,0.04486533457869614 +-0.210723156864584,-2.1805858771400985 +-0.5601075806531877,-0.3769406686719678 +-0.2512162584979908,-0.24683093354460156 +-1.57987739674686,-0.13470223413438884 +0.4171286085752105,-0.7803151860974653 +1.3048631431727824,0.9688008654053288 +-0.4139762437275944,-0.07159372748909659 +-0.634882765299227,1.2219886120058183 +0.07046283117438193,-0.04876784324289363 +1.2757154756692617,0.5368044387597584 +-0.8238967220491237,-1.0578874656036141 +-0.48366951411492837,-0.7418667054178377 +-2.01160266964125,-0.650288329547343 +-0.7076921023269187,0.9434564191184408 +-1.1057099298972894,-2.3632389863560537 +-1.0035773588447192,1.3700199925682557 +0.6286604273154789,1.2606200666701226 +0.22978491026261744,-0.42047268293476864 +-0.6492957080129554,-0.5737740141524689 +0.9152651725281473,1.9097380942059026 +1.5232554379145447,0.22259961564447261 +-0.6305071390407486,-0.4842627273972022 +-0.22219863097282772,-0.30978693316016304 +0.6631192085592964,-0.30836537606606096 +-0.5212294499115746,-0.33803739262974625 +-0.27087398475260466,1.3251459964653174 +-0.5808906780373599,2.0436943564183703 +-0.9207066553566768,0.3687316684597964 +1.675914397632655,1.1163498138218093 +-1.0093165770287913,0.8418770952919018 +-0.4119574638679049,-1.235460930020419 +-0.18084033690831097,-1.5440332737459173 +-0.39923644412640913,-0.975875202331773 +0.25231547172273106,0.24072079963256612 +-2.0223362234866076,1.3229645327060875 +0.3196272715721434,0.47375076614239847 +-1.3381500857898445,1.1514522522148107 +1.5669892870111521,0.10109782302114441 +-0.26746489609945995,-0.4669753315169544 +-1.2588922446981776,-1.5976214025378035 +-0.4467489056525206,0.9721194788859443 +1.2398566092811116,0.39432820968850374 +-1.1008271687308644,0.46586224934630316 +-0.7227675492592988,-1.3232507937264655 +0.6064576112044092,1.0267407300883928 +-0.04861712889686784,-0.32160582330022885 +0.2691720449094336,-0.19946337695875416 +0.29316237957015406,-2.2346344194223042 +-0.1562409981182633,0.43797550967157156 +1.1817110455858475,1.8407640470607778 +0.2789099782276007,1.236325860582907 +2.1603796623614735,-0.46208088491433913 +0.3109450644942771,0.5205699542749868 +1.914520004845812,-0.3877083173077306 +-0.1465569397942808,0.32694080764217864 +0.6588456146096874,0.8821216342464769 +1.7282507338626458,-0.2639434252591822 +-0.39556568077765675,-1.1680493160156555 +-0.9778153487051146,-0.20946849823296984 +-1.0934308931057681,0.46217756475978017 +1.3438682944930351,-0.9146928084387849 +-1.1497052986688179,0.936746269026655 +1.7213286535834567,2.325065465291657 +0.21330308134009565,-0.22630390727838973 +0.11768583730063362,1.5190732274292302 +-0.06540715841466907,-0.4502427285474765 +-1.1472664332615403,-0.5164726077094528 +1.316678762728142,0.8119294901898304 +0.4947098408012637,0.43155231346298756 +-0.4348398166489771,-0.8410658006235336 +-1.1026120891403193,0.2723083117529033 +-1.9610160561864127,-1.2442079734393166 +0.9171281259709059,-0.8513981080284988 +-0.5899142593591982,0.671506543878724 +0.2992270414710336,0.30085934551708304 +-0.5739027747316474,0.4167995176188234 +2.26517309994735,-0.31715196338940244 +0.7437906945346505,-2.96849358034209 +-0.9400887257854789,1.291763400344791 +-0.6606231937062936,-0.4559399017369433 +0.42632639369715564,-1.1180357962378338 +-0.007982483862828647,-0.21230981543238472 +-0.8518057782817932,1.1043544169216881 +0.19056907228043582,0.010036922059188467 +-1.3611300611056723,-0.9220547532849753 +1.2373233557489731,1.0549589389676295 +0.14296121373497372,-2.022351104582391 +0.700769141392561,-0.3712632004174983 +0.4624210852181194,0.9323510311356591 +-1.189220758237636,1.3813033208194605 +0.6441983042745664,0.37212041124184 +1.6690992245854068,0.4189862024580888 +1.4398561407721238,0.7080719097842861 +-0.8188170383623364,-1.0464245659908882 +-0.9222137733567871,-0.9695373180612131 +-1.1863165728697105,-0.34181847975534 +-0.5866666374154152,-0.19275613701952155 +0.2230059437272028,1.7121173547050097 +-1.0582959617039025,1.7556231484778728 +-2.22823860429648,0.958007101152074 diff --git a/docs/src/assets/distgen.jl b/docs/src/assets/distgen.jl new file mode 100644 index 0000000000..c1761ba4ae --- /dev/null +++ b/docs/src/assets/distgen.jl @@ -0,0 +1,6 @@ +using Distributions, CSV, Tables +a = MvNormal([0.; 0.],1.) +s = sampler(a) +data = cat([rand(s) for i=1:500]...;dims=2) +t = Tables.table(data') +CSV.write("data.csv",t) diff --git a/docs/src/assets/logo.tex b/docs/src/assets/logo.tex new file mode 100644 index 0000000000..8c345e4124 --- /dev/null +++ b/docs/src/assets/logo.tex @@ -0,0 +1,53 @@ +\documentclass[tikz]{standalone} +\usepackage{ifthen, fontspec, fontawesome5,pgfplots} +\setmainfont{Tamil MN} +\usetikzlibrary{arrows.meta} +\newboolean{darkmode} +\setboolean{darkmode}{true} +\ifthenelse{\boolean{darkmode}}{% + \definecolor{fgc}{rgb}{1.0, 1.0, 1.0} + \definecolor{bgc}{rgb}{0.0,0.0,0.0} +}{% + \definecolor{fgc}{rgb}{0.0, 0.0, 0.0} + \definecolor{bgc}{rgb}{0.9, 0.9, 0.9} +} +\definecolor{jblue}{rgb}{0.251, 0.388, 0.847} +\definecolor{jred}{rgb}{0.796, 0.235, 0.2} +\definecolor{jgreen}{rgb}{0.22, 0.596, 0.149} +\definecolor{jpurple}{rgb}{0.584, 0.345, 0.698} + +\tikzset{blob/.style={circle, minimum size=1cm,line width=0pt}} +\tikzset{blobred/.style={fill=jred, draw=jred, blob}} +\tikzset{blobpurple/.style={fill=jgreen, draw=jgreen, blob}} +\tikzset{blobgreen/.style={fill=jpurple, draw=jpurple, blob}} + +\tikzset{geodesic/.style={very thick,jblue,cap=round}} +\tikzset{geodesic2/.style={very thick,jblue,cap=round,opacity=0.2}} +\tikzset{tangent/.style={very thick,fgc,->,>={Stealth[round]} }} + +\pagecolor{bgc} + +\begin{document} + \begin{tikzpicture}[scale=2] + \draw[geodesic] (-0.5,0.0) node (a) {} (0.5,0.0) node (b) {} (0.0, {sqrt(3)/4}) node (c) {}; + \draw[geodesic] (c.center) .. controls (-0.2,{sqrt(3)/4}) and (-0.5,0.2) .. (a.center); + \draw[geodesic] (c.center) .. controls (0.2,{sqrt(3)/4}) and (0.5,0.2) .. (b.center); + \draw[geodesic] (a.center) to [bend right=35] (b.center); + \draw (a) node[blobred] {}; + \draw (b) node[blobgreen] {}; + \draw (c) node[blobpurple] {}; + + \draw[geodesic2] (c.center) .. controls (-0.2,{sqrt(3)/4}) and (-0.5,0.2) .. (a.center); + \draw[geodesic2] (c.center) .. controls (0.2,{sqrt(3)/4}) and (0.5,0.2) .. (b.center); + \draw[geodesic2] (a.center) to [bend right=35] (b.center); + + \draw[tangent] (c.center) -- ++ (0.5,0); + \draw (a) node {\large\color{fgc}\faIcon{redo}}; + \draw (b) node {\begin{tikzpicture}[scale=0.15] + \begin{axis}[axis lines=none] + \addplot[fgc,draw opacity=0.8,only marks,mark=*,mark options={fill=fgc, fill opacity=0.8, scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; + \end{axis} +\end{tikzpicture}}; + \draw (0.0,-0.45) node (T) {\color{fgc}\large\bfseries Manifolds.jl}; + \end{tikzpicture} +\end{document} diff --git a/docs/src/assets/logo2-3.png b/docs/src/assets/logo2-3.png new file mode 100644 index 0000000000000000000000000000000000000000..358468e2908c48adf3bf546a27822dcf394be9bb GIT binary patch literal 26183 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 P00000NkvXXu0mjfFg{w$ literal 0 HcmV?d00001 diff --git a/docs/src/assets/logo2.tex b/docs/src/assets/logo2.tex new file mode 100644 index 0000000000..a0ef7d29a3 --- /dev/null +++ b/docs/src/assets/logo2.tex @@ -0,0 +1,70 @@ +\documentclass[tikz]{standalone} +\usepackage{ifthen, fontspec, fontawesome5,pgfplots,amssymb} +\setmainfont{Tamil MN} +\usetikzlibrary{arrows.meta, positioning} +\newboolean{darkmode} +\setboolean{darkmode}{false} +\newboolean{lettering} +\setboolean{lettering}{false} +\ifthenelse{\boolean{darkmode}}{% + \definecolor{fgc}{rgb}{1.0, 1.0, 1.0} + \definecolor{bgc}{rgb}{0.0,0.0,0.0} +}{% + \definecolor{fgc}{rgb}{0.0, 0.0, 0.0} + \definecolor{bgc}{rgb}{0.9, 0.9, 0.9} +} +\definecolor{jblue}{rgb}{0.251, 0.388, 0.847} +\definecolor{jred}{rgb}{0.796, 0.235, 0.2} +\definecolor{jgreen}{rgb}{0.22, 0.596, 0.149} +\definecolor{jpurple}{rgb}{0.584, 0.345, 0.698} + +\tikzset{blob/.style={circle, minimum size=1.2cm,line width=0pt}} +\tikzset{blobred/.style={fill=jred, draw=jred, blob}} +\tikzset{blobpurple/.style={fill=jgreen, draw=jgreen, blob}} +\tikzset{blobgreen/.style={fill=jpurple, draw=jpurple, blob}} + +\tikzset{geodesic/.style={very thick,jblue,cap=round}} +\tikzset{geodesic2/.style={very thick,jblue,cap=round,opacity=0.2}} +\tikzset{tangent/.style={very thick,fgc,->,>={Stealth[round]},line cap=round}} +\tikzset{tangentsum/.style={very thick,fgc!66!bgc,->,>={Stealth[round]} }} +\tikzset{tangentvec/.style={very thick,fgc!33!bgc, dash pattern=on 0pt off 2\pgflinewidth, line cap=round}} + +\tikzset{rotation/.style={very thick,fgc,->,>={Stealth[round,scale=0.75]},line cap=round}} + +\pagecolor{bgc} +\newcommand{\lp}{0.4} +\begin{document} + \begin{tikzpicture}[scale=2] + % centroid of triangle at zero + \draw (-0.5,{-2*sqrt(3)/6}) node (a) {} (0.5,{-2*sqrt(3)/6}) node (b) {} (0.0, {sqrt(3)/6}) node (c) {}; + \draw[geodesic] (c.center) .. controls +(210:{\lp}) and +(90:{\lp}) .. (a.center) node[pos=0.22] (ca) {}; + \draw[geodesic] (c.center) .. controls +(-30:{\lp}) and +(90:{\lp}) .. (b.center) node[pos=0.22] (cb) {}; + \draw[geodesic] (a.center) .. controls +(-30:{\lp}) and +(210:{\lp}) .. (b.center); + \draw (a) node[blobred] {}; + \draw (b) node[blobgreen] {}; + \draw (c) node[blobpurple] {}; + \draw[geodesic2] (c.center) .. controls +(210:{\lp}) and +(90:{\lp}) .. (a.center); + \draw[geodesic2] (c.center) .. controls +(-30:{\lp}) and +(90:{\lp}) .. (b.center); + \draw[geodesic2] (a.center) .. controls +(-30:{\lp}) and +(210:{\lp}) .. (b.center); + %tangent + \draw (a.center) ++ (75:{0.4}) node (t1) {}; + \draw (a.center) ++ (345:{0.4}) node (t2) {}; + \draw (a.center) ++ (30:{0.4*sqrt(2)}) node (t12) {}; + \draw[tangentvec] (t1.center) -- (t12.center) -- (t2.center); + \draw[tangentsum] (a.center) -- ++ (30:{0.4*sqrt(2)}) node (t12) {}; + \draw[tangent] (a.center) -- ++ (75:{0.4}) node (t1) {}; + \draw[tangent] (a.center) -- ++ (345:{0.4}) node (t2) {}; + \draw[rotation] (ca.center) .. controls +(315:0.11) and +(225:0.11) .. (cb.center); + \draw (c) node {\large\color{fgc}$\mathfrak g$}; + \draw (b) node {% + \begin{tikzpicture}[scale=0.15] + \begin{axis}[axis lines=none] + \addplot[fgc,draw opacity=0.8,only marks,mark=*,mark options={fill=fgc, fill opacity=0.8, scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; + \end{axis} + \end{tikzpicture} + }; + \ifthenelse{\boolean{lettering}}{% + \draw (0.0,{-2*sqrt(3)/3}) node (T) {\color{fgc}\Large\bfseries Manifolds.jl}; + }{} + \end{tikzpicture} +\end{document} diff --git a/docs/src/assets/logo2start.png b/docs/src/assets/logo2start.png new file mode 100644 index 0000000000000000000000000000000000000000..da28904899e6b513021dbae172f571ca78cf3a76 GIT binary patch literal 30097 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 u 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 VFHqcqySskjKHt4RHhZ$q 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 literal 0 HcmV?d00001 diff --git a/docs/src/assets/logo_b.png b/docs/src/assets/logo_b.png new file mode 100644 index 0000000000000000000000000000000000000000..9eaa38ea3c2756a0d10c7195f9e1f771d329b77f GIT binary patch literal 22647 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

{BvFESxYI+gf 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%0DRf7ki($r3Q)1un`)=7`t|pD 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 IM6N<$f|m-6sQ>@~ literal 0 HcmV?d00001 diff --git a/docs/src/assets/scatter.tex b/docs/src/assets/scatter.tex new file mode 100644 index 0000000000..c6079df308 --- /dev/null +++ b/docs/src/assets/scatter.tex @@ -0,0 +1,9 @@ +\documentclass[tikz]{standalone} +\usepackage{ifthen, fontspec, fontawesome5,pgfplots} +\begin{document} +\begin{tikzpicture} + \begin{axis}[axis lines=none] + \addplot+[only marks,mark=*,mark options={fill=blue,scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; + \end{axis} +\end{tikzpicture} +\end{document} diff --git a/docs/src/notation.md b/docs/src/notation.md index b229b686ec..cee0093801 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -17,7 +17,7 @@ A | ``F`` | A fiber | | | ``⟨·,·⟩`` | inner product (in ``T^*_x ℳ``) | ``⟨·,·⟩_x, g_x(·,·)`` | | ``ℳ`` | A manifold | ``ℳ_1, ℳ_2,…,𝒩`` | | -| ``𝒫_{y\gets x}X`` | parallel Transport | +| ``𝒫_{y←x}X`` | parallel Transport | | ``x`` | A point on ℳ | ``x_1,x_2,…,y,z`` | | | ``T_x ℳ`` | The tangent space at ``x`` | | | | ``X`` | A tangent vector from ``T_x ℳ`` | ``X_1,X_2,…,Y,Z`` | sometimes with the base point in the index, ``X_x``. | diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 6e0e6cb4a7..b6824b48f9 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -191,7 +191,7 @@ Parallely transport the tangent vector `v` at `x` along the geodesic to `y` on to the [`CholeskySpace`](@ref) manifold `M`. The formula reads ````math -\mathcal 𝒫_{y\gets x}(v) = \lfloor v \rfloor +\mathcal 𝒫_{y←x}(v) = \lfloor v \rfloor + \operatorname{diag}(y)\operatorname{diag}(x)^{-1}\operatorname{diag}(v), ```` diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 279dd74c2f..404ea0f248 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -320,7 +320,7 @@ 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 -𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℂ}(x,y)} +𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℂ}(x,y)} \bigl(\log_xy + \log_yx \bigr), ```` where [`log`](@ref) denotes the logarithmic map on `M`. diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 8e6fd8861e..1e4f3bebce 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -250,7 +250,7 @@ Compute the paralllel transport of the `v` from the tangent space at `x` on the connecting `x` and `y`. The formula reads ````math -𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℍ^n}(x,y)} +𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℍ^n}(x,y)} \bigl(\log_xy + \log_yx \bigr). ```` """ diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index be837d963c..6341e4be65 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -326,7 +326,7 @@ end Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by ````math -𝒫_{y\gets x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{𝕊^n}(x,y)} +𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{𝕊^n}(x,y)} \bigl(\log_xy + \log_yx \bigr). ```` """ diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 2996cde9fd..79b04bfd8d 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -324,7 +324,7 @@ of `v` from the tangent space at `x` on the [`SymmetricMatrices`](@ref) `M` to ` Since the metric is inherited from the embedding space, this is just the identity, i.e. ````math -𝒫_{y\gets x}(v) = v. +𝒫_{y←x}(v) = v. ```` """ vector_transport_to(::SymmetricMatrices, ::Any...) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index fe057464b7..e684b86a30 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -206,7 +206,7 @@ Compute the parallel transport on the [`SymmetricPositiveDefinite`](@ref) as a The formula reads ```math -𝒫_{y\gets x}(v) = x^{\frac{1}{2}} +𝒫_{y←x}(v) = x^{\frac{1}{2}} \operatorname{Exp}\bigl( \frac{1}{2}x^{-\frac{1}{2}}\log_x(y)x^{-\frac{1}{2}} \bigr) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 48c8585936..d906464b94 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -146,7 +146,7 @@ triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With $u$ the pa transport on [`CholeskySpace`](@ref) from $l$ to $k$. The formula hear reads ````math - \mathcal 𝒫_{y\gets x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. + \mathcal 𝒫_{y←x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. ```` """ vector_transport_to( From 3cfa995905620a13716832be9e60f91c5f77e51b Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:28:06 +0100 Subject: [PATCH 11/74] =?UTF-8?q?\dots=20to=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/manifolds/product.md | 4 ++-- docs/src/orthonormal_bases.md | 2 +- src/groups/translation_group.jl | 2 +- src/manifolds/PowerManifold.jl | 4 ++-- src/manifolds/ProductManifold.jl | 4 ++-- src/manifolds/Rotations.jl | 2 +- src/manifolds/Sphere.jl | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index f3490c7e55..cba649610c 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # Product manifold -Product manifold $M = M_1 ⨉ M_2 ⨉ \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 $\Pi_i \colon M \to M_i$ for $i \in 1, 2, …, n$ provided by [`submanifold_component`](@ref). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/orthonormal_bases.md b/docs/src/orthonormal_bases.md index e4105ff0de..0948c515de 100644 --- a/docs/src/orthonormal_bases.md +++ b/docs/src/orthonormal_bases.md @@ -1,7 +1,7 @@ # 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 ℳ$ 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$. +An orthonormal basis of the tangent space $T_x ℳ$ 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 \in \{1, 2, …, N\}$ where $g_x$ is the Riemannian metric at point $x$. A vector $v$ from the tangent space $T_x ℳ$ 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))$. The main types are: diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 0f3423782b..714db189f6 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -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/PowerManifold.jl b/src/manifolds/PowerManifold.jl index bfa47ba810..f3f636fd32 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -41,7 +41,7 @@ abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation @doc doc""" PowerManifold{TM<:Manifold, TSize<:Tuple, TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{TM} -The power manifold $ℳ^{n_1⨉ n_2 ⨉ \dots ⨉ n_d}$ with power geometry +The power manifold $ℳ^{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 ⨉ N_2 ⨉ \dots ⨉ 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 diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 3c5c8bba24..37065e472c 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,7 +1,7 @@ @doc doc""" ProductManifold{TM<:Tuple, TRanges<:Tuple, TSizes<:Tuple} <: Manifold -Product manifold $M_1 ⨉ M_2 ⨉ \dots ⨉ 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 ⨉ M_2 ⨉ \dots ⨉ 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`. """ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 49a5d960cb..fc333ee588 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -533,7 +533,7 @@ 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} +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$. diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 6341e4be65..6432bf8303 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -272,7 +272,7 @@ project_tangent!(S::Sphere, w, x, v) = (w .= v .- dot(x, v) .* x) 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)$. +$2\frac{x_p x_p^\mathrm{T}}{x_p^\mathrm{T} x_p} - I$ where $x_p = x + (1, 0, …, 0)$. """ function get_coordinates(M::Sphere{N}, x, v, B::ArbitraryOrthonormalBasis) where {N} if isapprox(x[1], 1) From 7aba7f84caf482775e92e84a7e023f5b97b0c3d0 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:30:48 +0100 Subject: [PATCH 12/74] =?UTF-8?q?Replaces=20\to=20with=20=E2=86=92=20in=20?= =?UTF-8?q?docstrings.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/rotations.md | 4 ++-- docs/src/manifolds/vector_bundle.md | 2 +- src/groups/semidirect_product_group.jl | 2 +- src/manifolds/VectorBundle.jl | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index cba649610c..7eb62787f3 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # Product manifold 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 $\Pi_i \colon M \to M_i$ for $i \in 1, 2, …, n$ provided by [`submanifold_component`](@ref). +Points on the product manifold can be constructed using [`ProductRepr`](@ref) with canonical projections $\Pi_i \colon M → M_i$ for $i \in 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 2a668168dc..afda9bcdaa 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -12,8 +12,8 @@ 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)$. +Let $L_R\colon \mathrm{SO}(n) → \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) → 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)$. 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. diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index eb468306e4..1ec6d13d4c 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 $ℳ$ (base space). -It is characterized by a continuous function $\Pi \colon E \to ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $\Pi$, $\Pi^{-1}(\{x\})$, has a structure of a vector space. +It is characterized by a continuous function $\Pi \colon E → ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $\Pi$, $\Pi^{-1}(\{x\})$, 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/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 4e6ea643a8..6b024923ee 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -20,7 +20,7 @@ const SemidirectProductGroup{N,H,A} = 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 diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 1b51256a74..d15bd8c167 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -189,10 +189,10 @@ Notation: 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(p_x, p_y)^2 + d_F(\xi_x, \xi_{y→x})^2}$ where $d_ℳ$ is the distance on manifold $ℳ$, $d_F$ is the distance -between two vectors from the fiber $F$ and $\xi_{y\to x}$ is the result +between two vectors from the fiber $F$ and $\xi_{y→x}$ is the result of parallel transport of vector $\xi_y$ to point $p_x$. The default behavior of [`vector_transport_to`](@ref) is used to compute the vector transport. @@ -250,7 +250,7 @@ from the vector space of type `M` at point `x` from the underlying [`Manifold`]( The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$\flat \colon Tℳ \to T^{*}ℳ$ +$\flat \colon Tℳ → T^{*}ℳ$ """ function flat(M::Manifold, x, w::FVector) v = allocate_result(M, flat, w, x) @@ -533,7 +533,7 @@ from the vector space `M` at point `x` 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^{*}ℳ \to Tℳ$ +$\sharp \colon T^{*}ℳ → Tℳ$ """ function sharp(M::Manifold, x, w::FVector) v = allocate_result(M, sharp, w, x) From b0db476d355a1184ab56f0457363af223d5d3e7a Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:36:22 +0100 Subject: [PATCH 13/74] Replace greek letters. --- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/vector_bundle.md | 2 +- src/Manifolds.jl | 4 +- src/manifolds/SymmetricPositiveDefinite.jl | 2 +- src/manifolds/VectorBundle.jl | 66 +++++++++++----------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 7eb62787f3..1bfb1b5bc1 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # Product manifold 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 $\Pi_i \colon M → M_i$ for $i \in 1, 2, …, n$ provided by [`submanifold_component`](@ref). +Points on the product manifold can be constructed using [`ProductRepr`](@ref) with canonical projections $Π_i \colon M → M_i$ for $i \in 1, 2, …, n$ provided by [`submanifold_component`](@ref). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 1ec6d13d4c..037e57a47d 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 $ℳ$ (base space). -It is characterized by a continuous function $\Pi \colon E → ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $\Pi$, $\Pi^{-1}(\{x\})$, has a structure of a vector space. +It is characterized by a continuous function $Π \colon E → ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $Π$, $Π^{-1}(\{x\})$, 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/src/Manifolds.jl b/src/Manifolds.jl index 41a62a4f8d..f567a22490 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -116,7 +116,7 @@ component vector $v^i$, compute the equivalent vector representation $v=v^i e_i$, where Einstein summation notation is used: ````math -\wedge: v^i \mapsto v^i e_i +\wedge: v^i ↦ v^i e_i ```` For array manifolds, this converts a vector representation of the tangent @@ -141,7 +141,7 @@ vector $v$, compute the vector components $v^i$, such that $v = v^i e_i$, where Einstein summation notation is used: ````math -\vee: v^i e_i \mapsto v^i +\vee: v^i e_i ↦ v^i ```` For array manifolds, this converts an array representation of the tangent diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 536ff8b30b..21c8d23da0 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -7,7 +7,7 @@ The manifold of symmetric positive definite matrices, i.e. \mathcal P(n) = \bigl\{ x \in ℝ^{n\times n} : -\xi^\mathrm{T}x\xi > 0 \text{ for all } \xi \in ℝ^{n}\backslash\{0\} +ξ^\mathrm{T}xξ > 0 \text{ for all } ξ \in ℝ^{n}\backslash\{0\} \bigr\} ``` diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index d15bd8c167..16a67d763a 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -182,18 +182,18 @@ Distance between points $x$ and $y$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, \xi_y)$. + Similarly, $y = (p_y, ξ_y)$. The distance is calculated as -$d_B(x, y) = \sqrt{d_M(p_x, p_y)^2 + d_F(\xi_x, \xi_{y→x})^2}$ +$d_B(x, y) = \sqrt{d_M(p_x, p_y)^2 + d_F(ξ_x, ξ_{y→x})^2}$ where $d_ℳ$ is the distance on manifold $ℳ$, $d_F$ is the distance -between two vectors from the fiber $F$ and $\xi_{y→x}$ is the result -of parallel transport of vector $\xi_y$ to point $p_x$. The default +between two vectors from the fiber $F$ and $ξ_{y→x}$ is the result +of parallel transport of vector $ξ_y$ to point $p_x$. The default behavior of [`vector_transport_to`](@ref) is used to compute the vector transport. """ @@ -216,20 +216,20 @@ Exponential map of tangent vector $v$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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}ℳ$ and - $\xi_{v,F}$ is a tangent vector from the tangent space $T_{\xi_x}F$ (isomorphic to $F$). + * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) \in T_{x}B$ where + $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and + $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_x}F$ (isomorphic to $F$). The exponential map is calculated as -$\exp_{x}(v) = (\exp_{p_x}(\xi_{v,M}), \xi_{\exp})$ +$\exp_{x}(v) = (\exp_{p_x}(ξ_{v,M}), ξ_{\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 $ξ_{\exp}$ is the result of vector transport of $ξ_x + ξ_{v,F}$ +to the point $\exp_{p_x}(ξ_{v,M})$. +The sum $ξ_x + ξ_{v,F}$ corresponds to the exponential map in the vector space $F$. """ exp(::VectorBundle, ::Any) @@ -372,17 +372,17 @@ Inner product of tangent vectors `v` and `w` at point `x` from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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}ℳ$ 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,M}, ξ_{v,F}) \in T_{x}B$ where + $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and + $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_x}F$ (isomorphic to $F$). + Similarly for the other tangent vector $w = (ξ_{w,M}, ξ_{w,F}) \in T_{x}B$. The inner product is calculated as -$⟨v, w⟩_{B} = ⟨\xi_{v,M}, \xi_{w,M}⟩_{M} + ⟨\xi_{v,F}, \xi_{w,F}⟩_{F}.$ +$⟨v, w⟩_{B} = ⟨ξ_{v,M}, ξ_{w,M}⟩_{M} + ⟨ξ_{v,F}, ξ_{w,F}⟩_{F}.$ """ function inner(B::VectorBundle, x, v, w) px, ξx = submanifold_components(B.M, x) @@ -410,18 +410,18 @@ Logarithmic map of the point $y$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, \xi_y)$. + Similarly, $y = (p_y, ξ_y)$. The logarithmic map is calculated as -$\log_{x}(y) = (\log_{p_x}(p_y), \xi_{\log} - \xi_x)$ +$\log_{x}(y) = (\log_{p_x}(p_y), ξ_{\log} - ξ_x)$ -where $\xi_{\log}$ is the result of vector transport of $\xi_y$ +where $ξ_{\log}$ is the result of vector transport of $ξ_y$ to the point $p_x$. -The difference $\xi_{\log} - \xi_x$ corresponds to the logarithmic map in the vector space $F$. +The difference $ξ_{\log} - ξ_x$ corresponds to the logarithmic map in the vector space $F$. """ log(::VectorBundle, ::Any...) @@ -453,13 +453,13 @@ Project the point $x$ from the ambient space of the vector bundle `B` over manifold `B.VS` (denoted $ℳ$) to the vector bundle. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x$ belongs to the ambient space of $ℳ$ - and $\xi_x$ belongs to the ambient space of the + * The point $x = (p_x, ξ_x)$ where $p_x$ belongs to the ambient space of $ℳ$ + and $ξ_x$ belongs to the ambient space of the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. The projection is calculated by projecting the point $p_x$ to the manifold $ℳ$ -and then projecting the vector $\xi_x$ to the tangent space $T_{p_x}ℳ$. +and then projecting the vector $ξ_x$ to the tangent space $T_{p_x}ℳ$. """ project_point(::VectorBundle, ::Any...) @@ -477,16 +477,16 @@ Project the element $v$ of the ambient space of the tangent space $T_x B$ to the tangent space $T_x B$. Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ 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}ℳ$ - and $\xi_{v,F}$ belongs to the ambient space of the + * The vector $x = (ξ_{v,M}, ξ_{v,F})$ where $p_x$ belongs to the ambient space of $T_{p_x}ℳ$ + and $ξ_{v,F}$ belongs to the ambient space of the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. -The projection is calculated by projecting $\xi_{v,M}$ to tangent space $T_{p_x}ℳ$ -and then projecting the vector $\xi_{v,F}$ to the fiber $F$. +The projection is calculated by projecting $ξ_{v,M}$ to tangent space $T_{p_x}ℳ$ +and then projecting the vector $ξ_{v,F}$ to the fiber $F$. """ project_tangent(::VectorBundle, ::Any...) @@ -640,7 +640,7 @@ Zero tangent vector at point $x$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{x}B$ Notation: - * The point $x = (p_x, \xi_x)$ where $p_x \in ℳ$ and $\xi_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. From ab8dc0e36bec878f12ecca86afbac921b19b2047 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:44:51 +0100 Subject: [PATCH 14/74] Replaces \in \neq and \colon with their utf8s. --- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/rotations.md | 8 +++---- .../manifolds/symmetricpositivedefinite.md | 2 +- docs/src/manifolds/vector_bundle.md | 2 +- docs/src/orthonormal_bases.md | 2 +- src/manifolds/Circle.jl | 6 ++--- src/manifolds/Euclidean.jl | 4 ++-- src/manifolds/FixedRankMatrices.jl | 18 +++++++-------- src/manifolds/Grassmann.jl | 10 ++++----- src/manifolds/Hyperbolic.jl | 10 ++++----- src/manifolds/MetricManifold.jl | 2 +- src/manifolds/Rotations.jl | 2 +- src/manifolds/Sphere.jl | 2 +- src/manifolds/Stiefel.jl | 10 ++++----- src/manifolds/Symmetric.jl | 4 ++-- src/manifolds/SymmetricPositiveDefinite.jl | 14 ++++++------ .../SymmetricPositiveDefiniteLinearAffine.jl | 2 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 4 ++-- .../SymmetricPositiveDefiniteLogEuclidean.jl | 2 +- src/manifolds/VectorBundle.jl | 22 +++++++++---------- src/statistics.jl | 6 ++--- 21 files changed, 67 insertions(+), 67 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 1bfb1b5bc1..5ea5ba1842 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # Product manifold 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 \colon M → M_i$ for $i \in 1, 2, …, n$ provided by [`submanifold_component`](@ref). +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 afda9bcdaa..1427a9fbc9 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -2,7 +2,7 @@ The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n⨉ n}$, i.e. -$\mathrm{SO}(n) = \bigl\{R \in ℝ^{n⨉ n} \big| RR^{\mathrm{T}} = +$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n⨉ n} \big| RR^{\mathrm{T}} = R^{\mathrm{T}}R = \mathrm{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. @@ -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) → \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) → 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 $\mathrm{I}_n$ by the differential of $L_R$ at identity, $(\mathrm{d}L_R)_{\mathrm{I}_n} : T_{\mathrm{I}_n} \mathrm{SO}(n) → T_R \mathrm{SO}(n)$. +For a tangent vector at the identity rotation $v ∈ 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 ∈ 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 deb5909025..2ea88b1512 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 ℝ^{n⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0\neq x \inℝ^n \bigr\} +𝒫(n) = \bigl\{ A ∈ ℝ^{n⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \inℝ^n \bigr\} ``` ```@docs diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 037e57a47d..4ba7c904e4 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 $ℳ$ (base space). -It is characterized by a continuous function $Π \colon E → ℳ$, such that for each point $x \in ℳ$ the preimage of $x$ by $Π$, $Π^{-1}(\{x\})$, has a structure of a vector space. +It is characterized by a continuous function $Π : E → ℳ$, such that for each point $x ∈ ℳ$ the preimage of $x$ by $Π$, $Π^{-1}(\{x\})$, 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/orthonormal_bases.md b/docs/src/orthonormal_bases.md index 0948c515de..3a3683369d 100644 --- a/docs/src/orthonormal_bases.md +++ b/docs/src/orthonormal_bases.md @@ -1,7 +1,7 @@ # 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 ℳ$ 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 \in \{1, 2, …, N\}$ where $g_x$ is the Riemannian metric at point $x$. +An orthonormal basis of the tangent space $T_x ℳ$ 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 ℳ$ 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))$. The main types are: diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 404ea0f248..0a0b5c04ff 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -2,7 +2,7 @@ Circle{F} <: Manifold The circle $𝕊^1$ as a manifold ere manifold represented by -real-valued data in $[-\pi,\pi)$ or complex-valued data $z\in ℂ$ of absolute value +real-valued data in $[-\pi,\pi)$ or complex-valued data $z ∈ ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor @@ -20,8 +20,8 @@ Circle(f::AbstractNumbers = ℝ) = Circle{f}() check_manifold_point(M::Circle, x) 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 ℂ$ with $\lvert x \rvert = 1$. +For the real-valued case, `x` is an angle and hence it checks that $x ∈ [-\pi,\pi)$. +for the complex-valued case its a unit number, $x ∈ ℂ$ with $\lvert x \rvert = 1$. """ check_manifold_point(::Circle, ::Any...) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index d529933851..64fc26f0df 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -131,9 +131,9 @@ the inner product on the real-valued or complex valued vector space 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_x(v,w) = \sum_{k ∈ I} \overline{v}_{k} w_{k}, ```` -where $I$ is the set of integer vectors $k\in ℕ^i$, such that for all +where $I$ is the set of integer 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 diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 1792366996..be6b36fd8c 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -3,23 +3,23 @@ The manifold of $m ⨉ n$ real-valued (complex-valued) matrices of fixed rank $k$, i.e. ````math -ℳ = \{ x \in ℝ^{m ⨉ n} : \operatorname{rank}(x) = k \}. +ℳ = \{ x ∈ ℝ^{m ⨉ n} : \operatorname{rank}(x) = k \}. ```` # Representation with 3 matrix factors -A point $x\in ℳ$ can be stored using orthonormal matrices -$U\in ℝ^{m ⨉ k}$, $V\in ℝ^{n ⨉ k}$ as well as the $k$ singular +A point $x ∈ ℳ$ can be stored using orthonormal matrices +$U ∈ ℝ^{m ⨉ k}$, $V ∈ ℝ^{n ⨉ 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 -The tangent space $T_x ℳ$ at a point $x\in ℳ$ with $x=USV^\mathrm{T}$ +The tangent space $T_x ℳ$ at a point $x ∈ ℳ$ with $x=USV^\mathrm{T}$ is given by ````math T_xℳ = \bigl\{ UMV^\mathrm{T} + U_xV^\mathrm{T} + UV_x^\mathrm{T} : - M \in ℝ^{k ⨉ k}, - U_x \in ℝ^{m ⨉ k}, - V_x \in ℝ^{n ⨉ k} + M ∈ ℝ^{k ⨉ k}, + U_x ∈ ℝ^{m ⨉ k}, + V_x ∈ ℝ^{n ⨉ k} \text{ s.t. } U_x^\mathrm{T}U = 0_k, V_x^\mathrm{T}V = 0_k @@ -86,7 +86,7 @@ together with its base point, see for example [`FixedRankMatrices`](@ref) # 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 ℝ^{k ⨉ k}$ + inner dimensions $k$, i.e. in $UMV^\mathrm{T}$, $M ∈ ℝ^{k ⨉ k}$ """ struct UMVTVector{TU<:AbstractMatrix,TM<:AbstractMatrix,TVt<:AbstractMatrix} <: TVector U::TU @@ -241,7 +241,7 @@ end project_tangent(M, x, A) project_tangent(M, x, v) -Project the matrix $A\in ℝ^{m,n}$ or a [`UMVTVector`](@ref) `v` from the embedding or +Project the matrix $A ∈ ℝ^{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]. diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index a44a4a2874..c87edb3c0e 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -3,15 +3,15 @@ The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned by $k$ linear independent vectors $𝔽^n$, where -$𝔽 \in \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. +$𝔽 ∈ \{ℝ, ℂ\}$ 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 𝔽^{n ⨉ k}, \bar{x}^\mathrm{T}x = I_k\}, +\operatorname{Gr}(n,k) :eqq \bigl\{ \operatorname{span}(x) +: x ∈ 𝔽^{n ⨉ k}, \bar{x}^\mathrm{T}x = I_k\}, ```` where ${\bar·}^{\mathrm{T}}$ denotes the complex conjugate transpose and @@ -32,13 +32,13 @@ The tangent space at a point (subspace) $x$ is given by ````math T_x\mathrm{Gr}(n,k) = \bigl\{ -v \in 𝔽^{n ⨉ k} : +v ∈ 𝔽^{n ⨉ k} : {\bar v}^{\mathrm{T}}x + {\bar x}^{\mathrm{T}}v = 0_{k} \bigr\}, ```` where $0_{k}$ denotes the $k ⨉ k$ zero matrix. -Note that a point $x\in \operatorname{Gr}(n,k)$ might be represented by +Note that a point $x ∈ \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)$ diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 26e907094f..a34549d7f8 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -5,7 +5,7 @@ The hyperbolic space $ℍ^n$ represented by $n+1$-Tuples, i.e. in by vectors in $ℝ^{n+1}$ using the Minkowsi metric, i.e. ```math -ℍ^n = \Bigl\{x\in ℝ^{n+1} +ℍ^n = \Bigl\{x ∈ ℝ^{n+1} \ \Big|\ ⟨x,x⟩_{\mathrm{M}}= -x_{n+1}^2 + \displaystyle\sum_{k=1}^n x_k^2 = -1, x_{n+1} > 0\Bigr\}, ``` @@ -14,8 +14,8 @@ where $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) is Minkowski inner product. The tangent space $T_x ℍ^n$ is given by ````math -T_x ℍ^n \coloneqq \bigl\{ -v \in ℝ^{n+1} \ \bigl |\ ⟨x,v⟩_{\mathrm{M}} = 0 +T_x ℍ^n :eqq \bigl\{ +v ∈ ℝ^{n+1} \ \bigl |\ ⟨x,v⟩_{\mathrm{M}} = 0 \bigr\}. ```` The Minkowski inner product inntroduces the [`MinkowskiMetric`](@ref), which is @@ -45,7 +45,7 @@ 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ℳ$, - $x\in ℳ$, of the [`Hyperbolic`](@ref) space $ℳ$. + $x ∈ ℳ$, of the [`Hyperbolic`](@ref) space $ℳ$. """ struct MinkowskiMetric <: LorentzMetric end @@ -158,7 +158,7 @@ is_default_metric(::Hyperbolic, ::MinkowskiMetric) = Val(true) Compute the logarithmic map on the [`Hyperbolic`](@ref) space $ℍ^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$ +The formula reads for $x ≠ y$ ```math \log_x y = d_{ℍ^n}(x,y) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index f1e9c17f8b..38953685db 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -41,7 +41,7 @@ abstract type LorentzMetric <: Metric end 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 ℳ$, the +products. The positive definite property means that for $v ∈ T_x ℳ$, the inner product $g(v, v) > 0$ whenever $v$ is not the zero vector. """ abstract type RiemannianMetric <: Metric end diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index fc333ee588..d2dd3726d0 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -663,7 +663,7 @@ 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)$. +$\omega_{i (i - 3)/2 + j + 1} = \Omega_{ij}$, for $i ∈ [4, n], j ∈ [1,i)$. """ vee(::Rotations, ::Any...) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 6432bf8303..0dd00ec956 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -164,7 +164,7 @@ end 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$ +The formula reads for $x ≠ -y$ ````math \log_x y = d_{𝕊^n}(x,y) diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 7306ac1c5f..c5302f8670 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -4,17 +4,17 @@ The Stiefel manifold consists of all $n ⨉ k$, $n\geq k$ orthonormal matrices, i.e. ````math -ℳ = \{ x \in 𝔽^{n ⨉ k} : x^{\mathrm{H}}x = I_k \}, +ℳ = \{ x ∈ 𝔽^{n ⨉ k} : x^{\mathrm{H}}x = I_k \}, ```` -where $𝔽 \in \{ℝ, ℂ\}$, +where $𝔽 ∈ \{ℝ, ℂ\}$, $·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and -$I_n \in ℝ^{n\times n}$ denotes the $k \times k$ identity matrix. +$I_n ∈ ℝ^{n\times n}$ denotes the $k \times k$ identity matrix. -The tangent space at a point $x\in ℳ$ is given by +The tangent space at a point $x∈ ℳ$ is given by ````math -T_x ℳ = \{ v \in 𝔽^{n\times k} : x^{\mathrm{H}}v + v^{\mathrm{H}}x=0_n\}, +T_x ℳ = \{ v ∈ 𝔽^{n\times k} : x^{\mathrm{H}}v + v^{\mathrm{H}}x=0_n\}, ```` where $0_n$ is the $k\times k$ zero matrix. diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 79b04bfd8d..6c9b0cf20e 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -5,10 +5,10 @@ The [`Manifold`](@ref) $ \operatorname{Sym} (n)$ consisting of the real- or comp symmetric matrices of size $ n\times n$, i.e. the set ````math -\operatorname{Sym}(n) = \bigl\{A \in 𝔽^{n\times n} \big| A^{\mathrm{H}} = A \bigr\}, +\operatorname{Sym}(n) = \bigl\{A ∈ 𝔽^{n\times n} \big| A^{\mathrm{H}} = A \bigr\}, ```` where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed -and the field $𝔽 \in \{ ℝ, ℂ\}$ 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. diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 21c8d23da0..ad97597cb5 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -4,10 +4,10 @@ The manifold of symmetric positive definite matrices, i.e. ```math -\mathcal P(n) = +𝒫(n) = \bigl\{ -x \in ℝ^{n\times n} : -ξ^\mathrm{T}xξ > 0 \text{ for all } ξ \in ℝ^{n}\backslash\{0\} +x ∈ ℝ^{n\times n} : +ξ^\mathrm{T}xξ > 0 \text{ for all } ξ ∈ ℝ^{n}\backslash\{0\} \bigr\} ``` @@ -15,7 +15,7 @@ x \in ℝ^{n\times n} : SymmetricPositiveDefinite(n) -generates the manifold $\mathcal P(n) \subset ℝ^{n\times n}$ +generates the manifold $𝒫(n) \subset ℝ^{n\times n}$ """ struct SymmetricPositiveDefinite{N} <: Manifold end @@ -91,9 +91,9 @@ injectivity_radius(M::SymmetricPositiveDefinite{N}, args...) where {N} = Inf manifold_dimension(M::SymmetricPositiveDefinite) returns the dimension of -[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n\in ℕ$, i.e. +[`SymmetricPositiveDefinite`](@ref) `M`$=𝒫(n), n ∈ ℕ$, i.e. ````math -\dim \mathcal P(n) = \frac{n(n+1)}{2} +\dim 𝒫(n) = \frac{n(n+1)}{2} ```` """ @generated function manifold_dimension(M::SymmetricPositiveDefinite{N}) where {N} @@ -129,7 +129,7 @@ end 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 -symmetric positive definite matrix on $ℳ = \mathcal P(n)$. +symmetric positive definite matrix on $ℳ = 𝒫(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index e684b86a30..88df48e246 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -16,7 +16,7 @@ Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between as a [`MetricManifold`](@ref) with [`LinearAffineMetric`](@ref). The formula reads ```math -d_{\mathcal P(n)}(x,y) +d_{𝒫(n)}(x,y) = \lVert \operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})\rVert_{\mathrm{F}}., ``` where $\operatorname{Log}$ denotes the matrix logarithm and diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index d906464b94..9070e46d89 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -30,7 +30,7 @@ nmatrices, i.e. between two symmetric positive definite matrices `x` and `y` with respect to the [`LogCholeskyMetric`](@ref). The formula reads ````math -d_{\mathcal P(n)}(x,y) = \sqrt{ +d_{𝒫(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 }\ \ , ```` @@ -146,7 +146,7 @@ triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With $u$ the pa transport on [`CholeskySpace`](@ref) from $l$ to $k$. The formula hear reads ````math - \mathcal 𝒫_{y←x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. + 𝒫_{y←x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. ```` """ vector_transport_to( diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index 261282d82a..d3ac15a902 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -14,7 +14,7 @@ Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between The formula reads ```math - d_{\mathcal P(n)}(x,y) = \lVert \Log x - \Log y \rVert_{\mathrm{F}} + d_{𝒫(n)}(x,y) = \lVert \Log x - \Log y \rVert_{\mathrm{F}} ``` where $\operatorname{Log}$ denotes the matrix logarithm and diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 16a67d763a..6392c90866 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -182,7 +182,7 @@ Distance between points $x$ and $y$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. Similarly, $y = (p_y, ξ_y)$. @@ -216,10 +216,10 @@ Exponential map of tangent vector $v$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) \in T_{x}B$ where + * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) ∈ T_{x}B$ where $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_x}F$ (isomorphic to $F$). @@ -250,7 +250,7 @@ from the vector space of type `M` at point `x` from the underlying [`Manifold`]( The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$\flat \colon Tℳ → T^{*}ℳ$ +$\flat : Tℳ → T^{*}ℳ$ """ function flat(M::Manifold, x, w::FVector) v = allocate_result(M, flat, w, x) @@ -372,13 +372,13 @@ Inner product of tangent vectors `v` and `w` at point `x` from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) \in T_{x}B$ where + * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) ∈ T_{x}B$ where $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_x}F$ (isomorphic to $F$). - Similarly for the other tangent vector $w = (ξ_{w,M}, ξ_{w,F}) \in T_{x}B$. + Similarly for the other tangent vector $w = (ξ_{w,M}, ξ_{w,F}) ∈ T_{x}B$. The inner product is calculated as @@ -410,7 +410,7 @@ Logarithmic map of the point $y$ at point $x$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. Similarly, $y = (p_y, ξ_y)$. @@ -477,7 +477,7 @@ Project the element $v$ of the ambient space of the tangent space $T_x B$ to the tangent space $T_x B$. Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. * The vector $x = (ξ_{v,M}, ξ_{v,F})$ where $p_x$ belongs to the ambient space of $T_{p_x}ℳ$ @@ -533,7 +533,7 @@ from the vector space `M` at point `x` 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^{*}ℳ → Tℳ$ +$\sharp : T^{*}ℳ → Tℳ$ """ function sharp(M::Manifold, x, w::FVector) v = allocate_result(M, sharp, w, x) @@ -640,7 +640,7 @@ Zero tangent vector at point $x$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{x}B$ Notation: - * The point $x = (p_x, ξ_x)$ where $p_x \in ℳ$ and $ξ_x$ belongs to the + * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_x$ belongs to the fiber $F=\pi^{-1}(\{p_x\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. diff --git a/src/statistics.jl b/src/statistics.jl index 874b93e890..f68c903d7e 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -33,7 +33,7 @@ t_k &= \frac{w_k}{\sum_{i=1}^k w_i}\\ where $x_k$ are points, $w_k$ are weights, $\mu_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 ℳ$. The algorithm +between points $x,y ∈ ℳ$. The algorithm terminates when all $x_k$ have been considered. In the [`Euclidean`](@ref) case, this exactly computes the weighted mean. @@ -119,7 +119,7 @@ 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 ℳ} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}^2(y,x_i), +\argmin_{y ∈ ℳ} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}^2(y,x_i), ```` where $\mathrm{d}_{ℳ}$ denotes the Riemannian [`distance`](@ref). @@ -386,7 +386,7 @@ end 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 ℳ} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}(y,x_i), +\argmin_{y ∈ ℳ} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}(y,x_i), ```` where $\mathrm{d}_{ℳ}$ denotes the Riemannian [`distance`](@ref). This function is nonsmooth (i.e nondifferentiable). From 783cebb21dd72bd88ae5c5810880c8ff519e12b2 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 09:57:42 +0100 Subject: [PATCH 15/74] Changes lfloor and lfloor to their utf8s. --- docs/src/manifolds/symmetricpositivedefinite.md | 2 +- src/manifolds/CholeskySpace.jl | 12 ++++++------ .../SymmetricPositiveDefiniteLogCholesky.jl | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 2ea88b1512..6ec27dd707 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -𝒫(n) = \bigl\{ A ∈ ℝ^{n⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \inℝ^n \bigr\} +𝒫(n) = \bigl\{ A ∈ ℝ^{n ⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \in ℝ^n \bigr\} ``` ```@docs diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index b6824b48f9..3475529cb4 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -103,11 +103,11 @@ triangular matrix with positive diagonal `x` towards the lower triangular matrix The formula reads ````math -\exp_x v = \lfloor x \rfloor + \lfloor v \rfloor + \operatorname{diag}(x) +\exp_x v = ⌊ x ⌋ + ⌊ v ⌋ + \operatorname{diag}(x) \operatorname{diag}(x)\exp\bigl( \operatorname{diag}(v)\operatorname{diag}(x)^{-1}\bigr), ```` -where $\lfloor x\rfloor$ denotes the strictly lower triangular matrix of $x$ and +where $⌊ x⌋$ denotes the strictly lower triangular matrix of $x$ and $\operatorname{diag}(x)$ the diagonal matrix of $x$ """ exp(::CholeskySpace, ::Any...) @@ -148,11 +148,11 @@ from the lower triangular matrix with positive diagonal `x` towards `y`. The formula reads ````math -\log_x v = \lfloor x \rfloor - \lfloor y \rfloor +\log_x v = ⌊ x ⌋ - ⌊ y ⌋ +\operatorname{diag}(x)\log\bigl(\operatorname{diag}(y)\operatorname{diag}(x)^{-1}\bigr), ```` -where $\lfloor x\rfloor$ denotes the strictly lower triangular matrix of $x$ and +where $⌊ x⌋$ denotes the strictly lower triangular matrix of $x$ and $\operatorname{diag}(x)$ the diagonal matrix of $x$ """ log(::Cholesky, ::Any...) @@ -191,11 +191,11 @@ Parallely transport the tangent vector `v` at `x` along the geodesic to `y` on to the [`CholeskySpace`](@ref) manifold `M`. The formula reads ````math -\mathcal 𝒫_{y←x}(v) = \lfloor v \rfloor +𝒫_{y←x}(v) = ⌊ v ⌋ + \operatorname{diag}(y)\operatorname{diag}(x)^{-1}\operatorname{diag}(v), ```` -where $\lfloor·\rfloor$ denotes the strictly lower triangular matrix, +where $⌊·⌋$ denotes the strictly lower triangular matrix, and $\operatorname{diag}$ extracts the diagonal matrix. """ vector_transport_to(::CholeskySpace, ::Any, ::Any, ::Any, ::ParallelTransport) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 9070e46d89..c5b34220bf 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -31,12 +31,12 @@ with respect to the [`LogCholeskyMetric`](@ref). The formula reads ````math d_{𝒫(n)}(x,y) = \sqrt{ - \lVert \lfloor l \rfloor - \lfloor k \rfloor \rVert_{\mathrm{F}}^2 + \lVert ⌊ l ⌋ - ⌊ k ⌋ \rVert_{\mathrm{F}}^2 + \lVert \log(\operatorname{diag}(l)) - \log(\operatorname{diag}(k))\rVert_{\mathrm{F}}^2 }\ \ , ```` where $l$ and $k$ are the cholesky factors of $x$ and $y$, respectively, -$\lfloor·\rfloor$ denbotes the strictly lower triangular matrix of its argument, +$⌊·⌋$ denbotes the strictly lower triangular matrix of its argument, and $\lVert·\rVert_{\mathrm{F}}$ denotes the Frobenius norm. """ function distance( From 7427d2afe49bee8f8718fad838ea73416c9553a7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 10:41:03 +0100 Subject: [PATCH 16/74] =?UTF-8?q?revert=20=E2=A8=89=20back=20to=20\times.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/rotations.md | 4 ++-- .../manifolds/symmetricpositivedefinite.md | 2 +- src/groups/special_euclidean.jl | 2 +- src/manifolds/CholeskySpace.jl | 2 +- src/manifolds/Euclidean.jl | 4 ++-- src/manifolds/FixedRankMatrices.jl | 20 +++++++++---------- src/manifolds/Grassmann.jl | 14 ++++++------- src/manifolds/PowerManifold.jl | 4 ++-- src/manifolds/ProductManifold.jl | 4 ++-- src/manifolds/Rotations.jl | 4 ++-- src/manifolds/Stiefel.jl | 4 ++-- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 5ea5ba1842..83f834b670 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,6 +1,6 @@ # Product manifold -Product manifold $M = M_1 ⨉ M_2 ⨉ … M_n$ of manifolds $M_1, M_2, …, M_n$. +Product manifold $M = M_1 \times M_2 \times … 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 diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index 1427a9fbc9..bbd6c83be0 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -1,8 +1,8 @@ # Rotations -The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n⨉ n}$, i.e. +The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n\times n}$, i.e. -$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n⨉ n} \big| RR^{\mathrm{T}} = +$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n\times n} \big| RR^{\mathrm{T}} = R^{\mathrm{T}}R = \mathrm{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. diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 6ec27dd707..16bccda4f7 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -𝒫(n) = \bigl\{ A ∈ ℝ^{n ⨉ n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \in ℝ^n \bigr\} +𝒫(n) = \bigl\{ A ∈ ℝ^{n \times n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \in ℝ^n \bigr\} ``` ```@docs diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index b679556de9..007812ec1e 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -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) ⨉ \mathrm{SO}(n)$ or as affine matrices with size `(n + 1, n + 1)`. +$\mathrm{T}(n) \times \mathrm{SO}(n)$ or as affine matrices with size `(n + 1, n + 1)`. """ const SpecialEuclidean{N} = SemidirectProductGroup{ TranslationGroup{Tuple{N},ℝ}, diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 3475529cb4..dd459db13e 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -9,7 +9,7 @@ are for example summarized in Table 1 of [^Lin2019]. CholeskySpace(n) -Generate the manifold of $n⨉ n$ lower triangular matrices with positive diagonal. +Generate the manifold of $n\times n$ lower triangular matrices with positive diagonal. [^Lin2019]: > Lin, Zenhua: "Riemannian Geometry of Symmetric Positive Definite Matrices via diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 64fc26f0df..3910861e87 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -13,7 +13,7 @@ Generate the $n$-dimensional vector space $ℝ^n$. Generate the vector space of $k=n_1n_2·… n_i$ values, i.e. the $𝔽^{n_1, n_2,…, n_d}$ whose -elements are interpreted as $n_1 ⨉,n_2 ⨉ … ⨉ n_i$ arrays. +elements are interpreted as $n_1 \times,n_2 \times … \times 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 @@ -128,7 +128,7 @@ injectivity_radius(::Euclidean) = Inf 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 ⨉ n_2 ⨉ … ⨉ n_i$, i.e. +of arrays (or tensors) of size $n_1 \times n_2 \times … \times n_i$, i.e. ````math g_x(v,w) = \sum_{k ∈ I} \overline{v}_{k} w_{k}, diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index be6b36fd8c..6aa5f237f6 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,14 +1,14 @@ @doc doc""" FixedRankMatrices{m,n,k,T} <: Manifold -The manifold of $m ⨉ n$ real-valued (complex-valued) matrices of fixed rank $k$, i.e. +The manifold of $m \times n$ real-valued (complex-valued) matrices of fixed rank $k$, i.e. ````math -ℳ = \{ x ∈ ℝ^{m ⨉ n} : \operatorname{rank}(x) = k \}. +ℳ = \{ x ∈ ℝ^{m \times n} : \operatorname{rank}(x) = k \}. ```` # Representation with 3 matrix factors A point $x ∈ ℳ$ can be stored using orthonormal matrices -$U ∈ ℝ^{m ⨉ k}$, $V ∈ ℝ^{n ⨉ k}$ as well as the $k$ singular +$U ∈ ℝ^{m \times k}$, $V ∈ ℝ^{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 @@ -17,18 +17,18 @@ The tangent space $T_x ℳ$ at a point $x ∈ ℳ$ with $x=USV^\mathrm{T}$ is given by ````math T_xℳ = \bigl\{ UMV^\mathrm{T} + U_xV^\mathrm{T} + UV_x^\mathrm{T} : - M ∈ ℝ^{k ⨉ k}, - U_x ∈ ℝ^{m ⨉ k}, - V_x ∈ ℝ^{n ⨉ k} + M ∈ ℝ^{k \times k}, + U_x ∈ ℝ^{m \times k}, + V_x ∈ ℝ^{n \times k} \text{ s.t. } U_x^\mathrm{T}U = 0_k, V_x^\mathrm{T}V = 0_k \bigr\}, ```` -where $0_k$ is the $k ⨉ k$ zero matrix. See [`UMVTVector`](@ref) for details. +where $0_k$ is the $k \times k$ zero matrix. See [`UMVTVector`](@ref) for details. The (default) metric of this manifold is obtained by restricting the metric -on $ℝ^{m ⨉ n}$ to the tangent bundle. This implementation follows[^Vandereycken2013]. +on $ℝ^{m \times n}$ to the tangent bundle. This implementation follows[^Vandereycken2013]. # Constructor FixedRankMatrics(m,n,k,t=ℝ) @@ -86,7 +86,7 @@ together with its base point, see for example [`FixedRankMatrices`](@ref) # 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 ∈ ℝ^{k ⨉ k}$ + inner dimensions $k$, i.e. in $UMV^\mathrm{T}$, $M ∈ ℝ^{k \times k}$ """ struct UMVTVector{TU<:AbstractMatrix,TM<:AbstractMatrix,TVt<:AbstractMatrix} <: TVector U::TU @@ -293,7 +293,7 @@ Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by comput y = U_kS_kV_k^\mathrm{T}, ```` 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 ⨉ k$ with the $k$ largest +in the sense that $S_k$ is the diagonal matrix of size $k \times k$ with the $k$ largest singular values and $U$ and $V$ are shortened accordingly. """ retract(::FixedRankMatrices, ::Any, ::Any, ::PolarRetraction) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index c87edb3c0e..84188abd82 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -11,11 +11,11 @@ The manifold can be represented as ````math \operatorname{Gr}(n,k) :eqq \bigl\{ \operatorname{span}(x) -: x ∈ 𝔽^{n ⨉ k}, \bar{x}^\mathrm{T}x = I_k\}, +: x ∈ 𝔽^{n \times k}, \bar{x}^\mathrm{T}x = I_k\}, ```` where ${\bar·}^{\mathrm{T}}$ denotes the complex conjugate transpose and -$I_k$ is the $k ⨉ k$ identity matrix. This means, that the columns of $x$ +$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 $\operatorname{Gr}(n,k)$, and hence the subspace can actually be represented by a whole equivalence class of representers. @@ -26,17 +26,17 @@ 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 ⨉ k$ matrices. +the orthogonal group $\operatorname{O}(k)$ of orthogonal $k \times k$ matrices. The tangent space at a point (subspace) $x$ is given by ````math T_x\mathrm{Gr}(n,k) = \bigl\{ -v ∈ 𝔽^{n ⨉ k} : +v ∈ 𝔽^{n \times k} : {\bar v}^{\mathrm{T}}x + {\bar x}^{\mathrm{T}}v = 0_{k} \bigr\}, ```` -where $0_{k}$ denotes the $k ⨉ k$ zero matrix. +where $0_{k}$ denotes the $k \times k$ zero matrix. Note that a point $x ∈ \operatorname{Gr}(n,k)$ might be represented by different matrices (i.e. matrices with orthonormal column vectors that span @@ -102,7 +102,7 @@ Check whether `v` is a tangent vector in the tangent space of `x` on the [`Grass ```` where $·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ -denotes the $k ⨉ k$ zero natrix. +denotes the $k \times 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) @@ -346,7 +346,7 @@ Compute the QR-based retraction [`QRRetraction`](@ref) on the ````math \operatorname{retr}_xv = QD, ```` -where D is a $m ⨉ n$ matrix with +where D is a $m \times n$ matrix with ````math D = \operatorname{diag}( \operatorname{sgn}(R_{ii}+0,5)_{i=1}^n ). ```` diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index f3f636fd32..5f751b4b5a 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -41,7 +41,7 @@ abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation @doc doc""" PowerManifold{TM<:Manifold, TSize<:Tuple, TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{TM} -The power manifold $ℳ^{n_1⨉ n_2 ⨉ … ⨉ n_d}$ with power geometry +The power manifold $ℳ^{n_1\times n_2 \times … \times 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 ⨉ N_2 ⨉ … ⨉ N_n}$. +Generate the power manifold $M^{N_1 \times N_2 \times … \times 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 diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 37065e472c..9c051cefc8 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,7 +1,7 @@ @doc doc""" ProductManifold{TM<:Tuple, TRanges<:Tuple, TSizes<:Tuple} <: Manifold -Product manifold $M_1 ⨉ M_2 ⨉ … ⨉ M_n$ with product geometry. +Product manifold $M_1 \times M_2 \times … \times 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 ⨉ M_2 ⨉ … ⨉ M_n$. +generates the product manifold $M_1 \times M_2 \times … \times M_n$. Alternatively, the same manifold can be contructed using the `×` operator: `M_1 × M_2 × M_3`. """ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index d2dd3726d0..494f07a27e 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,14 +1,14 @@ @doc doc""" Rotations{N} <: Manifold -Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n ⨉ n$ +Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n \times n$ real-valued orthogonal matrices with determinant $+1$. # Constructor Rotations(n) -Generate the $\mathrm{SO}(n) \subset ℝ^{n ⨉ n}$ +Generate the $\mathrm{SO}(n) \subset ℝ^{n \times n}$ """ struct Rotations{N} <: Manifold end diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index c5302f8670..20c312d2d5 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,10 +1,10 @@ @doc doc""" Stiefel{n,k,T} <: Manifold -The Stiefel manifold consists of all $n ⨉ k$, $n\geq k$ orthonormal matrices, i.e. +The Stiefel manifold consists of all $n \times k$, $n\geq k$ orthonormal matrices, i.e. ````math -ℳ = \{ x ∈ 𝔽^{n ⨉ k} : x^{\mathrm{H}}x = I_k \}, +ℳ = \{ x ∈ 𝔽^{n \times k} : x^{\mathrm{H}}x = I_k \}, ```` where $𝔽 ∈ \{ℝ, ℂ\}$, From adf01f71ede0ca3771e643762158a86ee002fd40 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 10:46:25 +0100 Subject: [PATCH 17/74] =?UTF-8?q?revert=20=E2=97=A6=20back=20to=20\circ.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/groups/group.jl | 32 +++++++++++++------------- src/groups/semidirect_product_group.jl | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 7d011131f1..6d1840bbee 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -237,7 +237,7 @@ end inv(G::AbstractGroupManifold, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x ◦ x^{-1} = x^{-1} ◦ x = e ∈ G$. +$x \circ x^{-1} = x^{-1} \circ x = e ∈ G$. """ inv(M::Manifold, x) = inv(M, x, is_decorator_manifold(M)) inv(M::Manifold, x, ::Val{true}) = inv(M.manifold, x) @@ -253,7 +253,7 @@ end inv!(G::AbstractGroupManifold, y, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that -$x ◦ x^{-1} = x^{-1} ◦ x = e ∈ G$. +$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)) @@ -265,7 +265,7 @@ end @doc doc""" identity(G::AbstractGroupManifold, x) -Identity element $e ∈ G$, such that for any element $x ∈ G$, $x ◦ e = e ◦ x = x$. +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(M::Manifold, x) = identity(M, x, is_decorator_manifold(M)) @@ -315,7 +315,7 @@ isapprox(::GT, ::E, ::E; kwargs...) where {GT<:GroupManifold,E<:Identity{GT}} = @doc doc""" compose(G::AbstractGroupManifold, x, y) -Compose elements $x,y ∈ G$ using the group operation $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) @@ -343,8 +343,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x ◦ y\\ -R_x &: y ↦ y ◦ x. +L_x &: y ↦ x \circ y\\ +R_x &: y ↦ y \circ x. \end{aligned} ``` """ @@ -369,8 +369,8 @@ For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified conventi left $L_x$ or right $R_x$, defined as ```math \begin{aligned} -L_x &: y ↦ x ◦ y\\ -R_x &: y ↦ y ◦ x. +L_x &: y ↦ x \circ y\\ +R_x &: y ↦ y \circ x. \end{aligned} ``` Result of the operation is saved in `z`. @@ -396,8 +396,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified either left $L_x^{-1}$ or right $R_x^{-1}$, defined as ```math \begin{aligned} -L_x^{-1} &: y ↦ x^{-1} ◦ y\\ -R_x^{-1} &: y ↦ y ◦ x^{-1}. +L_x^{-1} &: y ↦ x^{-1} \circ y\\ +R_x^{-1} &: y ↦ y \circ x^{-1}. \end{aligned} ``` """ @@ -422,8 +422,8 @@ For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified either left $L_x^{-1}$ or right $R_x^{-1}$, defined as ```math \begin{aligned} -L_x^{-1} &: y ↦ x^{-1} ◦ y\\ -R_x^{-1} &: y ↦ y ◦ x^{-1}. +L_x^{-1} &: y ↦ x^{-1} \circ y\\ +R_x^{-1} &: y ↦ y \circ x^{-1}. \end{aligned} ``` Result is saved in `z`. @@ -450,8 +450,8 @@ differential of the translation by $x$ on $v$, written as $(\mathrm{d}τ_x)_y (v specified left or right convention. The differential transports vectors: ```math \begin{aligned} -(\mathrm{d}L_x)_y (v) &: T_y G → T_{x ◦ y} G\\ -(\mathrm{d}R_x)_y (v) &: T_y G → T_{y ◦ x} G\\ +(\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\\ \end{aligned} ``` """ @@ -494,8 +494,8 @@ $((\mathrm{d}τ_x)_y)^{-1} (v) = (\mathrm{d}τ_{x^{-1}})_y (v)$, with the specif right convention. The differential transports vectors: ```math \begin{aligned} -((\mathrm{d}L_x)_y)^{-1} (v) &: T_y G → T_{x^{-1} ◦ y} G\\ -((\mathrm{d}R_x)_y)^{-1} (v) &: T_y G → T_{y ◦ x^{-1}} G\\ +((\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\\ \end{aligned} ``` """ diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 6b024923ee..83caf385c4 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -24,7 +24,7 @@ $G = N ⋊_θ H$, where $θ: H × N → N$ is an automorphism action of $H$ on $N$. The group $G$ has the composition rule ````math -g ◦ g' = (n, h) ◦ (n', h') = (n ◦ θ_h(n'), h ◦ h') +g \circ g' = (n, h) \circ (n', h') = (n \circ θ_h(n'), h \circ h') ```` and the inverse From eadf25ade1275a70fc66122104b049394aec6258 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 11:01:58 +0100 Subject: [PATCH 18/74] removes double-mentioned functions. --- docs/src/interface.md | 3 +++ docs/src/lib/internals.md | 2 -- docs/src/lib/public.md | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) 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 From c7e8bbf9481d4d8570b40ae065dca34734a8774c Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 12:20:24 +0100 Subject: [PATCH 19/74] Fixes the notations table. --- docs/src/notation.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index b54eab4d13..08f670aa7b 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -1,24 +1,22 @@ # 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 by terminology. Thy might be used in a plain form within the code or -when referring to that code. This is for example the case the caligraphic symbols. +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. -A - | Symbol | Description | Also used | Comment | |:--:|:-------------- |:--:|:--- | -| ``T^*_x ℳ`` | The cotangent space at ``x`` | | | -| ``ξ`` | A cotangent vector from ``T^*_x ℳ`` | ``ξ_1,ξ_2,…,η,𝛇`` | | -| ``F`` | A fiber | | -| ``⟨·,·⟩`` | inner product (in ``T^*_x ℳ``) | ``⟨·,·⟩_x, g_x(·,·)`` | -| ``ℳ`` | A manifold | ``ℳ_1, ℳ_2,…,𝒩`` | | -| ``𝒫_{y←x}X`` | parallel Transport | -| ``x`` | A point on ℳ | ``x_1,x_2,…,y,z`` | | -| ``T_x ℳ`` | The tangent space at ``x`` | | | -| ``X`` | A tangent vector from ``T_x ℳ`` | ``X_1,X_2,…,Y,Z`` | sometimes with the base point in the index, ``X_x``. | -| ``B`` | The Vector bundle | | +| $T^*_x \mathcal M$ | The cotangent space at $x$ | | | +| $\xi$ | A cotangent vector from $T^*_x \mathcal M$ | $\xi_1, \xi_2,\ldots,\eta,\zeta$ | | +| $F$ | A fiber | | +| $\langle\cdot,\cdot\rangle$ | inner product (in $T^*_x \mathcal M$) | $\langle\cdot,\cdot\rangle_x, g_x(\cdot,\cdot)$ | +| $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | +| $\mathcal P_{y\gets x}X$ | parallel Transport | +| $x$ | A point on $\mathcal M$ | $x_1,x_2,\ldots,y,z$ | | +| $T_x \mathcal M$ | The tangent space at $x$ | | | +| $X$ | A tangent vector from $T_x \mathcal M$ | $X_1,X_2,\ldots,Y,Z$ | sometimes with the base point in the index, $X_x$. | +| $B$ | The Vector bundle | | From e87d942a5cff678fd0969b9da027af547c1262cb Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 12:54:30 +0100 Subject: [PATCH 20/74] Fixes a copy-paste-error. --- docs/src/notation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 08f670aa7b..7fbfc46b09 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -13,7 +13,7 @@ as long as that renders still in $\TeX$ within this documentation. | $T^*_x \mathcal M$ | The cotangent space at $x$ | | | | $\xi$ | A cotangent vector from $T^*_x \mathcal M$ | $\xi_1, \xi_2,\ldots,\eta,\zeta$ | | | $F$ | A fiber | | -| $\langle\cdot,\cdot\rangle$ | inner product (in $T^*_x \mathcal M$) | $\langle\cdot,\cdot\rangle_x, g_x(\cdot,\cdot)$ | +| $\langle\cdot,\cdot\rangle$ | inner product (in $T_x \mathcal M$) | $\langle\cdot,\cdot\rangle_x, g_x(\cdot,\cdot)$ | | $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | | $\mathcal P_{y\gets x}X$ | parallel Transport | | $x$ | A point on $\mathcal M$ | $x_1,x_2,\ldots,y,z$ | | From d8d3c597aa0f4808be7aacec4d197b46b286c6e9 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 28 Jan 2020 12:57:44 +0100 Subject: [PATCH 21/74] removes a few files that I added accidentially. --- docs/src/assets/data.csv | 501 -------------------------------- docs/src/assets/distgen.jl | 6 - docs/src/assets/logo.tex | 53 ---- docs/src/assets/logo2-3.png | Bin 26183 -> 0 bytes docs/src/assets/logo2.tex | 70 ----- docs/src/assets/logo2start.png | Bin 30097 -> 0 bytes docs/src/assets/logo2start2.png | Bin 43569 -> 0 bytes docs/src/assets/logo_b.png | Bin 22647 -> 0 bytes docs/src/assets/logo_d.png | Bin 22839 -> 0 bytes docs/src/assets/scatter.tex | 9 - 10 files changed, 639 deletions(-) delete mode 100644 docs/src/assets/data.csv delete mode 100644 docs/src/assets/distgen.jl delete mode 100644 docs/src/assets/logo.tex delete mode 100644 docs/src/assets/logo2-3.png delete mode 100644 docs/src/assets/logo2.tex delete mode 100644 docs/src/assets/logo2start.png delete mode 100644 docs/src/assets/logo2start2.png delete mode 100644 docs/src/assets/logo_b.png delete mode 100644 docs/src/assets/logo_d.png delete mode 100644 docs/src/assets/scatter.tex diff --git a/docs/src/assets/data.csv b/docs/src/assets/data.csv deleted file mode 100644 index cb3f3c85a0..0000000000 --- a/docs/src/assets/data.csv +++ /dev/null @@ -1,501 +0,0 @@ -Column1,Column2 -0.20925327738754995,-0.8198152103661166 --1.2375159316247513,0.2780649524278085 -1.304704806666583,-0.48802888909159314 --1.014055767982855,1.6702031348499604 --0.8249339638660002,0.7869135342806515 --0.42443914696236124,1.2868384293974038 --0.5069378224589008,0.7517522095304663 -0.021607745981637224,-0.6957781678549813 --0.6752089361520257,-0.32690317331943547 -0.13352705143786522,-0.2699751190321647 -0.44003756846744985,-0.6490843041541213 --1.2633002887557327,0.733370809300122 --0.812343841131522,0.41107030441625664 -0.040527398378345056,0.6006505684416966 --0.3696441558151978,1.1997633196198099 -1.6190199681164392,1.1669211091231826 --0.7933753479813779,1.8439006366085011 -1.4818190108864036,0.6681715126786485 -0.39227771229115194,-0.7299409703238529 -2.1535806948171894,-0.819278387567473 -0.5402137976890716,1.4393175803452996 -0.1524847929018447,-0.12202799879353918 --0.5169311437487083,-0.656550597213316 --1.3296522881490533,1.1853610124760732 --0.5830844544294357,0.49635692659984215 -1.0549906797161193,1.535480046982853 -0.19961348871136966,0.5473394356376905 --2.0317268803227067,0.6204805495129485 --1.1846323324805526,2.0233109820616204 --0.8383067244402802,-0.002960080034038997 --0.19719961853083573,0.01162432089656421 --0.876732156125276,0.5134611034072128 -1.4444382840997283,-1.5480047307621652 --0.21765453432903134,-1.3600143580881323 -0.5097790315186881,2.3315092839007834 --1.2000240116398087,-0.5454096176740909 --0.5543908443359303,-1.361026975616243 --0.42145784009541387,1.1287385508544776 --0.4162438472130716,-0.6693415016331465 -0.5815859596211345,-0.6364102071983105 -1.6865178028342303,0.39682831446483735 --2.1485589150755855,0.3789264001099479 -1.4633839272953002,-0.1570896576049031 --0.6142425305276377,-1.6144157008660154 --0.6920142319146345,-1.2635962956082882 -0.5297117265245924,1.2885415502464046 --0.5783882075322441,0.21036324163085332 -0.8537007367718846,-1.3004166169553726 -0.2387354994460789,-1.141056672808649 -0.44721942133987835,-0.013700992812876588 -0.8486606652743779,1.4578922253239885 --0.06465700826069337,-1.2856198177338611 --0.0256329281019438,1.90848170940853 -0.1304860743216497,-0.21668290789297612 -0.5088784437842744,-2.471689670168774 -0.9189970198413899,-1.2666717783403252 -2.1517826072689767,-0.5721125239908472 -0.7565289243993455,0.771590662525266 --0.33792912549181486,-1.9835754280726403 --0.5228609666669066,1.103076476972974 -1.0485066785805952,-0.10353217108228314 -0.21493934084311667,-1.5483679040062617 -0.021795604688018003,0.8152892437251155 -1.6000944585083674,1.6001367742104688 -1.9225049856984067,-0.7503233372929577 -0.4856957960493018,-0.9539650279306797 -2.2324720780889575,-0.14826737071230928 -1.2907003483480892,0.07023454791668922 -1.5026877879990928,-0.37703334287349816 --0.004597043741592526,0.6268780132220775 -1.7070547713227697,-0.9853665740323286 --0.6591872853968587,0.7718903637092069 --0.27829968158936097,1.1875305610828857 -0.22413504624288524,-0.16322262511759422 --3.1367752651691463,-0.54948675106928 -1.4222672599740964,-0.6401898029871066 -0.29153094130253604,1.173863959748359 --0.07970845084719652,0.10374547723068062 -1.1752636222734072,0.043498218355799874 --1.2603757079650668,0.9188171812655966 --1.763262176293205,-0.7211190308095813 --0.8425550698438031,0.5932725294479552 --0.5804225135839073,-0.19106259241394635 -0.2609231790700467,2.2688403524084153 -0.7717347991884914,-0.15120662460284198 -0.03717803949457743,-0.22351744405399038 -0.41013559338739775,-0.22485805629103348 --0.12579185096357087,-1.5059598908747502 -1.49291142898796,0.7543479963935693 -0.7751774719693498,-0.05352258363806766 --0.10324996801731351,0.4470669456912923 -0.9034382387634113,-1.1390543572402494 --0.3908357830067875,0.19124155895988032 --0.21932536028106456,-1.537948292586809 --0.6457068469039726,-0.595992730963507 -1.0421104551995974,-0.8621436956775052 --0.16602083130725678,-0.1414973031950705 -0.23386253086626543,-1.9766242963665435 --1.6309324668384733,0.440535497717788 -0.08720419144571004,-0.15353124779651434 -0.6806103201689528,0.26099302801614593 -0.7396500767429188,0.2201436106363608 --1.4031839397423733,-0.29745169957078255 --0.6473699639285514,-1.2216927986547592 -1.537490234146802,1.006462705697397 -0.22088899686952984,0.05964396839218332 --0.8771392377373487,0.38136795446031413 --1.1069605346268045,0.3284131333384977 --1.61889858413774,-0.3665534698058741 --0.4810507242335761,0.8961772329815396 --0.9740471656769839,0.3662284856191772 --0.08605763364522674,0.3675251466821569 -0.7931476515990055,-2.0891713194419546 --0.6909177552749688,0.42814393382034643 --1.615228358304979,1.133443929237911 --0.6723715321222826,-2.783685752268319 -2.017517152027973,0.16540380353678152 -0.5790923158925938,1.3699532544986102 --2.2048235305084836,1.7237354387670187 --0.6058544375395607,0.2859501013504142 -0.7538458428689907,0.18043439723595692 -1.5987551875336228,-0.31900574056038905 --0.9615487099678645,-0.42310951199869723 --0.28540666589661795,-1.4453301777166825 --0.8233415746224023,0.19501174692274206 --0.38480757368389484,-1.534282189555801 -0.6535232271506434,0.03608725535016844 --1.0652947178602814,-1.5117635477575704 -0.29045261377270337,-0.6776286820916985 --0.4117802448609643,-0.9858432613174591 --0.9745995453928522,0.5340406418015569 -0.5190038790642223,-0.9052337808820757 -0.16195471501505157,0.1802787312006402 -0.4765814681658343,1.4156611379693895 --1.2120555164420173,0.8151011519509629 --1.4049866420488497,-1.6506316060858535 -1.7614565810823388,0.8064244699560507 -0.5783005929235394,-0.0355582099350019 -1.0207157683268422,0.5755632690975645 --2.188256883531687,0.23433179137069388 -1.1630547334296317,-1.143249674500201 --0.3931256515376709,-0.11137912575615719 -0.7965005173379419,0.4235478439240067 --0.6184801703633964,0.9352224359361652 -0.876618139190272,0.7166556917221233 --0.5561293738292874,-0.7718126672657308 --0.08918399792515726,0.3875676248737453 --1.2425099198509577,0.43965580056858516 --0.9148002112811672,-0.6585252572632969 -2.338330755443459,1.0984610783517668 --0.6688223533227194,-1.375522618739578 -2.193814549454824,-0.005511122117891882 --1.9980439807478227,-0.7431188268434156 --0.6249027944488548,-0.7946546898984421 --0.07280246634346817,1.6353094552743106 -0.8901161451643341,0.12434583767200093 --0.5840336822792623,0.4752680397301709 -0.5098922574562021,-0.6341822369799789 -2.920776034979655,1.1141729332606483 --1.1065324191867454,-2.075590096153505 --0.1486735971881187,0.23150245628627533 -1.1975755597395987,0.517826284162693 --0.7341948944016199,-0.4235540395300665 -0.10737541917825233,0.07112491597623669 -0.8683313082256237,-0.8207567861031164 -0.08471829454336718,0.15920423305531078 -0.25529513899306266,1.0667090935065797 -1.4195808972844797,0.11683437906931887 -1.2978679827821356,-1.1007432300412743 --1.36253729779608,0.2471096794379451 --0.74530941407898,0.7786742523748338 -0.17894322742608113,0.33084023772932386 --0.23858363624255882,-0.02878690589206565 --0.36400809328542516,0.23608952314532497 -0.2785842506453619,-0.6836247466038713 -0.04570786528874005,1.1674946396655035 -0.12300326113166653,-1.6509696247354544 -0.9902578346586426,-0.4304789643062097 -0.7396581278949144,-1.2147550662303785 --0.4854282152927687,-0.889589364634145 --1.1823552203208014,-1.217946204010757 --0.5823813131159803,0.5747103526987678 -0.2795240808414551,-2.030318635676795 -0.4095483033457007,0.35903719168595116 -1.1358137961687802,-1.839413715008768 -0.9136573000176265,0.4596709320611816 --0.7582209842350823,-0.2164098425700527 -1.4853905059499748,0.6024985095168356 --1.1348310931312249,1.7004073096812582 --0.039614290696515765,1.596867378809741 --1.01023726959206,-2.0125571075517614 -0.4341334181689187,-0.4704507055457917 --0.4395362349213977,-2.1828081189770785 -1.3386828148917396,0.9279191428214413 --1.2759874746613895,-0.9939846221695626 -0.9408242272225353,0.5783381896447548 --1.9370399932267697,-0.4358988150333681 -2.206962275372279,-2.235003644548357 -1.8734196852547227,0.6948886100052856 --0.5874618471965549,-1.7655860831953067 -0.05969511541624782,0.6843758327465203 --0.08971273020548921,-1.2807757417391585 --0.6760223602432806,-0.21164660376511943 -1.7175414320911377,-0.4787328780455168 -0.47279206116353184,0.3708241004727506 --1.6107202153392217,0.12878632324114822 -0.3407438473927114,0.24877489443667297 -1.9473102145674297,0.8577223738782214 -1.4000239354264268,-1.4332261232470174 --1.6009731148630897,0.4109530439774122 --0.03302730584748071,-0.5953924869417049 -0.8784260193173496,-0.17688563530099977 --1.3700547715745968,0.33879243592374125 --0.08425258650003066,-0.26225235198739655 --1.2223159270173345,0.4161779453839043 -0.1261284348399706,-0.46112667714961336 -1.6713054833929968,-0.050594593772474994 -1.2133052158890176,-0.8809453083423139 -0.7838013271880482,1.295347704039081 --0.2606004327647152,-0.9445325323220984 -0.42991364630851137,1.4877227836246028 --0.18391551957496874,0.5928519404759451 --0.5171531256363292,-0.31126965968768167 -0.8974439100178079,-0.24620190095548614 --0.7612619866268598,-1.3816739004575191 -2.120298578006751,0.3866425020677111 --1.002574174670511,1.0489768194057139 -1.24071439701067,0.34683277672072504 -0.9424466477319209,-1.1901592554979785 --0.21274600588203385,-0.5980088931297092 --0.6662146743219123,-0.12303565163319465 --0.10839318140516134,-0.341876930205938 -0.7240273701268818,-0.33903582536431 --1.4165715715950367,1.366101058065914 -1.889022098413358,2.2021992400359025 --1.809452348093021,0.756108396435671 --0.5963801759889581,1.2296048220423788 --1.2590615174547624,-1.622775827508899 --0.23207266116181025,1.04362146601353 -0.9662222128461432,-0.8353636669604038 -0.7709449279358161,-1.4061220536645491 -0.5435608533459653,0.6851641267029456 --0.124195088013306,-0.010956241144021432 -1.02362042736966,-0.09354620797538536 --0.8867283865262188,-0.03293397265795115 -1.1871341694617725,1.076583895509326 -1.5008175087834852,1.1919084379841514 --0.10171389509048433,-0.20725147533465663 --0.30617403788514586,0.593750053021037 -0.16593969549340595,0.07085183039057677 -1.6949783757618488,-0.38539437706155233 -1.1594682147714586,-1.8185767799090713 --0.12380277009651296,0.036404231295301105 -0.8753325042402745,1.7691717568515994 --0.6738461808200675,0.6250863296407262 --0.3920641987212776,-0.03893165117029216 --0.06038965220490704,1.977285403350687 -0.22327641647265678,-0.25183479845192946 -0.797314938454692,-0.1423496142092209 -1.018338195516711,-0.7952365481025468 --0.3882877901023438,1.6979599646756065 -1.6350394709628604,-1.8704975836151914 -0.7225521289199064,0.22986902922360838 --0.41095041464161325,-0.04289951839157107 -0.8216272789907563,-1.245910847706707 -1.1482269815450588,-1.409055968110682 -0.3679455692316837,-0.41641995912572244 -0.6775141081529522,-0.16603421904221488 --0.17953656708395252,-0.40731399845064603 -0.45934412509766176,0.5258948413155806 --1.6073338245545745,0.6174662801596772 -0.3795079260987492,-0.2265315311338671 -0.37053963630106046,-0.36306861273417745 --1.4064790458848673,0.4316367650582259 --0.9239223859546113,0.8514374556346487 --0.19813473286691904,1.0915577588356662 -0.47805411504436124,-1.9796529268008705 -0.9096356845761988,1.9504075166939363 -1.1121864071351912,0.8169446641997076 --0.76392035916547,-1.9996658573924553 -0.9116095691451693,1.374745664802311 --1.3073444452855485,-0.7973928806480879 --0.009518698942546155,-1.1346364242274858 --0.4162256085972741,0.9497397553266617 -0.49139190846461417,1.5998889664554186 --0.9709171930549572,0.04593985446294173 -1.005784325023403,-0.2649783606949649 -0.13263856992440676,0.7074688738981193 -0.9423669144267327,-1.6572325168991833 --0.04873899341087811,1.2629660504844864 --1.1529445903174524,1.845424423263187 --0.7571962042748505,-0.6099026413644854 -0.10076587651728887,-1.1575773748041835 --0.23246085296994107,1.0444055354400077 --1.3904752640829892,1.935067519210738 --0.8275051574603665,1.1165826177908906 --0.3532977368791006,0.9762276770047948 -0.04555514713055206,1.1010339442745045 --0.4798494095416221,-0.5846501693381377 -0.07600913731545283,0.6452937030142104 -0.4005441433340665,-0.8198942641155922 -0.3281976746952413,1.8795273434110265 --0.11638950438046095,-0.8508859026531531 -0.9464520007826062,-0.7963791676029883 --0.40924349996092974,0.18323106084622415 -0.0457878718431123,0.8490769298808012 --1.629864129257769,0.7132218805686016 -0.818570882630934,-0.36879077667051957 --0.4825470271238746,-0.39399469376924207 --0.4066698075512619,-0.7295142941575049 --2.4166127392162524,1.4361676897364186 --1.7128550397184765,0.9393046156428141 -0.2969233033198962,-0.008761620775262807 -0.09475997102957479,0.13291709320131737 --1.102822384805198,1.3749718618841524 --0.1991124924253596,1.1187991965646242 -1.3079620695641467,-2.794306320777006 --1.1289393623664903,-0.7528044133289846 --0.8352574037747552,2.8346581300743963 -0.048236775166861104,0.18380732752201018 --0.8047753262686002,1.1947441034029387 -0.2008406604651372,-0.626240601577856 --0.12442551827381988,2.291686808407615 -1.195862288989434,-0.7773083689695514 --1.415364003563011,2.6355257886035535 --0.6689472928937514,1.3028183693261257 --0.1972508510566795,0.38147110643902826 -0.2926560251284588,0.9058815725778023 --0.1433314671848272,0.3137048556384926 -0.8462921722716078,-2.4802590132298854 --1.009391574040696,-0.6151053193514879 -1.9465016279634086,-0.9273074198790414 -1.0680830606775091,-1.447904986110108 -0.49973926959112563,1.4341388578535592 --1.1650854448734136,-0.16388896295184432 -0.9803533861459229,-0.22621649742808633 -2.4752915271452482,-2.0040862331421416 --1.3708395925805268,-1.1600679095400648 -0.4751740582157677,-1.5370049438849613 --0.648577580900969,2.322571175843582 --0.7344456972327366,-0.27949247723809817 -0.033314832029100995,0.7647039926196152 -0.40502498786658897,-0.11499821285080047 -1.6907705466366305,0.19629802609880695 --0.16027970104034442,0.22417370619866644 -1.7082909119570338,0.44893137069158623 --1.5879657476595934,0.485201193984783 --0.11448381425488455,-0.45700981488683956 --0.9392115278739103,0.016844435943600834 -0.14491239321542976,0.4569708291605997 --0.23040928643973008,0.4303178072552902 --0.8414329611503186,0.25542999522229093 -0.2680790535836209,-0.21530551744328486 -1.017541776547052,0.7168812670145285 --0.3071759697090416,0.7081708067000178 --0.6189929768852306,0.43645547065889595 -0.6864653646281125,-0.13840049602023893 -2.0602113381869844,1.051240184879768 -1.1522489630803732,0.46350924617163364 -0.8038004091711378,-0.7442547370151313 --1.4266860683641622,-0.2485580756738553 -1.3256691351947092,1.1606693004961317 --0.1797608486391368,0.40472867593631834 --0.7477437878819732,0.4287667149165427 --0.19650439042216764,0.6123505795876122 --0.6823174808680208,0.3677541144306763 --0.031101730683239237,0.17008058482380087 -0.05297765052557897,-0.2863631195451015 -0.9974306444526836,-0.5699193095739777 --0.8698140279472525,-0.8861422020525384 --1.2710146130140438,-0.255990678882614 --0.2563433685873366,0.3755783314755773 -1.8403282805749002,-0.018371249845875112 --2.8198896365564425,0.18034287562734888 --0.859736917257879,0.7109218804284065 -0.4324327934194318,0.3332988218126938 --0.565361668477626,0.3807024266942734 --1.4566264572618028,0.18009141014137356 --0.8161806409460663,1.3639023182353618 --0.7505456374513456,0.22170777405420683 --0.4548076250769765,-1.3355975103825863 --1.0398647789512183,0.8354775900105511 --1.297248932390027,-0.2796818585519065 -0.1227521338249744,-1.6528638583640762 -0.18606429131956786,-2.2247215020370232 -0.5858403357681301,1.1174444250612563 -0.10550047249195406,0.6305943872716262 -1.3071625584409796,1.8402844194288526 -1.4621868422862605,0.8402399506474633 --0.5899928532788463,0.5005475788920318 -0.9625124467075618,0.1184281575820163 --0.5734686157994059,0.7731973345635538 --0.44906653634942467,1.9755941856499282 --1.3953589766233268,0.5618541928618719 -1.0424982870390762,1.4147989614168979 -1.3031824626870165,-0.9430373141532785 --0.9709115216011411,-0.14329715791873932 -0.062148522814003515,0.8341962507578219 -0.7611678151721707,0.8057535428671398 --0.20677165455713256,0.04486533457869614 --0.210723156864584,-2.1805858771400985 --0.5601075806531877,-0.3769406686719678 --0.2512162584979908,-0.24683093354460156 --1.57987739674686,-0.13470223413438884 -0.4171286085752105,-0.7803151860974653 -1.3048631431727824,0.9688008654053288 --0.4139762437275944,-0.07159372748909659 --0.634882765299227,1.2219886120058183 -0.07046283117438193,-0.04876784324289363 -1.2757154756692617,0.5368044387597584 --0.8238967220491237,-1.0578874656036141 --0.48366951411492837,-0.7418667054178377 --2.01160266964125,-0.650288329547343 --0.7076921023269187,0.9434564191184408 --1.1057099298972894,-2.3632389863560537 --1.0035773588447192,1.3700199925682557 -0.6286604273154789,1.2606200666701226 -0.22978491026261744,-0.42047268293476864 --0.6492957080129554,-0.5737740141524689 -0.9152651725281473,1.9097380942059026 -1.5232554379145447,0.22259961564447261 --0.6305071390407486,-0.4842627273972022 --0.22219863097282772,-0.30978693316016304 -0.6631192085592964,-0.30836537606606096 --0.5212294499115746,-0.33803739262974625 --0.27087398475260466,1.3251459964653174 --0.5808906780373599,2.0436943564183703 --0.9207066553566768,0.3687316684597964 -1.675914397632655,1.1163498138218093 --1.0093165770287913,0.8418770952919018 --0.4119574638679049,-1.235460930020419 --0.18084033690831097,-1.5440332737459173 --0.39923644412640913,-0.975875202331773 -0.25231547172273106,0.24072079963256612 --2.0223362234866076,1.3229645327060875 -0.3196272715721434,0.47375076614239847 --1.3381500857898445,1.1514522522148107 -1.5669892870111521,0.10109782302114441 --0.26746489609945995,-0.4669753315169544 --1.2588922446981776,-1.5976214025378035 --0.4467489056525206,0.9721194788859443 -1.2398566092811116,0.39432820968850374 --1.1008271687308644,0.46586224934630316 --0.7227675492592988,-1.3232507937264655 -0.6064576112044092,1.0267407300883928 --0.04861712889686784,-0.32160582330022885 -0.2691720449094336,-0.19946337695875416 -0.29316237957015406,-2.2346344194223042 --0.1562409981182633,0.43797550967157156 -1.1817110455858475,1.8407640470607778 -0.2789099782276007,1.236325860582907 -2.1603796623614735,-0.46208088491433913 -0.3109450644942771,0.5205699542749868 -1.914520004845812,-0.3877083173077306 --0.1465569397942808,0.32694080764217864 -0.6588456146096874,0.8821216342464769 -1.7282507338626458,-0.2639434252591822 --0.39556568077765675,-1.1680493160156555 --0.9778153487051146,-0.20946849823296984 --1.0934308931057681,0.46217756475978017 -1.3438682944930351,-0.9146928084387849 --1.1497052986688179,0.936746269026655 -1.7213286535834567,2.325065465291657 -0.21330308134009565,-0.22630390727838973 -0.11768583730063362,1.5190732274292302 --0.06540715841466907,-0.4502427285474765 --1.1472664332615403,-0.5164726077094528 -1.316678762728142,0.8119294901898304 -0.4947098408012637,0.43155231346298756 --0.4348398166489771,-0.8410658006235336 --1.1026120891403193,0.2723083117529033 --1.9610160561864127,-1.2442079734393166 -0.9171281259709059,-0.8513981080284988 --0.5899142593591982,0.671506543878724 -0.2992270414710336,0.30085934551708304 --0.5739027747316474,0.4167995176188234 -2.26517309994735,-0.31715196338940244 -0.7437906945346505,-2.96849358034209 --0.9400887257854789,1.291763400344791 --0.6606231937062936,-0.4559399017369433 -0.42632639369715564,-1.1180357962378338 --0.007982483862828647,-0.21230981543238472 --0.8518057782817932,1.1043544169216881 -0.19056907228043582,0.010036922059188467 --1.3611300611056723,-0.9220547532849753 -1.2373233557489731,1.0549589389676295 -0.14296121373497372,-2.022351104582391 -0.700769141392561,-0.3712632004174983 -0.4624210852181194,0.9323510311356591 --1.189220758237636,1.3813033208194605 -0.6441983042745664,0.37212041124184 -1.6690992245854068,0.4189862024580888 -1.4398561407721238,0.7080719097842861 --0.8188170383623364,-1.0464245659908882 --0.9222137733567871,-0.9695373180612131 --1.1863165728697105,-0.34181847975534 --0.5866666374154152,-0.19275613701952155 -0.2230059437272028,1.7121173547050097 --1.0582959617039025,1.7556231484778728 --2.22823860429648,0.958007101152074 diff --git a/docs/src/assets/distgen.jl b/docs/src/assets/distgen.jl deleted file mode 100644 index c1761ba4ae..0000000000 --- a/docs/src/assets/distgen.jl +++ /dev/null @@ -1,6 +0,0 @@ -using Distributions, CSV, Tables -a = MvNormal([0.; 0.],1.) -s = sampler(a) -data = cat([rand(s) for i=1:500]...;dims=2) -t = Tables.table(data') -CSV.write("data.csv",t) diff --git a/docs/src/assets/logo.tex b/docs/src/assets/logo.tex deleted file mode 100644 index 8c345e4124..0000000000 --- a/docs/src/assets/logo.tex +++ /dev/null @@ -1,53 +0,0 @@ -\documentclass[tikz]{standalone} -\usepackage{ifthen, fontspec, fontawesome5,pgfplots} -\setmainfont{Tamil MN} -\usetikzlibrary{arrows.meta} -\newboolean{darkmode} -\setboolean{darkmode}{true} -\ifthenelse{\boolean{darkmode}}{% - \definecolor{fgc}{rgb}{1.0, 1.0, 1.0} - \definecolor{bgc}{rgb}{0.0,0.0,0.0} -}{% - \definecolor{fgc}{rgb}{0.0, 0.0, 0.0} - \definecolor{bgc}{rgb}{0.9, 0.9, 0.9} -} -\definecolor{jblue}{rgb}{0.251, 0.388, 0.847} -\definecolor{jred}{rgb}{0.796, 0.235, 0.2} -\definecolor{jgreen}{rgb}{0.22, 0.596, 0.149} -\definecolor{jpurple}{rgb}{0.584, 0.345, 0.698} - -\tikzset{blob/.style={circle, minimum size=1cm,line width=0pt}} -\tikzset{blobred/.style={fill=jred, draw=jred, blob}} -\tikzset{blobpurple/.style={fill=jgreen, draw=jgreen, blob}} -\tikzset{blobgreen/.style={fill=jpurple, draw=jpurple, blob}} - -\tikzset{geodesic/.style={very thick,jblue,cap=round}} -\tikzset{geodesic2/.style={very thick,jblue,cap=round,opacity=0.2}} -\tikzset{tangent/.style={very thick,fgc,->,>={Stealth[round]} }} - -\pagecolor{bgc} - -\begin{document} - \begin{tikzpicture}[scale=2] - \draw[geodesic] (-0.5,0.0) node (a) {} (0.5,0.0) node (b) {} (0.0, {sqrt(3)/4}) node (c) {}; - \draw[geodesic] (c.center) .. controls (-0.2,{sqrt(3)/4}) and (-0.5,0.2) .. (a.center); - \draw[geodesic] (c.center) .. controls (0.2,{sqrt(3)/4}) and (0.5,0.2) .. (b.center); - \draw[geodesic] (a.center) to [bend right=35] (b.center); - \draw (a) node[blobred] {}; - \draw (b) node[blobgreen] {}; - \draw (c) node[blobpurple] {}; - - \draw[geodesic2] (c.center) .. controls (-0.2,{sqrt(3)/4}) and (-0.5,0.2) .. (a.center); - \draw[geodesic2] (c.center) .. controls (0.2,{sqrt(3)/4}) and (0.5,0.2) .. (b.center); - \draw[geodesic2] (a.center) to [bend right=35] (b.center); - - \draw[tangent] (c.center) -- ++ (0.5,0); - \draw (a) node {\large\color{fgc}\faIcon{redo}}; - \draw (b) node {\begin{tikzpicture}[scale=0.15] - \begin{axis}[axis lines=none] - \addplot[fgc,draw opacity=0.8,only marks,mark=*,mark options={fill=fgc, fill opacity=0.8, scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; - \end{axis} -\end{tikzpicture}}; - \draw (0.0,-0.45) node (T) {\color{fgc}\large\bfseries Manifolds.jl}; - \end{tikzpicture} -\end{document} diff --git a/docs/src/assets/logo2-3.png b/docs/src/assets/logo2-3.png deleted file mode 100644 index 358468e2908c48adf3bf546a27822dcf394be9bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26183 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 P00000NkvXXu0mjfFg{w$ diff --git a/docs/src/assets/logo2.tex b/docs/src/assets/logo2.tex deleted file mode 100644 index a0ef7d29a3..0000000000 --- a/docs/src/assets/logo2.tex +++ /dev/null @@ -1,70 +0,0 @@ -\documentclass[tikz]{standalone} -\usepackage{ifthen, fontspec, fontawesome5,pgfplots,amssymb} -\setmainfont{Tamil MN} -\usetikzlibrary{arrows.meta, positioning} -\newboolean{darkmode} -\setboolean{darkmode}{false} -\newboolean{lettering} -\setboolean{lettering}{false} -\ifthenelse{\boolean{darkmode}}{% - \definecolor{fgc}{rgb}{1.0, 1.0, 1.0} - \definecolor{bgc}{rgb}{0.0,0.0,0.0} -}{% - \definecolor{fgc}{rgb}{0.0, 0.0, 0.0} - \definecolor{bgc}{rgb}{0.9, 0.9, 0.9} -} -\definecolor{jblue}{rgb}{0.251, 0.388, 0.847} -\definecolor{jred}{rgb}{0.796, 0.235, 0.2} -\definecolor{jgreen}{rgb}{0.22, 0.596, 0.149} -\definecolor{jpurple}{rgb}{0.584, 0.345, 0.698} - -\tikzset{blob/.style={circle, minimum size=1.2cm,line width=0pt}} -\tikzset{blobred/.style={fill=jred, draw=jred, blob}} -\tikzset{blobpurple/.style={fill=jgreen, draw=jgreen, blob}} -\tikzset{blobgreen/.style={fill=jpurple, draw=jpurple, blob}} - -\tikzset{geodesic/.style={very thick,jblue,cap=round}} -\tikzset{geodesic2/.style={very thick,jblue,cap=round,opacity=0.2}} -\tikzset{tangent/.style={very thick,fgc,->,>={Stealth[round]},line cap=round}} -\tikzset{tangentsum/.style={very thick,fgc!66!bgc,->,>={Stealth[round]} }} -\tikzset{tangentvec/.style={very thick,fgc!33!bgc, dash pattern=on 0pt off 2\pgflinewidth, line cap=round}} - -\tikzset{rotation/.style={very thick,fgc,->,>={Stealth[round,scale=0.75]},line cap=round}} - -\pagecolor{bgc} -\newcommand{\lp}{0.4} -\begin{document} - \begin{tikzpicture}[scale=2] - % centroid of triangle at zero - \draw (-0.5,{-2*sqrt(3)/6}) node (a) {} (0.5,{-2*sqrt(3)/6}) node (b) {} (0.0, {sqrt(3)/6}) node (c) {}; - \draw[geodesic] (c.center) .. controls +(210:{\lp}) and +(90:{\lp}) .. (a.center) node[pos=0.22] (ca) {}; - \draw[geodesic] (c.center) .. controls +(-30:{\lp}) and +(90:{\lp}) .. (b.center) node[pos=0.22] (cb) {}; - \draw[geodesic] (a.center) .. controls +(-30:{\lp}) and +(210:{\lp}) .. (b.center); - \draw (a) node[blobred] {}; - \draw (b) node[blobgreen] {}; - \draw (c) node[blobpurple] {}; - \draw[geodesic2] (c.center) .. controls +(210:{\lp}) and +(90:{\lp}) .. (a.center); - \draw[geodesic2] (c.center) .. controls +(-30:{\lp}) and +(90:{\lp}) .. (b.center); - \draw[geodesic2] (a.center) .. controls +(-30:{\lp}) and +(210:{\lp}) .. (b.center); - %tangent - \draw (a.center) ++ (75:{0.4}) node (t1) {}; - \draw (a.center) ++ (345:{0.4}) node (t2) {}; - \draw (a.center) ++ (30:{0.4*sqrt(2)}) node (t12) {}; - \draw[tangentvec] (t1.center) -- (t12.center) -- (t2.center); - \draw[tangentsum] (a.center) -- ++ (30:{0.4*sqrt(2)}) node (t12) {}; - \draw[tangent] (a.center) -- ++ (75:{0.4}) node (t1) {}; - \draw[tangent] (a.center) -- ++ (345:{0.4}) node (t2) {}; - \draw[rotation] (ca.center) .. controls +(315:0.11) and +(225:0.11) .. (cb.center); - \draw (c) node {\large\color{fgc}$\mathfrak g$}; - \draw (b) node {% - \begin{tikzpicture}[scale=0.15] - \begin{axis}[axis lines=none] - \addplot[fgc,draw opacity=0.8,only marks,mark=*,mark options={fill=fgc, fill opacity=0.8, scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; - \end{axis} - \end{tikzpicture} - }; - \ifthenelse{\boolean{lettering}}{% - \draw (0.0,{-2*sqrt(3)/3}) node (T) {\color{fgc}\Large\bfseries Manifolds.jl}; - }{} - \end{tikzpicture} -\end{document} diff --git a/docs/src/assets/logo2start.png b/docs/src/assets/logo2start.png deleted file mode 100644 index da28904899e6b513021dbae172f571ca78cf3a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30097 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 u 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 VFHqcqySskjKHt4RHhZ$q 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 diff --git a/docs/src/assets/logo_b.png b/docs/src/assets/logo_b.png deleted file mode 100644 index 9eaa38ea3c2756a0d10c7195f9e1f771d329b77f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22647 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

{BvFESxYI+gf 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%0DRf7ki($r3Q)1un`)=7`t|pD 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 IM6N<$f|m-6sQ>@~ diff --git a/docs/src/assets/scatter.tex b/docs/src/assets/scatter.tex deleted file mode 100644 index c6079df308..0000000000 --- a/docs/src/assets/scatter.tex +++ /dev/null @@ -1,9 +0,0 @@ -\documentclass[tikz]{standalone} -\usepackage{ifthen, fontspec, fontawesome5,pgfplots} -\begin{document} -\begin{tikzpicture} - \begin{axis}[axis lines=none] - \addplot+[only marks,mark=*,mark options={fill=blue,scale=1,draw=none}] table[x=Column1, y=Column2,col sep=comma] {data.csv}; - \end{axis} -\end{tikzpicture} -\end{document} From 177363eefaaeba8f11f6d80e7ff819be932b2eb7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 29 Jan 2020 11:57:28 +0100 Subject: [PATCH 22/74] =?UTF-8?q?move=20from=20x=20to=20p=20(to=20avoid=20?= =?UTF-8?q?confusion=20with=20x=20and=20X),=20revert=20=C2=B7=20to=20\cdot?= =?UTF-8?q?=20since=20spaing=20is=20off;=20refactor=20CholeskySpace.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/notation.md | 2 +- src/manifolds/CholeskySpace.jl | 143 +++++++++--------- src/manifolds/Circle.jl | 4 +- src/manifolds/Euclidean.jl | 4 +- src/manifolds/Grassmann.jl | 22 +-- src/manifolds/Hyperbolic.jl | 8 +- src/manifolds/PowerManifold.jl | 2 +- src/manifolds/Stiefel.jl | 6 +- src/manifolds/Symmetric.jl | 8 +- .../SymmetricPositiveDefiniteLinearAffine.jl | 2 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 10 +- .../SymmetricPositiveDefiniteLogEuclidean.jl | 2 +- 12 files changed, 106 insertions(+), 107 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 7fbfc46b09..66b323e67c 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -16,7 +16,7 @@ as long as that renders still in $\TeX$ within this documentation. | $\langle\cdot,\cdot\rangle$ | inner product (in $T_x \mathcal M$) | $\langle\cdot,\cdot\rangle_x, g_x(\cdot,\cdot)$ | | $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | | $\mathcal P_{y\gets x}X$ | parallel Transport | -| $x$ | A point on $\mathcal M$ | $x_1,x_2,\ldots,y,z$ | | +| $p$ | A point on $\mathcal M$ | $p_1,p_2,\ldots,q$ | | | $T_x \mathcal M$ | The tangent space at $x$ | | | | $X$ | A tangent vector from $T_x \mathcal M$ | $X_1,X_2,\ldots,Y,Z$ | sometimes with the base point in the index, $X_x$. | | $B$ | The Vector bundle | | diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index dd459db13e..628cb0e58e 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -20,148 +20,147 @@ struct CholeskySpace{N} <: Manifold end CholeskySpace(n::Int) = CholeskySpace{n}() @doc doc""" - check_manifold_point(M::CholeskySpace, x; kwargs...) + 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` +Checks whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. +atfer [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of 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) + 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_{ℳ}(x,y) = \sqrt{\sum_{i>j} (x_{ij}-y_{ij})^2 + -\sum_{j=1}^m (\log x_{jj} - \log y_{jj})^2 +d_{ℳ}(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) + 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` +triangular matrix with positive diagonal `p` towards the lower triangular matrix `X` The formula reads ````math -\exp_x v = ⌊ x ⌋ + ⌊ v ⌋ + \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 $⌊ x⌋$ 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) + 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} ```` """ -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) + 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`. +from the lower triangular matrix with positive diagonal `p` towards `q`. The formula reads ````math -\log_x v = ⌊ x ⌋ - ⌊ y ⌋ -+\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 $⌊ x⌋$ 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 @@ -180,38 +179,38 @@ Return the representation size for the [`CholeskySpace`](@ref)`{N}` `M`, i.e. `( @generated representation_size(::CholeskySpace{N}) where {N} = (N, 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) + 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 -𝒫_{y←x}(v) = ⌊ v ⌋ -+ \operatorname{diag}(y)\operatorname{diag}(x)^{-1}\operatorname{diag}(v), +𝒫_{q←p}(X) = ⌊ X ⌋ ++ \operatorname{diag}(q)\operatorname{diag}(p)^{-1}\operatorname{diag}(X), ```` -where $⌊·⌋$ 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) + 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 0a0b5c04ff..0cb7ee4080 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -100,7 +100,7 @@ Compute the exponential map on the [`Circle`](@ref). ````math \exp_xv = (x+v)_{2\pi}, ```` -where $(·)$ is the (symmetric) remainder with respect to division by $2\pi$, +where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. For the complex-valued case the formula is the same as for the [`Sphere`](@ref) @@ -203,7 +203,7 @@ Compute the logarithmic map on the [`Circle`](@ref) `M`. ````math \exp_xv = (y,x)_{2\pi}, ```` -where $(·)$ is the (symmetric) remainder with respect to division by $2\pi$, +where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. For the complex-valued case the formula is the same as for the [`Sphere`](@ref) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 3910861e87..c11833aac9 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -11,7 +11,7 @@ Generate the $n$-dimensional vector space $ℝ^n$. Euclidean(n₁,n₂,...,nᵢ; field=ℝ) -Generate the vector space of $k=n_1n_2·… n_i$ values, i.e. the +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 \times,n_2 \times … \times n_i$ arrays. For $d=2$ we obtain a matrix space. @@ -140,7 +140,7 @@ 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, ```` -where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ inner(::Euclidean, ::Any...) @inline inner(::Euclidean, x, v, w) = dot(v, w) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 84188abd82..6abed5d1af 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -14,7 +14,7 @@ The manifold can be represented as : x ∈ 𝔽^{n \times k}, \bar{x}^\mathrm{T}x = I_k\}, ```` -where ${\bar·}^{\mathrm{T}}$ denotes the complex conjugate transpose and +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 $\operatorname{Gr}(n,k)$, and hence the subspace can actually be represented by @@ -101,7 +101,7 @@ Check whether `v` is a tangent vector in the tangent space of `x` on the [`Grass x^{\mathrm{H}}v + v^{\mathrm{H}}x = 0_k, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and $0_k$ denotes the $k \times k$ zero natrix. """ function check_tangent_vector(G::Grassmann{n,k,F}, x, v; kwargs...) where {n,k,F} @@ -139,7 +139,7 @@ end 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 $·^{\mathrm{H}}$ denotes the complex +$x^\mathrm{H}y$, 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)). @@ -166,7 +166,7 @@ Then the exponential map is written using z = x V\cos(S)V^\mathrm{H} + U\sin(S)V^\mathrm{H}, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +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 @@ -197,7 +197,7 @@ The formula reads g_x(v,w) = \operatorname{trace}(v^{\mathrm{H}}w), ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inner(::Grassmann, x, v, w) = dot(v, w) @@ -211,7 +211,7 @@ Compute the inverse retraction for the [`PolarRetraction`](@ref), on the \operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inverse_retract(M::Grassmann, ::Any, ::Any, ::PolarInverseRetraction) @@ -227,7 +227,7 @@ Compute the inverse retraction valid of the [`QRRetraction`](@ref) ````math \operatorname{retr}_x^{-1}y = y*(x^\mathrm{H}y)^{-1} - x, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ inverse_retract(::Grassmann, ::Any, ::Any, ::QRInverseRetraction) @@ -246,10 +246,10 @@ i.e. the tangent vector `v` whose corresponding [`geodesic`](@ref) starting from reaches `y` after time 1 on `M`. The formula reads ````math -\log_xy = V· \operatorname{atan}(S) · U^\mathrm{H}, +\log_xy = V\cdot \operatorname{atan}(S) \cdot U^\mathrm{H}, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +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 ````math @@ -314,7 +314,7 @@ which is computed by \operatorname{proj_x}(w) = w - xx^{\mathrm{H}}w, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ project_tangent(::Grassmann, ::Any...) @@ -337,7 +337,7 @@ Compute the SVD-based retraction [`PolarRetraction`](@ref) on the \operatorname{retr}_x v = UV^\mathrm{H}, ```` -where $·^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. retract(M::Grassmann, x, v, ::QRRetraction ) diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index a34549d7f8..6196c5e776 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -10,7 +10,7 @@ vectors in $ℝ^{n+1}$ using the Minkowsi metric, i.e. + \displaystyle\sum_{k=1}^n x_k^2 = -1, x_{n+1} > 0\Bigr\}, ``` -where $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) +where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) is Minkowski inner product. The tangent space $T_x ℍ^n$ is given by ````math @@ -106,7 +106,7 @@ Compute the distance on the [`Hyperbolic`](@ref) `M`, which reads d_{ℍ^n}(x,y) = \operatorname{acosh}( - ⟨x, y⟩_{\mathrm{M}}), ```` -where $⟨·,·⟩_{\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)) @@ -121,7 +121,7 @@ from `x` towards `v`, which is optionally scaled by `t`. The formula reads + \sinh(\sqrt{⟨v,v⟩_{\mathrm{M}}})\frac{v}{\sqrt{⟨v,v⟩_{\mathrm{M}}}}, ```` -where $⟨·,·⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). +where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ exp(::Hyperbolic, ::Any...) @@ -225,7 +225,7 @@ The formula reads ````math w = v + ⟨x,v⟩_{\mathrm{M}} x, ```` -where $⟨·, ·⟩_{\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...) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 5f751b4b5a..97858b6427 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -501,7 +501,7 @@ size of the power manifold and $d_{ℳ}$ the dimension of the base manifold $ℳ$, the manifold is of dimension ````math -d_{𝒩} = d_{ℳ}\prod_{i=1}^d n_i = n_1n_2·…· n_d d_{ℳ}. +d_{𝒩} = d_{ℳ}\prod_{i=1}^d n_i = n_1n_2\cdot…\cdot n_d d_{ℳ}. ```` """ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 20c312d2d5..fb9b5e98ae 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -8,7 +8,7 @@ The Stiefel manifold consists of all $n \times k$, $n\geq k$ orthonormal matrice ```` where $𝔽 ∈ \{ℝ, ℂ\}$, -$·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and +$\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_n ∈ ℝ^{n\times n}$ denotes the $k \times k$ identity matrix. The tangent space at a point $x∈ ℳ$ is given by @@ -41,7 +41,7 @@ Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() 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 $·^{\mathrm{H}}$ is the complex conjugate +is (approximatly) 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} @@ -126,7 +126,7 @@ $\operatorname{exp}_{x} v = \begin{pmatrix} \begin{pmatrix} \exp( -x^{\mathrm{H}}v) \\ 0_n\end{pmatrix}$ where $\exp$ denotes matrix exponential, -$·^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_k$ and +$\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. """ exp(::Stiefel, ::Any...) diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 6c9b0cf20e..68ebf0ea6b 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -7,7 +7,7 @@ symmetric matrices of size $ n\times n$, i.e. the set ````math \operatorname{Sym}(n) = \bigl\{A ∈ 𝔽^{n\times n} \big| A^{\mathrm{H}} = A \bigr\}, ```` -where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed +where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed and the field $𝔽 ∈ \{ ℝ, ℂ\}$ is set by the [`AbstractNumbers`](@ref) `F`. @@ -220,7 +220,7 @@ metric from the embedding, i.e. ````math (v,w)_x = \operatorname{tr}(v^{\mathrm{H}}w), ```` -where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ @inline inner(M::SymmetricMatrices, x, w, v) = dot(w, v) @@ -274,7 +274,7 @@ Projects `x` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. \operatorname{proj}_{\operatorname{Sym}(n)}(x) = \frac{1}{2} \bigl( x + x^{\mathrm{H}} \bigr), ```` -where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ project_point(::SymmetricMatrices, ::Any...) @@ -289,7 +289,7 @@ Project the matrix `v` onto the tangent space at `x` on the [`SymmetricMatrices` \operatorname{proj}_x(v) = \frac{1}{2} \bigl( v + v^{\mathrm{H}} \bigr), ```` -where $·^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. +where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed. """ project_tangent(::SymmetricMatrices, ::Any...) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index 88df48e246..fc4deb728e 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -20,7 +20,7 @@ d_{𝒫(n)}(x,y) = \lVert \operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})\rVert_{\mathrm{F}}., ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert·\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance(M::SymmetricPositiveDefinite{N}, x, y) where {N} s = real.(eigvals(x, y)) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index c5b34220bf..691881e719 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -36,8 +36,8 @@ d_{𝒫(n)}(x,y) = \sqrt{ ```` where $l$ and $k$ are the cholesky factors of $x$ and $y$, respectively, -$⌊·⌋$ denbotes the strictly lower triangular matrix of its argument, -and $\lVert·\rVert_{\mathrm{F}}$ denotes the Frobenius norm. +$⌊\cdot⌋$ denbotes the strictly lower triangular matrix of its argument, +and $\lVert\cdot\rVert_{\mathrm{F}}$ denotes the Frobenius norm. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogCholeskyMetric}, @@ -59,7 +59,7 @@ Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with 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}$, -and $(·)_\frac{1}{2}$ +and $(\cdot)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$. """ exp(::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) @@ -88,7 +88,7 @@ a [`MetricManifold`](@ref) with [`LogCholeskyMetric`](@ref). The formula reads 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 $(·)_\frac{1}{2}$ +$p_l(w) = l (l^{-1}wl^{-\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( @@ -141,7 +141,7 @@ Parallely transport the tangent vector `v` at `x` along the geodesic to `y` with 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 $(·)_\frac{1}{2}$ denotes the lower +$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 diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index d3ac15a902..cc51bd38b0 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -18,7 +18,7 @@ The formula reads ``` where $\operatorname{Log}$ denotes the matrix logarithm and -$\lVert·\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. +$\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ function distance( M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, From b8d62cc9e8cd77c3a296fa9660b178509cb71706 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 29 Jan 2020 16:27:46 +0100 Subject: [PATCH 23/74] Reworks/unifies the circle. --- docs/make.jl | 2 +- docs/src/notation.md | 14 +- src/manifolds/Circle.jl | 275 ++++++++++++++++++++-------------------- 3 files changed, 143 insertions(+), 148 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 5d0c19209a..facc261d28 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,7 +4,7 @@ makedocs( # for development, we disable prettyurls format = Documenter.HTML(prettyurls = false), modules = [Manifolds, ManifoldsBase], - sitename = "Manifolds", + sitename = "Manifolds.jl", pages = [ "Home" => "index.md", "ManifoldsBase.jl" => "interface.md", diff --git a/docs/src/notation.md b/docs/src/notation.md index 66b323e67c..ec2085fa8d 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -10,13 +10,13 @@ as long as that renders still in $\TeX$ within this documentation. | Symbol | Description | Also used | Comment | |:--:|:-------------- |:--:|:--- | -| $T^*_x \mathcal M$ | The cotangent space at $x$ | | | -| $\xi$ | A cotangent vector from $T^*_x \mathcal M$ | $\xi_1, \xi_2,\ldots,\eta,\zeta$ | | +| $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$ | | | $F$ | A fiber | | -| $\langle\cdot,\cdot\rangle$ | inner product (in $T_x \mathcal M$) | $\langle\cdot,\cdot\rangle_x, g_x(\cdot,\cdot)$ | +| $\langle\cdot,\cdot\rangle$ | inner product (in $T_p \mathcal M$) | $\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)$ | | $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | -| $\mathcal P_{y\gets x}X$ | parallel Transport | -| $p$ | A point on $\mathcal M$ | $p_1,p_2,\ldots,q$ | | -| $T_x \mathcal M$ | The tangent space at $x$ | | | -| $X$ | A tangent vector from $T_x \mathcal M$ | $X_1,X_2,\ldots,Y,Z$ | sometimes with the base point in the index, $X_x$. | +| $\mathcal P_{q\gets p}X$ | parallel Transport | +| $p$ | A point on $\mathcal M$ | $p_1, p_2, \ldots,q$ | for 3 points one might use $x,y,z$ | +| $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$. | | $B$ | The Vector bundle | | diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 0cb7ee4080..b6b2a7eb3d 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -17,56 +17,55 @@ struct Circle{F} <: Manifold where {F<:AbstractNumbers} end Circle(f::AbstractNumbers = ℝ) = Circle{f}() @doc doc""" - check_manifold_point(M::Circle, x) + 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 ∈ [-\pi,\pi)$. -for the complex-valued case its a unit number, $x ∈ ℂ$ 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 ∈ [-\pi,\pi)$. +for the complex-valued case its 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 @@ -81,113 +80,112 @@ complex_dot(a, b) = dot(map(real, a), map(real, b)) + dot(map(imag, a), map(imag complex_dot(a::Number, b::Number) = (real(a) * real(b) + imag(a) * imag(b)) @doc doc""" - distance(M::Circle, x, y) + 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) + exp(M::Circle, p, X) Compute the exponential map on the [`Circle`](@ref). ````math -\exp_xv = (x+v)_{2\pi}, +\exp_pX = (p+X)_{2\pi}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, -i.e. in $[-\pi,\pi)$. +where $(\cdot)_{2\pi}$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. -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 formula is the same 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) +function get_basis(M::Circle{ℝ}, p, B::DiagonalizingOrthonormalBasis) sbv = sign(B.v[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) +get_coordinates(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X +function get_coordinates(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) sbv = sign(B.v[1]) - return v .* (sbv == 0 ? 1 : sbv) + 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) +get_vector(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X +function get_vector(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) sbv = sign(B.v[1]) - return v .* (sbv == 0 ? 1 : sbv) + 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]) + injectivity_radius(M::Circle[, p]) Return the injectivity radius on the [`Circle`](@ref) `M`, i.e. $\pi$. """ injectivity_radius(::Circle, args...) = π @doc doc""" - inner(M::Circle, x, w, v) + 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()) @@ -197,47 +195,46 @@ function inverse_retract(M::Circle, x::Number, y::Number, ::LogarithmicInverseRe end @doc doc""" - log(M::Circle, x, y) + 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\pi}, ```` -where $(\cdot)$ is the (symmetric) remainder with respect to division by $2\pi$, -i.e. in $[-\pi,\pi)$. +where $(\cdot)_{2\pi}$ is the (symmetric) remainder with respect to division by $2\pi$, i.e. in $[-\pi,\pi)$. -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 formula is the same 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""" @@ -251,54 +248,53 @@ manifold_dimension(::Circle) = 1 @doc doc""" mean(M::Circle, x::AbstractVector[, w::AbstractWeights]) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` on the -[`Circle`](@ref) $𝕊^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) + project_point(M::Circle, p) -Project a point `x` onto the [`Circle`](@ref) `M`. +Project a point `p` 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 +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) + 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{ℝ}, 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, ξ) @doc doc""" sym_rem(x,[T=π]) @@ -312,61 +308,60 @@ end sym_rem(x, T=π) where N = map(sym_rem, x, Ref(T)) @doc doc""" - vector_transport_to(M::Circle, x, v, y, ::ParallelTransport) + 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 -𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℂ}(x,y)} -\bigl(\log_xy + \log_yx \bigr), +𝒫_{q←p}(v) = 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) From 06d4058a6fb72a428b512698120cfcff9010c450 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 29 Jan 2020 22:10:09 +0100 Subject: [PATCH 24/74] Finishes the Torus by completeing a docstring, unifies docstringformat to use `@doc raw"""` (see Julia Documentation) for docstrings that contain math ($,\ and others.) --- CONTRIBUTING.md | 2 +- docs/make.jl | 1 + src/Manifolds.jl | 4 +- src/groups/circle_group.jl | 2 +- src/groups/group.jl | 26 +++++------ src/groups/group_action.jl | 10 ++--- src/groups/group_operation_action.jl | 2 +- src/groups/rotation_action.jl | 2 +- src/groups/semidirect_product_group.jl | 2 +- src/groups/special_euclidean.jl | 2 +- src/groups/special_orthogonal.jl | 2 +- src/groups/translation_action.jl | 2 +- src/groups/translation_group.jl | 2 +- src/manifolds/CholeskySpace.jl | 20 ++++----- src/manifolds/Circle.jl | 28 ++++++------ src/manifolds/Euclidean.jl | 14 +++--- src/manifolds/FixedRankMatrices.jl | 22 ++++----- src/manifolds/GraphManifold.jl | 18 ++++---- src/manifolds/Grassmann.jl | 28 ++++++------ src/manifolds/Hyperbolic.jl | 26 +++++------ src/manifolds/MetricManifold.jl | 36 +++++++-------- src/manifolds/PowerManifold.jl | 28 ++++++------ src/manifolds/ProductManifold.jl | 26 +++++------ src/manifolds/Rotations.jl | 36 +++++++-------- src/manifolds/Sphere.jl | 32 ++++++------- src/manifolds/Stiefel.jl | 22 ++++----- src/manifolds/Symmetric.jl | 30 ++++++------- src/manifolds/SymmetricPositiveDefinite.jl | 12 ++--- .../SymmetricPositiveDefiniteLinearAffine.jl | 14 +++--- .../SymmetricPositiveDefiniteLogCholesky.jl | 12 ++--- .../SymmetricPositiveDefiniteLogEuclidean.jl | 4 +- src/manifolds/Torus.jl | 45 +++++++++++-------- src/manifolds/VectorBundle.jl | 18 ++++---- src/numbers.jl | 2 +- src/orthonormal_bases.jl | 4 +- src/product_representations.jl | 6 +-- src/statistics.jl | 20 ++++----- src/utils.jl | 4 +- 38 files changed, 287 insertions(+), 279 deletions(-) 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 facc261d28..ba37b67b3d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -4,6 +4,7 @@ makedocs( # for development, we disable prettyurls format = Documenter.HTML(prettyurls = false), modules = [Manifolds, ManifoldsBase], + authors = "Seth Axen, Mateusz Baran, Ronny Bergmann, and contributors.", sitename = "Manifolds.jl", pages = [ "Home" => "index.md", diff --git a/src/Manifolds.jl b/src/Manifolds.jl index f567a22490..883e79326a 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -108,7 +108,7 @@ Abstract type for defining statistical estimation methods. """ abstract type AbstractEstimationMethod end -@doc doc""" +@doc raw""" hat(M::Manifold, x, vⁱ) Given a basis $e_i$ on the tangent space at a point $x$ and tangent @@ -133,7 +133,7 @@ function hat!(M::Manifold, v, x, vⁱ) error("hat! operator not defined for manifold $(typeof(M)), array $(typeof(v)), point $(typeof(x)), and vector $(typeof(vⁱ))") end -@doc doc""" +@doc raw""" vee(M::Manifold, x, v) Given a basis $e_i$ on the tangent space at a point $x$ and tangent diff --git a/src/groups/circle_group.jl b/src/groups/circle_group.jl index 3d4dd7ead9..46b878f51c 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 diff --git a/src/groups/group.jl b/src/groups/group.jl index 6d1840bbee..0e81b98b4c 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" AbstractGroupOperation Abstract type for smooth binary operations $∘$ on elements of a Lie group $G$: @@ -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 @@ -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$. @@ -233,7 +233,7 @@ end # Group-specific functions ########################## -@doc doc""" +@doc raw""" inv(G::AbstractGroupManifold, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that @@ -249,7 +249,7 @@ function inv(G::AbstractGroupManifold, x) return inv!(G, y, x) end -@doc doc""" +@doc raw""" inv!(G::AbstractGroupManifold, y, x) Inverse $x^{-1} ∈ G$ of an element $x ∈ G$, such that @@ -262,7 +262,7 @@ function inv!(M::Manifold, y, x, ::Val{false}) return error("inv! not implemented on $(typeof(M)) for points $(typeof(x))") end -@doc doc""" +@doc raw""" identity(G::AbstractGroupManifold, x) Identity element $e ∈ G$, such that for any element $x ∈ G$, $x \circ e = e \circ x = x$. @@ -312,7 +312,7 @@ function isapprox(G::GT, e::Identity{GT}, x; kwargs...) where {GT<:GroupManifold 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$. @@ -336,7 +336,7 @@ end _action_order(x, y, conv::LeftAction) = (x, y) _action_order(x, y, conv::RightAction) = (y, x) -@doc doc""" +@doc raw""" translate(G::AbstractGroupManifold, x, y[, conv::ActionDirection=LeftAction()]) For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified convention, either @@ -362,7 +362,7 @@ function translate(G::AbstractGroupManifold, x, y, conv::ActionDirection) return compose(G, _action_order(x, y, conv)...) end -@doc doc""" +@doc raw""" translate!(G::AbstractGroupManifold, z, x, y[, conv::ActionDirection=LeftAction()]) For group elements $x,y ∈ G$, translate $y$ by $x$ with the specified convention, either @@ -389,7 +389,7 @@ function translate!(G::AbstractGroupManifold, z, x, y, conv::ActionDirection) return compose!(G, z, _action_order(x, y, conv)...) end -@doc doc""" +@doc raw""" inverse_translate(G::AbstractGroupManifold, x, y, [conv::ActionDirection=Left()]) For group elements $x,y ∈ G$, inverse translate $y$ by $x$ with the specified convention, @@ -415,7 +415,7 @@ function inverse_translate(G::AbstractGroupManifold, x, y, conv::ActionDirection return translate(G, inv(G, x), y, conv) end -@doc doc""" +@doc raw""" 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, @@ -442,7 +442,7 @@ function inverse_translate!(G::AbstractGroupManifold, z, x, y, conv::ActionDirec return translate!(G, z, inv(G, x), y, conv) end -@doc doc""" +@doc raw""" translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=LeftAction()]) For group elements $x,y ∈ G$ and tangent vector $v ∈ T_y G$, compute the action of the @@ -485,7 +485,7 @@ function translate_diff!(M::Manifold, vout, x, y, v, conv::ActionDirection, ::Va 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))") end -@doc doc""" +@doc raw""" inverse_translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=Left()]) For group elements $x,y ∈ G$ and tangent vector $v ∈ T_y G$, compute the inverse of the diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 2b6aaa06af..ab2f25d21b 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -28,7 +28,7 @@ Get the direction of the action """ direction(::AbstractGroupAction{AD}) where {AD} = AD() -@doc doc""" +@doc raw""" apply(A::AbstractGroupAction, a, x) Apply action `a` to the point `x`. The action is specified by `A`. @@ -79,7 +79,7 @@ function inverse_apply!(A::AbstractGroupAction, y, a, x) return apply!(A, y, inva, x) end -@doc doc""" +@doc raw""" apply_diff(A::AbstractGroupAction, a, x, v) For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, compute the action of the @@ -102,7 +102,7 @@ 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))") end -@doc doc""" +@doc raw""" inverse_apply_diff(A::AbstractGroupAction, a, x, v) For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, compute the action of the @@ -131,7 +131,7 @@ 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) -@doc doc""" +@doc raw""" optimal_alignment(A::AbstractGroupAction, x1, x2) Calculate an action element of action `A` that acts upon `x1` to produce @@ -156,7 +156,7 @@ function optimal_alignment!(A::AbstractGroupAction, y, x1, x2) return copyto!(y, optimal_alignment(A, x1, x2)) end -@doc doc""" +@doc raw""" center_of_orbit( A::AbstractGroupAction, pts, diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index 12a8cb6725..8ca84e82d7 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. diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index f40b7629b8..9b6b28eef1 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, diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 83caf385c4..3552ef2123 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -16,7 +16,7 @@ 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 diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 007812ec1e..c31a04f297 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. diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index fea9038c47..dfa56e501e 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. diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 14793ad472..cbdf8bf3ab 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, diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 714db189f6..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. diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 628cb0e58e..5a3dbcec07 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 @@ -19,7 +19,7 @@ struct CholeskySpace{N} <: Manifold end CholeskySpace(n::Int) = CholeskySpace{n}() -@doc doc""" +@doc raw""" check_manifold_point(M::CholeskySpace, p; kwargs...) Check whether the matrix `p` lies on the [`CholeskySpace`](@ref) `M`, i.e. @@ -75,7 +75,7 @@ function check_tangent_vector(M::CholeskySpace, p, X; kwargs...) return nothing end -@doc doc""" +@doc raw""" distance(M::CholeskySpace, p, q) Compute the Riemannian distance on the [`CholeskySpace`](@ref) `M` between two @@ -95,7 +95,7 @@ function distance(::CholeskySpace, p, q) ) end -@doc doc""" +@doc raw""" exp(M::CholeskySpace, p, X) Compute the exponential map on the [`CholeskySpace`](@ref) `M` eminating from the lower @@ -121,7 +121,7 @@ function exp!(::CholeskySpace, q, p, X) return q end -@doc doc""" +@doc raw""" inner(M::CholeskySpace, p, X, Y) Compute the inner product on the [`CholeskySpace`](@ref) `M` at the @@ -140,7 +140,7 @@ function inner(::CholeskySpace, p, X, Y) ) end -@doc doc""" +@doc raw""" log(M::CholeskySpace, X, p, q) Compute the logarithmic map on the [`CholeskySpace`](@ref) `M` for the geodesic eminating @@ -164,14 +164,14 @@ function log!(::CholeskySpace, X, p, q) ) 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}$. """ @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)`. @@ -183,7 +183,7 @@ strictlyLowerTriangular(p) = LowerTriangular(p) - Diagonal(diag(p)) strictlyUpperTriangular(p) = UpperTriangular(p) - Diagonal(diag(p)) -@doc doc""" +@doc raw""" vector_transport_to(M::CholeskySpace, p, X, q, ::ParallelTransport) Parallely transport the tangent vector `X` at `p` along the geodesic to `q` @@ -206,7 +206,7 @@ function vector_transport_to!(::CholeskySpace, Y, p, X, q, ::ParallelTransport) ) end -@doc doc""" +@doc raw""" zero_tangent_vector(M::CholeskySpace, p) Return the zero tangent vector on the [`CholeskySpace`](@ref) `M` at `p`. diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index b6b2a7eb3d..2e84788b48 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Circle{F} <: Manifold The circle $𝕊^1$ as a manifold ere manifold represented by @@ -16,7 +16,7 @@ struct Circle{F} <: Manifold where {F<:AbstractNumbers} end Circle(f::AbstractNumbers = ℝ) = Circle{f}() -@doc doc""" +@doc raw""" check_manifold_point(M::Circle, p) Check whether `p` is a point on the [`Circle`](@ref) `M`. @@ -71,7 +71,7 @@ function check_tangent_vector(M::Circle{ℂ}, p, X; kwargs...) return nothing end -@doc doc""" +@doc raw""" complex_dot(a, b) Compute the inner product of two (complex) numbers with in the complex plane. @@ -79,7 +79,7 @@ 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""" +@doc raw""" distance(M::Circle, p, q) Compute the distance on the [`Circle`](@ref) `M`, which is @@ -92,7 +92,7 @@ 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""" +@doc raw""" exp(M::Circle, p, X) Compute the exponential map on the [`Circle`](@ref). @@ -156,14 +156,14 @@ Return tangent vector from the coordinates in the Lie algebra of the circle. """ get_vector(M::Circle{ℂ}, p, X, B::ArbitraryOrthonormalBasis) = @SVector [1im * X[1] * p[1]] -@doc doc""" +@doc raw""" injectivity_radius(M::Circle[, p]) Return the injectivity radius on the [`Circle`](@ref) `M`, i.e. $\pi$. """ injectivity_radius(::Circle, args...) = π -@doc doc""" +@doc raw""" inner(M::Circle, p, X, Y) Compute the inner product of the two tangent vectors `X,Y` from the tangent plane at `p` on @@ -194,7 +194,7 @@ function inverse_retract(M::Circle, x::Number, y::Number, ::LogarithmicInverseRe return log(M, x, y) end -@doc doc""" +@doc raw""" log(M::Circle, p, q) Compute the logarithmic map on the [`Circle`](@ref) `M`. @@ -237,7 +237,7 @@ function log!(M::Circle{ℂ}, X, p, q) return project_tangent!(M, X, p, X) end -@doc doc""" +@doc raw""" manifold_dimension(M::Circle) Return the dimension of the [`Circle`](@ref) `M`, @@ -245,7 +245,7 @@ i.e. $\operatorname{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` of points on the [`Circle`](@ref) $𝕊^1$, @@ -257,7 +257,7 @@ mean(::Circle, x::Array{<:Real}, w::AbstractVector; kwargs...) = sym_rem(sum(w . @inline norm(::Circle, p, X) = sum(abs, X) -@doc doc""" +@doc raw""" project_point(M::Circle, p) Project a point `p` onto the [`Circle`](@ref) `M`. @@ -272,7 +272,7 @@ project_point(::Circle{ℂ}, p::Number) = p / abs(p) project_point!(::Circle{ℝ}, p) = (p .= sym_rem(p)) project_point!(::Circle{ℂ}, p) = (p .= p / sum(abs.(p))) -@doc doc""" +@doc raw""" project_tangent(M::Circle, p, X) Project a value `X` onto the tangent space of the point `p` on the [`Circle`](@ref) `M`. @@ -296,7 +296,7 @@ sharp(M::Circle, p::Number, ξ::CoTFVector) = FVector(TangentSpace, ξ.data) sharp!(M::Circle, X::TFVector, p, ξ::CoTFVector) = copyto!(X, ξ) -@doc doc""" +@doc raw""" sym_rem(x,[T=π]) Compute symmetric remainder of `x` with respect to the interall 2*`T`, i.e. @@ -307,7 +307,7 @@ function sym_rem(x::N, T = π) where {N<:Number} end sym_rem(x, T=π) where N = map(sym_rem, x, Ref(T)) -@doc doc""" +@doc raw""" vector_transport_to(M::Circle, p, X, q, ::ParallelTransport) Compute the parallel transport of `X` from the tangent space at `p` to the tangent space at diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index c11833aac9..ed9a974a58 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Euclidean{T<:Tuple} <: Manifold Euclidean vector space $ℝ^n$. @@ -51,7 +51,7 @@ and higher order arrays, the matrix and ternsor Frobenius norm, respectively. """ distance(::Euclidean, x, y) = norm(x .- y) -@doc doc""" +@doc raw""" exp(M::Euclidean, x, v) Compute the exponential map on the [`Euclidean`](@ref) manifold `M` from `x` in direction @@ -116,14 +116,14 @@ end hat!(::Euclidean{N,ℝ}, v, x, 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$. """ injectivity_radius(::Euclidean) = Inf -@doc doc""" +@doc raw""" inner(M::Euclidean, x, v, w) Compute the inner product on the [`Euclidean`](@ref) `M`, which is just @@ -154,7 +154,7 @@ function local_metric(::MetricManifold{<:Manifold,EuclideanMetric}, x) return Diagonal(ones(SVector{size(x, 1),eltype(x)})) end -@doc doc""" +@doc raw""" log(M::Euclidean, x, y) Compute the logarithmic map on the [`Euclidean`](@ref) `M` from `x` to `y`, @@ -232,7 +232,7 @@ function median!(::Euclidean{Tuple{1}}, y, x::AbstractVector, w::AbstractWeights return copyto!(y, [median(vcat(x...), w)]) end -@doc doc""" +@doc raw""" norm(M::Euclidean, x, v) Compute the norm of a tangent vector `v` at `x` on the [`Euclidean`](@ref) @@ -253,7 +253,7 @@ function normal_tvector_distribution(M::Euclidean{Tuple{N}}, x, σ) where {N} return ProjectedFVectorDistribution(TangentBundleFibers(M), x, d, project_vector!, x) end -@doc doc""" +@doc raw""" project_point(M::Euclidean, x) Project an arbitrary point `x` onto the [`Euclidean`](@ref) `M`, which diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 6aa5f237f6..3a119f118d 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" FixedRankMatrices{m,n,k,T} <: Manifold The manifold of $m \times n$ real-valued (complex-valued) matrices of fixed rank $k$, i.e. @@ -46,7 +46,7 @@ function FixedRankMatrices(m::Int, n::Int, k::Int, t::AbstractNumbers = ℝ) return FixedRankMatrices{m,n,k,t}() end -@doc doc""" +@doc raw""" SVDMPoint <: MPoint A point on a certain manifold, where the data is stored in a svd like fashion, @@ -77,7 +77,7 @@ 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 @@ -107,7 +107,7 @@ 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""" +@doc raw""" check_manifold_point(M::FixedRankMatrices{m,n,k},x; kwargs...) Check whether the matrix or [`SVDMPoint`](@ref) `x` ids a valid point on the @@ -156,7 +156,7 @@ function check_manifold_point( return nothing end -@doc doc""" +@doc raw""" check_tangent_vector(M:FixedRankMatrices{m,n,k}, x, v) Check whether the tangent [`UMVTVector`](@ref) `v` is from the tangent space of @@ -193,7 +193,7 @@ function check_tangent_vector( end end -@doc doc""" +@doc raw""" inner(M::FixedRankMatrices, x::SVDMPoint, v::UMVTVector, w::UMVTVector) Compute the inner product of `v` and `w` in the tangent space of `x` on the @@ -221,7 +221,7 @@ function isapprox( ) end -@doc doc""" +@doc raw""" manifold_dimension(M::FixedRankMatrices{m,n,k,𝔽}) Return the manifold dimension for the `𝔽`-valued [`FixedRankMatrices`](@ref) `M` @@ -237,7 +237,7 @@ function manifold_dimension(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} return (m + n - k) * k * real_dimension(𝔽) end -@doc doc""" +@doc raw""" project_tangent(M, x, A) project_tangent(M, x, v) @@ -277,7 +277,7 @@ function project_tangent!( return project_tangent!(M, vto, x, v.U * v.M * v.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,7 +285,7 @@ the size of matrices on this manifold $(m,n)$. """ @generated representation_size(::FixedRankMatrices{m,n}) where {m,n} = (m, n) -@doc doc""" +@doc raw""" retract(M, x, v, ::PolarRetraction) Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by computing @@ -350,7 +350,7 @@ function copyto!(v::UMVTVector, w::UMVTVector) return v 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 diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index c3b2c272c3..9e5f77f872 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 @@ -45,7 +45,7 @@ end const EdgeGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,EdgeManifold} const VertexGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,VertexManifold} -@doc doc""" +@doc raw""" check_manifold_point(M::GraphManifold, x) Check whether `x` is a valid point on the [`GraphManifold`](@ref), i.e. its @@ -75,7 +75,7 @@ function check_manifold_point(M::EdgeGraphManifold, x; kwargs...) return check_manifold_point(PM, x; kwargs...) end -@doc doc""" +@doc raw""" check_tangent_vector(M::GraphManifold, x, v) Check whether `x` is a valid point on the [`GraphManifold`](@ref), and @@ -122,7 +122,7 @@ 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 @@ -184,7 +184,7 @@ function incident_log!( return v 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 @@ -196,7 +196,7 @@ d_{𝒩} = \lvert V \rVert d_ℳ. 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 diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 6abed5d1af..09dc39b0f4 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Grassmann{n,k,F} <: Manifold The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned @@ -57,7 +57,7 @@ struct Grassmann{n,k,F} <: Manifold end Grassmann(n::Int, k::Int, F::AbstractNumbers = ℝ) = Grassmann{n,k,F}() -@doc doc""" +@doc raw""" check_manifold_point(M::Grassmann{n,k,F}, x) Check whether `x` is representing a point on the [`Grassmann`](@ref) `M`, i.e. its @@ -91,7 +91,7 @@ function check_manifold_point(M::Grassmann{n,k,F}, x; kwargs...) where {n,k,F} end end -@doc doc""" +@doc raw""" check_tangent_vector(M::Grassmann{n,k,F}, x, v) Check whether `v` is a tangent vector in the tangent space of `x` on the [`Grassmann`](@ref) @@ -133,7 +133,7 @@ function check_tangent_vector(G::Grassmann{n,k,F}, x, v; kwargs...) where {n,k,F end end -@doc doc""" +@doc raw""" distance(M::Grassmann, x, y) Compute the Riemannian distance on [`Grassmann`](@ref) manifold `M`$= \mathrm{Gr}(n,k)$. @@ -155,7 +155,7 @@ function distance(M::Grassmann, x, y) return sqrt(sum((acos.(a)) .^ 2)) end -@doc doc""" +@doc raw""" exp(M::Grassmann, x, v) Compute the exponential map on the [`Grassmann`](@ref) `M`$= \mathrm{Gr}(n,k)$ starting in @@ -186,7 +186,7 @@ end injectivity_radius(::Grassmann) = π / 2 -@doc doc""" +@doc raw""" inner(M::Grassmann, x, v, w) Compute the inner product for two tangent vectors `v`, `w` from the @@ -201,7 +201,7 @@ where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian """ inner(::Grassmann, x, v, w) = dot(v, w) -@doc doc""" +@doc raw""" inverse_retract(M::Grassmann, x, y, ::PolarInverseRetraction) Compute the inverse retraction for the [`PolarRetraction`](@ref), on the @@ -219,7 +219,7 @@ function inverse_retract!(::Grassmann, v, x, y, ::PolarInverseRetraction) return copyto!(v, y / (x' * y) - x) end -@doc doc""" +@doc raw""" inverse_retract(M, x, y, ::QRInverseRetraction) Compute the inverse retraction valid of the [`QRRetraction`](@ref) @@ -238,7 +238,7 @@ function isapprox(M::Grassmann, x, v, w; kwargs...) end isapprox(M::Grassmann, x, y; kwargs...) = isapprox(distance(M, x, y), 0.0; kwargs...) -@doc doc""" +@doc raw""" log(M::Grassmann, x, y) Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = ℳ=\mathrm{Gr}(n,k)$, @@ -267,7 +267,7 @@ function log!(M::Grassmann, v, x, y) return copyto!(v, 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. @@ -304,7 +304,7 @@ function mean!( return mean!(M, y, x, w, GeodesicInterpolationWithinRadius(π / 4); kwargs...) end -@doc doc""" +@doc raw""" project_tangent(M::Grassmann, x, w) Project the `n`-by-`k` `w` onto the tangent space of `x` on the [`Grassmann`](@ref) `M`, @@ -320,7 +320,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,7 +328,7 @@ 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""" +@doc raw""" retract(M::Grassmann, x, v, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](@ref) on the @@ -366,7 +366,7 @@ function retract!(::Grassmann{N,K}, y, x, v, ::QRRetraction) where {N,K} return copyto!(y, Array(qrfac.Q) * D) end -@doc doc""" +@doc raw""" zero_tangent_vector(M::Grassmann, x) Return the zero tangent vector from the tangent space at `x` on the [`Grassmann`](@ref) `M`, diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 6196c5e776..72c64e7f62 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Hyperbolic{N} <: Manifold The hyperbolic space $ℍ^n$ represented by $n+1$-Tuples, i.e. in by @@ -31,7 +31,7 @@ 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. @@ -97,7 +97,7 @@ function check_tangent_vector(M::Hyperbolic, x, v; kwargs...) return nothing end -@doc doc""" +@doc raw""" distance(M::Hyperbolic, x, y) Compute the distance on the [`Hyperbolic`](@ref) `M`, which reads @@ -110,7 +110,7 @@ where $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref). """ distance(M::Hyperbolic, x, y) = acosh(max(-minkowski_dot(x, y), 1.0)) -@doc doc""" +@doc raw""" exp(M::Hyperbolic, x, v) Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ eminating @@ -133,14 +133,14 @@ end flat!(M::Hyperbolic, v::CoTFVector, x, w::TFVector) = copyto!(v, w) -@doc doc""" +@doc raw""" injectivity_radius(M::Hyperbolic[, x]) Return the injectivity radius on the [`Hyperbolic`](@ref), which is always $\infty$. """ injectivity_radius(H::Hyperbolic, args...) = Inf -@doc doc""" +@doc raw""" inner(M::Hyperbolic, x, v, w) Compute the Riemannian inner product for two tangent vectors `v` and `w` @@ -152,7 +152,7 @@ inner product on $ℝ^{n+1}$. is_default_metric(::Hyperbolic, ::MinkowskiMetric) = Val(true) -@doc doc""" +@doc raw""" log(M::Hyperbolic, x, y) Compute the logarithmic map on the [`Hyperbolic`](@ref) space $ℍ^n$, the tangent @@ -177,7 +177,7 @@ function log!(M::Hyperbolic, v, x, y) return v 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. @@ -190,7 +190,7 @@ 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 $ℍ^n$, i.e. $n$. @@ -215,7 +215,7 @@ function mean!(M::Hyperbolic, y, x::AbstractVector, w::AbstractVector; kwargs... return mean!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) end -@doc doc""" +@doc raw""" project_tangent(M::Hyperbolic, x, v) Perform an orthogonal projection with respect to the Minkowski inner product of `v` onto @@ -232,7 +232,7 @@ project_tangent(::Hyperbolic, ::Any...) project_tangent!(::Hyperbolic, w, x, v) = (w .= v .+ minkowski_dot(x, v) .* x) -@doc doc""" +@doc raw""" representation_size(M::Hyperbolic) Return the representation size on the [`Hyperbolic`](@ref), i.e. for the `n`-diomensional @@ -242,7 +242,7 @@ hyperbolic manifold the dimention of the embedding, i.e. `n+1`. sharp!(M::Hyperbolic, v::TFVector, x, w::CoTFVector) = copyto!(v, w) -@doc doc""" +@doc raw""" vector_transport_to(M::Hyperbolic, x, v, y, ::ParallelTransport) Compute the paralllel transport of the `v` from the tangent space at `x` on the @@ -263,7 +263,7 @@ function vector_transport_to!(M::Hyperbolic, vto, x, v, y, ::ParallelTransport) return copyto!(vto, v - (inner(M, x, w, v) * (w + log(M, y, x)) / wn^2)) end -@doc doc""" +@doc raw""" zero_tangent_vector(M::Hyperbolic, x) Return the zero vector from the tangent space at `x` of the [`Hyperbolic`](@ref) `M`. diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 38953685db..c4e002a3a4 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,7 +37,7 @@ 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 @@ -54,7 +54,7 @@ function check_tangent_vector(M::MetricManifold, x, v; kwargs...) return check_tangent_vector(M.manifold, x, v; kwargs...) end -@doc doc""" +@doc raw""" christoffel_symbols_first(M::MetricManifold, x; backend=:default) Compute the Christoffel symbols of the first kind in local coordinates. @@ -74,7 +74,7 @@ function christoffel_symbols_first(M::MetricManifold, x; backend = :default) 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. @@ -94,7 +94,7 @@ function christoffel_symbols_second(M::MetricManifold, x; backend = :default) return Γ₂ end -@doc doc""" +@doc raw""" christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :default) Get partial derivatives of the Christoffel symbols of the second kind @@ -114,7 +114,7 @@ function christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :de return ∂Γ end -@doc doc""" +@doc raw""" det_local_metric(M::MetricManifold, x) Return the determinant of local matrix representation of the metric tensor $g$. @@ -135,7 +135,7 @@ function einstein_tensor(M::MetricManifold, x; backend = :default) return G end -@doc doc""" +@doc raw""" exp(N::MetricManifold{M,G}, x, v) Copute the exponential map on the [`Manifold`](@ref) `M` equipped with the [`Metric`](@ref) `G`. @@ -184,7 +184,7 @@ function exp!(M::MMT, ::Val{false}, y, x, v) where {MMT<:MetricManifold} return copyto!(y, sol.u[1][n+1:end]) end -@doc doc""" +@doc raw""" flat(N::MetricManifold{M,G}, x, w::FVector{TangentSpaceType}) Compute the musical isomorphism to transform the tangent vector `w` from the @@ -228,7 +228,7 @@ function injectivity_radius(M::MMT, args...) where {MMT<:MetricManifold} return injectivity_radius(base_manifold(M), args...) end -@doc doc""" +@doc raw""" inverse_local_metric(M::MetricManifold, x) Return the local matrix representation of the inverse metric (cometric) tensor, usually @@ -276,7 +276,7 @@ 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""" +@doc raw""" inner(N::MetricManifold{M,G}, x, v, w) Compute the inner product of `v`, `w` from the tangent space at `x` on the @@ -306,7 +306,7 @@ function inner( return dot(v, ginv * w) end -@doc doc""" +@doc raw""" local_metric(M::MetricManifold, x) Return the local matrix representation at the point `x` of the metric tensor $g$ on the @@ -318,7 +318,7 @@ function local_metric(M::MetricManifold, x) error("Local metric not implemented on $(typeof(M)) for point $(typeof(x))") end -@doc doc""" +@doc raw""" local_metric_jacobian(M::MetricManifold, x; backend=:default) Get partial derivatives of the local metric of `M` at `x` with respect to the @@ -339,7 +339,7 @@ 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)).") end -@doc doc""" +@doc raw""" log_local_metric_density(M::MetricManifold, x) Return the natural logarithm of the metric density $\rho$ of `M` at `x`, which @@ -407,7 +407,7 @@ function median!( return median!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) end -@doc doc""" +@doc raw""" metric(M::MetricManifold) Get the metric $g$ of the manifold `M`. @@ -489,7 +489,7 @@ function ricci_tensor(M::MetricManifold, x; kwargs...) return Ric end -@doc doc""" +@doc raw""" riemann_tensor(M::MetricManifold, x) Compute the Riemann tensor $R^l_{ijk}$, also known as the Riemann curvature @@ -506,7 +506,7 @@ function riemann_tensor(M::MetricManifold, x; backend = :default) return R end -@doc doc""" +@doc raw""" sharp(N::MetricManifold{M,G}, x, w::FVector{CotangentSpaceType}) Compute the musical isomorphism to transform the cotangent vector `w` from the @@ -527,7 +527,7 @@ function sharp!(M::N, v::TFVector, x, w::CoTFVector) where {N<:MetricManifold} return v end -@doc doc""" +@doc raw""" solve_exp_ode( M::MetricManifold, x, diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 97858b6427..10a7ed71d9 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -29,7 +29,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,7 +38,7 @@ 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 $ℳ^{n_1\times n_2 \times … \times n_d}$ with power geometry @@ -76,7 +76,7 @@ function PowerManifold(M::Manifold, ::TPR, size::Int...) where TPR<:AbstractPowe 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 @@ -231,7 +231,7 @@ function det_local_metric( return result end -@doc doc""" +@doc raw""" distance(M::AbstractPowerManifold, x, y) Compute the distance between `x` and `y` on an [`AbstractPowerManifold`](@ref), @@ -250,7 +250,7 @@ function distance(M::AbstractPowerManifold, x, y) return sqrt(sum_squares) end -@doc doc""" +@doc raw""" exp(M::AbstractPowerManifold, x, v) Compute the exponential map from `x` in direction `v` on the [`AbstractPowerManifold`](@ref) `M`, @@ -271,7 +271,7 @@ function exp!(M::AbstractPowerManifold, y, x, v) return y end -@doc doc""" +@doc raw""" flat(M::AbstractPowerManifold, x, w::FVector{TangentSpaceType}) use the musical isomorphism to transform the tangent vector `w` from the tangent space at @@ -369,7 +369,7 @@ function get_vector(M::AbstractPowerManifold, x, v, B::ArbitraryOrthonormalBasis return v_out end -@doc doc""" +@doc raw""" injectivity_radius(M::AbstractPowerManifold[, x]) the injectivity radius on an [`AbstractPowerManifold`](@ref) is for the global case @@ -393,7 +393,7 @@ function injectivity_radius(M::AbstractPowerManifold, x) end injectivity_radius(M::AbstractPowerManifold) = injectivity_radius(M.manifold) -@doc doc""" +@doc raw""" inverse_retract(M::AbstractPowerManifold, x, y, m::InversePowerRetraction) Compute the inverse retraction from `x` with respect to `y` on an [`AbstractPowerManifold`](@ref) `M` @@ -417,7 +417,7 @@ function inverse_retract!(M::AbstractPowerManifold, v, x, y, method::InversePowe return v end -@doc doc""" +@doc raw""" inner(M::AbstractPowerManifold, x, v, w) Compute the inner product of `v` and `w` from the tangent space at `x` on an @@ -470,7 +470,7 @@ function isapprox(M::AbstractPowerManifold, x, v, w; kwargs...) return result end -@doc doc""" +@doc raw""" log(M::AbstractPowerManifold, x, y) Compute the logarithmic map from `x` to `y` on the [`AbstractPowerManifold`](@ref) `M`, @@ -492,7 +492,7 @@ function log!(M::AbstractPowerManifold, v, x, y) end -@doc doc""" +@doc raw""" manifold_dimension(M::PowerManifold) Returns the manifold-dimension of an [`PowerManifold`](@ref) `M` @@ -508,7 +508,7 @@ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} return manifold_dimension(M.manifold) * prod(size_to_tuple(TSize)) end -@doc doc""" +@doc raw""" norm(M::AbstractPowerManifold, x, v) Compute the norm of `v` from the tangent space of `x` on an @@ -579,7 +579,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` @@ -603,7 +603,7 @@ function retract!(M::AbstractPowerManifold, y, x, v, method::PowerRetraction) return y end -@doc doc""" +@doc raw""" sharp(M::AbstractPowerManifold, x, w::FVector{CotangentSpaceType}) Use the musical isomorphism to transform the cotangent vector `w` from the tangent space at diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 9c051cefc8..f9334a9597 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" ProductManifold{TM<:Tuple, TRanges<:Tuple, TSizes<:Tuple} <: Manifold Product manifold $M_1 \times M_2 \times … \times M_n$ with product geometry. @@ -159,7 +159,7 @@ function check_tangent_vector( return nothing end -@doc doc""" +@doc raw""" cross(M,N) cross(M1, M2, M3,...) @@ -185,7 +185,7 @@ function det_local_metric(M::MetricManifold{ProductManifold,ProductMetric}, x::P return prod(dets) end -@doc doc""" +@doc raw""" distance(M::ProductManifold, x, y) compute the distance two points `x` and `y` on the [`ProductManifold`](@ref) `M`, which is @@ -202,7 +202,7 @@ function distance(M::ProductManifold, x, y) )) end -@doc doc""" +@doc raw""" exp(M::ProductManifold, x, v) compute the exponential map from `x` towards `v` on the [`ProductManifold`](@ref) `M`, @@ -229,7 +229,7 @@ function exp!(M::ProductManifold, y, x, v) return y end -@doc doc""" +@doc raw""" flat(M::ProductManifold, x, w::FVector{TangentSpaceType}) use the musical isomorphism to transform the tangent vector `w` from the tangent space at @@ -348,7 +348,7 @@ function hat!(M::ProductManifold, v, x, vⁱ) return v end -@doc doc""" +@doc raw""" injectivity_radius(M::ProductManifold[, x]) Compute the injectivity radius on the [`ProductManifold`](@ref), which is the @@ -360,7 +360,7 @@ function injectivity_radius(M::ProductManifold, x) end injectivity_radius(M::ProductManifold) = min(map(injectivity_radius, M.manifolds)...) -@doc doc""" +@doc raw""" inner(M::ProductManifold, x, v, w) compute the inner product of two tangent vectors `v`, `w` from the tangent space @@ -378,7 +378,7 @@ function inner(M::ProductManifold, x, v, w) return sum(subproducts) end -@doc doc""" +@doc raw""" inverse_retract(M::ProductManifold, x, y, m::InverseProductRetraction) Compute the inverse retraction from `x` with respect to `y` on the [`ProductManifold`](@ref) @@ -420,7 +420,7 @@ function isapprox(M::ProductManifold, x, v, w; kwargs...) ) end -@doc doc""" +@doc raw""" log(M::ProductManifold, x, y) Compute the logarithmic map from `x` to `y` on the [`ProductManifold`](@ref) `M`, @@ -447,7 +447,7 @@ function log!(M::ProductManifold, v, x, y) return v end -@doc doc""" +@doc raw""" manifold_dimension(M::ProductManifold) Return the manifold dimension of the [`ProductManifold`](@ref), which is the sum of the @@ -455,7 +455,7 @@ manifold dimensions the product is made of. """ manifold_dimension(M::ProductManifold) = mapreduce(manifold_dimension, +, M.manifolds) -@doc doc""" +@doc raw""" norm(M::PowerManifold, x, v) Compute the norm of `v` from the tangent space of `x` on the [`ProductManifold`](@ref), @@ -536,7 +536,7 @@ function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, v::ProductRepr) return v end -@doc doc""" +@doc raw""" retract(M::ProductManifold, x, v, m::ProductRetraction) Compute the retraction from `x` with tangent vector `v` on the [`ProductManifold`](@ref) `M` @@ -562,7 +562,7 @@ function representation_size(M::ProductManifold) return (mapreduce(m -> prod(representation_size(m)), +, M.manifolds),) end -@doc doc""" +@doc raw""" sharp(M::ProductManifold, x, w::FVector{CotangentSpaceType}) Use the musical isomorphism to transform the cotangent vector `w` from the tangent space at diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 494f07a27e..335f0548b4 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Rotations{N} <: Manifold Special orthogonal manifold $\mathrm{SO}(n)$ represented by $n \times n$ @@ -37,7 +37,7 @@ 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. @@ -109,7 +109,7 @@ function check_tangent_vector(M::Rotations{N}, x, v; kwargs...) where {N} 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 @@ -137,7 +137,7 @@ function cos_angles_4d_rotation_matrix(R) return (a + b, a - b) end -@doc doc""" +@doc raw""" exp(M::Rotations, x, v) Compute the exponential map on the [`Rotations`](@ref) from `x` into direction @@ -260,7 +260,7 @@ function hat!(M::Rotations{2}, Ω, x, θ::Real) end hat!(M::Rotations{2}, Ω, x, ω) = hat!(M, Ω, x, ω[1]) -@doc doc""" +@doc raw""" hat(M::Rotations, x, ω) Convert the unique tangent vector components $\omega$ at point $x$ on rotations @@ -295,7 +295,7 @@ function hat!(M::Rotations{N}, Ω, x, ω) where {N} return Ω end -@doc doc""" +@doc raw""" injectivity_radius(M::Rotations) injectivity_radius(M::Rotations, x) @@ -313,7 +313,7 @@ Return the radius of injectivity for the [`PolarRetraction`](@ref) on the injectivity_radius(::Rotations) = π * sqrt(2.0) injectivity_radius(::Rotations, x, ::PolarRetraction) = π / sqrt(2.0) -@doc doc""" +@doc raw""" inner(M::Rotations, x, w, v) Compute the inner product of the two tangent vectors `w, v` from the tangent @@ -326,7 +326,7 @@ Tangent vectors are represented by matrices. """ inner(M::Rotations, x, w, v) = dot(w, v) -@doc doc""" +@doc raw""" inverse_retract(M, x, y, ::PolarInverseRetraction) Compute a vector from the tangent space $T_x\mathrm{SO}(n)$ @@ -382,7 +382,7 @@ function inverse_retract!(M::Rotations{N}, v, x, y, ::QRInverseRetraction) where return v end -@doc doc""" +@doc raw""" log(M::Rotations, x, y) Compute the logarithmic map on the [`Rotations`](@ref) manifold @@ -445,7 +445,7 @@ function log!(M::Rotations{4}, v, x, y) return project_tangent!(M, v, x, v) 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}$. @@ -470,7 +470,7 @@ function mean!(M::Rotations, y, x::AbstractVector, w::AbstractVector; kwargs...) return mean!(M, y, x, w, GeodesicInterpolationWithinRadius(π / 2 / √2); kwargs...) end -@doc doc""" +@doc raw""" norm(M::Rotations, x, v) Compute the norm of a tangent vector `v` from the tangent space at `x` on the @@ -485,7 +485,7 @@ elements from the Lie group. """ norm(M::Rotations, x, v) = norm(v) -@doc doc""" +@doc raw""" normal_rotation_distribution(M::Rotations, x, σ::Real) Return a random point on the manifold [`Rotations`](@ref) `M` @@ -523,7 +523,7 @@ function normal_tvector_distribution(M::Rotations, x, σ) return ProjectedFVectorDistribution(TangentBundleFibers(M), x, d, project_vector!, x) end -@doc doc""" +@doc raw""" project_point(M::Rotations, x; check_det = true) Project `x` to the nearest point on manifold `M`. @@ -554,7 +554,7 @@ function project_point!(M::Rotations{N}, y, x; check_det = true) where {N} return y end -@doc doc""" +@doc raw""" project_tangent(M::Rotations, x, v) Project the matrix `v` onto the tangent space by making `v` skew symmetric, @@ -568,7 +568,7 @@ project_tangent(::Rotations, ::Any...) project_tangent!(M::Rotations, w, x, v) = (w .= (v .- transpose(v)) ./ 2) -@doc doc""" +@doc raw""" representation_size(M::Rotations) Return the `size()` of a point on the [`Rotations`](@ref) `M`, i.e. for the @@ -608,7 +608,7 @@ function _fix_random_rotation(A::AbstractMatrix) return C end -@doc doc""" +@doc raw""" retract(M, x, v) retract(M, x, v, ::QRRetraction) @@ -648,7 +648,7 @@ function retract!(M::Rotations, y, x, v, method::PolarRetraction) return project_point!(M, y, A; check_det = false) end -@doc doc""" +@doc raw""" vee(M::Rotations, x, Ω) Extract the unique tangent vector components $\omega$ at point $x$ on rotations @@ -688,7 +688,7 @@ function vee!(M::Rotations{2}, ω, x, Ω) return ω end -@doc doc""" +@doc raw""" zero_tangent_vector(M::Rotations, x) Return the zero tangent vector from the tangent space art `x` on the [`Rotations`](@ref) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 0dd00ec956..ca4a97db62 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Sphere{N} <: Manifold The unit sphere manifold $𝕊^n$ represented by $n+1$-Tuples, i.e. in by @@ -78,7 +78,7 @@ function check_tangent_vector(S::Sphere{N}, x, v; kwargs...) where {N} return nothing end -@doc doc""" +@doc raw""" distance(M::Sphere, x, y) Compute the geodesic distance betweeen `x` and `y` on the [`Sphere`](@ref) `M`. @@ -91,7 +91,7 @@ d_{𝕊^n}(x,y) = \operatorname{acos}(⟨x, y⟩). """ distance(S::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) -@doc doc""" +@doc raw""" exp(M::Sphere, x, v) Compute the exponential map from `x` into the tangent direction `v` on the [`Sphere`](@ref) @@ -113,7 +113,7 @@ end flat!(M::Sphere, v::CoTFVector, x, w::TFVector) = copyto!(v, w) -@doc doc""" +@doc raw""" injectivity_radius(M::Sphere[, x]) Return the injectivity radius for the [`Sphere`](@ref) `M`, which is globally $\pi$. @@ -126,7 +126,7 @@ Return the injectivity radius for the [`ProjectionRetraction`](@ref) on the injectivity_radius(::Sphere, ::Any...) = π injectivity_radius(::Sphere, ::Any, ::ProjectionRetraction) = π / 2 -@doc doc""" +@doc raw""" inner(S::Sphere, x, w, v) Compute the inner product of the two tangent vectors `w,v` from the tangent @@ -142,7 +142,7 @@ function get_vector(M::Sphere{N}, x, v, B::ArbitraryOrthonormalBasis) where {N} return 2 * xp1 * dot(xp1, v0) / dot(xp1, xp1) - v0 end -@doc doc""" +@doc raw""" inverse_retract(M::Sphere, x, y, ::ProjectionInverseRetraction) Compute the inverse of the projection based retraction on the [`Sphere`](@ref), @@ -159,7 +159,7 @@ function inverse_retract!(::Sphere, v, x, y, ::ProjectionInverseRetraction) return (v .= y ./ dot(x, y) .- x) end -@doc doc""" +@doc raw""" log(M::Sphere, x, y) Compute the logarithmic map on the [`Sphere`](@ref) `M`, i.e. the tangent vector, whose @@ -194,7 +194,7 @@ function log!(S::Sphere, v, x, y) return project_tangent!(S, v, x, v) end -@doc doc""" +@doc raw""" manifold_dimension(S::Sphere) Return the dimension of the manifold $𝕊^n$, i.e. $n$. @@ -219,7 +219,7 @@ function mean!(S::Sphere, y, x::AbstractVector, w::AbstractVector; kwargs...) return mean!(S, y, x, w, GeodesicInterpolationWithinRadius(π / 2); kwargs...) end -@doc doc""" +@doc raw""" norm(M::Sphere, x, v) Compute the length of the tangent vector `v` from the tangent space at `x` on the @@ -241,7 +241,7 @@ function normal_tvector_distribution(S::Sphere, x, σ) return ProjectedFVectorDistribution(TangentBundleFibers(S), x, d, project_vector!, x) end -@doc doc""" +@doc raw""" project_point(M::Sphere, x) Project the point `x` from the embedding onto the [`Sphere`](@ref) `M`. @@ -254,7 +254,7 @@ project_point(::Sphere, ::Any...) project_point!(S::Sphere, x) = (x ./= norm(x)) -@doc doc""" +@doc raw""" project_tangent(M::Sphere, x, v) Project the point `v` onto the tangent space at `x` on the [`Sphere`](@ref) `M`. @@ -267,7 +267,7 @@ project_tangent(::Sphere, ::Any...) project_tangent!(S::Sphere, w, x, v) = (w .= v .- dot(x, v) .* x) -@doc doc""" +@doc raw""" get_coordinates(M::Sphere, x, v, B::ArbitraryOrthonormalBasis) Represent the tangent vector `v` at point `x` from a sphere `M` in @@ -283,7 +283,7 @@ function get_coordinates(M::Sphere{N}, x, v, B::ArbitraryOrthonormalBasis) where 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,7 +291,7 @@ for the `n`-dimensional [`Sphere`](@ref) it is vectors of size `(n+1,)`. """ @generated representation_size(::Sphere{N}) where {N} = (N + 1,) -@doc doc""" +@doc raw""" retract(M::Sphere, x, y, ::ProjectionRetraction) Compute the retraction that is based on projection, i.e. @@ -320,7 +320,7 @@ function uniform_distribution(S::Sphere, x) return ProjectedPointDistribution(S, d, project_point!, x) end -@doc doc""" +@doc raw""" vector_transport_to(M::Sphere, x, v, y, ::ParallelTransport) Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by @@ -343,7 +343,7 @@ function vector_transport_to!(M::Sphere, vto, x, v, y, ::ParallelTransport) return vto end -@doc doc""" +@doc raw""" zero_tangent_vector(M::Sphere, x) Return the zero tangent vector from the tangent space at `x` on the [`Sphere`](@ref) `M`, diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index fb9b5e98ae..a53c682834 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" Stiefel{n,k,T} <: Manifold The Stiefel manifold consists of all $n \times k$, $n\geq k$ orthonormal matrices, i.e. @@ -36,7 +36,7 @@ struct Stiefel{n,k,F} <: Manifold end Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() -@doc doc""" +@doc raw""" check_manifold_point(M::Stiefel, x; kwargs...) Check whether `x` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, @@ -72,7 +72,7 @@ function check_manifold_point(M::Stiefel{n,k,T}, x; kwargs...) where {n,k,T} end end -@doc doc""" +@doc raw""" check_tangent_vector(M::Stiefel, x, v; kwargs...) Check whether `v` is a valid tangent vector at `x` on the [`Stiefel`](@ref) @@ -109,7 +109,7 @@ function check_tangent_vector(M::Stiefel{n,k,T}, x, v; kwargs...) where {n,k,T} end end -@doc doc""" +@doc raw""" exp(M, x, v) Compute the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` @@ -140,7 +140,7 @@ function exp!(M::Stiefel{n,k}, y, x, v) where {n,k} ) end -@doc doc""" +@doc raw""" inner(M::Stiefel, x, v, w) Compute the inner product for two tangent vectors `v`, `w` from the @@ -154,7 +154,7 @@ space. For the complex-valued case this is the Hermitian metric, to be precise. """ inner(::Stiefel, x, v, w) = dot(v, w) -@doc doc""" +@doc raw""" inverse_retract(M::Stiefel, x, y, ::PolarInverseRetraction) Compute the inverse retraction based on a singular value decomposition @@ -214,7 +214,7 @@ function isapprox(M::Stiefel, x, v, w; kwargs...) end isapprox(M::Stiefel, x, y; kwargs...) = isapprox(norm(x - y), 0; kwargs...) -@doc doc""" +@doc raw""" manifold_dimension(M::Stiefel) Return the dimension of the [`Stiefel`](@ref) manifold `M`=$\operatorname{St}(n,k,𝔽)$. @@ -230,7 +230,7 @@ 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""" +@doc raw""" project_tangent(M, x, v) Project `v` onto the tangent space of `x` to the [`Stiefel`](@ref) manifold `M`. @@ -247,7 +247,7 @@ project_tangent(::Stiefel, ::Any...) project_tangent!(::Stiefel, w, x, v) = copyto!(w, v - x * Symmetric(x' * v)) -@doc doc""" +@doc raw""" retract(M, x, v, ::PolarRetraction) Compute the SVD-based retraction [`PolarRetraction`](@ref) on the @@ -286,7 +286,7 @@ function retract!(::Stiefel, y, x, v, ::QRRetraction) return copyto!(y, 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)$, @@ -294,7 +294,7 @@ i.e. `(n,k)`, which is the matrix dimensions. """ @generated representation_size(::Stiefel{n,k}) where {n,k} = (n, k) -@doc doc""" +@doc raw""" zero_tangent_vector(M::Stiefel, x) Returns the zero tangent vector from the tangent space at `x` on the [`Stiefel`](@ref) diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 68ebf0ea6b..30f19e65a7 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" SymmetricMatrices{n,F} <: Manifold The [`Manifold`](@ref) $ \operatorname{Sym} (n)$ consisting of the real- or complex-valued @@ -23,7 +23,7 @@ struct SymmetricMatrices{n,F} <: Manifold end SymmetricMatrices(n::Int, F::AbstractNumbers = ℝ) = SymmetricMatrices{n,F}() -@doc doc""" +@doc raw""" check_manifold_point(M::SymmetricMatrices{n,F}, x; kwargs...) Check whether `x` is a valid manifold point on the [`SymmetricMatrices`](@ref) `M`, i.e. @@ -99,7 +99,7 @@ function check_tangent_vector(M::SymmetricMatrices{n,F}, x, v; kwargs...) where return nothing end -@doc doc""" +@doc raw""" distance(M::SymmetricMatrices, x, y) Compute distance using the inherited metric, i.e. taking the Frobenius-norm of the @@ -107,7 +107,7 @@ difference. """ distance(M::SymmetricMatrices, x, y) = norm(x - y) -@doc doc""" +@doc raw""" exp(M::SymmetricMatrices, x, v) Compute the exponential map eminating from `x` in tangent direction `v` on the @@ -121,7 +121,7 @@ exp(::SymmetricMatrices, ::Any...) exp!(M::SymmetricMatrices, y, x, v) = (y .= x .+ v) -@doc doc""" +@doc raw""" flat(M::SymmetricMatrices, x, w::FVector{TangentSpaceType}) Compute the [`flat`](@ref flat(M::Manifold, x, w::FVector)) isomorphism of the @@ -211,7 +211,7 @@ function get_vector( return vout end -@doc doc""" +@doc raw""" inner(M::SymmetricMatrices, x, w, v) Compute the inner product of the two tangent vectors `w`, `v` from the tangent @@ -224,7 +224,7 @@ where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transpo """ @inline inner(M::SymmetricMatrices, x, w, v) = dot(w, v) -@doc doc""" +@doc raw""" log(M::SymmetricMatrices, x, y) Compute the logarithmic map from `x` to `y` on the [`SymmetricMatrices`](@ref) `M`, which reads @@ -237,7 +237,7 @@ log(::SymmetricMatrices, ::Any...) log!(M::SymmetricMatrices, v, x, y) = (v .= y .- x) -@doc doc""" +@doc raw""" manifold_dimension(M::SymmetricMatrices{n,𝔽}) Return the dimension of the [`SymmetricMatrices`](@ref) matrix `M` over the number system @@ -253,7 +253,7 @@ function manifold_dimension(::SymmetricMatrices{N,𝔽}) where {N,𝔽} return div(N * (N + 1), 2) * real_dimension(𝔽) end -@doc doc""" +@doc raw""" norm(M::SymmetricMatrices, x, v) Compute the norm of the tangent vector `v` from the tangent space at `x` on the @@ -265,7 +265,7 @@ Compute the norm of the tangent vector `v` from the tangent space at `x` on the """ norm(M::SymmetricMatrices, x, v) = norm(v) -@doc doc""" +@doc raw""" project_point(M::SymmetricMatrices,x) Projects `x` from the embedding onto the [`SymmetricMatrices`](@ref) `M`, i.e. @@ -280,7 +280,7 @@ project_point(::SymmetricMatrices, ::Any...) project_point!(M::SymmetricMatrices, x) = (x .= (x + transpose(x)) ./ 2) -@doc doc""" +@doc raw""" project_tangent(M::SymmetricMatrices, x, v) Project the matrix `v` onto the tangent space at `x` on the [`SymmetricMatrices`](@ref) `M`, @@ -295,7 +295,7 @@ project_tangent(::SymmetricMatrices, ::Any...) project_tangent!(M::SymmetricMatrices, w, x, v) = (w .= (v .+ transpose(v)) ./ 2) -@doc doc""" +@doc raw""" representation_size(M::SymmetricMatrices) Returns the size points on the [`SymmetricMatrices`](@ref) `M` are represented as, i.e. @@ -303,7 +303,7 @@ for the $n\times n$ it's `(n,n)`. """ @generated representation_size(::SymmetricMatrices{N}) where {N} = (N, N) -@doc doc""" +@doc raw""" sharp(M::SymmetricMatrices, x, w::FVector{CotangentSpaceType}) Compute the [`sharp`](@ref sharp(M::Manifold, x, w::FVector)) isomorphism of the @@ -315,7 +315,7 @@ sharp(::SymmetricMatrices, ::Any...) sharp!(M::SymmetricMatrices, v::TFVector, x, w::CoTFVector) = copyto!(v, w) -@doc doc""" +@doc raw""" vector_transport_to(M::SymmetricMatrices, x, v, y, ::ParallelTransport) Compute the parallel @@ -333,7 +333,7 @@ function vector_transport_to!(M::SymmetricMatrices, vto, x, v, y, ::ParallelTran return copyto!(vto, v) end -@doc doc""" +@doc raw""" zero_tangent_vector(M, x) Return the zero tangent vector for the tangent space at `x` on the diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index ad97597cb5..993c8f4ad5 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -1,4 +1,4 @@ -@doc doc""" +@doc raw""" SymmetricPositiveDefinite{N} <: Manifold The manifold of symmetric positive definite matrices, i.e. @@ -21,7 +21,7 @@ struct SymmetricPositiveDefinite{N} <: Manifold end SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}() -@doc doc""" +@doc raw""" check_manifold_point(M::SymmetricPositiveDefinite, x; kwargs...) checks, whether `x` is a valid point on the [`SymmetricPositiveDefinite`](@ref) `M`, i.e. is a matrix @@ -76,7 +76,7 @@ function check_tangent_vector(M::SymmetricPositiveDefinite{N}, x, v; kwargs...) return nothing end -@doc doc""" +@doc raw""" injectivity_radius(M::SymmetricPositiveDefinite[, x]) injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}[, x]) injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}[, x]) @@ -87,7 +87,7 @@ Since `M` is a Hadamard manifold with respect to the [`LinearAffineMetric`](@ref """ injectivity_radius(M::SymmetricPositiveDefinite{N}, args...) where {N} = Inf -@doc doc""" +@doc raw""" manifold_dimension(M::SymmetricPositiveDefinite) returns the dimension of @@ -124,7 +124,7 @@ function mean!( return mean!(M, y, x, w, GeodesicInterpolation(); kwargs...) end -@doc doc""" +@doc raw""" representation_size(M::SymmetricPositiveDefinite) Return the size of an array representing an element on the @@ -133,7 +133,7 @@ symmetric positive definite matrix on $ℳ = 𝒫(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) -@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 fc4deb728e..f1b5870418 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,7 +8,7 @@ struct LinearAffineMetric <: RiemannianMetric end is_default_metric(::SymmetricPositiveDefinite, ::LinearAffineMetric) = Val(true) -@doc doc""" +@doc raw""" distance(M::SymmetricPositiveDefinite, x, y) distance(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}) @@ -27,7 +27,7 @@ function distance(M::SymmetricPositiveDefinite{N}, x, y) where {N} return any(s .<= eps()) ? 0 : sqrt(sum(abs.(log.(s)) .^ 2)) end -@doc doc""" +@doc raw""" exp(M::SymmetricPositiveDefinite, x, v) exp(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, v) @@ -59,7 +59,7 @@ function exp!(M::SymmetricPositiveDefinite{N}, y, x, v) where {N} return copyto!(y, xue * Se * transpose(xue)) end -@doc doc""" +@doc raw""" [Ξ,κ] = get_basis(M::SymmetricPositiveDefinite, x, B::DiagonalizingOrthonormalBasis) [Ξ,κ] = get_basis(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, B::DiagonalizingOrthonormalBasis) @@ -150,7 +150,7 @@ function get_vector( return get_vector(base_manifold(M), x, v, B) end -@doc doc""" +@doc raw""" inner(M::SymmetricPositiveDefinite, x, v, w) inner(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, w) @@ -167,7 +167,7 @@ function inner(M::SymmetricPositiveDefinite, x, v, w) return tr((F \ Symmetric(v)) * (F \ Symmetric(w))) end -@doc doc""" +@doc raw""" log(M::SymmetricPositiveDefinite, x, y) log(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, y) @@ -197,7 +197,7 @@ function log!(M::SymmetricPositiveDefinite{N}, v, x, y) where {N} return mul!(v, xue, Se * transpose(xue)) end -@doc doc""" +@doc raw""" vector_transport_to(M::SymmetricPositiveDefinite, x, v, y, ::ParallelTransport) vector_transport_to(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, y, ::ParallelTransport) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 691881e719..06471b3751 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 @@ -22,7 +22,7 @@ function spd_to_cholesky(x, l, v) return (l, l * (LowerTriangular(w) - Diagonal(w) / 2)) end -@doc doc""" +@doc raw""" distance(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) Compute the distance on the manifold of [`SymmetricPositiveDefinite`](@ref) @@ -47,7 +47,7 @@ function distance( return distance(CholeskySpace{N}(), cholesky(x).L, cholesky(y).L) end -@doc doc""" +@doc raw""" exp(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v) Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with @@ -75,7 +75,7 @@ function exp!( return copyto!(y, z * z') end -@doc doc""" +@doc raw""" inner(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v, w) Compute the inner product of two matrices `v`, `w` in the tangent space of `x` @@ -102,7 +102,7 @@ function inner( return inner(CholeskySpace{N}(), l, vl, wl) end -@doc doc""" +@doc raw""" log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) Compute the logarithmic map on [`SymmetricPositiveDefinite`](@ref) `M` with @@ -128,7 +128,7 @@ function log!( return tangent_cholesky_to_tangent_spd!(l, v) end -@doc doc""" +@doc raw""" vector_transport_to( M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index cc51bd38b0..6d551738b3 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,7 +6,7 @@ into the Lie Algebra, i.e. performing a matrix logarithm beforehand. """ struct LogEuclideanMetric <: RiemannianMetric end -@doc doc""" +@doc raw""" distance(M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, x, y) Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between diff --git a/src/manifolds/Torus.jl b/src/manifolds/Torus.jl index 70a1286cc4..e988f11cfa 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} @@ -12,36 +12,43 @@ end Torus(n::Int) = Torus{n}(Circle()) -@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 6392c90866..ce953e30bb 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -175,7 +175,7 @@ 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(B::VectorBundleFibers, x, v, w) = norm(B, x, v - w) -@doc doc""" +@doc raw""" distance(B::VectorBundle, x, y) Distance between points $x$ and $y$ from the @@ -209,7 +209,7 @@ end number_eltype(::Type{FVector{TType,TData}}) where {TType<:VectorSpaceType,TData} = number_eltype(TData) number_eltype(v::FVector) = number_eltype(v.data) -@doc doc""" +@doc raw""" exp(B::VectorBundle, x, v) Exponential map of tangent vector $v$ at point $x$ from @@ -242,7 +242,7 @@ function exp!(B::VectorBundle, y, x, v) return y end -@doc doc""" +@doc raw""" flat(M::Manifold, x, w::FVector) Compute the flat isomorphism (one of the musical isomorphisms) of tangent vector `w` @@ -365,7 +365,7 @@ function inner(B::VectorBundleFibers{<:CotangentSpaceType}, x, v, w) sharp(B.M, x, FVector(CotangentSpace, w)).data, ) end -@doc doc""" +@doc raw""" inner(B::VectorBundle, x, v, w) Inner product of tangent vectors `v` and `w` at point `x` from the @@ -403,7 +403,7 @@ function isapprox(B::VectorBundle, x, v, w; kwargs...) return isapprox(B.M, ξvM, ξwM; kwargs...) && isapprox(B.M, px, ξvF, ξwF; kwargs...) end -@doc doc""" +@doc raw""" log(B::VectorBundle, x, y) Logarithmic map of the point $y$ at point $x$ from @@ -446,7 +446,7 @@ at point `x` from manifold `B.M`. norm(B::VectorBundleFibers, x, v) = sqrt(inner(B, x, v, v)) norm(B::VectorBundleFibers{<:TangentSpaceType}, x, v) = norm(B.M, x, v) -@doc doc""" +@doc raw""" project_point(B::VectorBundle, x) Project the point $x$ from the ambient space of the vector bundle `B` @@ -470,7 +470,7 @@ function project_point!(B::VectorBundle, x) return x end -@doc doc""" +@doc raw""" project_tangent(B::VectorBundle, x, v) Project the element $v$ of the ambient space of the tangent space $T_x B$ @@ -525,7 +525,7 @@ function representation_size(B::VectorBundle) return (len_manifold + len_vs,) end -@doc doc""" +@doc raw""" sharp(M::Manifold, x, w::FVector) Compute the sharp isomorphism (one of the musical isomorphisms) of vector `w` @@ -633,7 +633,7 @@ function zero_vector!(B::VectorBundleFibers{<:TangentSpaceType}, v, x) return zero_tangent_vector!(B.M, v, x) end -@doc doc""" +@doc raw""" zero_tangent_vector(B::VectorBundle, x) Zero tangent vector at point $x$ from the vector bundle `B` 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 174283bdf0..8b55395275 100644 --- a/src/orthonormal_bases.jl +++ b/src/orthonormal_bases.jl @@ -70,7 +70,7 @@ function ProjectedOrthonormalBasis(method::Symbol, F::AbstractNumbers = ℝ) return ProjectedOrthonormalBasis{method,F}() end -@doc doc""" +@doc raw""" DiagonalizingOrthonormalBasis(v, F::AbstractNumbers = ℝ) An orthonormal basis `Ξ` as a vector of tangent vectors (of length determined by @@ -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 diff --git a/src/product_representations.jl b/src/product_representations.jl index 3741c37bd0..75a1b902ef 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -157,7 +157,7 @@ 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` @@ -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/statistics.jl b/src/statistics.jl index f68c903d7e..5eb25d429e 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 @@ -112,7 +112,7 @@ struct GeodesicInterpolationWithinRadius{T} <: AbstractEstimationMethod end end -@doc doc""" +@doc raw""" mean(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) Compute the (optionally weighted) Riemannian center of mass also known as @@ -189,7 +189,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, @@ -380,7 +380,7 @@ function mean!( return y 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 @@ -456,7 +456,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, @@ -518,7 +518,7 @@ function median!( return y 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...) @@ -553,7 +553,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...) @@ -570,7 +570,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 @@ -613,7 +613,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 @@ -725,7 +725,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..1c389b65ef 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. From 4040b41fb74e732b46c7cdb635623a2d87ed78a6 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 10:02:12 +0100 Subject: [PATCH 25/74] update Euclidean docstrings. --- docs/src/notation.md | 4 +- src/manifolds/Euclidean.jl | 162 ++++++++++++++++++------------------- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index ec2085fa8d..0f0f01a94a 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -12,7 +12,9 @@ as long as that renders still in $\TeX$ within this documentation. |:--:|:-------------- |:--:|:--- | | $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$ | | -| $F$ | A fiber | | +| $m$ | dimension (of a manifold) | $n_1,n_2,\ldots,m$| | +| $F$ | A fiber | | | +| $k$ | indices | $i,j$ | | | $\langle\cdot,\cdot\rangle$ | inner product (in $T_p \mathcal M$) | $\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)$ | | $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | | $\mathcal P_{q\gets p}X$ | parallel Transport | diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 6f81c6a163..9480a22310 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -50,129 +50,129 @@ det_local_metric(M::MetricManifold{<:Manifold,EuclideanMetric}, x) = one(eltype( """ distance(M::Euclidean, x, y) -Compute the Euclidean distance between two points on the [`Euclidean`](@ref) +Computes 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 raw""" - exp(M::Euclidean, x, v) + 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 +Computes the exponential map on the [`Euclidean`](@ref) manifold `M` from `x` in direction +`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 +Transforms 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 raw""" injectivity_radius(M::Euclidean) -Return the injectivity radius on the [`Euclidean`](@ref) `M`, which is $\infty$. +Returns the injectivity radius on the [`Euclidean`](@ref) `M`, which is $\infty$. """ injectivity_radius(::Euclidean) = Inf @doc raw""" - inner(M::Euclidean, x, v, w) + inner(M::Euclidean, p, X, Y) -Compute the inner product on the [`Euclidean`](@ref) `M`, which is just +Computes 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 … \times n_i$, i.e. ````math -g_x(v,w) = \sum_{k ∈ 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 ∈ ℕ^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 raw""" - log(M::Euclidean, x, y) + log(M::Euclidean, p, q) -Compute the logarithmic map on the [`Euclidean`](@ref) `M` from `x` to `y`, +Computes 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 raw""" - norm(M::Euclidean, x, v) + norm(M::Euclidean, p, X) -Compute the norm of a tangent vector `v` at `x` on the [`Euclidean`](@ref) +Computes 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(p) """ - 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 raw""" - project_point(M::Euclidean, x) + project_point(M::Euclidean, p) -Project an arbitrary point `x` onto the [`Euclidean`](@ref) `M`, which +Projects an arbitrary point `p` onto the [`Euclidean`](@ref) `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 +Projects 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)) @@ -295,44 +295,44 @@ end """ representation_size(M::Euclidean) -Return the array dimensions required to represent an element on the +Returns the array dimensions required to represent an element on the [`Euclidean`](@ref) `M`, i.e. the vector of all array dimensions. """ @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`. +Transforms 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) From 0a76a380ff4a395d7e5f2d48e05a66f2530aa646 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 10:04:32 +0100 Subject: [PATCH 26/74] =?UTF-8?q?Replace=20\infty=20with=20=E2=88=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifolds/Euclidean.jl | 2 +- src/manifolds/Hyperbolic.jl | 2 +- src/manifolds/SymmetricPositiveDefinite.jl | 2 +- src/statistics.jl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 9480a22310..aa491eaf75 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -124,7 +124,7 @@ hat!(::Euclidean{N,ℝ}, v, p, vⁱ) where {N} = copyto!(v, vⁱ) @doc raw""" injectivity_radius(M::Euclidean) -Returns the injectivity radius on the [`Euclidean`](@ref) `M`, which is $\infty$. +Returns the injectivity radius on the [`Euclidean`](@ref) `M`, which is $∞$. """ injectivity_radius(::Euclidean) = Inf diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index f226422284..ee3ef10fdd 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -136,7 +136,7 @@ flat!(M::Hyperbolic, v::CoTFVector, x, w::TFVector) = copyto!(v, w) @doc raw""" injectivity_radius(M::Hyperbolic[, x]) -Return the injectivity radius on the [`Hyperbolic`](@ref), which is always $\infty$. +Return the injectivity radius on the [`Hyperbolic`](@ref), which is always $∞$. """ injectivity_radius(H::Hyperbolic, args...) = Inf diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 6d5341702e..ab7aa7e017 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -83,7 +83,7 @@ end 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 diff --git a/src/statistics.jl b/src/statistics.jl index 27f6eefdd3..a92ef6b3cf 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -43,7 +43,7 @@ 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: From 59ba59bc4b351eded31ae1898f34a00db4f18cdc Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 11:00:40 +0100 Subject: [PATCH 27/74] Starts reworking FixedRank. --- src/manifolds/FixedRankMatrices.jl | 53 ++++++++++++++++-------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index f3e18f89f3..d68b170c77 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,39 +1,40 @@ @doc raw""" FixedRankMatrices{m,n,k,T} <: Manifold -The manifold of $m \times n$ real-valued (complex-valued) matrices of fixed rank $k$, i.e. +The manifold of $m \times n$ real-valued or complex-valued matrices of fixed rank $k$, i.e. ````math -ℳ = \{ x ∈ ℝ^{m \times n} : \operatorname{rank}(x) = k \}. +\{ p ∈ 𝔽^{m \times 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 ∈ ℳ$ can be stored using orthonormal matrices -$U ∈ ℝ^{m \times k}$, $V ∈ ℝ^{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 ∈ ℳ$ can be stored using unitary matrices $U ∈ 𝔽^{m \times k}$, $V ∈ 𝔽^{n \times 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 ℳ$ at a point $x ∈ ℳ$ with $x=USV^\mathrm{T}$ +The tangent space $T_p ℳ$ at a point $p ∈ ℳ$ with $p=USV^\mathrm{H}$ is given by ````math -T_xℳ = \bigl\{ UMV^\mathrm{T} + U_xV^\mathrm{T} + UV_x^\mathrm{T} : - M ∈ ℝ^{k \times k}, - U_x ∈ ℝ^{m \times k}, - V_x ∈ ℝ^{n \times k} +T_pℳ = \bigl\{ UMV^\mathrm{T} + U_pV^\mathrm{H} + UV_p^\mathrm{H} : + M ∈ 𝔽^{k \times k}, + U_p ∈ 𝔽^{m \times k}, + V_p ∈ 𝔽^{n \times 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. The (default) metric of this manifold is obtained by restricting the metric -on $ℝ^{m \times n}$ to the tangent bundle. This implementation follows[^Vandereycken2013]. +on $ℝ^{m \times n}$ to the tangent bundle[^Vandereycken2013]. # Constructor 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, @@ -50,9 +51,9 @@ end 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\}$) @@ -80,13 +81,14 @@ SVDMPoint(U, S, Vt, k::Int) = SVDMPoint(U[:, 1:k], S[1:k], Vt[1:k, :]) @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 ∈ ℝ^{k \times k}$ + inner dimensions $k$, i.e. in $UMV^\mathrm{H}$, where $M$ is a $k\times k$ matrix. """ struct UMVTVector{TU<:AbstractMatrix,TM<:AbstractMatrix,TVt<:AbstractMatrix} <: TVector U::TU @@ -108,12 +110,13 @@ UMVTVector(U, M, Vt, k::Int) = UMVTVector(U[:, 1:k], M[1:k, 1:k], Vt[1:k, :]) ==(v::UMVTVector, w::UMVTVector) = (v.U == w.U) && (v.M == w.M) && (v.Vt == w.Vt) @doc raw""" - check_manifold_point(M::FixedRankMatrices{m,n,k},x; kwargs...) + 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...) From 2f8a1fa2663bff214570507ec89a30a1727d4dd4 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 16:22:10 +0100 Subject: [PATCH 28/74] finishes documentation and variables until end of Hyperbolic manifold. --- docs/src/notation.md | 2 +- src/manifolds/CholeskySpace.jl | 4 +- src/manifolds/Euclidean.jl | 26 +-- src/manifolds/FixedRankMatrices.jl | 232 ++++++++++++------------- src/manifolds/GraphManifold.jl | 109 ++++++------ src/manifolds/Grassmann.jl | 263 +++++++++++++++-------------- src/manifolds/Hyperbolic.jl | 161 +++++++++--------- src/manifolds/Stiefel.jl | 81 +++++---- 8 files changed, 440 insertions(+), 438 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 0f0f01a94a..953e23122b 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -12,7 +12,7 @@ as long as that renders still in $\TeX$ within this documentation. |:--:|:-------------- |:--:|:--- | | $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$ | | -| $m$ | dimension (of a manifold) | $n_1,n_2,\ldots,m$| | +| $n$ | dimension (of a manifold) | $n_1,n_2,\ldots,m$| | | $F$ | A fiber | | | | $k$ | indices | $i,j$ | | | $\langle\cdot,\cdot\rangle$ | inner product (in $T_p \mathcal M$) | $\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)$ | diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index f023b112b8..15fc25636a 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -52,7 +52,7 @@ end """ check_tangent_vector(M::CholeskySpace, p, X; kwargs... ) -Checks whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. +Check whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. atfer [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `x` and a symmetric matrix. The tolerance for the tests can be set using the `kwargs...`. @@ -130,7 +130,7 @@ lower triangular matric with positive diagonal `p` and the two tangent vectors 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, p, X, Y) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index aa491eaf75..c2416e5c5d 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -45,12 +45,12 @@ 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) -Computes the Euclidean distance between two points on the [`Euclidean`](@ref) +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. """ @@ -59,7 +59,7 @@ distance(::Euclidean, p, q) = norm(p .- q) @doc raw""" exp(M::Euclidean, p, X) -Computes the exponential map on the [`Euclidean`](@ref) manifold `M` from `x` in direction +Compute the exponential map on the [`Euclidean`](@ref) manifold `M` from `x` in direction `X`, which in this case is just ````math \exp_p X = p + X. @@ -72,7 +72,7 @@ exp!(M::Euclidean, q, p, X) = (q .= p .+ X) """ flat(M::Euclidean, p, X) -Transforms a tangent vector `X` 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 `p` on `M`. """ @@ -124,14 +124,14 @@ hat!(::Euclidean{N,ℝ}, v, p, vⁱ) where {N} = copyto!(v, vⁱ) @doc raw""" injectivity_radius(M::Euclidean) -Returns the injectivity radius on the [`Euclidean`](@ref) `M`, which is $∞$. +Return the injectivity radius on the [`Euclidean`](@ref) `M`, which is $∞$. """ injectivity_radius(::Euclidean) = Inf @doc raw""" inner(M::Euclidean, p, X, Y) -Computes the inner product on the [`Euclidean`](@ref) `M`, which is just +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 … \times n_i$, i.e. @@ -162,7 +162,7 @@ end @doc raw""" log(M::Euclidean, p, q) -Computes the logarithmic map on the [`Euclidean`](@ref) `M` from `p` to `q`, +Compute the logarithmic map on the [`Euclidean`](@ref) `M` from `p` to `q`, which in this case is just ````math \log_p q = q-p. @@ -240,12 +240,12 @@ end @doc raw""" norm(M::Euclidean, p, X) -Computes the norm of a tangent vector `X` at `p` 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 `X`. """ norm(::Euclidean, p, X) = norm(X) -norm(::MetricManifold{<:Manifold,EuclideanMetric}, p, X) = norm(p) +norm(::MetricManifold{<:Manifold,EuclideanMetric}, p, X) = norm(X) """ normal_tvector_distribution(M::Euclidean, p, σ) @@ -261,7 +261,7 @@ end @doc raw""" project_point(M::Euclidean, p) -Projects an arbitrary point `p` onto the [`Euclidean`](@ref) `M`, which +Project an arbitrary point `p` onto the [`Euclidean`](@ref) `M`, which is of course just the identity map. """ project_point(::Euclidean, ::Any...) @@ -271,7 +271,7 @@ project_point!(M::Euclidean, p) = p """ project_tangent(M::Euclidean, p, X) -Projects an arbitrary vector `X` into the tangent space of a point `p` 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`. """ @@ -295,7 +295,7 @@ end """ representation_size(M::Euclidean) -Returns the array dimensions required to represent an element on the +Return the array dimensions required to represent an element on the [`Euclidean`](@ref) `M`, i.e. the vector of all array dimensions. """ @generated representation_size(::Euclidean{N}) where {N} = size_to_tuple(N) @@ -303,7 +303,7 @@ Returns the array dimensions required to represent an element on the """ sharp(M::Euclidean, p, ξ) -Transforms the cotangent vector `ξ` at `p` on the [`Euclidean`](@ref) `M` to a tangent vector `X`. +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. """ diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index d68b170c77..e7b40ad2a2 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -32,7 +32,7 @@ The (default) metric of this manifold is obtained by restricting the metric on $ℝ^{m \times 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`. @@ -118,11 +118,11 @@ rank `k`. For the [`SVDMPoint`](@ref) the internal representation also has to ha 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)).")) @@ -160,66 +160,63 @@ function check_manifold_point( end @doc raw""" - check_tangent_vector(M:FixedRankMatrices{m,n,k}, x, v) + 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 raw""" - inner(M::FixedRankMatrices, x::SVDMPoint, v::UMVTVector, w::UMVTVector) + 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 @@ -241,43 +238,36 @@ function manifold_dimension(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} end @doc raw""" - project_tangent(M, x, A) - project_tangent(M, x, v) - -Project the matrix $A ∈ ℝ^{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]. + 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 raw""" @@ -289,13 +279,13 @@ the size of matrices on this manifold $(m,n)$. @generated representation_size(::FixedRankMatrices{m,n}) where {m,n} = (m, n) @doc raw""" - retract(M, x, v, ::PolarRetraction) + 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$, +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 \times k$ with the $k$ largest singular values and $U$ and $V$ are shortened accordingly. """ @@ -303,94 +293,94 @@ 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 raw""" @@ -400,22 +390,22 @@ Return a [`UMVTVector`](@ref) representing the zero tangent vector in the tangen `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 3fff9d106a..d36a8674da 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -47,77 +47,76 @@ const EdgeGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,EdgeManifold} const VertexGraphManifold = GraphManifold{<:AbstractGraph,<:Manifold,VertexManifold} @doc raw""" - check_manifold_point(M::GraphManifold, x) + 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 raw""" - check_tangent_vector(M::GraphManifold, x, v) + 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 einty 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) @@ -134,52 +133,52 @@ 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 raw""" diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 464e3118cb..86d6f4de91 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -1,22 +1,20 @@ @doc raw""" Grassmann{n,k,F} <: Manifold -The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned -by $k$ linear independent vectors $𝔽^n$, where -$𝔽 ∈ \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. -This yields all $k$-dimensional subspaces of $ℝ^n$ for the real-valued case and all -$2k$-dimensional subspaces of $ℂ^n$ for the second. +The Grassmann manifold $\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) :eqq \bigl\{ \operatorname{span}(x) -: x ∈ 𝔽^{n \times k}, \bar{x}^\mathrm{T}x = I_k\}, +\operatorname{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n \times k}, p^\mathrm{H}p = I_k\}, ```` -where ${\bar\cdot}^{\mathrm{T}}$ denotes the complex conjugate transpose and +where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian 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 +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 @@ -32,16 +30,16 @@ The tangent space at a point (subspace) $x$ is given by ````math T_x\mathrm{Gr}(n,k) = \bigl\{ -v ∈ 𝔽^{n \times k} : -{\bar v}^{\mathrm{T}}x + {\bar x}^{\mathrm{T}}v = 0_{k} \bigr\}, +X ∈ 𝔽^{n \times k} : +X^{\mathrm{H}}p + p^{\mathrm{H}}X = 0_{k} \bigr\}, ```` where $0_{k}$ denotes the $k \times k$ zero matrix. -Note that a point $x ∈ \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). @@ -58,213 +56,225 @@ struct Grassmann{n,k,F} <: Manifold end Grassmann(n::Int, k::Int, F::AbstractNumbers = ℝ) = Grassmann{n,k,F}() @doc raw""" - check_manifold_point(M::Grassmann{n,k,F}, x) + 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 noe 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 neiter real- nor complex-valued matrix, so it does noe 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) is 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 raw""" - check_tangent_vector(M::Grassmann{n,k,F}, x, v) + 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. """ -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 neiter real- nor complex-valued matrix, so it can not bea 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) is 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) is 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 raw""" - distance(M::Grassmann, x, y) + 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\\ +\operatorname{acos}(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 raw""" - exp(M::Grassmann, x, v) + 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) + +Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is $\frac{\pi}{2}$. +""" +injectivity_radius(::Grassmann, ::Any...) = π / 2 @doc raw""" - inner(M::Grassmann, x, v, w) + inner(M::Grassmann, p, X, Y) -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 +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{trace}(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 raw""" - inverse_retract(M::Grassmann, x, y, ::PolarInverseRetraction) + 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 raw""" - inverse_retract(M, x, y, ::QRInverseRetraction) + 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 raw""" - log(M::Grassmann, x, y) + log(M::Grassmann, p, q) Compute the logarithmic map on the [`Grassmann`](@ref) `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 raw""" @@ -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 raw""" - project_tangent(M::Grassmann, x, w) + 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. @@ -329,51 +339,54 @@ 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 raw""" - retract(M::Grassmann, x, v, ::PolarRetraction) + 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 ````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 raw""" - zero_tangent_vector(M::Grassmann, x) + 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 ee3ef10fdd..025f8dc6a3 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -5,17 +5,16 @@ The hyperbolic space $ℍ^n$ represented by $n+1$-Tuples, i.e. in by vectors in $ℝ^{n+1}$ using the Minkowsi metric, i.e. ```math -ℍ^n = \Bigl\{x ∈ ℝ^{n+1} -\ \Big|\ ⟨x,x⟩_{\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 $⟨\cdot,\cdot⟩_{\mathrm{M}}$ denotes the [`minkowski_dot`](@ref) -is Minkowski inner product. The tangent space $T_x ℍ^n$ is given by +is Minkowski inner product. The tangent space $T_p ℍ^n$ is given by ````math -T_x ℍ^n :eqq \bigl\{ -v ∈ ℝ^{n+1} \ \bigl |\ ⟨x,v⟩_{\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 @@ -52,133 +51,135 @@ 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 raw""" - distance(M::Hyperbolic, x, y) + distance(M::Hyperbolic, p, q) Compute the distance on the [`Hyperbolic`](@ref) `M`, which reads ````math -d_{ℍ^n}(x,y) = \operatorname{acosh}( - ⟨x, y⟩_{\mathrm{M}}), +d_{ℍ^n}(p,q) = \operatorname{acosh}( - ⟨p, q⟩_{\mathrm{M}}), ```` 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 raw""" - exp(M::Hyperbolic, x, v) + exp(M::Hyperbolic, p, X) Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ eminating -from `x` towards `v`, which is optionally scaled by `t`. The formula reads +from `p` towards `X`. The formula reads ````math -\exp_x v = \cosh(\sqrt{⟨v,v⟩_{\mathrm{M}}})x -+ \sinh(\sqrt{⟨v,v⟩_{\mathrm{M}}})\frac{v}{\sqrt{⟨v,v⟩_{\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 $⟨\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 raw""" - injectivity_radius(M::Hyperbolic[, x]) + injectivity_radius(M::Hyperbolic) + injectivity_radius(M::Hyperbolic, p) -Return the injectivity radius on the [`Hyperbolic`](@ref), which is always $∞$. +Return the injectivity radius on the [`Hyperbolic`](@ref), which is $∞$. """ -injectivity_radius(H::Hyperbolic, args...) = Inf +injectivity_radius(H::Hyperbolic, ::Any...) = Inf @doc raw""" - inner(M::Hyperbolic, x, v, w) + inner(M::Hyperbolic, p, X, Y) -Compute the Riemannian inner product for two tangent vectors `v` and `w` -from $T_x ℍ^n$ of the [`Hyperbolic`](@ref) space $ℍ^n$ given by -$⟨w, v⟩_{\mathrm{M}}$ the [`minkowski_dot`](@ref) Minkowski +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 raw""" - log(M::Hyperbolic, x, y) + log(M::Hyperbolic, p, q) Compute the logarithmic map on the [`Hyperbolic`](@ref) space $ℍ^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 ≠ y$ +vector representing the [`geodesic`](@ref) starting from `p` +reaches `q` after time 1. The formula reads for $x ≠ y$ ```math -\log_x y = d_{ℍ^n}(x,y) -\frac{y-⟨x,y⟩_{\mathrm{M}} x}{\lVert y-⟨x,y⟩_{\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 raw""" minkowski_dot(a,b) + Compute the Minkowski inner product of two Vectors `a` and `b` of same length `n+1`, i.e. @@ -211,26 +212,26 @@ 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 raw""" - project_tangent(M::Hyperbolic, x, v) + 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 + ⟨x,v⟩_{\mathrm{M}} x, +Y = X + ⟨p,X⟩_{\mathrm{M}} p, ```` 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 raw""" representation_size(M::Hyperbolic) @@ -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 raw""" - vector_transport_to(M::Hyperbolic, x, v, y, ::ParallelTransport) + 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 $ℍ^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 -𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{ℍ^n}(x,y)} -\bigl(\log_xy + \log_yx \bigr). +𝒫_{q←p}X = X - \frac{⟨\log_p q,X⟩_x}{d^2_{ℍ^n}(p,q)} +\bigl(\log_pq + \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 raw""" - zero_tangent_vector(M::Hyperbolic, x) + 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/Stiefel.jl b/src/manifolds/Stiefel.jl index 35ac50ef46..2764fc40aa 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,20 +1,20 @@ @doc raw""" Stiefel{n,k,T} <: Manifold -The Stiefel manifold consists of all $n \times k$, $n\geq k$ orthonormal matrices, i.e. +The Stiefel manifold consists of all $n \times k$, $n\geq k$ unitary matrices, i.e. ````math -ℳ = \{ x ∈ 𝔽^{n \times k} : x^{\mathrm{H}}x = I_k \}, +\{ p ∈ 𝔽^{n \times k} : p^{\mathrm{H}}p = I_k \}, ```` where $𝔽 ∈ \{ℝ, ℂ\}$, $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_n ∈ ℝ^{n\times n}$ denotes the $k \times k$ identity matrix. -The tangent space at a point $x∈ ℳ$ is given by +The tangent space at a point $p ∈ ℳ$ is given by ````math -T_x ℳ = \{ v ∈ 𝔽^{n\times k} : x^{\mathrm{H}}v + v^{\mathrm{H}}x=0_n\}, +T_p ℳ = \{ X ∈ 𝔽^{n\times k} : p^{\mathrm{H}}X + X^{\mathrm{H}}p=0_n\}, ```` where $0_n$ is the $k\times k$ zero matrix. @@ -37,82 +37,81 @@ struct Stiefel{n,k,F} <: Manifold end Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() @doc raw""" - check_manifold_point(M::Stiefel, x; kwargs...) + 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 (approximatly) 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,T}, p; kwargs...) where {n,k,T} + if (T === ℝ) && !(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 (T === ℂ) && !(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 raw""" - check_tangent_vector(M::Stiefel, x, v; kwargs...) + 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 (approximtly) 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,T}, p, X; kwargs...) where {n,k,T} + mpe = check_manifold_point(M, p) mpe === nothing || return mpe - if (T === ℝ) && !(eltype(v) <: Real) + if (T === ℝ) && !(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 (T === ℂ) && !(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 raw""" - exp(M, x, v) + exp(M, p, X) -Compute the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` +Computes the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` eminating from `x` into tangent direction `v`. $\operatorname{exp}_{x} v = \begin{pmatrix} From fa1139443ed4232d7adce5d7e17eb97a6ecbf812 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 21:36:17 +0100 Subject: [PATCH 29/74] Finishes rework of Metric- and Powermanifold as well as Stiefel --- src/manifolds/MetricManifold.jl | 362 ++++++++++++++++---------------- src/manifolds/PowerManifold.jl | 222 ++++++++++---------- src/manifolds/Stiefel.jl | 156 ++++++++------ 3 files changed, 379 insertions(+), 361 deletions(-) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 7368a92e6b..7e6273b293 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -41,33 +41,33 @@ abstract type LorentzMetric <: Metric end RiemannianMetric <: Metric Abstract type for Riemannian metrics, a family of positive definite inner -products. The positive definite property means that for $v ∈ T_x ℳ$, 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 ℳ$, 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 raw""" - christoffel_symbols_first(M::MetricManifold, x; backend=:default) + 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],$ +$\Gamma_{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{\partial}{\partial 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]) @@ -86,26 +86,26 @@ where $\Gamma_{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 raw""" - christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :default) + 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{\partial}{\partial p^l} \Gamma^{k}_{ij} = \Gamma^{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, @@ -115,33 +115,33 @@ function christoffel_symbols_second_jacobian(M::MetricManifold, x; backend = :de end @doc raw""" - det_local_metric(M::MetricManifold, x) + 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 raw""" - exp(N::MetricManifold{M,G}, x, v) + 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,59 @@ 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) +function exp(M::MMT, p, X, T::AbstractVector{T} where {T}) where {MMT<:MetricManifold} + return exp(M, is_default_metric(M), p, X, T) end function exp( M::MMT, ::Val{true}, - x, - v, + p, + X, T::AbstractVector{T} where {T}, ) where {MMT<:MetricManifold} - return exp(base_manifold(M), x, v, T) + return exp(base_manifold(M), p, X, T) end function exp( M::MMT, ::Val{false}, - x, - v, + p, + X, T::AbstractVector{T} where {T}, ) where {MMT<:MetricManifold} - sol = solve_exp_ode(M, x, v, extrema(T); dense = false, saveat = T) - n = length(x) + sol = solve_exp_ode(M, p, X, extrema(T); dense = false, saveat = T) + n = length(p) 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 raw""" - flat(N::MetricManifold{M,G}, x, w::FVector{TangentSpaceType}) + 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^\flat = G_pX, ```` -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,19 +209,19 @@ 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} @@ -229,12 +229,12 @@ function injectivity_radius(M::MMT, args...) where {MMT<:MetricManifold} end @doc raw""" - inverse_local_metric(M::MetricManifold, x) + 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) @@ -277,57 +277,57 @@ function _convert_with_default(M::MT, T::Type{<:Metric}, ::Val{false}) where {MT end @doc raw""" - inner(N::MetricManifold{M,G}, x, v, w) + 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) = ⟨v, G_x w⟩, +g_p(X, Y) = ⟨X, G_pY⟩, ```` -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.M, p) + return dot(X, ginv * Y) end @doc raw""" - local_metric(M::MetricManifold, x) + 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 raw""" - local_metric_jacobian(M::MetricManifold, x; backend=:default) + 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{\partial}{\partial 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 @@ -335,76 +335,76 @@ log!(M::MMT, w, x, y) where {MMT<:MetricManifold} = log!(M, is_default_metric(M) function log!(M::MMT, ::Val{true}, w, x, y) where {MMT<:MetricManifold} return log!(base_manifold(M), w, x, y) 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}, Y, p, q) where {MMT<:MetricManifold} + error("Logarithmic map not implemented on $(typeof(M)) for points $(typeof(p)) and $(typeof(q)).") end @doc raw""" - log_local_metric_density(M::MetricManifold, x) + 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 $\rho$ 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 raw""" @@ -414,44 +414,44 @@ 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,25 +464,25 @@ 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] @@ -490,16 +490,16 @@ function ricci_tensor(M::MetricManifold, x; kwargs...) end @doc raw""" - riemann_tensor(M::MetricManifold, x) + 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] @@ -507,24 +507,24 @@ function riemann_tensor(M::MetricManifold, x; backend = :default) end @doc raw""" - sharp(N::MetricManifold{M,G}, x, w::FVector{CotangentSpaceType}) + 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, +ξ^\sharp = 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))") @@ -532,8 +532,8 @@ show(io::IO, M::MetricManifold) = print(io, "MetricManifold($(M.manifold), $(M.m @doc raw""" solve_exp_ode( M::MetricManifold, - x, - v, + p, + X, tspan; backend = :default, solver = AutoVern9(Rodas5()), @@ -544,7 +544,7 @@ 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 + \Gamma^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 the Einstein summation convention is assumed. The arguments `tspan` and @@ -562,43 +562,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 2890496c2e..d018365939 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -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 raw""" - distance(M::AbstractPowerManifold, x, y) + 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 raw""" - exp(M::AbstractPowerManifold, x, v) + 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 raw""" - flat(M::AbstractPowerManifold, x, w::FVector{TangentSpaceType}) + 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, so r every entry of `X` (and `p`) sparately """ 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,16 +363,15 @@ 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 @@ -381,18 +379,18 @@ function get_vector(M::AbstractPowerManifold, x, v, B::ArbitraryOrthonormalBasis end @doc raw""" - injectivity_radius(M::AbstractPowerManifold[, x]) + 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 @@ -405,47 +403,47 @@ end injectivity_radius(M::AbstractPowerManifold) = injectivity_radius(M.manifold) @doc raw""" - inverse_retract(M::AbstractPowerManifold, x, y, m::InversePowerRetraction) + 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 raw""" - inner(M::AbstractPowerManifold, x, v, w) + 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,28 +451,28 @@ 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 @@ -482,24 +480,24 @@ function isapprox(M::AbstractPowerManifold, x, v, w; kwargs...) end @doc raw""" - log(M::AbstractPowerManifold, x, y) + 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 @@ -520,18 +518,18 @@ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} end @doc raw""" - norm(M::AbstractPowerManifold, x, v) + 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 @@ -607,18 +605,18 @@ 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 raw""" @@ -630,17 +628,17 @@ This can be done elementwise, so for every entry of `w` (and `x`) sparately """ 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( diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 2764fc40aa..86f06f19cc 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -112,75 +112,84 @@ end exp(M, p, X) Computes the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` -eminating from `x` into tangent direction `v`. +eminating from `p` into 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. """ 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 raw""" - inner(M::Stiefel, x, v, w) + 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{trace}(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 raw""" - inverse_retract(M::Stiefel, x, y, ::PolarInverseRetraction) + 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\times 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]: @@ -188,30 +197,30 @@ 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 raw""" manifold_dimension(M::Stiefel) @@ -220,9 +229,11 @@ Return the dimension of the [`Stiefel`](@ref) manifold `M`=$\operatorname{St}(n, The dimension is given by ````math +\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) @@ -230,59 +241,67 @@ 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 raw""" - project_tangent(M, x, v) + 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}_{ℳ}(x,v) = v - x \operatorname{Sym}(x^{\mathrm{H}}v), +\operatorname{proj}_{ℳ}(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 raw""" - retract(M, x, v, ::PolarRetraction) + 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 + ````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 raw""" @@ -296,11 +315,12 @@ 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 raw""" - zero_tangent_vector(M::Stiefel, x) + 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) From 9873a60d47d14e72245bdb494b7c7ba6e88afc99 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 22:19:39 +0100 Subject: [PATCH 30/74] Refactor SPD notations, especially LogCholesky now has p,q (X,Y) for SPD points (vectors) and x,y (V,W) for Cholesky points (vectors)., which is hopefully better readable. --- src/manifolds/SymmetricPositiveDefinite.jl | 63 ++++---- .../SymmetricPositiveDefiniteLinearAffine.jl | 147 +++++++++--------- .../SymmetricPositiveDefiniteLogCholesky.jl | 136 ++++++++-------- .../SymmetricPositiveDefiniteLogEuclidean.jl | 12 +- 4 files changed, 179 insertions(+), 179 deletions(-) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index ab7aa7e017..5c51c995cc 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -3,13 +3,12 @@ The manifold of symmetric positive definite matrices, i.e. -```math +````math 𝒫(n) = \bigl\{ -x ∈ ℝ^{n\times n} : -ξ^\mathrm{T}xξ > 0 \text{ for all } ξ ∈ ℝ^{n}\backslash\{0\} +p ∈ ℝ^{n\times n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \bigr\} -``` +```` # Constructor @@ -22,64 +21,64 @@ struct SymmetricPositiveDefinite{N} <: Manifold end SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}() @doc raw""" - check_manifold_point(M::SymmetricPositiveDefinite, x; kwargs...) + 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 raw""" - injectivity_radius(M::SymmetricPositiveDefinite[, x]) - injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}[, x]) - injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}[, x]) + 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 @@ -116,12 +115,12 @@ 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 raw""" diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index f1b5870418..ababf00ce7 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -9,72 +9,72 @@ struct LinearAffineMetric <: RiemannianMetric end is_default_metric(::SymmetricPositiveDefinite, ::LinearAffineMetric) = Val(true) @doc raw""" - distance(M::SymmetricPositiveDefinite, x, y) - distance(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}) + 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_{𝒫(n)}(x,y) -= \lVert \operatorname{Log}(x^{-\frac{1}{2}}yx^{-\frac{1}{2}})\rVert_{\mathrm{F}}., +d_{𝒫(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 raw""" - exp(M::SymmetricPositiveDefinite, x, v) - exp(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, v) + 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) + T = Symmetric(xSqrtInv * X * xSqrtInv) eig1 = eigen(T) # numerical stabilization Se = Diagonal(exp.(eig1.values)) Ue = eig1.vectors xue = xSqrt * Ue - return copyto!(y, xue * Se * transpose(xue)) + return copyto!(q, xue * Se * transpose(xue)) end @doc raw""" - [Ξ,κ] = get_basis(M::SymmetricPositiveDefinite, x, B::DiagonalizingOrthonormalBasis) - [Ξ,κ] = get_basis(M::MetricManifold{SymmetricPositiveDefinite{N},LinearAffineMetric}, x, B::DiagonalizingOrthonormalBasis) + [Ξ,κ] = 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`. """ function get_basis( M::SymmetricPositiveDefinite{N}, - x, + p, B::DiagonalizingOrthonormalBasis, ) where {N} - xSqrt = sqrt(x) + xSqrt = sqrt(p) eigv = eigen(B.v) V = eigv.vectors Ξ = [ @@ -89,150 +89,151 @@ 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) + vout = 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 vout[k] = X[i, j] * scale k += 1 end return vout 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),) + vout = allocate_result(M, get_vector, p) + @assert size(X) == (div(N * (N + 1), 2),) @assert size(vout) == (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 vout[i, j] = X[k] * scale + @inbounds vout[j, i] = X[k] * scale k += 1 end return vout 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 raw""" - inner(M::SymmetricPositiveDefinite, x, v, w) - inner(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, w) + 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 raw""" - log(M::SymmetricPositiveDefinite, x, y) - log(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, y) + 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) + T = Symmetric(xSqrtInv * q * xSqrtInv) e2 = eigen(T) Se = Diagonal(log.(max.(e2.values, eps()))) xue = xSqrt * e2.vectors - return mul!(v, xue, Se * transpose(xue)) + return mul!(X, xue, Se * transpose(xue)) end @doc raw""" - vector_transport_to(M::SymmetricPositiveDefinite, x, v, y, ::ParallelTransport) - vector_transport_to(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}, x, v, y, ::ParallelTransport) + 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 -𝒫_{y←x}(v) = x^{\frac{1}{2}} +𝒫_{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) @@ -240,8 +241,8 @@ function vector_transport_to!( 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) + tv = Symmetric(xSqrtInv * X * xSqrtInv) + ty = Symmetric(xSqrtInv * q * xSqrtInv) e2 = eigen(ty) Se = Diagonal(log.(e2.values)) Ue = e2.vectors @@ -251,5 +252,5 @@ function vector_transport_to!( Uf = e3.vectors xue = xSqrt * Uf * Sf * transpose(Uf) vtp = Symmetric(xue * tv * transpose(xue)) - return copyto!(vto, vtp) + return copyto!(Y, vtp) end diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 06471b3751..7ec1f29c10 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -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 raw""" - distance(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) + 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_{𝒫(n)}(x,y) = \sqrt{ - \lVert ⌊ l ⌋ - ⌊ k ⌋ \rVert_{\mathrm{F}}^2 - + \lVert \log(\operatorname{diag}(l)) - \log(\operatorname{diag}(k))\rVert_{\mathrm{F}}^2 }\ \ , +d_{𝒫(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, +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}}$ denotes the Frobenius norm. +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 raw""" - exp(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v) + 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_pX = (\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 raw""" - inner(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, v, w) + 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 raw""" - log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, x, y) + 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) eminating 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 colesky factor of $p$ and $W=\log_xy$ 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 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 - 𝒫_{y←x}(v) = ku^{\mathrm{T}} + uk^{\mathrm{T}}. +𝒫_{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}(), V, x, W, y, ParallelTransport()) + return tangent_cholesky_to_tangent_spd!(y, V) end diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index 6d551738b3..e10cce5ff8 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -7,14 +7,14 @@ into the Lie Algebra, i.e. performing a matrix logarithm beforehand. struct LogEuclideanMetric <: RiemannianMetric end @doc raw""" - distance(M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, x, y) + 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_{𝒫(n)}(x,y) = \lVert \Log x - \Log y \rVert_{\mathrm{F}} + d_{𝒫(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 From 3474ccacadfb10fb42507de22e08c7389fbfe14e Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Thu, 30 Jan 2020 22:40:24 +0100 Subject: [PATCH 31/74] Fixes bugs introduces by renaming Cholsky. --- src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 7ec1f29c10..9aef7ef2d9 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -12,7 +12,7 @@ struct LogCholeskyMetric <: RiemannianMetric end cholesky_to_spd(x, W) = (x * x', W * x' + x * W') -tangent_cholesky_to_tangent_spd!(x, W) = (w .= W * x' + x * W') +tangent_cholesky_to_tangent_spd!(x, W) = (W .= W * x' + x * W') spd_to_cholesky(p, X) = spd_to_cholesky(p, cholesky(p).L, X) @@ -167,6 +167,6 @@ function vector_transport_to!( ) where {N} y = cholesky(q).L (x, W) = spd_to_cholesky(p, X) - vector_transport_to!(CholeskySpace{N}(), V, x, W, y, ParallelTransport()) - return tangent_cholesky_to_tangent_spd!(y, V) + vector_transport_to!(CholeskySpace{N}(), Y, x, W, y, ParallelTransport()) + return tangent_cholesky_to_tangent_spd!(y, Y) end From bebdb2da96cfb36c1fedb025f4542ea781488270 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Fri, 31 Jan 2020 18:49:05 +0100 Subject: [PATCH 32/74] refactor Sphere & Vector Bundle. --- docs/src/manifolds/vector_bundle.md | 2 +- src/manifolds/Sphere.jl | 246 +++++++-------- src/manifolds/VectorBundle.jl | 451 ++++++++++++++-------------- test/runtests.jl | 2 +- test/vector_bundle.jl | 4 +- 5 files changed, 352 insertions(+), 353 deletions(-) diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 4ba7c904e4..9f9c90beb3 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 $ℳ$ (base space). -It is characterized by a continuous function $Π : E → ℳ$, such that for each point $x ∈ ℳ$ the preimage of $x$ by $Π$, $Π^{-1}(\{x\})$, has a structure of a vector space. +It is characterized by a continuous function $Π : E → ℳ$, such that for each point $p ∈ ℳ$ 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/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 5645b4986a..1ff1ac572b 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -30,91 +30,91 @@ 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 raw""" - distance(M::Sphere, x, y) + 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_{𝕊^n}(x,y) = \operatorname{acos}(⟨x, y⟩). +d_{𝕊^n}(p,q) = \operatorname{acos}(⟨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 raw""" - exp(M::Sphere, x, v) + 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` into 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 raw""" - injectivity_radius(M::Sphere[, x]) + injectivity_radius(M::Sphere[, p]) Return the injectivity radius for the [`Sphere`](@ref) `M`, which is globally $\pi$. @@ -127,77 +127,77 @@ injectivity_radius(::Sphere, ::Any...) = π injectivity_radius(::Sphere, ::Any, ::ProjectionRetraction) = π / 2 @doc raw""" - inner(S::Sphere, x, w, v) + 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=`$𝕊^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) +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) + v0 = vcat(0, X) return 2 * xp1 * dot(xp1, v0) / dot(xp1, xp1) - v0 end @doc raw""" - inverse_retract(M::Sphere, x, y, ::ProjectionInverseRetraction) + 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 $⟨x,v⟩ = 0$ and when $d_{𝕊^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{\pi}{2}$ that ````math -\operatorname{retr}_x^{-1}(y) = \frac{y}{⟨x, y⟩} - 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 raw""" - log(M::Sphere, x, y) + 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. +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_{𝕊^n}(x,y) -\frac{y-⟨x,y⟩ x}{\lVert y-⟨x,y⟩ 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 raw""" - manifold_dimension(S::Sphere) + manifold_dimension(M::Sphere) -Return the dimension of the manifold $𝕊^n$, i.e. $n$. +Return the dimension of the [`Sphere`](@ref)`(n) `M`, i.e. $𝕊^n$, which is $n$. """ manifold_dimension(S::Sphere{N}) where {N} = N @@ -215,71 +215,71 @@ 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 raw""" - norm(M::Sphere, x, v) + 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 raw""" - project_point(M::Sphere, x) + 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}_{𝕊^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 raw""" - project_tangent(M::Sphere, x, v) + 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 - ⟨x, v⟩_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 raw""" - get_coordinates(M::Sphere, x, v, B::ArbitraryOrthonormalBasis) + 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, …, 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 @@ -292,65 +292,65 @@ for the `n`-dimensional [`Sphere`](@ref) it is vectors of size `(n+1,)`. @generated representation_size(::Sphere{N}) where {N} = (N + 1,) @doc raw""" - retract(M::Sphere, x, y, ::ProjectionRetraction) + 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 raw""" - vector_transport_to(M::Sphere, x, v, y, ::ParallelTransport) + vector_transport_to(M::Sphere, p, X, q, ::ParallelTransport) Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by ````math -𝒫_{y←x}(v) = v - \frac{⟨\log_xy,v⟩_x}{d^2_{𝕊^n}(x,y)} -\bigl(\log_xy + \log_yx \bigr). +𝒫_{q←p}(X) = X - \frac{⟨\log_pq,X⟩_p}{d^2_{𝕊^n}(p,q)} +\bigl(\log_pq + \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 raw""" - zero_tangent_vector(M::Sphere, x) + 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/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 71e946fe94..3c728d566a 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -8,9 +8,9 @@ Every vector space `VS` 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!(VS, 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). @@ -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 +A vector space (fiber type `fiber` of a vector bundle) at point `p` from the manifold `fiber.M`. """ 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) @@ -144,16 +144,16 @@ 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) @@ -163,47 +163,47 @@ base_manifold(B::VectorBundle) = base_manifold(B.M) """ bundle_projection(B::VectorBundle, x::ProductRepr) -Projection of point `x` from the bundle `M` to the base manifold. +Projection of point `p` 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. +of `p` is attached. """ -bundle_projection(B::VectorBundle, x) = submanifold_component(B.M, x, Val(1)) +bundle_projection(B::VectorBundle, p) = submanifold_component(B.M, 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.M`, that is the base manifold of `M`. """ -distance(B::VectorBundleFibers, x, v, w) = norm(B, x, v - w) +distance(B::VectorBundleFibers, p, X, Y) = norm(B, p, X - Y) @doc raw""" - distance(B::VectorBundle, x, y) + distance(B::VectorBundle, p, q) Distance between points $x$ and $y$ from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, ξ_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(ξ_x, ξ_{y→x})^2}$ +$d_B(x, y) = \sqrt{d_M(x_p, x_q)^2 + d_F(V_p, V_{q←p})^2}$ where $d_ℳ$ is the distance on manifold $ℳ$, $d_F$ is the distance -between two vectors from the fiber $F$ and $ξ_{y→x}$ is the result -of parallel transport of vector $ξ_y$ to point $p_x$. The default +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.M, p) + xq, Vq = submanifold_components(B.M, q) + dist_man = distance(B.M, xp, xq) + vy_x = vector_transport_to(B.M, xq, Vq, xp) + dist_vec = distance(B.VS, xp, Vp, vy_x) return sqrt(dist_man^2 + dist_vec^2) end @@ -213,120 +213,120 @@ end number_eltype(v::FVector) = number_eltype(v.data) @doc raw""" - exp(B::VectorBundle, x, v) + exp(B::VectorBundle, p, X) -Exponential map of tangent vector $v$ at point $x$ from +Exponential map of tangent vector $X$ at point $p$ from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) ∈ T_{x}B$ where - $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and - $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_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}ℳ$ 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}(ξ_{v,M}), ξ_{\exp})$ +$\exp_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp})$ -where $ξ_{\exp}$ is the result of vector transport of $ξ_x + ξ_{v,F}$ -to the point $\exp_{p_x}(ξ_{v,M})$. -The sum $ξ_x + ξ_{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.M, p) + xq, Xq = submanifold_components(B.M, q) + VXM, VXF = submanifold_components(B.M, X) + exp!(B.M, xq, xp, VXM) + vector_transport_to!(B.M, Xq, xp, Xp + VXF, xq) + return q end @doc raw""" - flat(M::Manifold, x, w::FVector) + 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 : Tℳ → T^{*}ℳ$ """ -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)) +function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) + xp1 = submanifold_component(p, 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) 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.M, 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.M, p) + VXM, VXF = submanifold_components(M.M, X) + coord1 = get_coordinates(M.M, px, VXM, B) + coord2 = get_coordinates(M.VS, 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.M, p) + VXM, VXF = submanifold_components(M.M, X) + coord1 = get_coordinates(M.M, px, VXM, B.base_basis) + coord2 = get_coordinates(M.VS, 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.M, 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.M) + xp1 = submanifold_component(p, Val(1)) + v1 = get_vector(M.M, xp1, X[1:n], B) + v2 = get_vector(M.VS, 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.M) + xp1 = submanifold_component(p, Val(1)) + v1 = get_vector(M.M, xp1, X[1:n], B.base_basis) + v2 = get_vector(M.VS, 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.M, p, X, B) end -function get_vectors(M::VectorBundle, x, B::PrecomputedVectorBundleOrthonormalBasis) - xp1 = submanifold_component(x, Val(1)) +function get_vectors(M::VectorBundle, p, B::PrecomputedVectorBundleOrthonormalBasis) + xp1 = submanifold_component(p, Val(1)) zero_m = zero_tangent_vector(M.M, xp1) zero_f = zero_vector(M.VS, xp1) vs = typeof(ProductRepr(zero_m, zero_f))[] @@ -339,7 +339,7 @@ function get_vectors(M::VectorBundle, x, B::PrecomputedVectorBundleOrthonormalBa 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,176 +347,175 @@ 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.VS` +at point `p` from manifold `B.M`. """ -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.M, 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, + p, + sharp(B.M, p, FVector(CotangentSpace, X)).data, + sharp(B.M, p, FVector(CotangentSpace, Y)).data, ) end @doc raw""" - inner(B::VectorBundle, x, v, w) + inner(B::VectorBundle, p, X, Y) -Inner product of tangent vectors `v` and `w` at point `x` from the +Inner product of tangent vectors `X` and `Y` at point `p` from the vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - * The tangent vector $v = (ξ_{v,M}, ξ_{v,F}) ∈ T_{x}B$ where - $ξ_{v,M}$ is a tangent vector from the tangent space $T_{p_x}ℳ$ and - $ξ_{v,F}$ is a tangent vector from the tangent space $T_{ξ_x}F$ (isomorphic to $F$). - Similarly for the other tangent vector $w = (ξ_{w,M}, ξ_{w,F}) ∈ 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}ℳ$ 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 -$⟨v, w⟩_{B} = ⟨ξ_{v,M}, ξ_{w,M}⟩_{M} + ⟨ξ_{v,F}, ξ_{w,F}⟩_{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.M, p) + VXM, VXF = submanifold_components(B.M, X) + VYM, VYF = submanifold_components(B.M, Y) + return inner(B.M, px, VXM, VYM) + inner(B.VS, 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.M, p) + xq, Vq = submanifold_components(B.M, q) + return isapprox(B.M, 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.M, p) + VXM, VXF = submanifold_components(B.M, X) + VYM, VYF = submanifold_components(B.M, Y) + return isapprox(B.M, VXM, VYM; kwargs...) && isapprox(B.M, px, VXF, VYF; kwargs...) end @doc raw""" - log(B::VectorBundle, x, y) + log(B::VectorBundle, p, q) -Logarithmic map of the point $y$ at point $x$ from +Logarithmic map of the point `y` at point `p` from vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: - * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - Similarly, $y = (p_y, ξ_y)$. + Similarly, $q = (x_q, V_q)$. The logarithmic map is calculated as -$\log_{x}(y) = (\log_{p_x}(p_y), ξ_{\log} - ξ_x)$ +$\log_pq = (\log_{x_p}(x_q), V_{\log} - V_p)$ -where $ξ_{\log}$ is the result of vector transport of $ξ_y$ -to the point $p_x$. -The difference $ξ_{\log} - ξ_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.M, p) + py, Vy = submanifold_components(B.M, q) + VXM, VXF = submanifold_components(B.M, X) + log!(B.M, VXM, px, py) + vector_transport_to!(B.M, VXF, py, Vy, px) + copyto!(VXF, VXF - Vx) + return X end manifold_dimension(B::VectorBundle) = manifold_dimension(B.M) + vector_space_dimension(B.VS) """ - 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.VS` +at point `p` from manifold `B.M`. """ -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.M, p, X) @doc raw""" - project_point(B::VectorBundle, x) + project_point(B::VectorBundle, p) -Project the point $x$ from the ambient space of the vector bundle `B` +Project the point `p` from the ambient space of the vector bundle `B` over manifold `B.VS` (denoted $ℳ$) to the vector bundle. Notation: - * The point $x = (p_x, ξ_x)$ where $p_x$ belongs to the ambient space of $ℳ$ - and $ξ_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 $ℳ$ + and $V_p$ belongs to the ambient space of the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. -The projection is calculated by projecting the point $p_x$ to the manifold $ℳ$ -and then projecting the vector $ξ_x$ to the tangent space $T_{p_x}ℳ$. +The projection is calculated by projecting the point $x_p$ to the manifold $ℳ$ +and then projecting the vector $V_p$ to the tangent space $T_{x_p}ℳ$. """ project_point(::VectorBundle, ::Any...) -function project_point!(B::VectorBundle, x) - px, ξx = submanifold_components(B.M, x) +function project_point!(B::VectorBundle, p) + px, Vx = submanifold_components(B.M, p) project_point!(B.M, px) - project_tangent!(B.M, ξx, px, ξx) - return x + project_tangent!(B.M, Vx, px, Vx) + return p end @doc raw""" - project_tangent(B::VectorBundle, x, v) + 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, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. - * The vector $x = (ξ_{v,M}, ξ_{v,F})$ where $p_x$ belongs to the ambient space of $T_{p_x}ℳ$ - and $ξ_{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}ℳ$ + and $V_{X,F}$ belongs to the ambient space of the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the canonical projection of that vector bundle $B$. -The projection is calculated by projecting $ξ_{v,M}$ to tangent space $T_{p_x}ℳ$ -and then projecting the vector $ξ_{v,F}$ to the fiber $F$. +The projection is calculated by projecting $V_{X,M}$ to tangent space $T_{x_p}ℳ$ +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.M, p) + VXM, VXF = submanifold_components(B.M, X) + VYM, VYF = submanifold_components(B.M, Y) + project_tangent!(B.M, VYM, px, VXM) + project_tangent!(B.M, 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.VS` 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.M, 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) @@ -529,25 +528,25 @@ function representation_size(B::VectorBundle) end @doc raw""" - sharp(M::Manifold, x, w::FVector) + 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 : T^{*}ℳ → Tℳ$ """ -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 @@ -567,7 +566,7 @@ 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 @@ -637,53 +636,53 @@ function vector_space_dimension(B::VectorBundleFibers{<:TensorProductType}) 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` +Compute the zero vector from the vector space of type `B.VS` at point `p` from manifold `B.M`. """ -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.VS` at point `p` +from manifold `B.M` 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.M, X, p) end @doc raw""" - zero_tangent_vector(B::VectorBundle, x) + zero_tangent_vector(B::VectorBundle, p) -Zero tangent vector at point $x$ from the vector bundle `B` -over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{x}B$ +Zero tangent vector at point `p` from the vector bundle `B` +over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{p}B$ Notation: - * The point $x = (p_x, ξ_x)$ where $p_x ∈ ℳ$ and $ξ_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 ∈ ℳ$ and $V_p$ belongs to the + fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ 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}ℳ$ and +where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}ℳ$ 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.M, p) + VXM, VXF = submanifold_components(B.M, X) + zero_tangent_vector!(B.M, VXM, xp) + zero_vector!(B.VS, VXF, Vp) + return X end 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/vector_bundle.jl b/test/vector_bundle.jl index 0a24542554..87358bb37e 100644 --- a/test/vector_bundle.jl +++ b/test/vector_bundle.jl @@ -102,8 +102,8 @@ struct TestVectorSpaceType <: VectorSpaceType end @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.point == x + @test ct_x.point == x end @testset "tensor product" begin From 4c904bdc263610091a5856e261c5ddf52466f498 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 10:50:30 +0100 Subject: [PATCH 33/74] =?UTF-8?q?Finishes=20Symmetric=20&=20Rotations,=20r?= =?UTF-8?q?eplaces=20\pi->=CF=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifolds/Circle.jl | 18 +- src/manifolds/Grassmann.jl | 2 +- src/manifolds/Rotations.jl | 408 +++++++++++++++++----------------- src/manifolds/Sphere.jl | 6 +- src/manifolds/Symmetric.jl | 202 ++++++++--------- src/manifolds/VectorBundle.jl | 16 +- src/statistics.jl | 6 +- 7 files changed, 334 insertions(+), 324 deletions(-) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 3eec40d62b..415f14a45d 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -2,7 +2,7 @@ Circle{F} <: Manifold The circle $𝕊^1$ as a manifold ere manifold represented by -real-valued data in $[-\pi,\pi)$ or complex-valued data $z ∈ ℂ$ of absolute value +real-valued data in $[-π,π)$ or complex-valued data $z ∈ ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor @@ -20,7 +20,7 @@ Circle(f::AbstractNumbers = ℝ) = Circle{f}() check_manifold_point(M::Circle, p) 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 ∈ [-\pi,\pi)$. +For the real-valued case, `x` is an angle and hence it checks that $p ∈ [-π,π)$. for the complex-valued case its a unit number, $p ∈ ℂ$ with $\lvert p \rvert = 1$. """ check_manifold_point(::Circle, ::Any...) @@ -97,9 +97,9 @@ distance(::Circle{ℂ}, p, q) = acos(clamp(complex_dot(p, q), -1, 1)) Compute the exponential map on the [`Circle`](@ref). ````math -\exp_pX = (p+X)_{2\pi}, +\exp_pX = (p+X)_{2π}, ```` -where $(\cdot)_{2\pi}$ 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) $𝕊^1$ is applied, to values in the complex plane. @@ -159,7 +159,7 @@ get_vector(M::Circle{ℂ}, p, X, B::ArbitraryOrthonormalBasis) = @SVector [1im * @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...) = π @@ -199,9 +199,9 @@ end Compute the logarithmic map on the [`Circle`](@ref) `M`. ````math -\log_p q = (q-p)_{2\pi}, +\log_p q = (q-p)_{2π}, ```` -where $(\cdot)_{2\pi}$ 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) $𝕊^1$ is applied, to values in the complex plane. @@ -261,7 +261,7 @@ mean(::Circle, x::Array{<:Real}, w::AbstractVector; kwargs...) = sym_rem(sum(w . project_point(M::Circle, p) Project a point `p` onto the [`Circle`](@ref) `M`. -For the real-valued case this is the remainder with respect to modulus $2\pi$. +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. """ @@ -302,7 +302,7 @@ show(io::IO, ::Circle{F}) where {F} = print(io, "Circle($(F))") 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)) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 86d6f4de91..40cda8caad 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -190,7 +190,7 @@ end injectivity_radius(M::Grassmann) injectivity_radius(M::Grassmann, p) -Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is $\frac{\pi}{2}$. +Return the injectivity radius on the [`Grassmann`](@ref) `M`, which is $\frac{π}{2}$. """ injectivity_radius(::Grassmann, ::Any...) = π / 2 diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 99fec37401..6f8db447db 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,8 +1,9 @@ @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 \times n$ +real-valued orthogonal matrices with determinant $+1$ is the manifold of `Rotations`, +since these matrices represent all rotations in $ℝ^n$. # Constructor @@ -40,9 +41,9 @@ end @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`](@ref) in $ℝ^4$, $\mathrm{SO}(4)$, consists of $4\times 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,50 +61,50 @@ 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 @@ -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) @@ -138,53 +138,55 @@ function cos_angles_4d_rotation_matrix(R) end @doc raw""" - exp(M::Rotations, x, v) + 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_pX = 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,25 +232,33 @@ 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 + v² = X * X + q .= a₀ .* p .+ p * (a₁ .* X .+ a₂ .* v² .+ a₃ .* (v² * 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)) +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 -function hat!(M::Rotations{2}, Ω, x, θ::Real) +@doc raw""" + hat(M::Rotations, p, ω) + +Convert the unique tangent vector components $\omega$ 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(::Rotations, ::Any...) + +function hat!(M::Rotations{2}, Ω, p, θ::Real) @assert length(Ω) == 4 @inbounds begin Ω[1] = 0 @@ -258,18 +268,8 @@ function hat!(M::Rotations{2}, Ω, x, θ::Real) end return Ω end -hat!(M::Rotations{2}, Ω, x, ω) = hat!(M, Ω, x, ω[1]) - -@doc raw""" - hat(M::Rotations, x, ω) - -Convert the unique tangent vector components $\omega$ at point $x$ on rotations -group $\mathrm{SO}(n)$ to the matrix representation $\Omega$ of the tangent -vector. See [`vee`](@ref) for the conventions used. -""" -hat(M::Rotations, ::Any...) - -function hat!(M::Rotations{N}, Ω, x, ω) where {N} +hat!(M::Rotations{2}, Ω, p, ω) = hat!(M, Ω, p, ω[1]) +function hat!(M::Rotations{N}, Ω, p, ω) where {N} @assert size(Ω) == (N, N) @assert length(ω) == manifold_dimension(M) @inbounds begin @@ -297,68 +297,73 @@ end @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 raw""" - inner(M::Rotations, x, w, v) + 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 raw""" - inverse_retract(M, x, y, ::PolarInverseRetraction) + 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}} + 2\mathrm{I}_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 raw""" - log(M::Rotations, x, y) + 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,11 +443,11 @@ 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 raw""" @@ -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 raw""" - norm(M::Rotations, x, v) + 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 +i.e. the Frobenius norm of `X`, where tangent vectors are represented by elements from the Lie group. """ -norm(M::Rotations, x, v) = norm(v) +norm(M::Rotations, p, X) = norm(X) @doc raw""" - normal_rotation_distribution(M::Rotations, x, σ::Real) + 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,68 +510,69 @@ $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 raw""" - project_point(M::Rotations, x; check_det = true) + 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 \Sigma V^\mathrm{T}$, with the singular values sorted in descending order, the projection is ````math -\operatorname{proj}_{\mathrm{SO}(n)}(x) = +\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 raw""" - project_tangent(M::Rotations, x, v) + 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 raw""" representation_size(M::Rotations) @@ -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, @@ -609,49 +615,51 @@ function _fix_random_rotation(A::AbstractMatrix) end @doc raw""" - 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, p, X, ::PolarRetraction) - retract(M::Rotations, x, v, ::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 SVD-based retraction on the [`Rotations`](@ref) `M` from `p` in direction `X` +(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(::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 raw""" - vee(M::Rotations, x, Ω) + vee(M::Rotations, P, Ω) Extract the unique tangent vector components $\omega$ at point $x$ on rotations group $\mathrm{SO}(n)$ from the matrix representation $\Omega$ of the tangent @@ -669,7 +677,7 @@ $\omega_{i (i - 3)/2 + j + 1} = \Omega_{ij}$, for $i ∈ [4, n], j ∈ [1,i)$. """ vee(::Rotations, ::Any...) -function vee!(M::Rotations{N}, ω, x, Ω) where {N} +function vee!(M::Rotations{N}, ω, p, Ω) where {N} @assert size(Ω) == (N, N) @assert length(ω) == manifold_dimension(M) @inbounds begin @@ -685,17 +693,17 @@ function vee!(M::Rotations{N}, ω, x, Ω) where {N} end return ω end -function vee!(M::Rotations{2}, ω, x, Ω) +function vee!(M::Rotations{2}, ω, p, Ω) ω[1] = Ω[2] return ω end @doc raw""" - zero_tangent_vector(M::Rotations, x) + 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 1ff1ac572b..bca389a374 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -116,12 +116,12 @@ flat!(M::Sphere, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, 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 @@ -147,7 +147,7 @@ end Compute the inverse of the projection based retraction on the [`Sphere`](@ref), i.e. rearranging $p+X = q\lVert p+X\rVert_2$ yields -since $⟨p,X⟩ = 0$ and when $d_{𝕊^2}(p,q) \leq \frac{\pi}{2}$ that +since $⟨p,X⟩ = 0$ and when $d_{𝕊^2}(p,q) \leq \frac{π}{2}$ that ````math \operatorname{retr}_p^{-1}(q) = \frac{q}{⟨p, q⟩} - p. diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 65e07663ee..835624052e 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -5,7 +5,7 @@ The [`Manifold`](@ref) $ \operatorname{Sym} (n)$ consisting of the real- or comp symmetric matrices of size $ n\times n$, i.e. the set ````math -\operatorname{Sym}(n) = \bigl\{A ∈ 𝔽^{n\times n} \big| A^{\mathrm{H}} = A \bigr\}, +\operatorname{Sym}(n) = \bigl\{p ∈ 𝔽^{n\times n} \big| p^{\mathrm{H}} = p \bigr\}, ```` where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed and the field $𝔽 ∈ \{ ℝ, ℂ\}$ is set by the @@ -24,149 +24,149 @@ struct SymmetricMatrices{n,F} <: Manifold end SymmetricMatrices(n::Int, F::AbstractNumbers = ℝ) = SymmetricMatrices{n,F}() @doc raw""" - check_manifold_point(M::SymmetricMatrices{n,F}, x; kwargs...) + 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 dimension `(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 raw""" - distance(M::SymmetricMatrices, x, y) + 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 raw""" - exp(M::SymmetricMatrices, x, v) + exp(M::SymmetricMatrices, p, X) -Compute the exponential map eminating from `x` in tangent direction `v` on the +Compute the exponential map eminating from `p` in tangent direction `X` on the [`SymmetricMatrices`](@ref) `M`, which reads ````math -\exp_xv = x + v. +\exp_pX = p + X. ```` """ exp(::SymmetricMatrices, ::Any...) -exp!(M::SymmetricMatrices, y, x, v) = (y .= x .+ v) +exp!(M::SymmetricMatrices, q, p, X) = (q .= p .+ X) @doc raw""" - flat(M::SymmetricMatrices, x, w::FVector{TangentSpaceType}) + 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 $ℝ$, 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) + vout = 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 vout[k] = X[i, j] * scale k += 1 end return vout 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) + vout = 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 vout[k] = real(X[i, j]) * scale k += 1 - @inbounds vout[k] = imag(v[i, j]) * scale + @inbounds vout[k] = imag(X[i, j]) * scale k += 1 end return vout @@ -174,37 +174,37 @@ 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),) + vout = allocate_result(M, get_vector, p) + @assert size(X) == (div(N * (N + 1), 2),) @assert size(vout) == (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 vout[i, j] = X[k] * scale + @inbounds vout[j, i] = X[k] * scale k += 1 end return vout 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),) + vout = allocate_result(M, get_vector, p, p .* 1im) + @assert size(X) == (N * (N + 1),) @assert size(vout) == (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[i, j] = Complex(X[k], X[k+1]) * scale @inbounds vout[j, i] = vout[i, j] k += 2 end @@ -212,30 +212,32 @@ function get_vector( end @doc raw""" - inner(M::SymmetricMatrices, x, w, v) + 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 raw""" - log(M::SymmetricMatrices, x, y) -Compute the logarithmic map from `x` to `y` on the [`SymmetricMatrices`](@ref) `M`, which + 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_pq = q-p. ```` """ log(::SymmetricMatrices, ::Any...) -log!(M::SymmetricMatrices, v, x, y) = (v .= y .- x) +log!(M::SymmetricMatrices, X, p, q) = (X .= q .- p) @doc raw""" manifold_dimension(M::SymmetricMatrices{n,𝔽}) @@ -254,46 +256,46 @@ function manifold_dimension(::SymmetricMatrices{N,𝔽}) where {N,𝔽} end @doc raw""" - norm(M::SymmetricMatrices, x, v) + 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 raw""" - project_point(M::SymmetricMatrices,x) + 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 raw""" - project_tangent(M::SymmetricMatrices, x, v) + 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 raw""" representation_size(M::SymmetricMatrices) @@ -313,36 +315,36 @@ Since `M` is already a vector space over $ℝ$, this returns just the vector `w` """ 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 raw""" - vector_transport_to(M::SymmetricMatrices, x, v, y, ::ParallelTransport) + 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 -𝒫_{y←x}(v) = v. +𝒫_{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 raw""" - zero_tangent_vector(M, x) + 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/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 3c728d566a..4ca2de8c30 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -184,7 +184,7 @@ vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. Similarly, $q = (x_q, V_q)$. @@ -220,7 +220,7 @@ vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. * 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}ℳ$ and @@ -376,7 +376,7 @@ vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is 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 = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}ℳ$ and @@ -414,7 +414,7 @@ vector bundle `B` over manifold `B.VS` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. Similarly, $q = (x_q, V_q)$. @@ -457,7 +457,7 @@ over manifold `B.VS` (denoted $ℳ$) to the vector bundle. Notation: * The point $p = (x_p, V_p)$ where $x_p$ belongs to the ambient space of $ℳ$ and $V_p$ belongs to the ambient space of the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is 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 $x_p$ to the manifold $ℳ$ @@ -480,11 +480,11 @@ to the tangent space $T_p B$. Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the canonical projection of that vector bundle $B$. * The vector $x = (V_{X,M}, V_{X,F})$ where $x_p$ belongs to the ambient space of $T_{x_p}ℳ$ and $V_{X,F}$ belongs to the ambient space of the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is 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 $V_{X,M}$ to tangent space $T_{x_p}ℳ$ @@ -667,7 +667,7 @@ over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{p Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the - fiber $F=\pi^{-1}(\{x_p\})$ of the vector bundle $B$ where $\pi$ is 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 diff --git a/src/statistics.jl b/src/statistics.jl index a92ef6b3cf..9ff5be3bce 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -47,9 +47,9 @@ points are in an open geodesic ball about the mean with corresponding radius + [`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]. From c5f41a1d393c575e19ba5d889a65355650181f8a Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 12:50:57 +0100 Subject: [PATCH 34/74] rework product manifold. --- src/manifolds/ProductManifold.jl | 271 ++++++++++++++++--------------- 1 file changed, 136 insertions(+), 135 deletions(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 0615281289..1bdf36eadf 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -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. 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::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 @@ -181,57 +181,57 @@ 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 raw""" - distance(M::ProductManifold, x, y) + distance(M::ProductManifold, p, q) -compute the distance two points `x` and `y` on the [`ProductManifold`](@ref) `M`, which is +compute the distance 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 raw""" - exp(M::ProductManifold, x, v) + exp(M::ProductManifold, p, X) -compute the exponential map from `x` towards `v` on the [`ProductManifold`](@ref) `M`, +compute the exponential map from `p` towards `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 raw""" - flat(M::ProductManifold, x, w::FVector{TangentSpaceType}) + flat(M::ProductManifold, p, w::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. @@ -239,54 +239,54 @@ This can be done elementwise, so for every entry of `w` (and `x`) sparately """ 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(p), submanifold_components(B.v), )) 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 +294,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 +303,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 +312,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 +322,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,117 +335,118 @@ 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)) + tvⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) hat!(t..., tvⁱ) i += dim end - return v + return X end @doc raw""" - injectivity_radius(M::ProductManifold[, x]) + 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 raw""" - inner(M::ProductManifold, x, v, w) + 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 raw""" - inverse_retract(M::ProductManifold, x, y, m::InverseProductRetraction) + 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 raw""" - log(M::ProductManifold, x, y) + 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 raw""" @@ -457,18 +458,18 @@ manifold dimensions the product is made of. manifold_dimension(M::ProductManifold) = mapreduce(manifold_dimension, +, M.manifolds) @doc raw""" - norm(M::PowerManifold, x, v) + 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 +477,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,8 +490,8 @@ 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).x, distributions)) + return ProductFVectorDistribution(type, p, distributions...) end function ProductFVectorDistribution(distributions::FVectorDistribution...) M = ProductManifold(map(d -> support(d).space.M, distributions)...) @@ -521,42 +522,42 @@ 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.M, X)) + return X end @doc raw""" - retract(M::ProductManifold, x, v, m::ProductRetraction) + 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) @@ -572,11 +573,11 @@ This can be done elementwise, so vor every entry of `w` (and `x`) sparately """ 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 = "") @@ -659,27 +660,27 @@ function support(tvd::ProductFVectorDistribution) ) 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)) + tvⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) vee!(SM, tvⁱ, 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 From 49e632924e6a342734f52a978bca03ba04895546 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 16:02:07 +0100 Subject: [PATCH 35/74] refactor to notation.md --- docs/src/notation.md | 1 + src/autodiff.jl | 30 +- src/groups/array_manifold.jl | 116 ++--- src/groups/circle_group.jl | 28 +- src/groups/group.jl | 581 ++++++++++++------------- src/groups/group_action.jl | 127 +++--- src/groups/group_operation_action.jl | 44 +- src/groups/product_group.jl | 198 ++++----- src/groups/rotation_action.jl | 46 +- src/groups/semidirect_product_group.jl | 132 +++--- src/groups/special_euclidean.jl | 38 +- src/groups/special_orthogonal.jl | 22 +- src/groups/translation_action.jl | 20 +- src/manifolds/ProductManifold.jl | 8 +- src/statistics.jl | 74 ++-- src/utils.jl | 8 +- 16 files changed, 722 insertions(+), 751 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 953e23122b..df059e71bb 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -14,6 +14,7 @@ as long as that renders still in $\TeX$ within this documentation. | $\xi$ | A cotangent vector from $T^*_p \mathcal M$ | $\xi_1, \xi_2,\ldots,\eta,\zeta$ | | | $n$ | dimension (of a manifold) | $n_1,n_2,\ldots,m$| | | $F$ | A fiber | | | +| $e$ | identity element of a group | | | $k$ | indices | $i,j$ | | | $\langle\cdot,\cdot\rangle$ | inner product (in $T_p \mathcal M$) | $\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)$ | | $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | 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/groups/array_manifold.jl b/src/groups/array_manifold.jl index e0ee5955af..0a2fb0830c 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))) +function inv(M::ArrayManifold, p; kwargs...) + is_manifold_point(M, p, true; kwargs...) + y = array_point(inv(M.manifold, array_value(p))) is_manifold_point(M, y, true; kwargs...) return y 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 @@ -95,60 +95,60 @@ function translate_diff(M::ArrayManifold, x, y, v, conv::ActionDirection; kwargs return vout 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...) +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...) vout = 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) + xinvy = inverse_translate(M, p, q, conv) is_tangent_vector(M, xinvy, vout, true; kwargs...) return vout 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 46b878f51c..ed7656d238 100644 --- a/src/groups/circle_group.jl +++ b/src/groups/circle_group.jl @@ -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 0e81b98b4c..6112d74d24 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -1,19 +1,19 @@ @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 @@ -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 @@ -202,31 +202,31 @@ 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 ########################## @@ -236,65 +236,58 @@ end @doc raw""" inv(G::AbstractGroupManifold, x) -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 raw""" - 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 raw""" - identity(G::AbstractGroupManifold, x) + 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,11 +297,11 @@ 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 @@ -317,241 +310,219 @@ isapprox(::GT, ::E, ::E; kwargs...) where {GT<:GroupManifold,E<:Identity{GT}} = 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 raw""" - translate(G::AbstractGroupManifold, x, y[, conv::ActionDirection=LeftAction()]) + 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 raw""" - 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 raw""" - inverse_translate(G::AbstractGroupManifold, x, y, [conv::ActionDirection=Left()]) + 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 raw""" - 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 raw""" - translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=LeftAction()]) + 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(vout)), $(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 raw""" - inverse_translate_diff(G::AbstractGroupManifold, x, y, v[, conv::ActionDirection=Left()]) + 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 is -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 ab2f25d21b..52d5717622 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 @@ -29,148 +29,148 @@ Get the direction of the action direction(::AbstractGroupAction{AD}) where {AD} = AD() @doc raw""" - apply(A::AbstractGroupAction, a, x) + 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 ∈ ℳ$ and action element $a$, the right action is +point $p ∈ ℳ$ 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 raw""" - apply_diff(A::AbstractGroupAction, a, x, v) + apply_diff(A::AbstractGroupAction, a, p, X) -For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, 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 ∈ ℳ$ and tangent vector $X ∈ T_p ℳ$, 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 ℳ → T_{a ⋅ x} ℳ\\ -(\mathrm{d}R_a)_x (v) &: T_x ℳ → T_{x ⋅ a} ℳ +(\mathrm{d}L_a)_p (X) &: T_p ℳ → T_{a ⋅ p} ℳ\\ +(\mathrm{d}R_a)_p (X) &: T_p ℳ → T_{p ⋅ a} ℳ \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, vout, a, p, X) + return error("apply_diff! not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), vectors $(typeof(vout)) and $(typeof(X))") end @doc raw""" - inverse_apply_diff(A::AbstractGroupAction, a, x, v) + inverse_apply_diff(A::AbstractGroupAction, a, p, X) -For group point $x ∈ ℳ$ and tangent vector $v ∈ T_x ℳ$, 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 ∈ ℳ$ and tangent vector $X ∈ T_p ℳ$, 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 ℳ → T_{a^{-1} ⋅ x} ℳ\\ -(\mathrm{d}R_a)_x^{-1} (v) &: T_x ℳ → T_{x ⋅ a^{-1}} ℳ +(\mathrm{d}L_a)_p^{-1} (X) &: T_p ℳ → T_{a^{-1} ⋅ p} ℳ\\ +(\mathrm{d}R_a)_p^{-1} (X) &: T_p ℳ → T_{p ⋅ a^{-1}} ℳ \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, vout, a, p, X) + return apply_diff!(A, vout, 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 raw""" - optimal_alignment(A::AbstractGroupAction, x1, x2) + 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 $ℳ$. """ -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 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. """ @@ -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 8ca84e82d7..d31261e412 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -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, vout, a, p, X) + return translate_diff!(A.group, vout, 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..8064587fe7 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) + vout = allocate_result(M, translate_diff, X, p, q) + return translate_diff!(M, vout, 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) + vout = allocate_result(M, inverse_translate_diff, X, p, q) + return inverse_translate_diff!(M, vout, 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 9b6b28eef1..101d57fbd0 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -40,46 +40,46 @@ function switch_direction(A::RotationAction{TM,TSO,TAD}) where {TM,TSO,TAD} return RotationAction(A.M, 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}, vout, a, p, X) where {N,F} + return mul!(vout, 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}, vout, a, p, X) where {N,F} + return mul!(vout, 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.M, p, true) + is_manifold_point(A.M, 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 3552ef2123..ab903c83a9 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -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) + nx, hx = submanifold_components(G, p) + ny, hy = submanifold_components(G, q) + nv, hv = submanifold_components(G, X) + nvout, hvout = submanifold_components(G, Y) 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 + @inbounds _padvector!(G, Y) + return Y end -function hat!(G::SemidirectProductGroup, V, x, v) +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 c31a04f297..76aead8944 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -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 dfa56e501e..4aa38b2d57 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -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 cbdf8bf3ab..1d4abee32f 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -36,20 +36,20 @@ function switch_direction(A::TranslationAction{TM,TRN,TAD}) where {TM,TRN,TAD} return TranslationAction(A.M, 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/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 1bdf36eadf..6693ebce7d 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -565,11 +565,11 @@ function representation_size(M::ProductManifold) end @doc raw""" - sharp(M::ProductManifold, x, w::FVector{CotangentSpaceType}) + 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, so vor every entry of `ξ` (and `p`) sparately """ sharp(::ProductManifold, ::Any...) diff --git a/src/statistics.jl b/src/statistics.jl index 9ff5be3bce..f4b266c78b 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -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,26 +332,26 @@ 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(); x0 = q, kwargs...) end end - return y + return q end function mean!( M::Manifold, - y, + q, x::AbstractVector, w::AbstractVector, ::CyclicProximalPointEstimation; @@ -365,23 +365,23 @@ 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, x0) + 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 = (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, 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 raw""" @@ -476,20 +476,20 @@ 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; @@ -503,23 +503,23 @@ 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, x0) + 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 raw""" diff --git a/src/utils.jl b/src/utils.jl index 1c389b65ef..e50d7c5522 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -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) From 46cbc0638207475e248c96d8646a9a51178e120d Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 16:10:06 +0100 Subject: [PATCH 36/74] use speaking and longer field names. --- src/manifolds/ProductManifold.jl | 6 +- src/manifolds/VectorBundle.jl | 226 +++++++++++++++---------------- src/projected_distribution.jl | 30 ++-- test/utils.jl | 4 +- test/vector_bundle.jl | 8 +- 5 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 6693ebce7d..a400be89dd 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -495,13 +495,13 @@ function ProductFVectorDistribution( 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) + 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...) + return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) end function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 4ca2de8c30..d462734afc 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, X, p)`](@ref) to construct zero vectors at point `p`, +* [`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,19 +47,19 @@ 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 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 +struct VectorBundleFibers{Tfiber<:VectorSpaceType,TM<:Manifold} + fiber::TVS + manifold::TM end const TangentBundleFibers{M} = VectorBundleFibers{TangentSpaceType,M} where {M<:Manifold} @@ -75,7 +75,7 @@ CotangentBundleFibers(M::Manifold) = VectorBundleFibers(CotangentSpace, M) VectorSpaceAtPoint(fiber::VectorBundleFibers, p) A vector space (fiber type `fiber` of a vector bundle) at point `p` from -the manifold `fiber.M`. +the manifold `fiber.manifold`. """ struct VectorSpaceAtPoint{TFiber<:VectorBundleFibers,TX} fiber::TFiber @@ -106,11 +106,11 @@ Vector bundle on manifold `M` of type `type`. struct VectorBundle{TVS<:VectorSpaceType,TM<:Manifold} <: Manifold type::TVS M::TM - VS::VectorBundleFibers{TVS,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} @@ -156,31 +156,31 @@ function copyto!(X::FVector, Y::FVector) 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 `p` from the bundle `M` to the base manifold. -Returns the point on the base manifold `B.M` at which the vector part +Returns the point on the base manifold `B.manifold` at which the vector part of `p` is attached. """ -bundle_projection(B::VectorBundle, p) = submanifold_component(B.M, p, Val(1)) +bundle_projection(B::VectorBundle, p) = submanifold_component(B.manifold, p, Val(1)) """ distance(B::VectorBundleFibers, p, X, Y) Distance between vectors `X` and `Y` from the vector space at point `p` -from the manifold `B.M`, that is the base manifold of `M`. +from the manifold `B.manifold`, that is the base manifold of `M`. """ 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 $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the @@ -199,11 +199,11 @@ behavior of [`vector_transport_to`](@ref) is used to compute the vector transport. """ function distance(B::VectorBundle, p, q) - xp, Vp = submanifold_components(B.M, p) - xq, Vq = submanifold_components(B.M, q) - dist_man = distance(B.M, xp, xq) - vy_x = vector_transport_to(B.M, xq, Vq, xp) - dist_vec = distance(B.VS, xp, Vp, vy_x) + 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 @@ -216,7 +216,7 @@ number_eltype(v::FVector) = number_eltype(v.data) exp(B::VectorBundle, p, X) Exponential map of tangent vector $X$ at point $p$ from -vector bundle `B` over manifold `B.VS` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the @@ -237,11 +237,11 @@ The sum $V_p + V_{X,F}$ corresponds to the exponential map in the vector space $ exp(::VectorBundle, ::Any) function exp!(B::VectorBundle, q, p, X) - xp, Xp = submanifold_components(B.M, p) - xq, Xq = submanifold_components(B.M, q) - VXM, VXF = submanifold_components(B.M, X) - exp!(B.M, xq, xp, VXM) - vector_transport_to!(B.M, Xq, xp, Xp + VXF, xq) + 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 @@ -271,20 +271,20 @@ end function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) xp1 = submanifold_component(p, Val(1)) bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(1))) - b1 = get_basis(M.M, xp1, bv1) + b1 = get_basis(M.manifold, xp1, bv1) bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(2))) - b2 = get_basis(M.VS, xp1, bv2) + b2 = get_basis(M.fiber, xp1, bv2) return PrecomputedVectorBundleOrthonormalBasis(b1, b2) end function get_basis(M::TangentBundleFibers, p, B::DiagonalizingOrthonormalBasis) - return get_basis(M.M, p, B) + return get_basis(M.manifold, p, B) end function get_coordinates(M::VectorBundle, p, X, B::ArbitraryOrthonormalBasis) where {N} - px, Vx = submanifold_components(M.M, p) - VXM, VXF = submanifold_components(M.M, X) - coord1 = get_coordinates(M.M, px, VXM, B) - coord2 = get_coordinates(M.VS, px, VXF, B) + 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( @@ -293,21 +293,21 @@ function get_coordinates( X, B::PrecomputedVectorBundleOrthonormalBasis, ) where {N} - px, Vx = submanifold_components(M.M, p) - VXM, VXF = submanifold_components(M.M, X) - coord1 = get_coordinates(M.M, px, VXM, B.base_basis) - coord2 = get_coordinates(M.VS, px, VXF, 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, p, X, B::AbstractBasis) where {N} - return get_coordinates(M.M, p, X, B) + return get_coordinates(M.manifold, p, X, B) end function get_vector(M::VectorBundle, p, X, B::ArbitraryOrthonormalBasis) where {N} - n = manifold_dimension(M.M) + n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) - v1 = get_vector(M.M, xp1, X[1:n], B) - v2 = get_vector(M.VS, xp1, X[n+1:end], B) + 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( @@ -316,24 +316,24 @@ function get_vector( X, B::PrecomputedVectorBundleOrthonormalBasis, ) where {N} - n = manifold_dimension(M.M) + n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) - v1 = get_vector(M.M, xp1, X[1:n], B.base_basis) - v2 = get_vector(M.VS, xp1, X[n+1:end], B.vec_basis) + 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, p, X, B::AbstractBasis) where {N} - return get_vector(M.M, p, X, B) + return get_vector(M.manifold, p, X, B) end function get_vectors(M::VectorBundle, p, B::PrecomputedVectorBundleOrthonormalBasis) xp1 = submanifold_component(p, Val(1)) - zero_m = zero_tangent_vector(M.M, xp1) - zero_f = zero_vector(M.VS, xp1) + 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 @@ -349,8 +349,8 @@ Base.@propagate_inbounds getindex(x::FVector, i) = getindex(x.data, i) """ inner(B::VectorBundleFibers, p, X, Y) -Inner product of vectors `X` and `Y` from the vector space of type `B.VS` -at point `p` 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, p, X, Y) error( @@ -359,20 +359,20 @@ function inner(B::VectorBundleFibers, p, X, Y) "vectors of types $(typeof(X)) and $(typeof(Y)).", ) end -inner(B::VectorBundleFibers{<:TangentSpaceType}, p, X, Y) = inner(B.M, p, X, Y) +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, + B.manifold, p, - sharp(B.M, p, FVector(CotangentSpace, X)).data, - sharp(B.M, p, FVector(CotangentSpace, Y)).data, + sharp(B.manifold, p, FVector(CotangentSpace, X)).data, + sharp(B.manifold, p, FVector(CotangentSpace, Y)).data, ) end @doc raw""" inner(B::VectorBundle, p, X, Y) Inner product of tangent vectors `X` and `Y` at point `p` from the -vector bundle `B` over manifold `B.VS` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the @@ -388,29 +388,29 @@ The inner product is calculated as $⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ """ function inner(B::VectorBundle, p, X, Y) - px, Vx = submanifold_components(B.M, p) - VXM, VXF = submanifold_components(B.M, X) - VYM, VYF = submanifold_components(B.M, Y) - return inner(B.M, px, VXM, VYM) + inner(B.VS, Vx, VXF, VYF) + 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, p, q; kwargs...) - xp, Vp = submanifold_components(B.M, p) - xq, Vq = submanifold_components(B.M, q) - return isapprox(B.M, xp, xq; kwargs...) && isapprox(Vp, Vq; 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, p, X, Y; kwargs...) - px, Vx = submanifold_components(B.M, p) - VXM, VXF = submanifold_components(B.M, X) - VYM, VYF = submanifold_components(B.M, Y) - return isapprox(B.M, VXM, VYM; kwargs...) && isapprox(B.M, px, VXF, VYF; 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 raw""" log(B::VectorBundle, p, q) Logarithmic map of the point `y` at point `p` from -vector bundle `B` over manifold `B.VS` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the @@ -428,31 +428,31 @@ The difference $V_{\log} - V_p$ corresponds to the logarithmic map in the vector log(::VectorBundle, ::Any...) function log!(B::VectorBundle, X, p, q) - px, Vx = submanifold_components(B.M, p) - py, Vy = submanifold_components(B.M, q) - VXM, VXF = submanifold_components(B.M, X) - log!(B.M, VXM, px, py) - vector_transport_to!(B.M, VXF, py, Vy, px) + 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, p, q) -Norm of the vector `X` from the vector space of type `B.VS` -at point `p` 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, p, X) = sqrt(inner(B, p, X, X)) -norm(B::VectorBundleFibers{<:TangentSpaceType}, p, X) = norm(B.M, p, X) +norm(B::VectorBundleFibers{<:TangentSpaceType}, p, X) = norm(B.manifold, p, X) @doc raw""" project_point(B::VectorBundle, p) Project the point `p` from the ambient space of the vector bundle `B` -over manifold `B.VS` (denoted $ℳ$) to the vector bundle. +over manifold `B.fiber` (denoted $ℳ$) to the vector bundle. Notation: * The point $p = (x_p, V_p)$ where $x_p$ belongs to the ambient space of $ℳ$ @@ -466,9 +466,9 @@ and then projecting the vector $V_p$ to the tangent space $T_{x_p}ℳ$. project_point(::VectorBundle, ::Any...) function project_point!(B::VectorBundle, p) - px, Vx = submanifold_components(B.M, p) - project_point!(B.M, px) - project_tangent!(B.M, Vx, px, Vx) + px, Vx = submanifold_components(B.manifold, p) + project_point!(B.manifold, px) + project_tangent!(B.manifold, Vx, px, Vx) return p end @@ -493,18 +493,18 @@ and then projecting the vector $V_{X,F}$ to the fiber $F$. project_tangent(::VectorBundle, ::Any...) function project_tangent!(B::VectorBundle, Y, p, X) - px, Vx = submanifold_components(B.M, p) - VXM, VXF = submanifold_components(B.M, X) - VYM, VYF = submanifold_components(B.M, Y) - project_tangent!(B.M, VYM, px, VXM) - project_tangent!(B.M, VYF, px, VXF) + 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, p, X) -Project vector `X` from the vector space of type `B.VS` at point `p`. +Project vector `X` from the vector space of type `B.fiber` at point `p`. """ function project_vector(B::VectorBundleFibers, p, X) Y = allocate_result(B, project_vector, p, X) @@ -512,7 +512,7 @@ function project_vector(B::VectorBundleFibers, p, X) end function project_vector!(B::VectorBundleFibers{<:TangentSpaceType}, Y, p, X) - return project_tangent!(B.M, Y, p, X) + return project_tangent!(B.manifold, Y, p, X) end 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)).") @@ -520,10 +520,10 @@ 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 @@ -556,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) @@ -570,9 +570,9 @@ function show(io::IO, mime::MIME"text/plain", vs::VectorSpaceAtPoint) 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)) @@ -581,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. """ @@ -600,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} @@ -626,11 +626,11 @@ 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 @@ -638,8 +638,8 @@ end """ zero_vector(B::VectorBundleFibers, p) -Compute the zero vector from the vector space of type `B.VS` at point `p` -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, p) X = allocate_result(B, zero_vector, p) @@ -649,21 +649,21 @@ end """ zero_vector!(B::VectorBundleFibers, X, p) -Save the zero vector from the vector space of type `B.VS` at point `p` -from manifold `B.M` to `X`. +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, X, p) error("zero_vector! not implemented for vector space family of type $(typeof(B)).") end function zero_vector!(B::VectorBundleFibers{<:TangentSpaceType}, X, p) - return zero_tangent_vector!(B.M, X, p) + return zero_tangent_vector!(B.manifold, X, p) end @doc raw""" zero_tangent_vector(B::VectorBundle, p) Zero tangent vector at point `p` from the vector bundle `B` -over manifold `B.VS` (denoted $ℳ$). The zero vector belongs to the space $T_{p}B$ +over manifold `B.fiber` (denoted $ℳ$). The zero vector belongs to the space $T_{p}B$ Notation: * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to the @@ -680,9 +680,9 @@ $\mathbf{0}_F$ is the zero element of the vector space $F$. zero_tangent_vector(::VectorBundle, ::Any...) function zero_tangent_vector!(B::VectorBundle, X, p) - xp, Vp = submanifold_components(B.M, p) - VXM, VXF = submanifold_components(B.M, X) - zero_tangent_vector!(B.M, VXM, xp) - zero_vector!(B.VS, VXF, Vp) + 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/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/test/utils.jl b/test/utils.jl index 92f2ceb22f..740411c8ac 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 diff --git a/test/vector_bundle.jl b/test/vector_bundle.jl index 87358bb37e..031d3d7b6f 100644 --- a/test/vector_bundle.jl +++ b/test/vector_bundle.jl @@ -98,10 +98,10 @@ 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.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 From f3269decf772d5f60a360e17a5b942ed20eb9306 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 16:13:05 +0100 Subject: [PATCH 37/74] fixes a typo. --- src/manifolds/VectorBundle.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index d462734afc..664988953e 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -57,7 +57,7 @@ as a representation of vector spaces from a vector bundle but without storing the point at which a vector space is attached (which is specified separately in various functions). """ -struct VectorBundleFibers{Tfiber<:VectorSpaceType,TM<:Manifold} +struct VectorBundleFibers{TVS<:VectorSpaceType,TM<:Manifold} fiber::TVS manifold::TM end From 2c4696384ac9e7888be1291e3db8f4d7d19aa3a7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 16:37:26 +0100 Subject: [PATCH 38/74] Fixes a few more replacement bugs. --- src/groups/rotation_action.jl | 12 ++++++------ src/groups/translation_action.jl | 6 +++--- src/manifolds/MetricManifold.jl | 8 ++++---- src/manifolds/PowerManifold.jl | 4 ++-- src/manifolds/ProductManifold.jl | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index 101d57fbd0..eaca3b3127 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -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,10 +34,10 @@ 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, p) where {N,F} = a * p @@ -70,8 +70,8 @@ end inverse_apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, p, X) where {N,F} = a * X function optimal_alignment(A::RotationActionOnVector{N,T,LeftAction}, p, q) where {N,T} - is_manifold_point(A.M, p, true) - is_manifold_point(A.M, q, true) + is_manifold_point(A.manifold, p, true) + is_manifold_point(A.manifold, q, true) Xmul = p * transpose(q) F = svd(Xmul) diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 1d4abee32f..bff5fc8088 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -25,15 +25,15 @@ 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, p) = p + a diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 7e6273b293..c52e9a2512 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -254,10 +254,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. """ @@ -302,7 +302,7 @@ function inner( X, Y, ) where {MMT<:MetricManifold} - ginv = inverse_local_metric(B.M, p) + ginv = inverse_local_metric(B.manifold, p) return dot(X, ginv * Y) end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index d018365939..699cb2bd1e 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -546,9 +546,9 @@ function rand(rng::AbstractRNG, d::PowerPointDistribution) 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) + for i in get_iterator(d.type.manifold) copyto!(d.distribution.x, _read(PM, rep_size, d.x, i)) _rand!(rng, d.distribution, _read(PM, rep_size, v, i)) end diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index a400be89dd..8910fbb07e 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -494,7 +494,7 @@ function ProductFVectorDistribution( return ProductFVectorDistribution(type, p, distributions...) end function ProductFVectorDistribution(distributions::FVectorDistribution...) - M = ProductManifold(map(d -> support(d).space.M, 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") @@ -534,7 +534,7 @@ function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, v::AbstractArra return copyto!(v, rand(rng, d)) end function _rand!(rng::AbstractRNG, d::ProductFVectorDistribution, X::ProductRepr) - map(t -> _rand!(rng, t[1], t[2]), d.distributions, submanifold_components(d.space.M, X)) + map(t -> _rand!(rng, t[1], t[2]), d.distributions, submanifold_components(d.space.manifold, X)) return X end From ae10f7b46e328f2d3b3e5ffc68b16737d02aecce Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 17:14:06 +0100 Subject: [PATCH 39/74] Finally renamex fields `.x` to `.point` --- src/distributions.jl | 4 ++-- src/manifolds/PowerManifold.jl | 16 ++++++++-------- src/manifolds/ProductManifold.jl | 6 +++--- src/manifolds/VectorBundle.jl | 2 +- test/utils.jl | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) 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/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 699cb2bd1e..b942079a62 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -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 @@ -535,12 +535,12 @@ function norm(M::AbstractPowerManifold, p, X) 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 @@ -549,7 +549,7 @@ function _rand!(rng::AbstractRNG, d::PowerFVectorDistribution, v::AbstractArray) PM = d.type.manifold rep_size = representation_size(PM.manifold) for i in get_iterator(d.type.manifold) - copyto!(d.distribution.x, _read(PM, rep_size, d.x, i)) + copyto!(d.distribution.point, _read(PM, rep_size, d.point, i)) _rand!(rng, d.distribution, _read(PM, rep_size, v, i)) end return v @@ -663,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 8910fbb07e..1b5d935149 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -490,7 +490,7 @@ function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, distributions::FVectorDistribution..., ) - p = ProductRepr(map(d -> support(d).x, distributions)) + p = ProductRepr(map(d -> support(d).point, distributions)) return ProductFVectorDistribution(type, p, distributions...) end function ProductFVectorDistribution(distributions::FVectorDistribution...) @@ -500,7 +500,7 @@ function ProductFVectorDistribution(distributions::FVectorDistribution...) 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)...) + x = ProductRepr(map(d -> support(d).point, distributions)...) return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) end @@ -656,7 +656,7 @@ 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 diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 664988953e..33fce2fad5 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -105,7 +105,7 @@ Vector bundle on manifold `M` of type `type`. """ struct VectorBundle{TVS<:VectorSpaceType,TM<:Manifold} <: Manifold type::TVS - M::TM + manifold::TM fiber::VectorBundleFibers{TVS,TM} end diff --git a/test/utils.jl b/test/utils.jl index 740411c8ac..337d00117f 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -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 From 968a942b6982aaf0b2711e5ac6f546d915d2ea56 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 18:03:57 +0100 Subject: [PATCH 40/74] Fixes further .manifold fields, though I have introduced a bug that I can't reengineer currently within semidrect groups. That has still to be found. --- src/groups/translation_action.jl | 2 +- test/groups/semidirect_product_group.jl | 1 - test/metric.jl | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index bff5fc8088..22b0b4270e 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -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 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..40a617a935 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -297,8 +297,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)) From 57d340c6c0874209c13528ba383fd3dd094442ef Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Sat, 1 Feb 2020 18:33:29 +0100 Subject: [PATCH 41/74] Fixes two minor typos that were introduced by refactoring. --- src/groups/rotation_action.jl | 2 +- src/groups/translation_action.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index eaca3b3127..df3438e978 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -34,7 +34,7 @@ const RotationActionOnVector{N,F,TAD} = RotationAction{ base_group(A::RotationAction) = A.SOn -g_manifold(A::RotationAction) = A.manifold, +g_manifold(A::RotationAction) = A.manifold function switch_direction(A::RotationAction{TM,TSO,TAD}) where {TM,TSO,TAD} return RotationAction(A.manifold, A.SOn, switch_direction(TAD())) diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 22b0b4270e..722155afdc 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -30,7 +30,7 @@ end base_group(A::TranslationAction) = A.Rn -g_manifold(A::TranslationAction) = A.manifold, +g_manifold(A::TranslationAction) = A.manifold function switch_direction(A::TranslationAction{TM,TRN,TAD}) where {TM,TRN,TAD} return TranslationAction(A.manifold, A.Rn, switch_direction(TAD())) From e4635235cae19839bbcc2c11bf1713d061553639 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 09:19:46 +0100 Subject: [PATCH 42/74] fixes two mingled docstrings. --- src/manifolds/PowerManifold.jl | 2 +- src/manifolds/Sphere.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index b942079a62..acd337f0cb 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -505,7 +505,7 @@ end manifold_dimension(M::PowerManifold) Returns the manifold-dimension of an [`PowerManifold`](@ref) `M` -$=𝒩 = (ℳ)^{n_1,…,n_d}, i.e. with $n=(n_1,…,n_d)$ the array +$=𝒩 = (ℳ)^{n_1,…,n_d}$, i.e. with $n=(n_1,…,n_d)$ the array size of the power manifold and $d_{ℳ}$ the dimension of the base manifold $ℳ$, the manifold is of dimension diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index bca389a374..357707da31 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -55,7 +55,7 @@ end """ check_tangent_vector(M, p, X; kwargs... ) -Check whether `X is a tangent vector to `p` on the [`Sphere`](@ref) `M`, i.e. +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...`. From d5cb228259b3cbc855bea42610602b45b5bcaaa0 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 09:50:21 +0100 Subject: [PATCH 43/74] improve capitalization and extend notation.md --- docs/src/notation.md | 30 +++++++++++++++++++----------- src/groups/group_action.jl | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index df059e71bb..9fc9471526 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -9,17 +9,25 @@ 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 | -|:--:|:-------------- |:--:|:--- | -| $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$ | | -| $n$ | dimension (of a manifold) | $n_1,n_2,\ldots,m$| | -| $F$ | A fiber | | | +|:--:|:--------------- |:--:|:-- | +| $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, \operatorname{dim}(\mathcal M)$| for the real dimension sometimes also $\operatorname{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 $\mathcal 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 | | | $e$ | identity element of a group | | | $k$ | indices | $i,j$ | | | $\langle\cdot,\cdot\rangle$ | inner product (in $T_p \mathcal M$) | $\langle\cdot,\cdot\rangle_p, g_p(\cdot,\cdot)$ | -| $\mathcal M$ | A manifold | $\mathcal M_1, \mathcal M_2,\ldots,\mathcal N$ | | -| $\mathcal P_{q\gets p}X$ | parallel Transport | -| $p$ | A point on $\mathcal M$ | $p_1, p_2, \ldots,q$ | for 3 points one might use $x,y,z$ | -| $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$. | -| $B$ | The Vector bundle | | +| $\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$ | +| $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$ | +| $B$ | a vector bundle | | diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 52d5717622..50ef269661 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -171,7 +171,7 @@ with respect to given set of points `pts`. The [`mean`](@ref) is calculated usin The orbit of $p$ with respect to the action of a group $G$ is the set ````math 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( From 6431b7d84ff01cdc560af50a1ceb42dd1ed820be Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 09:58:37 +0100 Subject: [PATCH 44/74] Update docs/src/orthonormal_bases.md Co-Authored-By: Seth Axen --- docs/src/orthonormal_bases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/orthonormal_bases.md b/docs/src/orthonormal_bases.md index 3a3683369d..5ab076f929 100644 --- a/docs/src/orthonormal_bases.md +++ b/docs/src/orthonormal_bases.md @@ -2,7 +2,7 @@ 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 ℳ$ 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 ℳ$ 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))$. +A vector $v$ from the tangent space $T_x ℳ$ 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. From 76217434b099671b8cde34b99a666a65049441f2 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 09:59:28 +0100 Subject: [PATCH 45/74] Update src/groups/group.jl Co-Authored-By: Seth Axen --- src/groups/group.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 6112d74d24..2dcc57a66b 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -462,7 +462,7 @@ end 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 is +right convention. The differential transports vectors as ```math \begin{aligned} From a44bddc461f7bb179483b122809d3a0276daca3d Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:02:41 +0100 Subject: [PATCH 46/74] replaces two further math notations by utf8 --- docs/src/manifolds/symmetricpositivedefinite.md | 2 +- docs/src/manifolds/torus.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 16bccda4f7..b4ce57f341 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -𝒫(n) = \bigl\{ A ∈ ℝ^{n \times n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x \in ℝ^n \bigr\} +𝒫(n) = \bigl\{ A ∈ ℝ^{n \times 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 686d614607..e147057a35 100644 --- a/docs/src/manifolds/torus.md +++ b/docs/src/manifolds/torus.md @@ -1,7 +1,7 @@ # Torus -The torus $𝕋^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ℝ^{d}$. +Points on the torus are hence row vectors, $x ∈ ℝ^{d}$. ## Example From 6e8ccb49af172d3ab48689d25c4a62b6605ae3ca Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:05:19 +0100 Subject: [PATCH 47/74] Update src/manifolds/CholeskySpace.jl Co-Authored-By: Seth Axen --- src/manifolds/CholeskySpace.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 15fc25636a..15e79401cf 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -53,7 +53,7 @@ end check_tangent_vector(M::CholeskySpace, p, X; kwargs... ) Check whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. -atfer [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `x` +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...`. """ From a514bf27a7b31049df606a8d824e55cfb0dd81ca Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:09:17 +0100 Subject: [PATCH 48/74] Update src/manifolds/Circle.jl Co-Authored-By: Seth Axen --- src/manifolds/Circle.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 415f14a45d..b4bfef9bcf 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -1,7 +1,7 @@ @doc raw""" Circle{F} <: Manifold -The circle $𝕊^1$ as a manifold ere manifold represented by +The circle $𝕊^1$ is a manifold here represented by real-valued data in $[-π,π)$ or complex-valued data $z ∈ ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor From 78a9b742842bef6f6b22e89afcc5eb702cf41ae7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:09:33 +0100 Subject: [PATCH 49/74] Update src/manifolds/ProductManifold.jl Co-Authored-By: Seth Axen --- src/manifolds/ProductManifold.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 1b5d935149..118ca43692 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -206,7 +206,7 @@ end @doc raw""" exp(M::ProductManifold, p, X) -compute the exponential map from `p` towards `X` 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...) From 0fc2b706664300208c9b21136601e4e8e38e25cf Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:09:56 +0100 Subject: [PATCH 50/74] Update src/manifolds/ProductManifold.jl Co-Authored-By: Seth Axen --- src/manifolds/ProductManifold.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 118ca43692..353000244a 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -235,7 +235,7 @@ end 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 +This can be done elementwise for every entry of `w` (and `x`) separately. """ flat(::ProductManifold, ::Any...) From 3c77b77de7d86468e73e9afd38f9fccd147d863e Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:10:10 +0100 Subject: [PATCH 51/74] Update src/manifolds/ProductManifold.jl Co-Authored-By: Seth Axen --- src/manifolds/ProductManifold.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 353000244a..6c489c3ea6 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -189,7 +189,7 @@ end @doc raw""" distance(M::ProductManifold, p, q) -compute the distance two points `p` and `q` 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, p, q) From 24c85890a35f7541fb9554c69145460fcb4741af Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:10:34 +0100 Subject: [PATCH 52/74] Replace wedge and vee by their utf8 representations. --- src/Manifolds.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 87dce29737..cd7cf2a039 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -116,7 +116,7 @@ component vector $v^i$, compute the equivalent vector representation $v=v^i e_i$, where Einstein summation notation is used: ````math -\wedge: v^i ↦ v^i e_i +∧ : v^i ↦ v^i e_i ```` For array manifolds, this converts a vector representation of the tangent @@ -141,7 +141,7 @@ vector $v$, compute the vector components $v^i$, such that $v = v^i e_i$, where Einstein summation notation is used: ````math -\vee: v^i e_i ↦ v^i +⋁ : v^i e_i ↦ v^i ```` For array manifolds, this converts an array representation of the tangent From e89dc644a9192041406c0bead4f613738fe6e130 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:10:46 +0100 Subject: [PATCH 53/74] Update src/manifolds/ProductManifold.jl Co-Authored-By: Seth Axen --- src/manifolds/ProductManifold.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 6c489c3ea6..1884639135 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -129,7 +129,7 @@ end check_tangent_vector(M::ProductManifold, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`ProductManifold`](@ref) -`M`, i.e. atfer [`check_manifold_point`](@ref)`(M, p)`, and all projections to +`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...`. From 5ec77b86bc95b27f412543967fe6c4e3a02581b4 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:11:14 +0100 Subject: [PATCH 54/74] Update src/manifolds/Circle.jl Co-Authored-By: Seth Axen --- src/manifolds/Circle.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index b4bfef9bcf..98935dd5bc 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -2,7 +2,7 @@ Circle{F} <: Manifold The circle $𝕊^1$ is a manifold here represented by -real-valued data in $[-π,π)$ or complex-valued data $z ∈ ℂ$ of absolute value +real-valued points in $[-π,π)$ or complex-valued points $z ∈ ℂ$ of absolute value $\lvert z\rvert = 1$. # Constructor From b1614d02457dfa703f40f34911c38fe82887f0ca Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:23:48 +0100 Subject: [PATCH 55/74] =?UTF-8?q?undo=20\vee=20since=20it=20rendered=20as?= =?UTF-8?q?=20a=20large=20or,=20replace=20`\times`=20with=20=C3=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/rotations.md | 4 ++-- .../manifolds/symmetricpositivedefinite.md | 2 +- src/Manifolds.jl | 2 +- src/groups/special_euclidean.jl | 2 +- src/manifolds/CholeskySpace.jl | 2 +- src/manifolds/Euclidean.jl | 4 ++-- src/manifolds/FixedRankMatrices.jl | 20 +++++++++---------- src/manifolds/Grassmann.jl | 14 ++++++------- src/manifolds/PowerManifold.jl | 4 ++-- src/manifolds/ProductManifold.jl | 4 ++-- src/manifolds/Rotations.jl | 6 +++--- src/manifolds/Stiefel.jl | 18 ++++++++--------- src/manifolds/Symmetric.jl | 10 +++++----- src/manifolds/SymmetricPositiveDefinite.jl | 6 +++--- src/product_representations.jl | 2 +- 16 files changed, 51 insertions(+), 51 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 83f834b670..f675cc3317 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,6 +1,6 @@ # Product manifold -Product manifold $M = M_1 \times M_2 \times … M_n$ of manifolds $M_1, M_2, …, M_n$. +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 diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index bbd6c83be0..f857669ad7 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -1,8 +1,8 @@ # Rotations -The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{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 ∈ ℝ^{n\times n} \big| RR^{\mathrm{T}} = +$\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n × n} \big| RR^{\mathrm{T}} = R^{\mathrm{T}}R = \mathrm{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. diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index b4ce57f341..4016ec3f59 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -𝒫(n) = \bigl\{ A ∈ ℝ^{n \times n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x ∈ ℝ^n \bigr\} +𝒫(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/src/Manifolds.jl b/src/Manifolds.jl index cd7cf2a039..23921d160f 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -141,7 +141,7 @@ vector $v$, compute the vector components $v^i$, such that $v = v^i e_i$, where Einstein summation notation is used: ````math -⋁ : v^i e_i ↦ v^i +\vee : v^i e_i ↦ v^i ```` For array manifolds, this converts an array representation of the tangent diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 76aead8944..41e90481e1 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -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},ℝ}, diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 15fc25636a..2068ec67f4 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -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 diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index c2416e5c5d..89bb497203 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -13,7 +13,7 @@ Generate the $n$-dimensional vector space $ℝ^n$. 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 \times,n_2 \times … \times n_i$ arrays. +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 @@ -133,7 +133,7 @@ injectivity_radius(::Euclidean) = Inf 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 … \times n_i$, i.e. +of arrays (or tensors) of size $n_1 × n_2 × … × n_i$, i.e. ````math g_p(X,Y) = \sum_{k ∈ I} \overline{X}_{k} Y_{k}, diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index e7b40ad2a2..25521607d9 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,15 +1,15 @@ @doc raw""" FixedRankMatrices{m,n,k,T} <: Manifold -The manifold of $m \times n$ real-valued or complex-valued matrices of fixed rank $k$, i.e. +The manifold of $m × n$ real-valued or complex-valued matrices of fixed rank $k$, i.e. ````math -\{ p ∈ 𝔽^{m \times n} : \operatorname{rank}(p) = 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 $p ∈ ℳ$ can be stored using unitary matrices $U ∈ 𝔽^{m \times k}$, $V ∈ 𝔽^{n \times k}$ as well as the $k$ +A point $p ∈ ℳ$ 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. @@ -18,18 +18,18 @@ The tangent space $T_p ℳ$ at a point $p ∈ ℳ$ with $p=USV^\mathrm{H}$ is given by ````math T_pℳ = \bigl\{ UMV^\mathrm{T} + U_pV^\mathrm{H} + UV_p^\mathrm{H} : - M ∈ 𝔽^{k \times k}, - U_p ∈ 𝔽^{m \times k}, - V_p ∈ 𝔽^{n \times k} + M ∈ 𝔽^{k × k}, + U_p ∈ 𝔽^{m × k}, + V_p ∈ 𝔽^{n × k} \text{ s.t. } 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 $ℝ^{m \times n}$ to the tangent bundle[^Vandereycken2013]. +on $ℝ^{m × n}$ to the tangent bundle[^Vandereycken2013]. # Constructor FixedRankMatrics(m, n, k[, t=ℝ]) @@ -88,7 +88,7 @@ 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{H}$, where $M$ is a $k\times k$ matrix. + 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 @@ -286,7 +286,7 @@ Compute an SVD-based retraction on the [`FixedRankMatrices`](@ref) `M` by comput q = U_kS_kV_k^\mathrm{H}, ```` where $U_k S_k V_k^\mathrm{H}$ is the shortened singular value decomposition $USV=p+X$, -in the sense that $S_k$ is the diagonal matrix of size $k \times k$ with the $k$ largest +in the sense that $S_k$ is the diagonal matrix of size $k × k$ with the $k$ largest singular values and $U$ and $V$ are shortened accordingly. """ retract(::FixedRankMatrices, ::Any, ::Any, ::PolarRetraction) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 40cda8caad..59efe78ca1 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -9,11 +9,11 @@ of $ℂ^n$ for the second. The manifold can be represented as ````math -\operatorname{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n \times k}, p^\mathrm{H}p = I_k\}, +\operatorname{Gr}(n,k) := \bigl\{ \operatorname{span}(p) : p ∈ 𝔽^{n × k}, p^\mathrm{H}p = I_k\}, ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian and -$I_k$ is the $k \times k$ identity matrix. This means, that the columns of $x$ +$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. @@ -24,17 +24,17 @@ 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\{ -X ∈ 𝔽^{n \times k} : +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 $p ∈ \operatorname{Gr}(n,k)$ might be represented by different matrices (i.e. matrices with unitary column vectors that span @@ -100,7 +100,7 @@ the [`Grassmann`](@ref) `M`, i.e. that `X` is of size and type as well as that ```` 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}, p, X; kwargs...) where {n,k,F} t = check_manifold_point(G, p) @@ -359,7 +359,7 @@ Compute the QR-based retraction [`QRRetraction`](@ref) on the ````math \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 ). ```` diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index acd337f0cb..8961195d33 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -41,7 +41,7 @@ abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation @doc raw""" PowerManifold{TM<:Manifold, TSize<:Tuple, TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{TM} -The power manifold $ℳ^{n_1\times n_2 \times … \times n_d}$ with power geometry +The power manifold $ℳ^{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 … \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 diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 1b5d935149..020cd67666 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,7 +1,7 @@ @doc raw""" ProductManifold{TM<:Tuple, TRanges<:Tuple, TSizes<:Tuple} <: Manifold -Product manifold $M_1 \times M_2 \times … \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 … \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`. """ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 6f8db447db..16ec6922c3 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,7 +1,7 @@ @doc raw""" Rotations{N} <: Manifold -The special orthogonal manifold $\mathrm{SO}(n)$ represented by $n \times n$ +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 in $ℝ^n$. @@ -9,7 +9,7 @@ since these matrices represent all rotations in $ℝ^n$. Rotations(n) -Generate the $\mathrm{SO}(n) \subset ℝ^{n \times n}$ +Generate the $\mathrm{SO}(n) \subset ℝ^{n × n}$ """ struct Rotations{N} <: Manifold end @@ -41,7 +41,7 @@ end @doc raw""" angles_4d_skew_sym_matrix(A) -The Lie algebra of [`Rotations`](@ref) in $ℝ^4$, $\mathrm{SO}(4)$, consists of $4\times 4$ +The Lie algebra of [`Rotations`](@ref) in $ℝ^4$, $\mathrm{SO}(4)$, consists of $4× 4$ skew-symmetric matrices. The unique imaginary components of their eigenvalues are the angles of the two plane rotations. This function computes these more efficiently than `eigvals`. diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 86f06f19cc..9e65cfd6b9 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,23 +1,23 @@ @doc raw""" Stiefel{n,k,T} <: Manifold -The Stiefel manifold consists of all $n \times k$, $n\geq k$ unitary matrices, i.e. +The Stiefel manifold consists of all $n × k$, $n\geq k$ unitary matrices, i.e. ````math -\{ p ∈ 𝔽^{n \times k} : p^{\mathrm{H}}p = I_k \}, +\{ p ∈ 𝔽^{n × k} : p^{\mathrm{H}}p = I_k \}, ```` where $𝔽 ∈ \{ℝ, ℂ\}$, $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and -$I_n ∈ ℝ^{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 $p ∈ ℳ$ is given by ````math -T_p ℳ = \{ X ∈ 𝔽^{n\times k} : p^{\mathrm{H}}X + X^{\mathrm{H}}p=0_n\}, +T_p ℳ = \{ 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 $ℝ^{n,k}$ for the real-valued case or the one inherited from interpreting the complex valued entries in the Gaussian @@ -30,7 +30,7 @@ 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 @@ -128,7 +128,7 @@ eminating from `p` into tangent direction `X`. 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...) @@ -166,7 +166,7 @@ This follows the folloing approach: From the Polar retraction we know that \operatorname{retr}_p^{-1}q = qs - t ```` -if such a symmetric positive definite $k\times k$ matrix exists. Since $qs - t$ +if such a symmetric positive definite $k × k$ matrix exists. Since $qs - t$ is also a tangent vector at $p$ we obtain ````math @@ -279,7 +279,7 @@ Compute the QR-based retraction [`QRRetraction`](@ref) on the \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), diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 835624052e..fd1aaf408d 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -2,22 +2,22 @@ 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\{p ∈ 𝔽^{n\times n} \big| p^{\mathrm{H}} = p \bigr\}, +\operatorname{Sym}(n) = \bigl\{p ∈ 𝔽^{n × n} \big| p^{\mathrm{H}} = p \bigr\}, ```` where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transposed 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 slighty redundant, usually the matrices are safed 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 @@ -301,7 +301,7 @@ project_tangent!(M::SymmetricMatrices, Y, p, X) = (Y .= (X .+ transpose(X)) ./ 2 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) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 5c51c995cc..0b1bb95d77 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -6,7 +6,7 @@ The manifold of symmetric positive definite matrices, i.e. ````math 𝒫(n) = \bigl\{ -p ∈ ℝ^{n\times n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} +p ∈ ℝ^{n × n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \bigr\} ```` @@ -14,7 +14,7 @@ p ∈ ℝ^{n\times n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backsl SymmetricPositiveDefinite(n) -generates the manifold $𝒫(n) \subset ℝ^{n\times n}$ +generates the manifold $𝒫(n) \subset ℝ^{n × n}$ """ struct SymmetricPositiveDefinite{N} <: Manifold end @@ -127,7 +127,7 @@ end 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 $ℳ = 𝒫(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) diff --git a/src/product_representations.jl b/src/product_representations.jl index 2e3b18db5f..758c680706 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -164,7 +164,7 @@ 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 ℝ^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` From 4224994499145b95f0f401d59e25785d704c2cb6 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:33:59 +0100 Subject: [PATCH 56/74] Return from caligraphic UTF8 to $\mathcal ...$ --- docs/src/manifolds/graph.md | 2 +- .../manifolds/symmetricpositivedefinite.md | 2 +- docs/src/manifolds/vector_bundle.md | 4 +- docs/src/orthonormal_bases.md | 4 +- src/groups/group.jl | 2 +- src/groups/group_action.jl | 16 +++---- src/manifolds/CholeskySpace.jl | 4 +- src/manifolds/Circle.jl | 2 +- src/manifolds/FixedRankMatrices.jl | 6 +-- src/manifolds/GraphManifold.jl | 4 +- src/manifolds/Grassmann.jl | 2 +- src/manifolds/Hyperbolic.jl | 6 +-- src/manifolds/MetricManifold.jl | 2 +- src/manifolds/PowerManifold.jl | 10 ++-- src/manifolds/Sphere.jl | 2 +- src/manifolds/Stiefel.jl | 6 +-- src/manifolds/Symmetric.jl | 2 +- src/manifolds/SymmetricPositiveDefinite.jl | 10 ++-- .../SymmetricPositiveDefiniteLinearAffine.jl | 4 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 4 +- .../SymmetricPositiveDefiniteLogEuclidean.jl | 2 +- src/manifolds/VectorBundle.jl | 46 +++++++++---------- src/statistics.jl | 14 +++--- 23 files changed, 78 insertions(+), 78 deletions(-) diff --git a/docs/src/manifolds/graph.md b/docs/src/manifolds/graph.md index 7838513d56..dd880bff1e 100644 --- a/docs/src/manifolds/graph.md +++ b/docs/src/manifolds/graph.md @@ -1,7 +1,7 @@ # Graph manifold 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 $ℳ^{\lvert V \rvert}$ for the case of a vertex manifold or a $ℳ^{\lvert E \rvert}$ for the case of a edge manifold. +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: diff --git a/docs/src/manifolds/symmetricpositivedefinite.md b/docs/src/manifolds/symmetricpositivedefinite.md index 4016ec3f59..fcdc1a19dc 100644 --- a/docs/src/manifolds/symmetricpositivedefinite.md +++ b/docs/src/manifolds/symmetricpositivedefinite.md @@ -3,7 +3,7 @@ The symmetric positive definite matrices ```math -𝒫(n) = \bigl\{ A ∈ ℝ^{n × n}\ \big|\ A = A^{\mathrm{T}} \text{ and } x^{\mathrm{T}}Ax > 0 \text{ for } 0 ≠ x ∈ ℝ^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/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 9f9c90beb3..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 $ℳ$ (base space). -It is characterized by a continuous function $Π : E → ℳ$, such that for each point $p ∈ ℳ$ the preimage of $p$ by $Π$, $Π^{-1}(\{p\})$, 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/orthonormal_bases.md b/docs/src/orthonormal_bases.md index 5ab076f929..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 ℳ$ 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 ℳ$ 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))$. +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/groups/group.jl b/src/groups/group.jl index 2dcc57a66b..037d0d9dab 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -234,7 +234,7 @@ end ########################## @doc raw""" - inv(G::AbstractGroupManifold, x) + inv(G::AbstractGroupManifold, p) Inverse $p^{-1} ∈ G$ of an element $p ∈ G$, such that $p \circ p^{-1} = p^{-1} \circ p = e ∈ G$. diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 50ef269661..ebf5782b0c 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -33,7 +33,7 @@ direction(::AbstractGroupAction{AD}) where {AD} = AD() 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 $p ∈ ℳ$ and action element $a$, the right action is +point $p ∈ \mathcal M$ and action element $a$, the right action is ````math p ⋅ a ≐ a^{-1} ⋅ p. @@ -82,15 +82,15 @@ end @doc raw""" apply_diff(A::AbstractGroupAction, a, p, X) -For group point $p ∈ ℳ$ and tangent vector $X ∈ T_p ℳ$, compute the action of the +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)_p (X) &: T_p ℳ → T_{a ⋅ p} ℳ\\ -(\mathrm{d}R_a)_p (X) &: T_p ℳ → T_{p ⋅ a} ℳ +(\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} ```` """ @@ -105,15 +105,15 @@ end @doc raw""" inverse_apply_diff(A::AbstractGroupAction, a, p, X) -For group point $p ∈ ℳ$ and tangent vector $X ∈ T_p ℳ$, compute the action of 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)_p^{-1} (X) &: T_p ℳ → T_{a^{-1} ⋅ p} ℳ\\ -(\mathrm{d}R_a)_p^{-1} (X) &: T_p ℳ → T_{p ⋅ a^{-1}} ℳ +(\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} ```` """ @@ -139,7 +139,7 @@ the element closest to `q` in the metric of the G-manifold: ```math \arg\min_{g ∈ G} d_M(g ⋅ p, q) ``` -where $G$ is the group that acts on the G-manifold $ℳ$. +where $G$ is the group that acts on the G-manifold $\mathcal M$. """ function optimal_alignment(A::AbstractGroupAction, p, q) error("optimal_alignment not implemented for $(typeof(A)) and points $(typeof(p)) and $(typeof(q)).") diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 2068ec67f4..6b9c36e646 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -83,7 +83,7 @@ matrices `p`, `q` that are lower triangular with positive diagonal. The formula reads ````math -d_{ℳ}(p,q) = \sqrt{\sum_{i>j} (p_{ij}-q_{ij})^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 } ```` @@ -192,7 +192,7 @@ Parallely transport the tangent vector `X` at `p` along the geodesic to `q` on the [`CholeskySpace`](@ref) manifold `M`. The formula reads ````math -𝒫_{q←p}(X) = ⌊ X ⌋ +\mathcal P_{q←p}(X) = ⌊ X ⌋ + \operatorname{diag}(q)\operatorname{diag}(p)^{-1}\operatorname{diag}(X), ```` diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 415f14a45d..265553401b 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -318,7 +318,7 @@ 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 -𝒫_{q←p}(v) = X - \frac{⟨\log_p q,X⟩_p}{d^2_{ℂ}(p,q)} +\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`. diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 25521607d9..df7be313a1 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -9,15 +9,15 @@ where $𝔽 ∈ \{ℝ,ℂ\}$ and the rank is the number of linearly independent # Representation with 3 matrix factors -A point $p ∈ ℳ$ can be stored using unitary matrices $U ∈ 𝔽^{m × k}$, $V ∈ 𝔽^{n × k}$ as well as the $k$ +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_p ℳ$ at a point $p ∈ ℳ$ with $p=USV^\mathrm{H}$ +The tangent space $T_p \mathcal M$ at a point $p ∈ \mathcal M$ with $p=USV^\mathrm{H}$ is given by ````math -T_pℳ = \bigl\{ UMV^\mathrm{T} + U_pV^\mathrm{H} + UV_p^\mathrm{H} : +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} diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index d36a8674da..162017ae97 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -187,7 +187,7 @@ end returns the manifold dimension of the [`GraphManifold`](@ref) `N` on the vertices of a graph $G=(V,E)$, i.e. ````math -d_{𝒩} = \lvert V \rVert d_ℳ. +d_{\mathcal N} = \lvert V \rVert d_\mathcal M. ```` """ function manifold_dimension(M::VertexGraphManifold) @@ -199,7 +199,7 @@ end returns the manifold dimension of the [`GraphManifold`](@ref) `N` on the edges of a graph $G=(V,E)$, i.e. ````math -d_{𝒩} = \lvert E \rVert d_ℳ. +d_{\mathcal N} = \lvert E \rVert d_\mathcal M. ```` """ function manifold_dimension(M::EdgeGraphManifold) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 59efe78ca1..722471047f 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -249,7 +249,7 @@ isapprox(M::Grassmann, p, q; kwargs...) = isapprox(distance(M, p, q), 0.0; kwarg @doc raw""" log(M::Grassmann, p, q) -Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = ℳ=\mathrm{Gr}(n,k)$, +Compute the logarithmic map on the [`Grassmann`](@ref) `M`$ = \mathcal M=\mathrm{Gr}(n,k)$, i.e. the tangent vector `X` whose corresponding [`geodesic`](@ref) starting from `p` reaches `q` after time 1 on `M`. The formula reads diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 025f8dc6a3..fc891a7930 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -43,8 +43,8 @@ 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ℳ$, - $x ∈ ℳ$, of the [`Hyperbolic`](@ref) space $ℳ$. + it is positive definite when restricted to a tangent space $T_x\mathcal M$, + $x ∈ \mathcal M$, of the [`Hyperbolic`](@ref) space $\mathcal M$. """ struct MinkowskiMetric <: LorentzMetric end @@ -253,7 +253,7 @@ Compute the paralllel transport of the `X` from the tangent space at `p` on the connecting `p` and `q`. The formula reads ````math -𝒫_{q←p}X = X - \frac{⟨\log_p q,X⟩_x}{d^2_{ℍ^n}(p,q)} +\mathcal P_{q←p}X = X - \frac{⟨\log_p q,X⟩_x}{d^2_{ℍ^n}(p,q)} \bigl(\log_pq + \log_qp \bigr). ```` """ diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index c52e9a2512..92de8b0d14 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -41,7 +41,7 @@ abstract type LorentzMetric <: Metric end RiemannianMetric <: Metric Abstract type for Riemannian metrics, a family of positive definite inner -products. The positive definite property means that for $X ∈ T_p ℳ$, the +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 diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 8961195d33..49e8234342 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -41,7 +41,7 @@ abstract type AbstractPowerManifold{M<:Manifold,TPR<:AbstractPowerRepresentation @doc raw""" PowerManifold{TM<:Manifold, TSize<:Tuple, TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{TM} -The power manifold $ℳ^{n_1× n_2 × … × 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 @@ -505,12 +505,12 @@ end manifold_dimension(M::PowerManifold) Returns the manifold-dimension of an [`PowerManifold`](@ref) `M` -$=𝒩 = (ℳ)^{n_1,…,n_d}$, i.e. with $n=(n_1,…,n_d)$ the array -size of the power manifold and $d_{ℳ}$ the dimension of the base manifold -$ℳ$, the manifold is of dimension +$=\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_{𝒩} = d_{ℳ}\prod_{i=1}^d n_i = n_1n_2\cdot…\cdot n_d d_{ℳ}. +d_{\mathcal N} = d_{\mathcal M}\prod_{i=1}^d n_i = n_1n_2\cdot…\cdot n_d d_{\mathcal M}. ```` """ function manifold_dimension(M::PowerManifold{<:Manifold,TSize}) where {TSize} diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 357707da31..3266f7fe6d 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -328,7 +328,7 @@ end Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by ````math -𝒫_{q←p}(X) = X - \frac{⟨\log_pq,X⟩_p}{d^2_{𝕊^n}(p,q)} +\mathcal P_{q←p}(X) = X - \frac{⟨\log_pq,X⟩_p}{d^2_{𝕊^n}(p,q)} \bigl(\log_pq + \log_qp \bigr). ```` """ diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 9e65cfd6b9..4cfd3d738d 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -11,10 +11,10 @@ where $𝔽 ∈ \{ℝ, ℂ\}$, $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_n ∈ ℝ^{n× n}$ denotes the $k × k$ identity matrix. -The tangent space at a point $p ∈ ℳ$ is given by +The tangent space at a point $p ∈ \mathcal M$ is given by ````math -T_p ℳ = \{ X ∈ 𝔽^{n × k} : p^{\mathrm{H}}X + X^{\mathrm{H}}p=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 × k$ zero matrix. @@ -247,7 +247,7 @@ Project `X` onto the tangent space of `p` to the [`Stiefel`](@ref) manifold `M`. The formula reads ````math -\operatorname{proj}_{ℳ}(p, X) = X - p \operatorname{Sym}(p^{\mathrm{H}}X), +\operatorname{proj}_{\mathcal M}(p, X) = X - p \operatorname{Sym}(p^{\mathrm{H}}X), ```` where $\operatorname{Sym}(q)$ is the symmetrization of $q$, e.g. by diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index fd1aaf408d..e8cf3af9d7 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -330,7 +330,7 @@ of `X` from the tangent space at `p` on the [`SymmetricMatrices`](@ref) `M` to ` Since the metric is inherited from the embedding space, this is just the identity, i.e. ````math -𝒫_{q←p}(X) = X. +\mathcal P_{q←p}(X) = X. ```` """ vector_transport_to(::SymmetricMatrices, ::Any...) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 0b1bb95d77..1260182356 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -4,7 +4,7 @@ The manifold of symmetric positive definite matrices, i.e. ````math -𝒫(n) = +\mathcal P(n) = \bigl\{ p ∈ ℝ^{n × n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\} \bigr\} @@ -14,7 +14,7 @@ p ∈ ℝ^{n × n} : a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash SymmetricPositiveDefinite(n) -generates the manifold $𝒫(n) \subset ℝ^{n × n}$ +generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$ """ struct SymmetricPositiveDefinite{N} <: Manifold end @@ -90,9 +90,9 @@ injectivity_radius(M::SymmetricPositiveDefinite{N}, args...) where {N} = Inf manifold_dimension(M::SymmetricPositiveDefinite) returns the dimension of -[`SymmetricPositiveDefinite`](@ref) `M`$=𝒫(n), n ∈ ℕ$, i.e. +[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n ∈ ℕ$, i.e. ````math -\dim 𝒫(n) = \frac{n(n+1)}{2} +\dim \mathcal P(n) = \frac{n(n+1)}{2} ```` """ @generated function manifold_dimension(M::SymmetricPositiveDefinite{N}) where {N} @@ -128,7 +128,7 @@ end Return the size of an array representing an element on the [`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. $n × n$, the size of such a -symmetric positive definite matrix on $ℳ = 𝒫(n)$. +symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. """ @generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index ababf00ce7..22e01c19fd 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -16,7 +16,7 @@ Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between as a [`MetricManifold`](@ref) with [`LinearAffineMetric`](@ref). The formula reads ```math -d_{𝒫(n)}(p,q) +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 @@ -207,7 +207,7 @@ tangent space at `q` on the [`SymmetricPositiveDefinite`](@ref) as a The formula reads ```math -𝒫_{q←p}X = p^{\frac{1}{2}} +\mathcal P_{q←p}X = p^{\frac{1}{2}} \operatorname{Exp}\bigl( \frac{1}{2}p^{-\frac{1}{2}}\log_p(q)p^{-\frac{1}{2}} \bigr) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 9aef7ef2d9..1c8ab99caa 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -30,7 +30,7 @@ nmatrices, i.e. between two symmetric positive definite matrices `p` and `q` with respect to the [`LogCholeskyMetric`](@ref). The formula reads ````math -d_{𝒫(n)}(p,q) = \sqrt{ +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 }\ \ , ```` @@ -146,7 +146,7 @@ triangular matrix with the diagonal multiplied by $\frac{1}{2}$. With $V$ the pa transport on [`CholeskySpace`](@ref) from $x$ to $y$. The formula hear reads ````math -𝒫_{q←p}X = yV^{\mathrm{T}} + Vy^{\mathrm{T}}. +\mathcal P_{q←p}X = yV^{\mathrm{T}} + Vy^{\mathrm{T}}. ```` """ vector_transport_to( diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index e10cce5ff8..ba530fb098 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -14,7 +14,7 @@ Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between The formula reads ```math - d_{𝒫(n)}(p,q) = \lVert \Log p - \Log q \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 diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 33fce2fad5..fbb21e9015 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -180,10 +180,10 @@ distance(B::VectorBundleFibers, p, X, Y) = norm(B, p, X - Y) distance(B::VectorBundle, p, q) Distance between points $x$ and $y$ from the -vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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, $q = (x_q, V_q)$. @@ -192,7 +192,7 @@ The distance is calculated as $d_B(x, y) = \sqrt{d_M(x_p, x_q)^2 + d_F(V_p, V_{q←p})^2}$ -where $d_ℳ$ is the distance on manifold $ℳ$, $d_F$ is the distance +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 @@ -216,14 +216,14 @@ number_eltype(v::FVector) = number_eltype(v.data) exp(B::VectorBundle, p, X) Exponential map of tangent vector $X$ at point $p$ from -vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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 $X = (V_{X,M}, V_{X,F}) ∈ T_pB$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}ℳ$ and + $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 @@ -253,7 +253,7 @@ from the vector space of type `M` at point `p` from the underlying [`Manifold`]( The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$\flat : Tℳ → T^{*}ℳ$ +$\flat : T\mathcal M → T^{*}\mathcal M$ """ function flat(M::Manifold, p, X::FVector) ξ = allocate_result(M, flat, X, p) @@ -372,14 +372,14 @@ end inner(B::VectorBundle, p, X, Y) Inner product of tangent vectors `X` and `Y` at point `p` from the -vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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 = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}ℳ$ and + $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$. @@ -410,10 +410,10 @@ end log(B::VectorBundle, p, q) Logarithmic map of the point `y` at point `p` from -vector bundle `B` over manifold `B.fiber` (denoted $ℳ$). +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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, $q = (x_q, V_q)$. @@ -452,16 +452,16 @@ norm(B::VectorBundleFibers{<:TangentSpaceType}, p, X) = norm(B.manifold, p, X) project_point(B::VectorBundle, p) Project the point `p` from the ambient space of the vector bundle `B` -over manifold `B.fiber` (denoted $ℳ$) to the vector bundle. +over manifold `B.fiber` (denoted $\mathcal M$) to the vector bundle. Notation: - * The point $p = (x_p, V_p)$ where $x_p$ belongs to the ambient space of $ℳ$ + * 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 $x_p$ to the manifold $ℳ$ -and then projecting the vector $V_p$ to the tangent space $T_{x_p}ℳ$. +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...) @@ -479,15 +479,15 @@ 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 $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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 = (V_{X,M}, V_{X,F})$ where $x_p$ belongs to the ambient space of $T_{x_p}ℳ$ + * 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 $V_{X,M}$ to tangent space $T_{x_p}ℳ$ +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...) @@ -535,7 +535,7 @@ 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 : T^{*}ℳ → Tℳ$ +$\sharp : T^{*}\mathcal M → T\mathcal M$ """ function sharp(M::Manifold, p, ξ::FVector) X = allocate_result(M, sharp, ξ, p) @@ -663,10 +663,10 @@ end zero_tangent_vector(B::VectorBundle, p) Zero tangent vector at point `p` from the vector bundle `B` -over manifold `B.fiber` (denoted $ℳ$). The zero vector belongs to the space $T_{p}B$ +over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ ℳ$ and $V_p$ belongs to 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$. @@ -674,7 +674,7 @@ The zero vector is calculated as $\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ -where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}ℳ$ 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...) diff --git a/src/statistics.jl b/src/statistics.jl index f4b266c78b..f23274234f 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -33,7 +33,7 @@ t_k &= \frac{w_k}{\sum_{i=1}^k w_i}\\ where $x_k$ are points, $w_k$ are weights, $\mu_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 ∈ ℳ$. 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. @@ -123,9 +123,9 @@ 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 ∈ ℳ} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}^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}_{ℳ}$ denotes the Riemannian [`distance`](@ref). +where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). In the general case, the [`GradientDescentEstimation`](@ref) is used to compute the mean. However, this default may be overloaded for specific manifolds. @@ -390,9 +390,9 @@ end 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 ∈ ℳ} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{ℳ}(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}_{ℳ}$ denotes the Riemannian [`distance`](@ref). +where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). This function is nonsmooth (i.e nondifferentiable). In the general case, the [`CyclicProximalPointEstimation`](@ref) is used to compute the @@ -530,7 +530,7 @@ compute the (optionally weighted) variance of a `Vector` `x` of `n` data points on the [`Manifold`](@ref) `M`, i.e. ````math -\frac{1}{c} \sum_{i=1}^n w_i d_{ℳ}^2 (x_i,m), +\frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m), ```` where `c` is a correction term, see [Statistics.var](https://juliastats.org/StatsBase.jl/stable/scalarstats/#Statistics.var). @@ -565,7 +565,7 @@ compute the optionally weighted standard deviation of a `Vector` `x` of `n` data points on the [`Manifold`](@ref) `M`, i.e. ````math -\sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{ℳ}^2 (x_i,m)}, +\sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m)}, ```` where `c` is a correction term, see [Statistics.std](https://juliastats.org/StatsBase.jl/stable/scalarstats/#Statistics.std). From 21837c3c2fafda3d6492488c92cd591376cbaf0a Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:45:40 +0100 Subject: [PATCH 57/74] =?UTF-8?q?replace=20`\partial`=20with=20`=E2=88=82`?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifolds/GraphManifold.jl | 2 +- src/manifolds/MetricManifold.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index 162017ae97..d5791bb8a3 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -82,7 +82,7 @@ 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 `X` -together with its corresponding einty of `p` passes the +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...) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 92de8b0d14..6087b5c759 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -62,7 +62,7 @@ The Christoffel symbols are (in Einstein summation convention) $\Gamma_{ijk} = \frac{1}{2} \Bigl[g_{kj,i} + g_{ik,j} - g_{ij,k}\Bigr],$ -where $g_{ij,k}=\frac{\partial}{\partial p^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)$. """ @@ -99,7 +99,7 @@ end Get partial derivatives of the Christoffel symbols of the second kind for manifold `M` at `p` with respect to the coordinates of `p`, -$\frac{\partial}{\partial p^l} \Gamma^{k}_{ij} = \Gamma^{k}_{ij,l}.$ +$\frac{∂}{∂ p^l} \Gamma^{k}_{ij} = \Gamma^{k}_{ij,l}.$ The dimensions of the resulting multi-dimensional array are ordered $(i,j,k,l)$. """ function christoffel_symbols_second_jacobian(M::MetricManifold, p; backend = :default) @@ -322,7 +322,7 @@ end local_metric_jacobian(M::MetricManifold, p; backend=:default) Get partial derivatives of the local metric of `M` at `p` with respect to the -coordinates of `p`, $\frac{\partial}{\partial p^k} g_{ij} = g_{ij,k}$. 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, p; backend = :default) From ddd69b33558d937d10a13bd89ce4207ae5da6aa3 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:56:14 +0100 Subject: [PATCH 58/74] Apply suggestions from code review Co-Authored-By: Seth Axen --- src/manifolds/Circle.jl | 10 +++++----- src/manifolds/Euclidean.jl | 2 +- src/manifolds/Grassmann.jl | 12 ++++++------ src/manifolds/MetricManifold.jl | 12 ++++++------ src/manifolds/PowerManifold.jl | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 98935dd5bc..a1b8e085c4 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -21,7 +21,7 @@ Circle(f::AbstractNumbers = ℝ) = Circle{f}() 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 its a unit number, $p ∈ ℂ$ with $\lvert p \rvert = 1$. +for the complex-valued case, it is a unit number, $p ∈ ℂ$ with $\lvert p \rvert = 1$. """ check_manifold_point(::Circle, ::Any...) @@ -49,7 +49,7 @@ end 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 `X` are valid, since the tangent space is the whole real line. +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. """ @@ -97,11 +97,11 @@ distance(::Circle{ℂ}, p, q) = acos(clamp(complex_dot(p, q), -1, 1)) Compute the exponential map on the [`Circle`](@ref). ````math -\exp_pX = (p+X)_{2π}, +\exp_p X = (p+X)_{2π}, ```` 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) $𝕊^1$ is applied, to values in the +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...) @@ -203,7 +203,7 @@ Compute the logarithmic map on the [`Circle`](@ref) `M`. ```` 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) $𝕊^1$ is applied, to values in the +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...) diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index c2416e5c5d..e11445935d 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -261,7 +261,7 @@ end @doc raw""" project_point(M::Euclidean, p) -Project an arbitrary point `p` 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...) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 40cda8caad..80976d9638 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -65,19 +65,19 @@ function check_manifold_point(M::Grassmann{n,k,F}, p; kwargs...) where {n,k,F} if (F === ℝ) && !(eltype(p) <: Real) return DomainError( eltype(p), - "The matrix $(p) is not a real-valued matrix, so it does noe lie on the Grassmann manifold of dimension ($(n),$(k)).", + "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(p) <: Real) && !(eltype(p) <: Complex) return DomainError( eltype(p), - "The matrix $(p) is neiter real- nor complex-valued matrix, so it does noe lie on the complex Grassmann manifold of dimension ($(n),$(k)).", + "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(p) != representation_size(M) return DomainError( size(p), - "The matrix $(p) is does not lie on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + "The matrix $(p) does not lie on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", ) end c = p' * p @@ -114,19 +114,19 @@ function check_tangent_vector(G::Grassmann{n,k,F}, p, X; kwargs...) where {n,k,F if (F === ℂ) && !(eltype(X) <: Real) && !(eltype(X) <: Complex) return DomainError( eltype(X), - "The matrix $(X) is neiter real- nor complex-valued matrix, so it can not bea tangent vector to the complex Grassmann manifold of dimension ($(n),$(k)).", + "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(X) != representation_size(G) return DomainError( size(X), - "The matrix $(X) is does not lie in the tangent space of $(p) on the Grassmann manifold of dimension ($(n),$(k)), since its dimensions are wrong.", + "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(p' * X + X' * p, zeros(k, k); kwargs...) return DomainError( norm(p' * X + X' * p), - "The matrix $(X) is 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.", + "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 diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index c52e9a2512..52435d60be 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -192,7 +192,7 @@ Compute the musical isomorphism to transform the tangent vector `X` from the computing ````math -X^\flat = G_pX, +X^♭= G_p X, ```` where $G_p$ is the local matrix representation of `G`, see [`local_metric`](@ref) """ @@ -285,7 +285,7 @@ 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_p(X, Y) = ⟨X, G_pY⟩, +g_p(X, Y) = ⟨X, G_p Y⟩, ```` where $G_p$ is the local matrix representation of the [`Metric`](@ref) `G`. """ @@ -342,7 +342,7 @@ end @doc raw""" log_local_metric_density(M::MetricManifold, p) -Return the natural logarithm of the metric density $\rho$ of `M` at `p`, 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, p) = log(abs(det_local_metric(M, p))) / 2 @@ -514,7 +514,7 @@ Compute the musical isomorphism to transform the cotangent vector `ξ` from the computing ````math -ξ^\sharp = G_p^{-1}ξ, +ξ^♯ = G_p^{-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}$. @@ -544,9 +544,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} p^k + \Gamma^k_{ij} \frac{d}{dt} p_i \frac{d}{dt} p_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`. diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index b942079a62..a3e6e27bbc 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -282,7 +282,7 @@ end 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, so r every entry of `X` (and `p`) sparately +This can be done elementwise for each entry of `X` (and `p`). """ flat(::AbstractPowerManifold, ::Any...) From 59be2b28824879ebef48211e3e71397f2aaae9e7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:56:32 +0100 Subject: [PATCH 59/74] =?UTF-8?q?Replace=20`\Gamme`=20with=20`=CE=93`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifolds/MetricManifold.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 6087b5c759..b1a1187e23 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -60,7 +60,7 @@ end Compute the Christoffel symbols of the first kind in local coordinates. The Christoffel symbols are (in Einstein summation convention) -$\Gamma_{ijk} = \frac{1}{2} \Bigl[g_{kj,i} + g_{ik,j} - g_{ij,k}\Bigr],$ +$Γ_{ijk} = \frac{1}{2} \Bigl[g_{kj,i} + g_{ik,j} - g_{ij,k}\Bigr],$ where $g_{ij,k}=\frac{∂}{∂ p^k} g_{ij}$ is the coordinate derivative of the local representation of the metric tensor. The dimensions of @@ -80,9 +80,9 @@ end 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)$. """ @@ -99,7 +99,7 @@ end Get partial derivatives of the Christoffel symbols of the second kind for manifold `M` at `p` with respect to the coordinates of `p`, -$\frac{∂}{∂ p^l} \Gamma^{k}_{ij} = \Gamma^{k}_{ij,l}.$ +$\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, p; backend = :default) @@ -544,9 +544,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} p^k + \Gamma^k_{ij} \frac{d}{dt} p_i \frac{d}{dt} p_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`. From 2a10ef72004b563d0ad60a0b54d4175d94f0eb85 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 10:56:49 +0100 Subject: [PATCH 60/74] update `\dim`. --- src/manifolds/PowerManifold.jl | 2 +- src/manifolds/VectorBundle.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 49e8234342..bee385841d 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -510,7 +510,7 @@ size of the power manifold and $d_{\mathcal M}$ the dimension of the base manifo $\mathcal M$, the manifold is of dimension ````math -d_{\mathcal N} = d_{\mathcal M}\prod_{i=1}^d n_i = n_1n_2\cdot…\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} diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index fbb21e9015..a310f34b82 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -253,7 +253,7 @@ from the vector space of type `M` at point `p` from the underlying [`Manifold`]( The function can be used for example to transform vectors from the tangent bundle to vectors from the cotangent bundle -$\flat : T\mathcal M → T^{*}\mathcal M$ +$♭ : T\mathcal M → T^{*}\mathcal M$ """ function flat(M::Manifold, p, X::FVector) ξ = allocate_result(M, flat, X, p) @@ -535,7 +535,7 @@ 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 : T^{*}\mathcal M → T\mathcal M$ +$♯ : T^{*}\mathcal M → T\mathcal M$ """ function sharp(M::Manifold, p, ξ::FVector) X = allocate_result(M, sharp, ξ, p) From e1c06b0e9cdf8e353c230eef178c464fd4c34af4 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 11:08:28 +0100 Subject: [PATCH 61/74] use `\dim` more consistently throughout the docs of `manifold_dimension`. --- src/manifolds/CholeskySpace.jl | 6 +++++- src/manifolds/Circle.jl | 2 +- src/manifolds/FixedRankMatrices.jl | 2 +- src/manifolds/GraphManifold.jl | 4 ++-- src/manifolds/Hyperbolic.jl | 2 +- src/manifolds/Rotations.jl | 2 +- src/manifolds/Sphere.jl | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index da8059f237..f18f5c2b4c 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -167,7 +167,11 @@ end @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) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index cfdc42e8c0..c0e0ef3437 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -241,7 +241,7 @@ end manifold_dimension(M::Circle) Return the dimension of the [`Circle`](@ref) `M`, -i.e. $\operatorname{dim}(𝕊^1) = 1$. +i.e. $\dim(𝕊^1) = 1$. """ manifold_dimension(::Circle) = 1 diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index df7be313a1..a9c0ba24ca 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -228,7 +228,7 @@ Return the manifold dimension for the `𝔽`-valued [`FixedRankMatrices`](@ref) 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 `𝔽`. diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index d5791bb8a3..2d6036bfa3 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -187,7 +187,7 @@ end 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) @@ -199,7 +199,7 @@ end 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/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index fc891a7930..c113d50b36 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -194,7 +194,7 @@ end @doc raw""" manifold_dimension(H::Hyperbolic) -Return the dimension of the hyperbolic space manifold $ℍ^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 diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 16ec6922c3..6c81c3e8b7 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -453,7 +453,7 @@ end @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) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 3266f7fe6d..2d08b0b688 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -197,7 +197,7 @@ end @doc raw""" manifold_dimension(M::Sphere) -Return the dimension of the [`Sphere`](@ref)`(n) `M`, i.e. $𝕊^n$, which is $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 From 07f25b7c74c61ad01a1516c5d9c33fd8fa9e159f Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 13:26:37 +0100 Subject: [PATCH 62/74] rename B.v to B.frame_direction in the DiagonalizingONB. --- docs/src/manifolds/graph.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/manifolds/graph.md b/docs/src/manifolds/graph.md index dd880bff1e..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 + ```@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 From 3f72d23a8aab71864004d9388951dc9a4ad0d4aa Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 13:42:08 +0100 Subject: [PATCH 63/74] adds a few test cases, rename B.v to B.frame_direction and unify MetricManifold to pass down only the mutating version of exp, since we do the same for log. --- src/manifolds/Circle.jl | 6 ++--- src/manifolds/MetricManifold.jl | 23 ------------------- src/manifolds/ProductManifold.jl | 2 +- src/manifolds/Sphere.jl | 6 ++--- .../SymmetricPositiveDefiniteLinearAffine.jl | 4 ++-- src/manifolds/VectorBundle.jl | 4 ++-- src/orthonormal_bases.jl | 12 +++++----- test/symmetric_positive_definite.jl | 3 +++ test/vector_bundle.jl | 3 +++ 9 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index c0e0ef3437..b90e1eb0e1 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -123,14 +123,14 @@ flat(M::Circle, p::Number, X::TFVector) = FVector(CotangentSpace, X.data) flat!(::Circle, ξ::CoTFVector, p, X::TFVector) = copyto!(ξ, X) function get_basis(M::Circle{ℝ}, p, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) + sbv = sign(B.frame_direction[1]) vs = @SVector [@SVector [sbv == 0 ? one(sbv) : sbv]] return PrecomputedDiagonalizingOrthonormalBasis(vs, @SVector [0]) end get_coordinates(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X function get_coordinates(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) + sbv = sign(B.frame_direction[1]) return X .* (sbv == 0 ? 1 : sbv) end """ @@ -146,7 +146,7 @@ end get_vector(M::Circle{ℝ}, p, X, B::ArbitraryOrthonormalBasis) = X function get_vector(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) - sbv = sign(B.v[1]) + sbv = sign(B.frame_direction[1]) return X .* (sbv == 0 ? 1 : sbv) end """ diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index c4317a4986..7cd7f0caf5 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -149,29 +149,6 @@ coordinate chart that covers the entire manifold. This excludes coordinates in an embedded space. """ exp(::MetricManifold, ::Any...) -function exp(M::MMT, p, X, T::AbstractVector{T} where {T}) where {MMT<:MetricManifold} - return exp(M, is_default_metric(M), p, X, T) -end -function exp( - M::MMT, - ::Val{true}, - p, - X, - T::AbstractVector{T} where {T}, -) where {MMT<:MetricManifold} - return exp(base_manifold(M), p, X, T) -end -function exp( - M::MMT, - ::Val{false}, - p, - X, - T::AbstractVector{T} where {T}, -) where {MMT<:MetricManifold} - sol = solve_exp_ode(M, p, X, extrema(T); dense = false, saveat = T) - n = length(p) - return map(i -> sol.u[i][n+1:end], 1:length(T)) -end 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} diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index c94b712698..2f1ba4e0a3 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -254,7 +254,7 @@ function get_basis(M::ProductManifold, p, B::DiagonalizingOrthonormalBasis) vs = map(ziptuples( M.manifolds, submanifold_components(p), - submanifold_components(B.v), + submanifold_components(B.frame_direction), )) do t return get_basis(t[1], t[2], DiagonalizingOrthonormalBasis(t[3])) end diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 2d08b0b688..5ef20d51d7 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -17,12 +17,12 @@ Sphere(n::Int) = Sphere{n}() function get_basis(M::Sphere{N}, x, B::DiagonalizingOrthonormalBasis) where {N} A = zeros(N + 1, N + 1) A[1, :] = transpose(x) - A[2, :] = transpose(B.v) + 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, x, 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] diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index 22e01c19fd..dfcff3770e 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -67,7 +67,7 @@ Return a orthonormal basis `Ξ` as a vector of tangent vectors (of length [`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}, @@ -75,7 +75,7 @@ function get_basis( B::DiagonalizingOrthonormalBasis, ) where {N} xSqrt = sqrt(p) - eigv = eigen(B.v) + eigv = eigen(B.frame_direction) V = eigv.vectors Ξ = [ (i == j ? 1 / 2 : diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index a310f34b82..49999f72c4 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -270,9 +270,9 @@ end function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) xp1 = submanifold_component(p, Val(1)) - bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(1))) + bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) b1 = get_basis(M.manifold, xp1, bv1) - bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.v, Val(2))) + bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) b2 = get_basis(M.fiber, xp1, bv2) return PrecomputedVectorBundleOrthonormalBasis(b1, b2) end diff --git a/src/orthonormal_bases.jl b/src/orthonormal_bases.jl index cb11a9f007..004e65e798 100644 --- a/src/orthonormal_bases.jl +++ b/src/orthonormal_bases.jl @@ -71,20 +71,20 @@ function ProjectedOrthonormalBasis(method::Symbol, F::AbstractNumbers = ℝ) end @doc raw""" - DiagonalizingOrthonormalBasis(v, F::AbstractNumbers = ℝ) + 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 `frameDirection` $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 = @@ -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/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/vector_bundle.jl b/test/vector_bundle.jl index 031d3d7b6f..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] From 8c9cca5725db5accf4a6be805c58e644172a0697 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 14:01:17 +0100 Subject: [PATCH 64/74] fixes documentation for project tangent on the circle and adds a test. --- src/manifolds/Circle.jl | 1 + test/circle.jl | 1 + test/metric.jl | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index b90e1eb0e1..3e3ee85b5d 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -281,6 +281,7 @@ For the real-valued case this is just the identity. 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, ::Any, ::Any) project_tangent(::Circle{ℝ}, p::Real, X::Real) = X project_tangent(::Circle{ℂ}, p::Number, X::Number) = X - complex_dot(p, X) * p 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/metric.jl b/test/metric.jl index 40a617a935..8163eb724e 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, y, x, v) === exp(M, y, x, v) @test log!(MM2, v, x, y) === log!(M, v, x, y) + @test log(MM2, v, x, y) === log(M, v, 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) From 102c75560741ea71d98d0890de52055941639cef Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 15:33:38 +0100 Subject: [PATCH 65/74] unify Metric Manifold to not implement exp/log neither but only exp!/log! --- src/manifolds/MetricManifold.jl | 18 ++++++++++++++---- test/metric.jl | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 7cd7f0caf5..4af86c558b 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -308,11 +308,21 @@ function local_metric_jacobian(M, p; backend = :default) 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, X) + +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,X)`. +""" +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}, Y, p, q) where {MMT<:MetricManifold} +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 diff --git a/test/metric.jl b/test/metric.jl index 8163eb724e..f8364e871a 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -282,9 +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, 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, 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) From 0be2cf34e2c0eb3e872dba78ee79e43ac0f8959c Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 4 Feb 2020 17:02:44 +0100 Subject: [PATCH 66/74] Fixes a typo in MetricManifolds log documentation and explains that for other than the default metric one has to provide an implementation. --- src/manifolds/MetricManifold.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index 4af86c558b..c41b439843 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -309,12 +309,13 @@ function local_metric_jacobian(M, p; backend = :default) end @doc raw""" - log(N::MetricManifold{M,G}, p, X) + 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,X)`. +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...) From d20ab25ab0c6ba4480da7e289d6b84e9b245c7da Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 08:11:51 +0100 Subject: [PATCH 67/74] change vout to Y, introduce Cartesian product and power to notation, correct emanating. --- docs/src/notation.md | 2 + src/groups/group.jl | 2 +- src/groups/group_action.jl | 8 +- src/groups/group_operation_action.jl | 4 +- src/groups/product_group.jl | 8 +- src/manifolds/CholeskySpace.jl | 4 +- src/manifolds/Euclidean.jl | 2 +- src/manifolds/FixedRankMatrices.jl | 8 +- src/manifolds/Hyperbolic.jl | 2 +- src/manifolds/Sphere.jl | 2 +- src/manifolds/Stiefel.jl | 20 ++-- src/manifolds/Symmetric.jl | 36 +++---- .../SymmetricPositiveDefiniteLinearAffine.jl | 16 +-- .../SymmetricPositiveDefiniteLogCholesky.jl | 2 +- src/orthonormal_bases.jl | 18 ++-- test/groups/array_manifold.jl | 102 +++++++++--------- test/groups/group_utils.jl | 38 +++---- 17 files changed, 138 insertions(+), 136 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 9fc9471526..a3d8beb512 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -10,6 +10,8 @@ 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) | +| $\^$ | (n-ary) Cartesian power | | 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, \operatorname{dim}(\mathcal M)$| for the real dimension sometimes also $\operatorname{dim}_{\mathbb R}(\mathcal M)$| diff --git a/src/groups/group.jl b/src/groups/group.jl index 037d0d9dab..053c0888f3 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -433,7 +433,7 @@ 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, p, q, X, conv::ActionDirection, ::Val{false}) - return error("translate_diff not implemented on $(typeof(G)) for elements $(typeof(vout)), $(typeof(p)) and $(typeof(q)), vector $(typeof(X)), and direction $(typeof(conv))") + 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, p, q, X, conv::ActionDirection) pq = translate(G, p, q, conv) diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index ebf5782b0c..3925c579a0 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -98,8 +98,8 @@ 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, p, X) - return error("apply_diff! not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), vectors $(typeof(vout)) and $(typeof(X))") +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 raw""" @@ -121,8 +121,8 @@ 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, p, X) - return apply_diff!(A, vout, inv(base_group(A), a), p, X) +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) diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index d31261e412..be8e59dc80 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -42,8 +42,8 @@ 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, p, X) - return translate_diff!(A.group, vout, a, p, X, 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, p, X) diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index 8064587fe7..b2b1e00a46 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -207,8 +207,8 @@ function translate_diff( )...) end function translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) - vout = allocate_result(M, translate_diff, X, p, q) - return translate_diff!(M, vout, p, q, X, conv) + Y = allocate_result(M, translate_diff, X, p, q) + return translate_diff!(M, Y, p, q, X, conv) end function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) @@ -247,8 +247,8 @@ function inverse_translate_diff( )...) end function inverse_translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) - vout = allocate_result(M, inverse_translate_diff, X, p, q) - return inverse_translate_diff!(M, vout, p, q, X, conv) + 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, Y, p, q, X, conv::ActionDirection) diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index f18f5c2b4c..ded18cc9f3 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -98,7 +98,7 @@ end @doc raw""" exp(M::CholeskySpace, p, X) -Compute the exponential map on the [`CholeskySpace`](@ref) `M` eminating from the lower +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 @@ -143,7 +143,7 @@ end @doc raw""" log(M::CholeskySpace, X, p, q) -Compute the logarithmic map on the [`CholeskySpace`](@ref) `M` for the geodesic eminating +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 diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 43d81c55aa..2950645083 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -19,7 +19,7 @@ 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}() diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index a9c0ba24ca..06d6e16314 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - FixedRankMatrices{m,n,k,T} <: Manifold + FixedRankMatrices{m,n,k,F} <: Manifold The manifold of $m × n$ real-valued or complex-valued matrices of fixed rank $k$, i.e. ````math @@ -42,9 +42,9 @@ 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 raw""" diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index c113d50b36..e03414bd59 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -112,7 +112,7 @@ distance(M::Hyperbolic, p, q) = acosh(max(-minkowski_dot(p, q), 1.0)) @doc raw""" exp(M::Hyperbolic, p, X) -Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ eminating +Compute the exponential map on the [`Hyperbolic`](@ref) space $ℍ^n$ emanating from `p` towards `X`. The formula reads ````math diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 5ef20d51d7..d90a65e696 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -95,7 +95,7 @@ distance(::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) exp(M::Sphere, p, X) Compute the exponential map from `p` into the tangent direction `X` on the [`Sphere`](@ref) -`M` by following the great arc eminating from `p` in direction `X`. +`M` by following the great arc emanating from `p` in direction `X`. ````math \exp_p X = \cos(\lVert X \rVert_p)p + \sin(\lVert X \rVert_p)\frac{X}{\lVert X \rVert_p}, diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 4cfd3d738d..799730dab3 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,5 +1,5 @@ @doc raw""" - Stiefel{n,k,T} <: Manifold + Stiefel{n,k,F} <: Manifold The Stiefel manifold consists of all $n × k$, $n\geq k$ unitary matrices, i.e. @@ -7,7 +7,7 @@ The Stiefel manifold consists of all $n × k$, $n\geq k$ unitary matrices, i.e. \{ p ∈ 𝔽^{n × k} : p^{\mathrm{H}}p = I_k \}, ```` -where $𝔽 ∈ \{ℝ, ℂ\}$, +where `F`$=𝔽 ∈ \{ℝ, ℂ\}$, $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, and $I_n ∈ ℝ^{n× n}$ denotes the $k × k$ identity matrix. @@ -43,14 +43,14 @@ Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{S [`AbstractNumbers`](@ref) type and $p^{\mathrm{H}}p$ is (approximatly) 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}, p; kwargs...) where {n,k,T} - if (T === ℝ) && !(eltype(p) <: Real) +function check_manifold_point(M::Stiefel{n,k,F}, p; kwargs...) where {n,k,F} + if (F === ℝ) && !(eltype(p) <: Real) return DomainError( 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(p) <: Real) && !(eltype(p) <: Complex) + if (F === ℂ) && !(eltype(p) <: Real) && !(eltype(p) <: Complex) return DomainError( 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)).", @@ -79,16 +79,16 @@ Checks whether `X` is a valid tangent vector at `p` on the [`Stiefel`](@ref) it (approximtly) 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}, p, X; kwargs...) where {n,k,T} +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(X) <: Real) + if (F === ℝ) && !(eltype(X) <: Real) return DomainError( 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(X) <: Real) && !(eltype(X) <: Complex) + if (F === ℂ) && !(eltype(X) <: Real) && !(eltype(X) <: Complex) return DomainError( 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)).", @@ -111,8 +111,8 @@ end @doc raw""" exp(M, p, X) -Computes the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` -eminating from `p` into tangent direction `X`. +Computes the exponential map on the [`Stiefel`](@ref)`{n,k,F}`() manifold `M` +emanating from `p` into tangent direction `X`. ````math \exp_p X = \begin{pmatrix} diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index e8cf3af9d7..267ec9187a 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -110,7 +110,7 @@ distance(M::SymmetricMatrices, p, q) = norm(p - q) @doc raw""" exp(M::SymmetricMatrices, p, X) -Compute the exponential map eminating from `p` in tangent direction `X` on the +Compute the exponential map emanating from `p` in tangent direction `X` on the [`SymmetricMatrices`](@ref) `M`, which reads ````math @@ -140,16 +140,16 @@ function get_coordinates( B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = similar(X, dim) + 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] = X[i, j] * scale + @inbounds Y[k] = X[i, j] * scale k += 1 end - return vout + return Y end function get_coordinates( M::SymmetricMatrices{N,ℂ}, @@ -158,18 +158,18 @@ function get_coordinates( B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = similar(X, dim) + 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(X[i, j]) * scale + @inbounds Y[k] = real(X[i, j]) * scale k += 1 - @inbounds vout[k] = imag(X[i, j]) * scale + @inbounds Y[k] = imag(X[i, j]) * scale k += 1 end - return vout + return Y end function get_vector( @@ -179,17 +179,17 @@ function get_vector( B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, p) + Y = allocate_result(M, get_vector, p) @assert size(X) == (div(N * (N + 1), 2),) - @assert size(vout) == (N, N) + @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] = X[k] * scale - @inbounds vout[j, i] = X[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,ℂ}, @@ -198,17 +198,17 @@ function get_vector( B::ArbitraryOrthonormalBasis{ℝ}, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, p, p .* 1im) + Y = allocate_result(M, get_vector, p, p .* 1im) @assert size(X) == (N * (N + 1),) - @assert size(vout) == (N, N) + @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(X[k], X[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 raw""" diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index dfcff3770e..a90b4c9998 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -102,16 +102,16 @@ function get_coordinates( B::ArbitraryOrthonormalBasis, ) where {N} dim = manifold_dimension(M) - vout = similar(X, dim) + 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] = X[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}, @@ -129,17 +129,17 @@ function get_vector( B::ArbitraryOrthonormalBasis, ) where {N} dim = manifold_dimension(M) - vout = allocate_result(M, get_vector, p) + Y = allocate_result(M, get_vector, p) @assert size(X) == (div(N * (N + 1), 2),) - @assert size(vout) == (N, N) + @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] = X[k] * scale - @inbounds vout[j, i] = X[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}, diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 1c8ab99caa..f2fe99a073 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -106,7 +106,7 @@ end log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) Compute the logarithmic map on [`SymmetricPositiveDefinite`](@ref) `M` with -respect to the [`LogCholeskyMetric`](@ref) eminating from `p` to `q`. +respect to the [`LogCholeskyMetric`](@ref) emanating from `p` to `q`. The formula can be adapted from the [`CholeskySpace`](@ref) as ````math \log_p q = xW^{\mathrm{T}} + Wx^{\mathrm{T}}, diff --git a/src/orthonormal_bases.jl b/src/orthonormal_bases.jl index 004e65e798..f6c29373a1 100644 --- a/src/orthonormal_bases.jl +++ b/src/orthonormal_bases.jl @@ -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 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 From 92940a7c8094212314677dce28026724580431a4 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 08:32:25 +0100 Subject: [PATCH 68/74] unifies spacing in math formulae, trace to tr, and adds further terms to the notations table. --- docs/src/notation.md | 8 +++- src/groups/array_manifold.jl | 42 +++++++++---------- src/groups/rotation_action.jl | 8 ++-- src/groups/semidirect_product_group.jl | 16 +++---- src/manifolds/Euclidean.jl | 2 +- src/manifolds/Grassmann.jl | 2 +- src/manifolds/Hyperbolic.jl | 2 +- src/manifolds/Rotations.jl | 2 +- src/manifolds/Sphere.jl | 4 +- src/manifolds/Stiefel.jl | 2 +- src/manifolds/Symmetric.jl | 4 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 2 +- src/manifolds/VectorBundle.jl | 2 +- 13 files changed, 51 insertions(+), 45 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index a3d8beb512..4fd31a94f7 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -10,7 +10,7 @@ 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) | +| $\times$ | Cartesian product of two manifolds | | see [`ProductManifold`](@ref) | | $\^$ | (n-ary) Cartesian power | | 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$. | @@ -20,7 +20,9 @@ as long as that renders still in $\TeX$ within this documentation. | $\mathbb F$ | a field | | field a manifold is based on, usually $\mathcal 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 (vector or matrix) | | | $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 | | @@ -30,6 +32,10 @@ as long as that renders still in $\TeX$ within this documentation. | $\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 (vector or matrix) | | | $B$ | a vector bundle | | +| $0_k$ | the $k\times k$ zero matrix. | | diff --git a/src/groups/array_manifold.jl b/src/groups/array_manifold.jl index 0a2fb0830c..54e58628bb 100644 --- a/src/groups/array_manifold.jl +++ b/src/groups/array_manifold.jl @@ -5,9 +5,9 @@ array_point(e::Identity) = e function inv(M::ArrayManifold, p; kwargs...) is_manifold_point(M, p, true; kwargs...) - y = array_point(inv(M.manifold, array_value(p))) - is_manifold_point(M, y, true; kwargs...) - return y + q = array_point(inv(M.manifold, array_value(p))) + is_manifold_point(M, q, true; kwargs...) + return q end function inv!(M::ArrayManifold, q, p; kwargs...) @@ -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,20 +79,20 @@ 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, Y, p, q, X, conv::ActionDirection; kwargs...) @@ -116,7 +116,7 @@ function inverse_translate_diff(M::ArrayManifold, p, q, X, conv::ActionDirection is_manifold_point(M, p, true; kwargs...) is_manifold_point(M, q, true; kwargs...) is_tangent_vector(M, q, X, true; kwargs...) - vout = ArrayTVector(inverse_translate_diff( + Y = ArrayTVector(inverse_translate_diff( M.manifold, array_value(p), array_value(q), @@ -124,8 +124,8 @@ function inverse_translate_diff(M::ArrayManifold, p, q, X, conv::ActionDirection conv, )) xinvy = inverse_translate(M, p, q, conv) - is_tangent_vector(M, xinvy, vout, true; kwargs...) - return vout + is_tangent_vector(M, xinvy, Y, true; kwargs...) + return Y end function inverse_translate_diff!( diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index df3438e978..58ed606934 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -57,11 +57,11 @@ function apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, p, X) where { return inv(base_group(A), a) * X end -function apply_diff!(A::RotationActionOnVector{N,F,LeftAction}, vout, a, p, X) where {N,F} - return mul!(vout, a, X) +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, p, X) where {N,F} - return mul!(vout, inv(base_group(A), a), X) +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, p, X) where {N,F} diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index ab903c83a9..9837f8b32b 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -109,14 +109,14 @@ 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, p) - ny, hy = submanifold_components(G, q) - nv, hv = submanifold_components(G, X) - nvout, hvout = submanifold_components(G, Y) - 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) + 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 diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 2950645083..380528169a 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -138,7 +138,7 @@ of arrays (or tensors) of size $n_1 × n_2 × … × n_i$, i.e. ````math g_p(X,Y) = \sum_{k ∈ I} \overline{X}_{k} Y_{k}, ```` -where $I$ is the set of integer vectors $k ∈ ℕ^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 diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index f27f836349..0ea3424c78 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -201,7 +201,7 @@ Compute the inner product for two tangent vectors `X`, `Y` from the tangent spac of `p` on the [`Grassmann`](@ref) manifold `M`. The formula reads ````math -g_p(X,Y) = \operatorname{trace}(X^{\mathrm{H}}Y), +g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}Y), ```` where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index e03414bd59..b21f68e003 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -254,7 +254,7 @@ connecting `p` and `q`. The formula reads ````math \mathcal P_{q←p}X = X - \frac{⟨\log_p q,X⟩_x}{d^2_{ℍ^n}(p,q)} -\bigl(\log_pq + \log_qp \bigr). +\bigl(\log_p q + \log_qp \bigr). ```` """ vector_transport_to(::Hyperbolic, ::Any, ::Any, ::Any, ::ParallelTransport) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 6c81c3e8b7..411e24ac7c 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -144,7 +144,7 @@ Compute the exponential map on the [`Rotations`](@ref) from `p` into direction `X`, i.e. ````math -\exp_pX = p \operatorname{Exp}(X), +\exp_p X = p \operatorname{Exp}(X), ```` where $\operatorname{Exp}(X)$ denotes the matrix exponential of $X$. diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index d90a65e696..db8f0e1f5c 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -328,8 +328,8 @@ end Compute the [`ParallelTransport`](@ref) on the [`Sphere`](@ref) `M`, which is given by ````math -\mathcal P_{q←p}(X) = X - \frac{⟨\log_pq,X⟩_p}{d^2_{𝕊^n}(p,q)} -\bigl(\log_pq + \log_qp \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) diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 799730dab3..b64f2791eb 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -148,7 +148,7 @@ 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 -g_p(X,Y) = \operatorname{trace}(X^{\mathrm{H}}Y), +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. diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 267ec9187a..054d155301 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -114,7 +114,7 @@ Compute the exponential map emanating from `p` in tangent direction `X` on the [`SymmetricMatrices`](@ref) `M`, which reads ````math -\exp_pX = p + X. +\exp_p X = p + X. ```` """ exp(::SymmetricMatrices, ::Any...) @@ -232,7 +232,7 @@ Compute the logarithmic map from `p` to `q` on the [`SymmetricMatrices`](@ref) ` reads ````math -\log_pq = q-p. +\log_p q = q-p. ```` """ log(::SymmetricMatrices, ::Any...) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index f2fe99a073..bc2bd119d4 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -54,7 +54,7 @@ Compute the exponential map on the [`SymmetricPositiveDefinite`](@ref) `M` with [`LogCholeskyMetric`](@ref) from `p` into direction `X`. The formula reads ````math -\exp_pX = (\exp_y W)(\exp_y W)^\mathrm{T} +\exp_p X = (\exp_y W)(\exp_y W)^\mathrm{T} ```` where $\exp_xW$ is the exponential map on [`CholeskySpace`](@ref), $y$ is the cholesky diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 49999f72c4..fd50031619 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -420,7 +420,7 @@ Notation: The logarithmic map is calculated as -$\log_pq = (\log_{x_p}(x_q), V_{\log} - V_p)$ +$\log_p q = (\log_{x_p}(x_q), V_{\log} - V_p)$ 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$. From b06cad5f6cbac0047a4d8db90c9adff628264bd7 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 09:13:13 +0100 Subject: [PATCH 69/74] Apply suggestions from code review Co-Authored-By: Seth Axen --- docs/src/notation.md | 2 +- src/manifolds/ProductManifold.jl | 10 +++++----- src/manifolds/Rotations.jl | 18 +++++++++--------- src/manifolds/Sphere.jl | 4 ++-- src/manifolds/Stiefel.jl | 6 +++--- src/manifolds/Symmetric.jl | 4 ++-- .../SymmetricPositiveDefiniteLogCholesky.jl | 2 +- src/orthonormal_bases.jl | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index 9fc9471526..4418c52910 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -15,7 +15,7 @@ as long as that renders still in $\TeX$ within this documentation. | $n$ | dimension (of a manifold) | $n_1,n_2,\ldots,m, \operatorname{dim}(\mathcal M)$| for the real dimension sometimes also $\operatorname{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 $\mathcal F \in \{\mathbb R,\mathbb C\}$ | +| $\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 | | | $e$ | identity element of a group | | diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 2f1ba4e0a3..51f667f28d 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -342,8 +342,8 @@ function hat!(M::ProductManifold, X, p, Xⁱ) ts = ziptuples(M.manifolds, submanifold_components(M, X), submanifold_components(M, p)) for t ∈ ts dim = manifold_dimension(first(t)) - tvⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) - hat!(t..., tvⁱ) + tXⁱ = @inbounds view(Xⁱ, i:(i+dim-1)) + hat!(t..., tXⁱ) i += dim end return X @@ -569,7 +569,7 @@ end 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, so vor every entry of `ξ` (and `p`) sparately +This can be done elementwise for every entry of `ξ` (and `p`) separately """ sharp(::ProductManifold, ::Any...) @@ -668,8 +668,8 @@ function vee!(M::ProductManifold, Xⁱ, p, X) for t ∈ ts SM = first(t) dim = manifold_dimension(SM) - tvⁱ = @inbounds view(Xⁱ, 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 Xⁱ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 6c81c3e8b7..a40ed5baa9 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -3,7 +3,7 @@ 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 in $ℝ^n$. +since these matrices represent all rotations of points in $ℝ^n$. # Constructor @@ -41,7 +41,7 @@ end @doc raw""" angles_4d_skew_sym_matrix(A) -The Lie algebra of [`Rotations`](@ref) in $ℝ^4$, $\mathrm{SO}(4)$, consists of $4× 4$ +The Lie algebra of [`Rotations(4)`](@ref) in $ℝ^{4 × 4}$, $𝔰𝔬(4)$, consists of $4 × 4$ skew-symmetric matrices. The unique imaginary components of their eigenvalues are the angles of the two plane rotations. This function computes these more efficiently than `eigvals`. @@ -144,7 +144,7 @@ Compute the exponential map on the [`Rotations`](@ref) from `p` into direction `X`, i.e. ````math -\exp_pX = p \operatorname{Exp}(X), +\exp_p X = p \operatorname{Exp}(X), ```` where $\operatorname{Exp}(X)$ denotes the matrix exponential of $X$. @@ -232,8 +232,8 @@ function exp!(M::Rotations{4}, q, p, X) a₂ = (sincα - (1 - r) / 2 * cosα) * c a₃ = (e + (1 - r) * (e - sincα / 2)) * c end - v² = X * X - q .= a₀ .* p .+ p * (a₁ .* X .+ a₂ .* v² .+ a₃ .* (v² * X)) + X² = X * X + q .= a₀ .* p .+ p * (a₁ .* X .+ a₂ .* X² .+ a₃ .* (X² * X)) return q end @@ -486,7 +486,7 @@ Compute the norm of a tangent vector `X` from the tangent space at `p` on the ```` i.e. the Frobenius norm of `X`, where tangent vectors are represented by -elements from the Lie group. +elements from the Lie algebra. """ norm(M::Rotations, p, X) = norm(X) @@ -533,7 +533,7 @@ end Project `p` to the nearest point on manifold `M`. -Given the singular value decomposition $p = 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 @@ -636,8 +636,8 @@ retract(::Rotations, ::Any, ::Any, ::PolarRetraction) @doc raw""" retract(M, p, X, ::QRRetraction) -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 first-order approximation of the exponential map. +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) """ diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 5ef20d51d7..5260f539e6 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -8,7 +8,7 @@ vectors in $ℝ^{n+1}$ of unit length Sphere(n) -Generate the $𝕊^{n}\subset ℝ^{n+1}$ +Generate $𝕊^{n} ⊂ ℝ^{n+1}$. """ struct Sphere{N} <: Manifold end @@ -94,7 +94,7 @@ distance(::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) @doc raw""" exp(M::Sphere, p, X) -Compute the exponential map from `p` into the tangent direction `X` on the [`Sphere`](@ref) +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 diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 4cfd3d738d..d39b03e29f 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -40,7 +40,7 @@ Stiefel(n::Int, k::Int, F::AbstractNumbers = ℝ) = Stiefel{n,k,F}() check_manifold_point(M::Stiefel, p; kwargs...) Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. that it has the right -[`AbstractNumbers`](@ref) type and $p^{\mathrm{H}}p$ is (approximatly) the identity, where $\cdot^{\mathrm{H}}$ is the +[`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}, p; kwargs...) where {n,k,T} @@ -76,7 +76,7 @@ end 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 $p^{\mathrm{H}}X + X^{\mathrm{H}}p = 0$. +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}, p, X; kwargs...) where {n,k,T} @@ -112,7 +112,7 @@ end exp(M, p, X) Computes the exponential map on the [`Stiefel`](@ref)`{n,k,T}`() manifold `M` -eminating from `p` into tangent direction `X`. +emanating from `p` in tangent direction `X`. ````math \exp_p X = \begin{pmatrix} diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index e8cf3af9d7..78535601a2 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -11,7 +11,7 @@ where $\cdot^{\mathrm{H}}$ denotes the hermitian, i.e. complex conjugate transpo and the field $𝔽 ∈ \{ ℝ, ℂ\}$ is set by the [`AbstractNumbers`](@ref) `F`. -Though it is slighty redundant, usually the matrices are safed as $n × n$ arrays. +Though it is slightly redundant, usually the matrices are stored as $n × n$ arrays. # Constructor @@ -64,7 +64,7 @@ end check_tangent_vector(M::SymmetricMatrices{n,F}, p, X; kwargs... ) 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 dimension `(n,n)` +[`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 `p` and `X` can be set using `kwargs...`. diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 1c8ab99caa..d9bbeac967 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -111,7 +111,7 @@ The formula can be adapted from the [`CholeskySpace`](@ref) as ````math \log_p q = xW^{\mathrm{T}} + Wx^{\mathrm{T}}, ```` -where $x$ is the colesky factor of $p$ and $W=\log_xy$ for $y$ the cholesky factor +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...) diff --git a/src/orthonormal_bases.jl b/src/orthonormal_bases.jl index 004e65e798..ff208ec771 100644 --- a/src/orthonormal_bases.jl +++ b/src/orthonormal_bases.jl @@ -75,7 +75,7 @@ end 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 `frameDirection` $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. """ From 741085bbde54c1ae3c0daf1d6f11a7a6cfaafbec Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 09:18:06 +0100 Subject: [PATCH 70/74] apply the remaining changes from review. --- src/Manifolds.jl | 54 ++++++------ src/manifolds/Grassmann.jl | 2 +- src/manifolds/ProductManifold.jl | 9 +- src/manifolds/Rotations.jl | 86 +++++++++---------- src/manifolds/Sphere.jl | 12 +-- src/manifolds/Symmetric.jl | 9 +- .../SymmetricPositiveDefiniteLinearAffine.jl | 34 ++++---- src/statistics.jl | 36 ++++---- test/statistics.jl | 26 +++--- 9 files changed, 135 insertions(+), 133 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 23921d160f..3a835dc2f9 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -109,62 +109,62 @@ Abstract type for defining statistical estimation methods. abstract type AbstractEstimationMethod end @doc raw""" - hat(M::Manifold, x, vⁱ) + 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 -∧ : v^i ↦ 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 raw""" - vee(M::Manifold, x, v) + 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 ↦ 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/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 0ea3424c78..6b6a7140be 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -147,7 +147,7 @@ where ````math b_{i}=\begin{cases} 0 & \text{if} \; S_i \geq 1\\ -\operatorname{acos}(S_i) & \, \text{if} \; S_i<1. +\arccos(S_i) & \, \text{if} \; S_i<1. \end{cases} ```` """ diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 2f1ba4e0a3..13bd586a17 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -231,11 +231,12 @@ function exp!(M::ProductManifold, q, p, X) end @doc raw""" - flat(M::ProductManifold, p, w::FVector{TangentSpaceType}) + 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 for every entry of `w` (and `x`) separately. +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...) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 411e24ac7c..406b577842 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -250,49 +250,49 @@ function get_vector(M::Rotations, p, X, B::ArbitraryOrthonormalBasis) where {N} end @doc raw""" - hat(M::Rotations, p, ω) + hat(M::Rotations, p, Xⁱ) -Convert the unique tangent vector components $\omega$ at point `p` on [`Rotations`](@ref) +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(::Rotations, ::Any...) -function hat!(M::Rotations{2}, Ω, p, θ::Real) - @assert length(Ω) == 4 +function hat!(M::Rotations{2}, X, p, Xⁱ::Real) + @assert length(X) == 4 @inbounds begin - Ω[1] = 0 - Ω[3] = -θ - Ω[2] = θ - Ω[4] = 0 + X[1] = 0 + X[3] = -Xⁱ + X[2] = Xⁱ + X[4] = 0 end - return Ω + return X end -hat!(M::Rotations{2}, Ω, p, ω) = hat!(M, Ω, p, ω[1]) -function hat!(M::Rotations{N}, Ω, p, ω) where {N} - @assert size(Ω) == (N, N) - @assert length(ω) == manifold_dimension(M) +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 - Ω[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, 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 raw""" @@ -659,43 +659,43 @@ end show(io::IO, ::Rotations{N}) where {N} = print(io, "Rotations($(N))") @doc raw""" - vee(M::Rotations, P, Ω) + 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 ∈ [4, n], j ∈ [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}, ω, p, Ω) 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}, ω, p, Ω) - ω[1] = Ω[2] - return ω +function vee!(M::Rotations{2}, Xⁱ, p, X) + Xⁱ[1] = X[2] + return Xⁱ end @doc raw""" diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index db8f0e1f5c..5fed7aa73b 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -14,15 +14,15 @@ 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[1, :] = transpose(p) A[2, :] = transpose(B.frame_direction) V = nullspace(A) κ = ones(N) 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.frame_direction / norm(M, x, B.frame_direction), 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] @@ -86,7 +86,7 @@ The formula is given by the (shorter) great arc length on the (or a) great circl both `p` and `q` lie on. ````math -d_{𝕊^n}(p,q) = \operatorname{acos}(⟨p,q⟩). +d_{𝕊^n}(p,q) = \arccos(⟨p,q⟩). ```` """ distance(::Sphere, x, y) = acos(clamp(dot(x, y), -1, 1)) @@ -138,8 +138,8 @@ metric from the embedding, i.e. $ g_p(X,Y) = X^\mathrm{T}Y$. 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) - v0 = vcat(0, X) - return 2 * xp1 * dot(xp1, v0) / dot(xp1, xp1) - v0 + X0 = vcat(0, X) + return 2 * xp1 * dot(xp1, X0) / dot(xp1, xp1) - X0 end @doc raw""" diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 054d155301..8469d43b3e 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -306,12 +306,13 @@ for the $n × n$ it's `(n,n)`. @generated representation_size(::SymmetricMatrices{N}) where {N} = (N, N) @doc raw""" - sharp(M::SymmetricMatrices, x, w::FVector{CotangentSpaceType}) + 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 $ℝ$, 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...) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index a90b4c9998..1435c45bbd 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -49,14 +49,14 @@ function exp!(M::SymmetricPositiveDefinite{N}, q, p, X) where {N} 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 * X * 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!(q, xue * Se * transpose(xue)) + pUe = pSqrt * Ue + return copyto!(q, pUe * Se * transpose(pUe)) end @doc raw""" @@ -74,7 +74,7 @@ function get_basis( p, B::DiagonalizingOrthonormalBasis, ) where {N} - xSqrt = sqrt(p) + pSqrt = sqrt(p) eigv = eigen(B.frame_direction) V = eigv.vectors Ξ = [ @@ -188,13 +188,13 @@ function log!(M::SymmetricPositiveDefinite{N}, X, p, q) where {N} 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 * q * 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!(X, xue, Se * transpose(xue)) + pUe = pSqrt * e2.vectors + return mul!(X, pUe, Se * transpose(pUe)) end @doc raw""" @@ -239,10 +239,10 @@ function vector_transport_to!( 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 * X * xSqrtInv) - ty = Symmetric(xSqrtInv * q * 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 @@ -250,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)) + pUe = pSqrt * Uf * Sf * transpose(Uf) + vtp = Symmetric(pUe * tv * transpose(pUe)) return copyto!(Y, vtp) end diff --git a/src/statistics.jl b/src/statistics.jl index f23274234f..a5e1075dd7 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -24,13 +24,13 @@ 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 ∈ \mathcal M$. The algorithm @@ -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 @@ -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) @@ -344,7 +344,7 @@ function mean!( injectivity_radius(M, q) ≤ radius && return q for i in eachindex(x) @inbounds if distance(M, q, x[i]) ≥ radius - return mean!(M, q, x, w, GradientDescentEstimation(); x0 = q, kwargs...) + return mean!(M, q, x, w, GradientDescentEstimation(); p0 = q, kwargs...) end end return q @@ -355,7 +355,7 @@ function mean!( x::AbstractVector, w::AbstractVector, ::CyclicProximalPointEstimation; - x0 = x[1], + p0 = x[1], stop_iter = 1000000, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -365,18 +365,18 @@ 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!(q, x0) + copyto!(q, p0) yold = allocate_result(M, median, q) ytmp = copy(yold) - v = zero_tangent_vector(M, q) + X = zero_tangent_vector(M, q) wv = convert(Vector, w) ./ sum(w) for i = 1:stop_iter λ = 0.5 / i copyto!(yold, q) for j = 1:n @inbounds t = (2 * λ * wv[j]) / (1 + 2 * λ * wv[j]) - @inbounds inverse_retract!(M, v, q, x[j], inverse_retraction) - retract!(M, ytmp, q, v, t, retraction) + @inbounds inverse_retract!(M, X, q, x[j], inverse_retraction) + retract!(M, ytmp, q, X, t, retraction) copyto!(q, ytmp) end isapprox(M, q, yold; kwargs...) && break @@ -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 @@ -493,7 +493,7 @@ function median!( x::AbstractVector, w::AbstractVector, ::CyclicProximalPointEstimation; - x0 = x[1], + p0 = x[1], stop_iter = 1000000, retraction::AbstractRetractionMethod = ExponentialRetraction(), inverse_retraction::AbstractInverseRetractionMethod = LogarithmicInverseRetraction(), @@ -503,7 +503,7 @@ 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!(q, x0) + copyto!(q, p0) yold = allocate_result(M, median, q) ytmp = copy(yold) v = zero_tangent_vector(M, q) @@ -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 diff --git a/test/statistics.jl b/test/statistics.jl index ff94db891c..41d198005b 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))) + p0 = 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))] + 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()) From e7bba5c2c7e6260f510ea939e02160d31b449e45 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 09:31:26 +0100 Subject: [PATCH 71/74] Finalize notations.md. --- docs/src/manifolds/rotations.md | 6 +++--- docs/src/notation.md | 6 +++--- src/manifolds/Rotations.jl | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/manifolds/rotations.md b/docs/src/manifolds/rotations.md index f857669ad7..57cee3a049 100644 --- a/docs/src/manifolds/rotations.md +++ b/docs/src/manifolds/rotations.md @@ -3,7 +3,7 @@ The manifold $\mathrm{SO}(n)$ of orthogonal matrices with determinant $+1$ in $ℝ^{n× n}$, i.e. $\mathrm{SO}(n) = \bigl\{R ∈ ℝ^{n × n} \big| RR^{\mathrm{T}} = -R^{\mathrm{T}}R = \mathrm{I}_n, \det(R) = 1 \bigr\}$ +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. @@ -13,8 +13,8 @@ This convention allows for more efficient operations on tangent vectors. Tangent spaces at different points are different vector spaces. Let $L_R: \mathrm{SO}(n) → \mathrm{SO}(n)$ where $R ∈ \mathrm{SO}(n)$ be the left-multiplication by $R$, that is $L_R(S) = RS$. -The tangent space at rotation $R$, $T_R \mathrm{SO}(n)$, is related to the tangent space at the identity rotation $\mathrm{I}_n$ by the differential of $L_R$ at identity, $(\mathrm{d}L_R)_{\mathrm{I}_n} : T_{\mathrm{I}_n} \mathrm{SO}(n) → T_R \mathrm{SO}(n)$. -For a tangent vector at the identity rotation $v ∈ 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 ∈ T_R \mathrm{SO}(n)$. +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/notation.md b/docs/src/notation.md index 4fd31a94f7..b4a094ff5c 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -11,7 +11,7 @@ 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) | -| $\^$ | (n-ary) Cartesian power | | see [`PowerManifold`](@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, \operatorname{dim}(\mathcal M)$| for the real dimension sometimes also $\operatorname{dim}_{\mathbb R}(\mathcal M)$| @@ -20,7 +20,7 @@ as long as that renders still in $\TeX$ within this documentation. | $\mathbb F$ | a field | | field a manifold is based on, usually $\mathcal 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 (vector or matrix) | | +| $\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$ | | @@ -36,6 +36,6 @@ as long as that renders still in $\TeX$ within this documentation. | $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 (vector or matrix) | | +| $\cdot^\mathrm{T}$ | transposed | | | $B$ | a vector bundle | | | $0_k$ | the $k\times k$ zero matrix. | | diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 406b577842..d184a07748 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -344,7 +344,7 @@ The formula reads where $s$ is the solution to the Sylvester equation -$p^{\mathrm{T}}qs + s(p^{\mathrm{T}}q)^{\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) From fcb489570c9a2497e8a57a3e4e5862bd3f95ad8d Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 10:12:43 +0100 Subject: [PATCH 72/74] Fixes a final typo. --- test/statistics.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/statistics.jl b/test/statistics.jl index 41d198005b..6665cfe8b9 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -518,7 +518,7 @@ mean_and_var(M::TestStatsOverload1, x::AbstractVector, w::AbstractWeights, ::Tes rng = MersenneTwister(47) R = Manifolds.Rotations(3) p0 = collect(Diagonal(ones(3))) - v1 = hat(R, x0, [1.0, 0.0, 0.0]) + 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)]) From 4e21e0e349eca062f05f5d9888910ad7dc4fbb87 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 10:56:04 +0100 Subject: [PATCH 73/74] unifies usage of \dim. --- docs/src/notation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/notation.md b/docs/src/notation.md index f26f532cea..f6e1a0dd76 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -14,7 +14,7 @@ as long as that renders still in $\TeX$ within this documentation. | $^{\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, \operatorname{dim}(\mathcal M)$| for the real dimension sometimes also $\operatorname{dim}_{\mathbb R}(\mathcal M)$| +| $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\}$ | From 4bef2c9c124d17eca243234c976c3fc584243d29 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 5 Feb 2020 11:37:21 +0100 Subject: [PATCH 74/74] updates another docstring. --- src/manifolds/PowerManifold.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index c5ac114082..2409dfbff5 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -620,11 +620,11 @@ function retract!(M::AbstractPowerManifold, q, p, X, method::PowerRetraction) end @doc raw""" - sharp(M::AbstractPowerManifold, x, w::FVector{CotangentSpaceType}) + 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...)