diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d2c6b75ea..46c975d052 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,28 +38,28 @@ Even providing a single new method is a good contribution. A main contribution you can provide is another manifold that is not yet included in the package. -A manifold is a concrete type of [`Manifold`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.Manifold) from [`ManifoldsBase.jl`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html). +A manifold is a concrete type of [`AbstractManifold`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.AbstractManifold) from [`ManifoldsBase.jl`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html). This package also provides the main set of functions a manifold can/should implement. Don't worry if you can only implement some of the functions. If the application you have in mind only requires a subset of these functions, implement those. The [`ManifoldsBase.jl`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html) interface provides concrete error messages for the remaining unimplemented functions. One important detail is that the interface usually provides a mutating as well as a non-mutating variant -See for example [exp!](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.exp!-Tuple{Manifold,Any,Any,Any}) and [exp](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.exp-Tuple{Manifold,Any,Any}). +See for example [exp!](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.exp!-Tuple{AbstractManifold,Any,Any,Any}) and [exp](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.exp-Tuple{AbstractManifold,Any,Any}). The non-mutating one (e.g. `exp`) always falls back to use the mutating one, so in most cases it should suffice to implement the mutating one (e.g. `exp!`). -Note that since the first argument is _always_ the [`Manifold`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.Manifold), the mutated argument is always the second one in the signature. -In the example we have `exp(M, x, v)` for the exponential map and `exp!(M, y, v, x)` for the mutating one, that stores the result in `y`. +Note that since the first argument is _always_ the [`AbstractManifold`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#ManifoldsBase.AbstractManifold), the mutated argument is always the second one in the signature. +In the example we have `exp(M, p, X)` for the exponential map and `exp!(M, q, X, p)` for the mutating one, which stores the result in `q`. On the other hand, the user will most likely look for the documentation of the non-mutating version, so we recommend adding the docstring for the non-mutating one, where all different signatures should be collected in one string when reasonable. This can best be achieved by adding a docstring to the method with a general signature with the first argument being your manifold: ````julia -struct MyManifold <: Manifold end +struct MyManifold <: AbstractManifold end @doc raw""" - exp(M::MyManifold, x, v) + exp(M::MyManifold, p, X) Describe the function. """ @@ -73,7 +73,7 @@ We run [`JuliaFormatter.jl`](https://github.com/domluna/JuliaFormatter.jl) on th We also follow a few internal conventions: -- It is preferred that the `Manifold`'s struct contain a reference to the general theory. +- It is preferred that the `AbstractManifold`'s struct contain a reference to the general theory. - Any implemented function should be accompanied by its mathematical formulae if a closed form exists. - Within the source code of one manifold, the type of the manifold should be the first element of the file, and an alphabetical order of the functions is preferable. - The above implies that the mutating variant of a function follows the non-mutating variant. diff --git a/Project.toml b/Project.toml index 3a7e55e133..19b5475d23 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Manifolds" uuid = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Antoine Levitt "] -version = "0.4.27" +version = "0.5.0" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -30,7 +30,7 @@ FiniteDiff = "2" FiniteDifferences = "0.12" HybridArrays = "0.4" LightGraphs = "1" -ManifoldsBase = "0.10.6" +ManifoldsBase = "0.11" Plots = "~1.6, =1.10.5" RecipesBase = "1.1" Requires = "0.5, 1" diff --git a/README.md b/README.md index c4e97d6623..bcf049d651 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ To install the package just type ] add Manifolds ``` -Then you can directly start, for example to stop half way from the north pole on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) to a point on the the equator, you can generate the [`shortest_geodesic`](https://juliamanifolds.github.io/Manifolds.jl/stable/interface.html#ManifoldsBase.shortest_geodesic-Tuple{Manifold,Any,Any}). -It internally employs [`exp`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.exp-Tuple{Manifold,Any,Any}) and [`log`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.log-Tuple{Manifold,Any,Any}). +Then you can directly start, for example to stop half way from the north pole on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) to a point on the the equator, you can generate the [`shortest_geodesic`](https://juliamanifolds.github.io/Manifolds.jl/stable/interface.html#ManifoldsBase.shortest_geodesic-Tuple{AbstractManifold,Any,Any}). +It internally employs [`exp`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.exp-Tuple{AbstractManifold,Any,Any}) and [`log`](https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#Base.log-Tuple{AbstractManifold,Any,Any}). ```julia using Manifolds diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 0d2b89616d..b73bb9be40 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -12,7 +12,7 @@ SUITE = BenchmarkGroup() Random.seed!(12334) function add_manifold( - M::Manifold, + M::AbstractManifold, pts, name; test_tangent_vector_broadcasting=true, @@ -157,7 +157,7 @@ function add_manifold_benchmarks() add_manifold( m_prod, pts_prod_mpoints, - "ProductManifold with MPoint"; + "ProductManifold with AbstractManifoldPoint"; test_tangent_vector_broadcasting=false, ) diff --git a/docs/Project.toml b/docs/Project.toml index ba550568d5..4fcc3ed654 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -14,9 +14,9 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -Documenter = "0.24, 0.25" +Documenter = "0.24, 0.25, 0.26" HybridArrays = "0.4" -ManifoldsBase = "0.10" +ManifoldsBase = "0.11" Plots = "= 1.10.5" StaticArrays = "1.0" PyPlot = "2.9" diff --git a/docs/make.jl b/docs/make.jl index 55ec71b176..aedcb197d5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -69,6 +69,7 @@ makedocs( ], ], "Features on Manifolds" => [ + "Atlases and charts" => "features/atlases.md", "Differentiation" => "features/differentiation.md", "Distributions" => "features/distributions.md", "Statistics" => "features/statistics.md", diff --git a/docs/src/examples/manifold.md b/docs/src/examples/manifold.md index e5d0aac23b..d153399bf9 100644 --- a/docs/src/examples/manifold.md +++ b/docs/src/examples/manifold.md @@ -16,8 +16,8 @@ This tutorial demonstrates that you can get your first own manifold quite fast a This tutorial assumes that you heard of the exponential map, tangent vectors and the dimension of a manifold. If not, please read for example [[do Carmo, 1992](#doCarmo1992)], Chapter 3, first. -In general you need just a datatype (`struct`) that inherits from [`Manifold`](@ref) to define a manifold. No function is _per se_ required to be implemented. -However, it is a good idea to provide functions that might be useful to others, for example [`check_manifold_point`](@ref) and [`check_tangent_vector`](@ref), as we do in this tutorial. +In general you need just a datatype (`struct`) that inherits from [`AbstractManifold`](@ref) to define a manifold. No function is _per se_ required to be implemented. +However, it is a good idea to provide functions that might be useful to others, for example [`check_point`](@ref) and [`check_vector`](@ref), as we do in this tutorial. We start with two technical preliminaries. If you want to start directly, you can [skip](@ref manifold-tutorial-task) this paragraph and revisit it for two of the implementation details. @@ -30,14 +30,14 @@ After that, we will ## [Technical preliminaries](@id manifold-tutorial-prel) There are only two small technical things we need to explain at this point. -First of all our [`Manifold`](@ref)`{𝔽}` has a parameter `𝔽`. +First of all our [`AbstractManifold`](@ref)`{𝔽}` has a parameter `𝔽`. This parameter indicates the [`number_system`](@ref) the manifold is based on, for example `ℝ` for real manifolds. It is important primarily for defining bases of tangent spaces. See [`SymmetricMatrices`](@ref Main.Manifolds.SymmetricMatrices) as an example of defining both a real-valued and a complex-valued symmetric manifolds using one type. -Second, a main design decision of `Manifold.jl` is that most functions are implemented as mutating functions, i.e. as in-place-computations. There usually exists a non-mutating version that falls back to allocating memory and calling the mutating one. This means you only have to implement the mutating version, _unless_ there is a good reason to provide a special case for the non-mutating one, i.e. because in that case you know a far better performing implementation. +Second, a main design decision of `Manifolds.jl` is that most functions are implemented as mutating functions, i.e. as in-place-computations. There usually exists a non-mutating version that falls back to allocating memory and calling the mutating one. This means you only have to implement the mutating version, _unless_ there is a good reason to provide a special case for the non-mutating one, i.e. because in that case you know a far better performing implementation. Let's look at an example. The exponential map $\exp_p\colon T_p\mathcal M \to \mathcal M$ that maps a tangent vector $X\in T_p\mathcal M$ from the tangent space at $p\in \mathcal M$ to the manifold. -The function [`exp`](@ref exp(M::Manifold, p, X)) has to know the manifold `M`, the point `p` and the tangent vector `X` as input, so to compute the resulting point `q` you need to call +The function [`exp`](@ref exp(M::AbstractManifold, p, X)) has to know the manifold `M`, the point `p` and the tangent vector `X` as input, so to compute the resulting point `q` you need to call ```julia q = exp(M, p, X) @@ -50,11 +50,11 @@ q = similar(p) exp!(M, q, p, X) ``` -calls [`exp!`](@ref exp!(M::Manifold, q, p, X)), which modifies its input `q` and returns the resulting point in there. -Actually these two lines are (almost) the default implementation for [`exp`](@ref exp(M::Manifold, p, X)). [`allocate_result`](@ref) that is actually used there just calls `similar` for simple `Array`s. +calls [`exp!`](@ref exp!(M::AbstractManifold, q, p, X)), which modifies its input `q` and returns the resulting point in there. +Actually these two lines are (almost) the default implementation for [`exp`](@ref exp(M::AbstractManifold, p, X)). [`allocate_result`](@ref) that is actually used there just calls `similar` for simple `Array`s. Note that for a unified interface, the manifold `M` is _always_ the first parameter, and the variable the result will be stored to in the mutating variants is _always_ the second parameter. -Long story short: if possible, implement the mutating version [`exp!`](@ref exp!(M::Manifold, q, p, X)), you get the [`exp`](@ref exp(M::Manifold, p, X)) for free. +Long story short: if possible, implement the mutating version [`exp!`](@ref exp!(M::AbstractManifold, q, p, X)), you get the [`exp`](@ref exp(M::AbstractManifold, p, X)) for free. Many functions that build upon basic functions employ the mutating variant, too, to avoid reallocations. ## [Startup](@id manifold-tutorial-startup) @@ -64,7 +64,7 @@ For implementing a manifold, loading the interface should suffice for quite some ```@example manifold-tutorial using ManifoldsBase, LinearAlgebra, Test -import ManifoldsBase: check_manifold_point, check_tangent_vector, manifold_dimension, exp! +import ManifoldsBase: check_point, check_vector, manifold_dimension, exp! ``` ## [Goal](@id manifold-tutorial-task) @@ -81,11 +81,11 @@ For our example we define ```@example manifold-tutorial """ - MySphere{N} <: Manifold{ℝ} + MySphere{N} <: AbstractManifold{ℝ} Define an `n`-sphere of radius `r`. Construct by `MySphere(radius,n)` """ -struct MySphere{N} <: Manifold{ManifoldsBase.ℝ} where {N} +struct MySphere{N} <: AbstractManifold{ManifoldsBase.ℝ} where {N} radius::Float64 end MySphere(radius, n) = MySphere{n}(radius) @@ -103,7 +103,7 @@ S = MySphere(1.5, 2) ## [Checking points and tangents](@id manifold-tutorial-checks) If we have now a point, represented as an array, we would first like to check, that it is a valid point on the manifold. -For this one can use the easy interface [`is_manifold_point`](@ref is_manifold_point(M::Manifold, p; kwargs...)). This internally uses [`check_manifold_point`](@ref check_manifold_point(M, p; kwargs...)). +For this one can use the easy interface [`is_point`](@ref is_point(M::AbstractManifold, p; kwargs...)). This internally uses [`check_point`](@ref check_point(M, p; kwargs...)). This is what we want to implement. We have to return the error if `p` is not on `M` and `nothing` otherwise. @@ -112,7 +112,7 @@ To spare a few lines, we can use [short-circuit evaluation](https://docs.juliala If something has to only hold up to precision, we can pass that down, too using the `kwargs...`. ```@example manifold-tutorial -function check_manifold_point(M::MySphere{N}, p; kwargs...) where {N} +function check_point(M::MySphere{N}, p; kwargs...) where {N} (size(p)) == (N+1,) || return DomainError(size(p),"The size of $p is not $((N+1,)).") if !isapprox(norm(p), M.radius; kwargs...) return DomainError(norm(p), "The norm of $p is not $(M.radius).") @@ -125,11 +125,7 @@ nothing #hide Similarly, we can verify, whether a tangent vector `X` is valid. It has to fulfill the same size requirements and it has to be orthogonal to `p`. We can again use the `kwargs`, but also provide a way to check `p`, too. ```@example manifold-tutorial -function check_tangent_vector(M::MySphere, p, X, check_base_point = true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::MySphere, p, X; kwargs...) size(X) != size(p) && return DomainError(size(X), "The size of $X is not $(size(p)).") if !isapprox(dot(p,X), 0.0; kwargs...) return DomainError(dot(p,X), "The tangent $X is not orthogonal to $p.") @@ -142,17 +138,17 @@ nothing #hide to test points we can now use ```@example manifold-tutorial -is_manifold_point(S, [1.0,0.0,0.0]) # norm 1, so not on S, returns false -@test_throws DomainError is_manifold_point(S, [1.5,0.0], true) # only on R^2, throws an error. +is_point(S, [1.0,0.0,0.0]) # norm 1, so not on S, returns false +@test_throws DomainError is_point(S, [1.5,0.0], true) # only on R^2, throws an error. p = [1.5,0.0,0.0] X = [0.0,1.0,0.0] # The following two tests return true -[ is_manifold_point(S, p); is_tangent_vector(S,p,X) ] +[ is_point(S, p); is_vector(S,p,X) ] ``` ## [Functions on the manifold](@id manifold-tutorial-fn) -For the [`manifold_dimension`](@ref manifold_dimension(M::Manifold)) we have to just return the `N` parameter +For the [`manifold_dimension`](@ref manifold_dimension(M::AbstractManifold)) we have to just return the `N` parameter ```@example manifold-tutorial manifold_dimension(::MySphere{N}) where {N} = N @@ -188,21 +184,21 @@ A first easy check can be done taking `p` from above and any vector `X` of lengt ```@example manifold-tutorial q = exp(S,p, [0.0,1.5Ο€,0.0]) -[isapprox(p,-q); is_manifold_point(S,q)] +[isapprox(p,-q); is_point(S,q)] ``` ## [Conclusion](@id manifold-tutorial-outlook) You can now just continue implementing further functions from the [interface](../interface.md), -but with just [`exp!`](@ref exp!(M::Manifold, q, p, X)) you for example already have +but with just [`exp!`](@ref exp!(M::AbstractManifold, q, p, X)) you for example already have -* [`geodesic`](@ref geodesic(M::Manifold, p, X)) the (not necessarily shortest) geodesic emanating from `p` in direction `X`. -* the [`ExponentialRetraction`](@ref), that the [`retract`](@ref retract(M::Manifold, p, X)) function uses by default. +* [`geodesic`](@ref geodesic(M::AbstractManifold, p, X)) the (not necessarily shortest) geodesic emanating from `p` in direction `X`. +* the [`ExponentialRetraction`](@ref), that the [`retract`](@ref retract(M::AbstractManifold, p, X)) function uses by default. -For the [`shortest_geodesic`](@ref shortest_geodesic(M::Manifold, p, q)) the implementation of a logarithm [`log`](@ref ManifoldsBase.log(M::Manifold, p, q)), again better a [`log!`](@ref log!(M::Manifold, X, p, q)) is necessary. +For the [`shortest_geodesic`](@ref shortest_geodesic(M::AbstractManifold, p, q)) the implementation of a logarithm [`log`](@ref ManifoldsBase.log(M::AbstractManifold, p, q)), again better a [`log!`](@ref log!(M::AbstractManifold, X, p, q)) is necessary. -Sometimes a default implementation is provided; for example if you implemented [`inner`](@ref inner(M::Manifold, p, X, Y)), the [`norm`](@ref norm(M, p, X)) is defined. You should overwrite it, if you can provide a more efficient version. For a start the default should suffice. -With [`log!`](@ref log!(M::Manifold, X, p, q)) and [`inner`](@ref inner(M::Manifold, p, X, Y)) you get the [`distance`](@ref distance(M::Manifold, p, q)), and so. +Sometimes a default implementation is provided; for example if you implemented [`inner`](@ref inner(M::AbstractManifold, p, X, Y)), the [`norm`](@ref norm(M, p, X)) is defined. You should overwrite it, if you can provide a more efficient version. For a start the default should suffice. +With [`log!`](@ref log!(M::AbstractManifold, X, p, q)) and [`inner`](@ref inner(M::AbstractManifold, p, X, Y)) you get the [`distance`](@ref distance(M::AbstractManifold, p, q)), and so. In summary with just these few functions you can already explore the first things on your own manifold. Whenever a function from `Manifolds.jl` requires another function to be specifically implemented, you get a reasonable error message. diff --git a/docs/src/features/atlases.md b/docs/src/features/atlases.md new file mode 100644 index 0000000000..f7d9d98ce7 --- /dev/null +++ b/docs/src/features/atlases.md @@ -0,0 +1,36 @@ +# [Atlases and charts](@id atlases_and_charts) + +Atlases on an $n$-dimensional manifold $\mathcal M$ are collections of charts $\{(U_i, \varphi_i) \colon i \in I\}$ such that $U_i \subseteq \mathcal M$ and each chart $\varphi_i$ is a function from $U_i$ to $\mathbb{R}^n$. +They provide a basic connection between manifolds and Euclidean spaces. + +Most operations on manifolds in `Manifolds.jl` avoid operating in a chart through appropriate embeddings and formulas derived for particular manifolds, though atlases provide the most general way of working with manifolds. +They are extensively used in metric-related functions on [`MetricManifold`](@ref Main.Manifolds.MetricManifold)s. + +Atlases are represented by objects of subtypes of [`AbstractAtlas`](@ref Main.Manifolds.AbstractAtlas). +There are no type restrictions for indices of charts in atlases. + +Operations using atlases and charts are available through the following functions: + +* [`get_chart_index`](@ref Main.Manifolds.get_chart_index) can be used to select an appropriate chart for the neighborhood of a given point. +* [`get_point_coordinates`](@ref Main.Manifolds.get_point_coordinates) converts a point to its coordinates in a chart. +* [`get_point`](@ref Main.Manifolds.get_point) converts coordinates in a chart to the point that corresponds to them. +* [`induced_basis`](@ref Main.Manifolds.induced_basis) returns a basis of a given vector space at a point induced by a chart. +* [`transition_map`](@ref Main.Manifolds.transition_map) converts coordinates of a point between two charts. + +```@autodocs +Modules = [Manifolds,ManifoldsBase] +Pages = ["atlases.jl"] +Order = [:type, :function] +``` + +## Cotangent space and musical isomorphisms + +Related to atlases, there is also support for the cotangent space and coefficients of cotangent vectors in bases of the cotangent space. + +Functions [`sharp`](@ref) and [`flat`](@ref) implement musical isomorphisms for arbitrary vector bundles. + +```@autodocs +Modules = [Manifolds,ManifoldsBase] +Pages = ["cotangent_space.jl"] +Order = [:type, :function] +``` diff --git a/docs/src/features/differentiation.md b/docs/src/features/differentiation.md index e224f1399c..fde3765293 100644 --- a/docs/src/features/differentiation.md +++ b/docs/src/features/differentiation.md @@ -16,4 +16,4 @@ Order = [:type, :function, :constant] Modules = [Manifolds] Pages = ["riemannian_diff.jl"] Order = [:type, :function, :constant] -``` \ No newline at end of file +``` diff --git a/docs/src/features/utilities.md b/docs/src/features/utilities.md index 5ccc46f0a2..3f0aa49efc 100644 --- a/docs/src/features/utilities.md +++ b/docs/src/features/utilities.md @@ -1,6 +1,6 @@ # Ease of notation -The following terms introduce a nicer notation for some operations, for example using the ∈ operator, $p ∈ \mathcal M$, to determine whether $p$ is a point on the [`Manifold`](@ref) $\mathcal M$. +The following terms introduce a nicer notation for some operations, for example using the ∈ operator, $p ∈ \mathcal M$, to determine whether $p$ is a point on the [`AbstractManifold`](@ref) $\mathcal M$. ````@docs in diff --git a/docs/src/interface.md b/docs/src/interface.md index 5dd68bc1cf..966e929be8 100644 --- a/docs/src/interface.md +++ b/docs/src/interface.md @@ -63,16 +63,20 @@ julia> y[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. +* [`allocate_result`](@ref) allocates a result of a particular function (for example [`exp`](@ref), [`flat`](@ref), 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. ## Bases The following functions and types provide support for bases of the tangent space of different manifolds. +Moreover, bases of the cotangent space are also supported, though this description focuses on the tangent space. An orthonormal basis of the tangent space $T_p \mathcal M$ of (real) dimension $n$ has a real-coefficient basis $e_1, e_2, …, e_n$ if $\mathrm{Re}(g_p(e_i, e_j)) = Ξ΄_{ij}$ for each $i,j ∈ \{1, 2, …, n\}$ where $g_p$ is the Riemannian metric at point $p$. A vector $X$ from the tangent space $T_p \mathcal M$ can be expressed in Einstein notation as a sum $X = X^i e_i$, where (real) coefficients $X^i$ are calculated as $X^i = \mathrm{Re}(g_p(X, e_i))$. +Bases are closely related to [atlases](@ref atlases_and_charts). + The main types are: + * [`DefaultOrthonormalBasis`](@ref), which is designed to work when no special properties of the tangent space basis are required. It is designed to make [`get_coordinates`](@ref) and [`get_vector`](@ref) fast. * [`DiagonalizingOrthonormalBasis`](@ref), which diagonalizes the curvature tensor and makes the curvature in the selected direction equal to 0. @@ -86,12 +90,21 @@ The main functions are: * [`get_vector`](@ref) returns a vector for the specified coordinates. * [`get_vectors`](@ref) returns a vector of basis vectors. Calling it should be avoided for high-dimensional manifolds. +Coordinates of a vector in a basis can be stored in an [`FVector`](@ref) to explicitly indicate which basis they are expressed in. +It is useful to avoid potential ambiguities. + ```@autodocs Modules = [ManifoldsBase,Manifolds] Pages = ["bases.jl"] Order = [:type, :function] ``` +```@autodocs +Modules = [ManifoldsBase,Manifolds] +Pages = ["vector_spaces.jl"] +Order = [:type, :function] +``` + ## Vector transport There are three main functions for vector transport: @@ -113,8 +126,8 @@ Order = [:type, :function] ## A Decorator for manifolds -A decorator manifold extends the functionality of a [`Manifold`](@ref) in a semi-transparent way. -It internally stores the [`Manifold`](@ref) it extends and by default for functions defined in the [`ManifoldsBase`](interface.md) it acts transparently in the sense that it passes all functions through to the base except those that it actually affects. +A decorator manifold extends the functionality of a [`AbstractManifold`](@ref) in a semi-transparent way. +It internally stores the [`AbstractManifold`](@ref) it extends and by default for functions defined in the [`ManifoldsBase`](interface.md) it acts transparently in the sense that it passes all functions through to the base except those that it actually affects. For example, because the [`ValidationManifold`](@ref) affects nearly all functions, it overwrites nearly all functions, except a few like [`manifold_dimension`](@ref). On the other hand, the [`MetricManifold`](@ref) only affects functions that involve metrics, especially [`exp`](@ref) and [`log`](@ref) but not the [`manifold_dimension`](@ref). Contrary to the previous decorator, the [`MetricManifold`](@ref) does not overwrite functions. @@ -143,15 +156,15 @@ Order = [:macro, :type, :function] ## Abstract Power Manifold ```@autodocs -Modules = [ ManifoldsBase] -Pages = ["PowerManifold.jl"] +Modules = [Manifolds, ManifoldsBase] +Pages = ["src/PowerManifold.jl"] Order = [:macro, :type, :function] ``` ## ValidationManifold [`ValidationManifold`](@ref) is a simple decorator that β€œdecorates” a manifold with tests that all involved arrays are correct. For example involved input and output paratemers are checked before and after running a function, repectively. -This is done by calling [`is_manifold_point`](@ref) or [`is_tangent_vector`](@ref) whenever applicable. +This is done by calling [`is_point`](@ref) or [`is_vector`](@ref) whenever applicable. ```@autodocs Modules = [Manifolds, ManifoldsBase] @@ -181,7 +194,7 @@ and logarithmic maps as well as retractions and vector transports of the embeddi used for the embedded manifold as well. See [`SymmetricMatrices`](@ref) for an example. -In both cases of course [`check_manifold_point`](@ref) and [`check_tangent_vector`](@ref) have to be implemented. +In both cases of course [`check_point`](@ref) and [`check_vector`](@ref) have to be implemented. ### Further Embeddings diff --git a/docs/src/manifolds/group.md b/docs/src/manifolds/group.md index 207505e4d9..12b1d280fe 100644 --- a/docs/src/manifolds/group.md +++ b/docs/src/manifolds/group.md @@ -1,6 +1,6 @@ # Group manifolds and actions -Lie groups, groups that are [`Manifold`](@ref)s with a smooth binary group operation [`AbstractGroupOperation`](@ref), are implemented as subtypes of [`AbstractGroupManifold`](@ref) or by decorating an existing manifold with a group operation using [`GroupManifold`](@ref). +Lie groups, groups that are [`AbstractManifold`](@ref)s with a smooth binary group operation [`AbstractGroupOperation`](@ref), are implemented as subtypes of [`AbstractGroupManifold`](@ref) or by decorating an existing manifold with a group operation using [`GroupManifold`](@ref). The common addition and multiplication group operations of [`AdditionOperation`](@ref) and [`MultiplicationOperation`](@ref) are provided, though their behavior may be customized for a specific group. diff --git a/docs/src/manifolds/metric.md b/docs/src/manifolds/metric.md index 6d7a4b0c28..626ab04a45 100644 --- a/docs/src/manifolds/metric.md +++ b/docs/src/manifolds/metric.md @@ -5,11 +5,17 @@ A Riemannian manifold always consists of a [topological manifold](https://en.wik However, often there is an implicitly assumed (default) metric, like the usual inner product on [`Euclidean`](@ref) space. This decorator takes this into account. It is not necessary to use this decorator if you implement just one (or the first) metric. -If you later introduce a second, the old (first) metric can be used with the (non [`MetricManifold`](@ref)) [`Manifold`](@ref), i.e. without an explicitly stated metric. +If you later introduce a second, the old (first) metric can be used with the (non [`MetricManifold`](@ref)) [`AbstractManifold`](@ref), i.e. without an explicitly stated metric. This manifold decorator serves two purposes: -1. to implement different metrics (e.g. in closed form) for one [`Manifold`](@ref) -2. to provide a way to compute geodesics on manifolds, where this [`Metric`](@ref) does not yield closed formula. + +1. to implement different metrics (e.g. in closed form) for one [`AbstractManifold`](@ref) +2. to provide a way to compute geodesics on manifolds, where this [`AbstractMetric`](@ref) does not yield closed formula. + +```@contents +Pages = ["metric.md"] +Depth = 2 +``` Let's first look at the provided types. @@ -23,18 +29,24 @@ Order = [:type] ## Implement Different Metrics on the same Manifold -In order to distinguish different metrics on one manifold, one can introduce two [`Metric`](@ref)s and use this type to dispatch on the metric, see [`SymmetricPositiveDefinite`](@ref). -To avoid overhead, one [`Metric`](@ref) can then be marked as being the default, i.e. the one that is used, when no [`MetricManifold`](@ref) decorator is present. +In order to distinguish different metrics on one manifold, one can introduce two [`AbstractMetric`](@ref)s and use this type to dispatch on the metric, see [`SymmetricPositiveDefinite`](@ref). +To avoid overhead, one [`AbstractMetric`](@ref) can then be marked as being the default, i.e. the one that is used, when no [`MetricManifold`](@ref) decorator is present. This avoids reimplementation of the first existing metric, access to the metric-dependent functions that were implemented using the undecorated manifold, as well as the transparent fallback of the corresponding [`MetricManifold`](@ref) with default metric to the undecorated implementations. This does not cause any runtime overhead. -Introducing a default [`Metric`](@ref) serves a better readability of the code when working with different metrics. +Introducing a default [`AbstractMetric`](@ref) serves a better readability of the code when working with different metrics. ## Implementation of Metrics -For the case that a [`local_metric`](@ref) is implemented as a bilinear form that is positive definite, the following further functions are provided, unless the corresponding [`Metric`](@ref) is marked as default – then the fallbacks mentioned in the last section are used for e.g. the [`exp!`](@ref)onential map. +For the case that a [`local_metric`](@ref) is implemented as a bilinear form that is positive definite, the following further functions are provided, unless the corresponding [`AbstractMetric`](@ref) is marked as default – then the fallbacks mentioned in the last section are used for e.g. the [`exp!`](@ref)onential map. ```@autodocs Modules = [Manifolds, ManifoldsBase] Pages = ["manifolds/MetricManifold.jl"] Order = [:function] ``` + +## Metrics, charts and bases of vector spaces + +All metric-related functions take a basis of a vector space as one of the arguments. This needed because generally there is no way to define these functions without referencing a basis. In some cases there is no need to be explicit about this basis, and then for example a [`DefaultOrthonormalBasis`](@ref) object can be used. In cases where being explicit about these bases is needed, for example when using multiple charts, a basis can be specified, for example using [`induced_basis`](@ref Main.Manifolds.induced_basis). + +Metric-related functions can take bases of associated tangent spaces as arguments. For example [`local_metric`](@ref) can take the basis of the tangent space it is supposed to operate on instead of a custom basis of the space of symmetric bilinear operators. diff --git a/docs/src/manifolds/power.md b/docs/src/manifolds/power.md index 51300fd274..fcbb2d12a6 100644 --- a/docs/src/manifolds/power.md +++ b/docs/src/manifolds/power.md @@ -1,6 +1,6 @@ # Power manifold -A power manifold is based on a [`Manifold`](@ref) $\mathcal M$ to build a $\mathcal M^{n_1 \times n_2 \times \cdots \times n_m}$. +A power manifold is based on a [`AbstractManifold`](@ref) $\mathcal M$ to build a $\mathcal M^{n_1 \times n_2 \times \cdots \times n_m}$. In the case where $m=1$ we can represent a manifold-valued vector of data of length $n_1$, for example a time series. The case where $m=2$ is useful for representing manifold-valued matrices of data of size $n_1 \times n_2$, for example certain types of images. @@ -26,7 +26,7 @@ p = cat([1.0, 0.0, 0.0], which is a valid point i.e. ```@example 1 -is_manifold_point(M, p) +is_point(M, p) ``` This can also be used in combination with [HybridArrays.jl](https://github.com/mateuszbaran/HybridArrays.jl) and [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl), by setting @@ -54,7 +54,7 @@ p = [ [1.0, 0.0, 0.0], ] ``` -which is again a valid point so [`is_manifold_point`](@ref)`(M, p)` here also yields true. +which is again a valid point so [`is_point`](@ref)`(M, p)` here also yields true. A disadvantage might be that with nested arrays one loses a little bit of performance. The data however is nicely encapsulated. Accessing the first data item is just `p[1]`. diff --git a/docs/src/manifolds/sphere.md b/docs/src/manifolds/sphere.md index b2ec1f5f21..b21c2cdcb8 100644 --- a/docs/src/manifolds/sphere.md +++ b/docs/src/manifolds/sphere.md @@ -17,6 +17,12 @@ To create the unit sphere of $3Γ—2$ real-valued matrices, write `ArraySphere(3,2 ArraySphere ``` +There is also one atlas available on the sphere. + +```@docs +Manifolds.StereographicAtlas +``` + ## Functions on unit spheres ```@autodocs Modules = [Manifolds] diff --git a/docs/src/misc/notation.md b/docs/src/misc/notation.md index 9109b9a1ef..faa868103e 100644 --- a/docs/src/misc/notation.md +++ b/docs/src/misc/notation.md @@ -12,6 +12,7 @@ Within the documented functions, the utf8 symbols are used whenever possible, as | ``\tau_p`` | action map by group element ``p`` | ``\mathrm{L}_p``, ``\mathrm{R}_p`` | either left or right | | ``\times`` | Cartesian product of two manifolds |Β | see [`ProductManifold`](@ref) | | ``^{\wedge}`` | (n-ary) Cartesian power of a manifold |Β | see [`PowerManifold`](@ref) | +| ``a`` | coordinates of a point in a chart | | see [`Manifolds.get_point_coordinates`](@ref) | | ``T^*_p \mathcal M`` | the cotangent space at ``p`` | | | | ``ΞΎ`` | a cotangent vector from ``T^*_p \mathcal M`` | ``ΞΎ_1, ΞΎ_2,… ,Ξ·,\zeta`` | sometimes written with base point ``ΞΎ_p``. | | ``\mathrm{d}\phi_p(q)`` | Differential of a map ``\phi: \mathcal M \to \mathcal N`` with respect to ``p`` at a point ``q``. For functions of multiple variables, for example ``\phi(p, p_1)`` where ``p \in \mathcal M`` and ``p_1 \in \mathcal M_1``, variable ``p`` is explicitly stated to specify with respect to which argument the differential is calculated. | ``\mathrm{d}\phi_q``, ``(\mathrm{d}\phi)_q``, ``(\phi_*)_q``, ``D_p\phi(q)`` | pushes tangent vectors ``X \in T_q \mathcal M`` forward to ``\mathrm{d}\phi_p(q)[X] \in T_{\phi(q)} \mathcal N`` | diff --git a/src/Manifolds.jl b/src/Manifolds.jl index ad9ca71f5b..144697e61a 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -10,13 +10,14 @@ import ManifoldsBase: allocation_promotion_function, array_value, base_manifold, - check_manifold_point, - check_manifold_point__transparent, - check_tangent_vector, + check_point, + check_point__transparent, + check_vector, decorated_manifold, decorator_transparent_dispatch, default_decorator_dispatch, distance, + dual_basis, embed, embed!, exp!, @@ -34,8 +35,8 @@ import ManifoldsBase: injectivity_radius, inner, inner__intransparent, - is_manifold_point, - is_tangent_vector, + is_point, + is_vector, inverse_retract, inverse_retract!, log, @@ -52,12 +53,15 @@ import ManifoldsBase: retract, retract!, set_component!, + vector_space_dimension, vector_transport_direction, vector_transport_direction!, vector_transport_to, vector_transport_to!, - zero_tangent_vector, - zero_tangent_vector! + zero_vector, + zero_vector!, + CotangentSpace, + TangentSpace import Base: copyto!, convert, in, isapprox, isempty, length, showerror using Base.Iterators: repeated @@ -81,17 +85,24 @@ using ManifoldsBase: AbstractPowerRepresentation, AbstractVectorTransportMethod, AbstractLinearVectorTransportMethod, - DifferentiatedRetractionVectorTransport, ComponentManifoldError, CompositeManifoldError, + CotangentSpaceType, + CoTFVector, DefaultManifold, DefaultOrDiagonalizingBasis, DiagonalizingBasisData, + DifferentiatedRetractionVectorTransport, + FVector, InversePowerRetraction, PowerManifold, PowerManifoldNested, PowerRetraction, PowerVectorTransport, + TangentSpaceType, + TCoTSpaceType, + TFVector, + VectorSpaceType, VeeOrthogonalBasis, @decorator_transparent_fallback, @decorator_transparent_function, @@ -118,6 +129,9 @@ using StatsBase using StatsBase: AbstractWeights include("utils.jl") + +include("atlases.jl") +include("cotangent_space.jl") include("differentiation.jl") include("riemannian_diff.jl") @@ -199,24 +213,24 @@ include("groups/special_euclidean.jl") include("tests/ManifoldTests.jl") @doc raw""" - Base.in(p, M::Manifold; kwargs...) + Base.in(p, M::AbstractManifold; kwargs...) p ∈ M -Check, whether a point `p` is a valid point (i.e. in) a [`Manifold`](@ref) `M`. -This method employs [`is_manifold_point`](@ref) deaticating the error throwing option. +Check, whether a point `p` is a valid point (i.e. in) a [`AbstractManifold`](@ref) `M`. +This method employs [`is_point`](@ref) deaticating the error throwing option. """ -Base.in(p, M::Manifold; kwargs...) = is_manifold_point(M, p, false; kwargs...) +Base.in(p, M::AbstractManifold; kwargs...) = is_point(M, p, false; kwargs...) @doc raw""" Base.in(p, TpM::TangentSpaceAtPoint; kwargs...) X ∈ TangentSpaceAtPoint(M,p) Check whether `X` is a tangent vector from (in) the tangent space $T_p\mathcal M$, i.e. -the [`TangentSpaceAtPoint`](@ref) at `p` on the [`Manifold`](@ref) `M`. -This method uses [`is_tangent_vector`](@ref) deactivating the error throw option. +the [`TangentSpaceAtPoint`](@ref) at `p` on the [`AbstractManifold`](@ref) `M`. +This method uses [`is_vector`](@ref) deactivating the error throw option. """ function Base.in(X, TpM::TangentSpaceAtPoint; kwargs...) - return is_tangent_vector(base_manifold(TpM), TpM.point, X, false; kwargs...) + return is_vector(base_manifold(TpM), TpM.point, X, false; kwargs...) end function __init__() @@ -270,7 +284,7 @@ function __init__() end # -export CoTVector, Manifold, MPoint, TVector, Manifold +export CoTVector, AbstractManifold, AbstractManifoldPoint, TVector, AbstractManifold export AbstractSphere, AbstractProjectiveSpace export Euclidean, ArrayProjectiveSpace, @@ -331,7 +345,7 @@ export AbstractVectorTransportMethod, export PoleLadderTransport, SchildsLadderTransport export PowerVectorTransport, ProductVectorTransport export AbstractEmbeddedManifold -export Metric, +export AbstractMetric, RiemannianMetric, LorentzMetric, EmbeddedManifold, @@ -344,6 +358,7 @@ export Metric, ProductMetric, CanonicalMetric, MetricManifold +export AbstractAtlas, RetractionAtlas export AbstractEmbeddingType, AbstractIsometricEmbeddingType export DefaultEmbeddingType, DefaultIsometricEmbeddingType, TransparentIsometricEmbedding export AbstractVectorTransportMethod, ParallelTransport, ProjectionTransport @@ -375,6 +390,7 @@ export CachedBasis, DefaultOrthogonalBasis, DefaultOrthonormalBasis, DiagonalizingOrthonormalBasis, + InducedBasis, ProjectedOrthonormalBasis export ComponentManifoldError, CompositeManifoldError export Γ—, @@ -382,8 +398,8 @@ export Γ—, allocate_result, base_manifold, bundle_projection, - check_manifold_point, - check_tangent_vector, + check_point, + check_vector, christoffel_symbols_first, christoffel_symbols_second, christoffel_symbols_second_jacobian, @@ -392,6 +408,7 @@ export Γ—, decorated_manifold, det_local_metric, distance, + dual_basis, einstein_tensor, embed, embed!, @@ -401,10 +418,12 @@ export Γ—, flat!, gaussian_curvature, geodesic, + get_default_atlas, get_component, get_embedding, hat, hat!, + induced_basis, incident_log, injectivity_radius, inner, @@ -416,8 +435,8 @@ export Γ—, is_decorator_transparent, is_default_metric, is_default_decorator, - is_manifold_point, - is_tangent_vector, + is_point, + is_vector, isapprox, kurtosis, local_metric, @@ -474,9 +493,7 @@ export Γ—, vee, vee!, zero_vector, - zero_vector!, - zero_tangent_vector, - zero_tangent_vector! + zero_vector! # Lie group types & functions export AbstractGroupAction, AbstractGroupOperation, @@ -560,5 +577,7 @@ export get_basis, export AbstractDiffBackend, AbstractRiemannianDiffBackend, FiniteDifferencesBackend, RiemannianONBDiffBackend export diff_backend, diff_backend!, diff_backends +# atlases and charts +export get_point, get_point!, get_point_coordinates, get_point_coordinates! end # module diff --git a/src/approx_inverse_retraction.jl b/src/approx_inverse_retraction.jl index 741b9365f5..aba4c10a92 100644 --- a/src/approx_inverse_retraction.jl +++ b/src/approx_inverse_retraction.jl @@ -74,17 +74,24 @@ end inverse_retract(M, p, q method::NLsolveInverseRetraction; kwargs...) Approximate the inverse of the retraction specified by `method.retraction` from `p` with -respect to `q` on the [`Manifold`](@ref) `M` using NLsolve. This inverse retraction is +respect to `q` on the [`AbstractManifold`](@ref) `M` using NLsolve. This inverse retraction is not guaranteed to succeed and probably will not unless `q` is close to `p` and the initial guess `X0` is close. If the solver fails to converge, an [`OutOfInjectivityRadiusError`](@ref) is raised. See [`NLsolveInverseRetraction`](@ref) for configurable parameters. """ -inverse_retract(::Manifold, p, q, ::NLsolveInverseRetraction; kwargs...) +inverse_retract(::AbstractManifold, p, q, ::NLsolveInverseRetraction; kwargs...) -function inverse_retract!(M::Manifold, X, p, q, method::NLsolveInverseRetraction; kwargs...) - X0 = method.X0 === nothing ? zero_tangent_vector(M, p) : method.X0 +function inverse_retract!( + M::AbstractManifold, + X, + p, + q, + method::NLsolveInverseRetraction; + kwargs..., +) + X0 = method.X0 === nothing ? zero_vector(M, p) : method.X0 res = _inverse_retract_nlsolve( M, p, diff --git a/src/atlases.jl b/src/atlases.jl new file mode 100644 index 0000000000..ca50442c01 --- /dev/null +++ b/src/atlases.jl @@ -0,0 +1,271 @@ + +""" + AbstractAtlas{𝔽} + +An abstract class for atlases whith charts that have values in the vector space `𝔽ⁿ` +for some value of `n`. `𝔽` is a number system determined by an [`AbstractNumbers`](@ref) +object. +""" +abstract type AbstractAtlas{𝔽} end + +""" + RetractionAtlas{ + 𝔽, + TRetr<:AbstractRetractionMethod, + TInvRetr<:AbstractInverseRetractionMethod, + TBasis<:AbstractBasis, + } <: AbstractAtlas{𝔽} + +An atlas indexed by points on a manifold, such that coordinate transformations are performed +using retractions, inverse retractions, and coordinate calculation for a given basis. + +# See also + +[`AbstractAtlas`](@ref), [`AbstractInverseRetractionMethod`](@ref), +[`AbstractRetractionMethod`](@ref), [`AbstractBasis`](@ref) +""" +struct RetractionAtlas{ + 𝔽, + TRetr<:AbstractRetractionMethod, + TInvRetr<:AbstractInverseRetractionMethod, + TBasis<:AbstractBasis{<:Any,TangentSpaceType}, +} <: AbstractAtlas{𝔽} + retr::TRetr + invretr::TInvRetr + basis::TBasis +end + +function RetractionAtlas( + retr::AbstractRetractionMethod, + invretr::AbstractInverseRetractionMethod, +) + basis = DefaultOrthonormalBasis() + return RetractionAtlas{ℝ,typeof(retr),typeof(invretr),typeof(basis)}( + retr, + invretr, + basis, + ) +end +RetractionAtlas() = RetractionAtlas(ExponentialRetraction(), LogarithmicInverseRetraction()) + +""" + get_default_atlas(::AbstractManifold) + +Determine the default real-valued atlas for the given manifold. +""" +function get_default_atlas(::AbstractManifold) + return RetractionAtlas() +end + +""" + get_point_coordinates(M::AbstractManifold, A::AbstractAtlas, i, p) + +Calculate coordinates of point `p` on manifold `M` in chart from an [`AbstractAtlas`](@ref) +`A` at index `i`. Coordinates are in the number system determined by `A`. + +# See also + +[`get_point`](@ref), [`get_chart_index`](@ref) +""" +get_point_coordinates(::AbstractManifold, ::AbstractAtlas, ::Any, ::Any) + +function get_point_coordinates(M::AbstractManifold, A::AbstractAtlas, i, p) + a = allocate_result(M, get_point_coordinates, p) + get_point_coordinates!(M, a, A, i, p) + return a +end + +function allocate_result(M::AbstractManifold, f::typeof(get_point_coordinates), p) + T = allocate_result_type(M, f, (p,)) + return allocate(p, T, manifold_dimension(M)) +end + +function get_point_coordinates!(M::AbstractManifold, a, A::RetractionAtlas, i, p) + return get_coordinates!(M, a, i, inverse_retract(M, i, p, A.invretr), A.basis) +end + +function get_point_coordinates(M::AbstractManifold, A::RetractionAtlas, i, p) + return get_coordinates(M, i, inverse_retract(M, i, p, A.invretr), A.basis) +end + +""" + get_point(M::AbstractManifold, A::AbstractAtlas, i, a) + +Calculate point at coordinates `a` on manifold `M` in chart from an [`AbstractAtlas`](@ref) +`A` at index `i`. + +# See also + +[`get_point_coordinates`](@ref), [`get_chart_index`](@ref) +""" +get_point(::AbstractManifold, ::AbstractAtlas, ::Any, ::Any) + +function get_point(M::AbstractManifold, A::AbstractAtlas, i, a) + p = allocate_result(M, get_point, a) + get_point!(M, p, A, i, a) + return p +end + +function allocate_result(M::AbstractManifold, f::typeof(get_point), a) + T = allocate_result_type(M, f, (a,)) + return allocate(a, T, representation_size(M)...) +end + +function get_point(M::AbstractManifold, A::RetractionAtlas, i, a) + return retract(M, i, get_vector(M, i, a, A.basis), A.retr) +end + +function get_point!(M::AbstractManifold, p, A::RetractionAtlas, i, a) + return retract!(M, p, i, get_vector(M, i, a, A.basis), A.retr) +end + +""" + get_chart_index(M::AbstractManifold, A::AbstractAtlas, p) + +Select a chart from an [`AbstractAtlas`](@ref) `A` for manifold `M` that is suitable for +representing the neighborhood of point `p`. This selection should be deterministic, although +different charts may be selected for arbitrarily close but distinct points. + +# See also + +[`get_default_atlas`](@ref) +""" +get_chart_index(::AbstractManifold, ::AbstractAtlas, ::Any) + +get_chart_index(::AbstractManifold, ::RetractionAtlas, p) = p + +""" + transition_map(M::AbstractManifold, A_from::AbstractAtlas, i_from, A_to::AbstractAtlas, i_to, a) + transition_map(M::AbstractManifold, A::AbstractAtlas, i_from, i_to, a) + +Given coordinates `a` in chart `(A_from, i_from)` of a point on manifold `M`, returns +coordinates of that point in chart `(A_to, i_to)`. If `A_from` and `A_to` are equal, `A_to` +can be omitted. + +# See also + +[`AbstractAtlas`](@ref) +""" +function transition_map( + M::AbstractManifold, + A_from::AbstractAtlas, + i_from, + A_to::AbstractAtlas, + i_to, + a, +) + return get_point_coordinates(M, A_to, i_to, get_point(M, A_from, i_from, a)) +end + +function transition_map(M::AbstractManifold, A::AbstractAtlas, i_from, i_to, a) + return transition_map(M, A, i_from, A, i_to, a) +end + +function transition_map!( + M::AbstractManifold, + y, + A_from::AbstractAtlas, + i_from, + A_to::AbstractAtlas, + i_to, + a, +) + return get_point_coordinates!(M, y, A_to, i_to, get_point(M, A_from, i_from, a)) +end + +function transition_map!(M::AbstractManifold, y, A::AbstractAtlas, i_from, i_to, a) + return transition_map!(M, y, A, i_from, A, i_to, a) +end + +""" + induced_basis(M::AbstractManifold, A::AbstractAtlas, i, p, VST::VectorSpaceType) + +Basis of vector space of type `VST` at point `p` from manifold `M` induced by +chart (`A`, `i`). + +# See also + +[`VectorSpaceType`](@ref), [`AbstractAtlas`](@ref) +""" +induced_basis(M::AbstractManifold, A::AbstractAtlas, i, VST::VectorSpaceType) + +function induced_basis( + ::AbstractManifold, + A::RetractionAtlas{ + <:AbstractRetractionMethod, + <:AbstractInverseRetractionMethod, + <:DefaultOrthonormalBasis, + }, + i, + p, + ::TangentSpaceType, +) + return A.basis +end +function induced_basis( + M::AbstractManifold, + A::RetractionAtlas{ + <:AbstractRetractionMethod, + <:AbstractInverseRetractionMethod, + <:DefaultOrthonormalBasis, + }, + i, + p, + ::CotangentSpaceType, +) + return dual_basis(M, p, A.basis) +end + +""" + InducedBasis(vs::VectorSpaceType, A::AbstractAtlas, i) + +The basis induced by chart with index `i` from an [`AbstractAtlas`](@ref) `A` of vector +space of type `vs`. + +# See also + +[`VectorSpaceType`](@ref), [`AbstractBasis`](@ref) +""" +struct InducedBasis{𝔽,VST<:VectorSpaceType,TA<:AbstractAtlas,TI} <: AbstractBasis{𝔽,VST} + vs::VST + A::TA + i::TI +end + +""" + induced_basis(::AbstractManifold, A::AbstractAtlas, i, VST::VectorSpaceType) + +Get the basis induced by chart with index `i` from an [`AbstractAtlas`](@ref) `A` of vector +space of type `vs`. Returns an object of type [`InducedBasis`](@ref). +""" +function induced_basis( + ::AbstractManifold{𝔽}, + A::AbstractAtlas, + i, + VST::VectorSpaceType, +) where {𝔽} + return InducedBasis{𝔽,typeof(VST),typeof(A),typeof(i)}(VST, A, i) +end + +function dual_basis( + M::AbstractManifold{𝔽}, + ::Any, + B::InducedBasis{𝔽,TangentSpaceType}, +) where {𝔽} + return induced_basis(M, B.A, B.i, CotangentSpace) +end +function dual_basis( + M::AbstractManifold{𝔽}, + ::Any, + B::InducedBasis{𝔽,CotangentSpaceType}, +) where {𝔽} + return induced_basis(M, B.A, B.i, TangentSpace) +end + +""" + local_metric(M::AbstractManifold, p, B::InducedBasis) + +Compute the local metric tensor for vectors expressed in terms of coordinates +in basis `B` on manifold `M`. The point `p` is not checked. +""" +local_metric(::AbstractManifold, ::Any, ::InducedBasis) diff --git a/src/cotangent_space.jl b/src/cotangent_space.jl new file mode 100644 index 0000000000..1859b8d5b5 --- /dev/null +++ b/src/cotangent_space.jl @@ -0,0 +1,119 @@ + +""" + RieszRepresenterCotangentVector(M::AbstractManifold, p, X) + +Cotangent vector in Riesz representer form on manifold `M` at point `p` with Riesz +representer `X`. +""" +struct RieszRepresenterCotangentVector{TM<:AbstractManifold,TP,TX} + manifold::TM + p::TP + X::TX +end + +function allocate(ΞΎ::RieszRepresenterCotangentVector) + return RieszRepresenterCotangentVector(ΞΎ.manifold, copy(ΞΎ.p), allocate(ΞΎ.X)) +end + +function (ΞΎ::RieszRepresenterCotangentVector)(Y) + return inner(ΞΎ.manifold, ΞΎ.p, ΞΎ.X, Y) +end + +@decorator_transparent_signature flat!( + M::AbstractDecoratorManifold, + ΞΎ::CoTFVector, + p, + X::TFVector, +) + +@doc raw""" + flat(M::AbstractManifold, p, X) + +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 [`AbstractManifold`](@ref). + +The function can be used for example to transform vectors +from the tangent bundle to vectors from the cotangent bundle +$β™­ : T\mathcal M β†’ T^{*}\mathcal M$ +""" +flat(M::AbstractManifold, p, X) = RieszRepresenterCotangentVector(M, p, X) +function flat(M::AbstractManifold, p, X::TFVector{<:Any,<:AbstractBasis}) + return CoTFVector(X.data, dual_basis(M, p, X.basis)) +end + +function flat!(::AbstractManifold, ΞΎ::RieszRepresenterCotangentVector, p, X) + # TODO: maybe assert that ΞΎ.p is equal to p? Allowing for varying p in ΞΎ leads to + # issues with power manifold. + copyto!(ΞΎ.X, X) + return ΞΎ +end + +function get_coordinates( + M::AbstractManifold, + p, + ΞΎ::RieszRepresenterCotangentVector, + ::DefaultOrthonormalBasis{𝔽,CotangentSpaceType}, +) where {𝔽} + return get_coordinates(M, p, ΞΎ.X, DefaultOrthonormalBasis{𝔽}()) +end + +function get_coordinates!( + M::AbstractManifold, + v, + p, + ΞΎ::RieszRepresenterCotangentVector, + ::DefaultOrthonormalBasis{𝔽,CotangentSpaceType}, +) where {𝔽} + get_coordinates!(M, v, p, ΞΎ.X, DefaultOrthonormalBasis{𝔽}()) + return v +end + +function get_vector( + M::AbstractManifold, + p, + v, + ::DefaultOrthonormalBasis{𝔽,CotangentSpaceType}, +) where {𝔽} + X = get_vector(M, p, v, DefaultOrthonormalBasis{𝔽}()) + return RieszRepresenterCotangentVector(M, p, X) +end + +function get_vector!( + M::AbstractManifold, + ΞΎr::RieszRepresenterCotangentVector, + p, + v, + ::DefaultOrthonormalBasis{𝔽,CotangentSpaceType}, +) where {𝔽} + get_vector!(M, ΞΎr.X, p, v, DefaultOrthonormalBasis{𝔽}()) + return ΞΎr +end + +@doc raw""" + sharp(M::AbstractManifold, p, ΞΎ) + +Compute the sharp isomorphism (one of the musical isomorphisms) of vector `ΞΎ` +from the vector space `M` at point `p` from the underlying [`AbstractManifold`](@ref). + +The function can be used for example to transform vectors +from the cotangent bundle to vectors from the tangent bundle +$β™― : T^{*}\mathcal M β†’ T\mathcal M$ +""" +sharp(::AbstractManifold, p, ΞΎ) + +sharp(::AbstractManifold, p, ΞΎ::RieszRepresenterCotangentVector) = ΞΎ.X +function sharp(M::AbstractManifold, p, X::CoTFVector{<:Any,<:AbstractBasis}) + return TFVector(X.data, dual_basis(M, p, X.basis)) +end + +@decorator_transparent_signature sharp!( + M::AbstractDecoratorManifold, + X::TFVector, + p, + ΞΎ::CoTFVector, +) + +function sharp!(::AbstractManifold, X, p, ΞΎ::RieszRepresenterCotangentVector) + copyto!(X, ΞΎ.X) + return X +end diff --git a/src/differentiation.jl b/src/differentiation.jl index 60c663d5c5..17ed083186 100644 --- a/src/differentiation.jl +++ b/src/differentiation.jl @@ -17,7 +17,7 @@ an object of type [`Manifolds.AbstractDiffBackend`](@ref). If the backend is not specified, it is obtained using the function [`Manifolds.diff_backend`](@ref). This function calculates plain Euclidean derivatives, for Riemannian differentiation see -for example [`differential`](@ref Manifolds.differential(::Manifold, ::Any, ::Real, ::AbstractRiemannianDiffBackend)). +for example [`differential`](@ref Manifolds.differential(::AbstractManifold, ::Any, ::Real, ::AbstractRiemannianDiffBackend)). !!! note @@ -38,7 +38,7 @@ an object of type [`AbstractDiffBackend`](@ref). If the backend is not explicitl specified, it is obtained using the function [`diff_backend`](@ref). This function calculates plain Euclidean gradients, for Riemannian gradient calculation see -for example [`gradient`](@ref Manifolds.gradient(::Manifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend)). +for example [`gradient`](@ref Manifolds.gradient(::AbstractManifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend)). !!! note diff --git a/src/distributions.jl b/src/distributions.jl index 9918275832..5ba8e98836 100644 --- a/src/distributions.jl +++ b/src/distributions.jl @@ -7,7 +7,7 @@ is a vector from a fiber of a vector bundle. struct FVectorvariate <: VariateForm end """ - FVectorSupport(space::Manifold, VectorBundleFibers) + FVectorSupport(space::AbstractManifold, VectorBundleFibers) Value support for vector bundle fiber-valued distributions (values from a fiber of a vector bundle at a `point` from the given manifold). @@ -37,21 +37,21 @@ is a point on a manifold. struct MPointvariate <: VariateForm end """ - MPointSupport(M::Manifold) + MPointSupport(M::AbstractManifold) Value support for manifold-valued distributions (values from given -[`Manifold`](@ref) `M`). +[`AbstractManifold`](@ref) `M`). """ -struct MPointSupport{TM<:Manifold} <: ValueSupport +struct MPointSupport{TM<:AbstractManifold} <: ValueSupport manifold::TM end """ - MPointDistribution{TM<:Manifold} + MPointDistribution{TM<:AbstractManifold} An abstract distribution for points on manifold of type `TM`. """ -abstract type MPointDistribution{TM<:Manifold} <: +abstract type MPointDistribution{TM<:AbstractManifold} <: Distribution{MPointvariate,MPointSupport{TM}} end """ diff --git a/src/groups/array_manifold.jl b/src/groups/array_manifold.jl index 4e821eaddd..6c91b5da9f 100644 --- a/src/groups/array_manifold.jl +++ b/src/groups/array_manifold.jl @@ -5,70 +5,70 @@ array_point(p::ValidationMPoint) = p array_point(e::Identity) = Identity(e.group, array_point(e.p)) function Base.inv(M::ValidationManifold, p; kwargs...) - is_manifold_point(M, p, true; kwargs...) + is_point(M, p, true; kwargs...) q = array_point(inv(M.manifold, array_value(p))) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function inv!(M::ValidationManifold, q, p; kwargs...) - is_manifold_point(M, p, true; kwargs...) + is_point(M, p, true; kwargs...) inv!(M.manifold, array_value(q), array_value(p)) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function Base.identity(M::ValidationManifold, p; kwargs...) - is_manifold_point(M, p, true; kwargs...) + is_point(M, p, true; kwargs...) q = array_point(identity(M.manifold, array_value(p))) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function identity!(M::ValidationManifold, q, p; kwargs...) - is_manifold_point(M, p, true; kwargs...) + is_point(M, p, true; kwargs...) identity!(M.manifold, array_value(q), array_value(p)) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function compose(M::ValidationManifold, p, q; kwargs...) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) x = array_point(compose(M.manifold, array_value(p), array_value(q))) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end function compose!(M::ValidationManifold, x, p, q; kwargs...) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) compose!(M.manifold, array_value(x), array_value(p), array_value(q)) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end function translate(M::ValidationManifold, p, q, conv::ActionDirection; kwargs...) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) x = array_point(translate(M.manifold, array_value(p), array_value(q), conv)) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end function translate!(M::ValidationManifold, x, p, q, conv::ActionDirection; kwargs...) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) translate!(M.manifold, array_value(x), array_value(p), array_value(q), conv) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end function inverse_translate(M::ValidationManifold, p, q, conv::ActionDirection; kwargs...) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) x = array_point(inverse_translate(M.manifold, array_value(p), array_value(q), conv)) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end @@ -80,22 +80,22 @@ function inverse_translate!( conv::ActionDirection; kwargs..., ) - is_manifold_point(M, p, true; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) inverse_translate!(M.manifold, array_value(x), array_value(p), array_value(q), conv) - is_manifold_point(M, x, true; kwargs...) + is_point(M, x, true; kwargs...) return x end function translate_diff(M::ValidationManifold, 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...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) + is_vector(M, q, X, true; kwargs...) Y = ValidationTVector( translate_diff(M.manifold, array_value(p), array_value(q), array_value(X), conv), ) pq = translate(M, p, q, conv) - is_tangent_vector(M, pq, Y, true; kwargs...) + is_vector(M, pq, Y, true; kwargs...) return Y end @@ -108,9 +108,9 @@ function translate_diff!( 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...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) + is_vector(M, q, X, true; kwargs...) translate_diff!( M.manifold, array_value(Y), @@ -120,7 +120,7 @@ function translate_diff!( conv, ) pq = translate(M, p, q, conv) - is_tangent_vector(M, pq, Y, true; kwargs...) + is_vector(M, pq, Y, true; kwargs...) return Y end @@ -132,9 +132,9 @@ function inverse_translate_diff( 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...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) + is_vector(M, q, X, true; kwargs...) Y = ValidationTVector( inverse_translate_diff( M.manifold, @@ -145,7 +145,7 @@ function inverse_translate_diff( ), ) pinvq = inverse_translate(M, p, q, conv) - is_tangent_vector(M, pinvq, Y, true; kwargs...) + is_vector(M, pinvq, Y, true; kwargs...) return Y end @@ -158,9 +158,9 @@ function inverse_translate_diff!( 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...) + is_point(M, p, true; kwargs...) + is_point(M, q, true; kwargs...) + is_vector(M, q, X, true; kwargs...) inverse_translate_diff!( M.manifold, array_value(Y), @@ -170,12 +170,12 @@ function inverse_translate_diff!( conv, ) pinvq = inverse_translate(M, p, q, conv) - is_tangent_vector(M, pinvq, Y, true; kwargs...) + is_vector(M, pinvq, Y, true; kwargs...) return Y end function group_exp(M::ValidationManifold, X; kwargs...) - is_tangent_vector( + is_vector( M, make_identity(M.manifold, array_value(X)), array_value(X), @@ -184,12 +184,12 @@ function group_exp(M::ValidationManifold, X; kwargs...) kwargs..., ) q = array_point(group_exp(M.manifold, array_value(X))) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function group_exp!(M::ValidationManifold, q, X; kwargs...) - is_tangent_vector( + is_vector( M, make_identity(M.manifold, array_value(X)), array_value(X), @@ -198,14 +198,14 @@ function group_exp!(M::ValidationManifold, q, X; kwargs...) kwargs..., ) group_exp!(M.manifold, array_value(q), array_value(X)) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) return q end function group_log(M::ValidationManifold, q; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) X = ValidationTVector(group_log(M.manifold, array_value(q))) - is_tangent_vector( + is_vector( M, make_identity(M.manifold, array_value(X)), array_value(X), @@ -217,9 +217,9 @@ function group_log(M::ValidationManifold, q; kwargs...) end function group_log!(M::ValidationManifold, X, q; kwargs...) - is_manifold_point(M, q, true; kwargs...) + is_point(M, q, true; kwargs...) group_log!(M.manifold, array_value(X), array_value(q)) - is_tangent_vector( + is_vector( M, make_identity(M.manifold, array_value(X)), array_value(X), diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index 463bbe0c88..4e5dbbe497 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -25,8 +25,8 @@ function allocation_promotion_function(::GeneralLinear{n,β„‚}, f, ::Tuple) where return complex end -function check_manifold_point(G::GeneralLinear, p; kwargs...) - mpv = check_manifold_point(decorated_manifold(G), p; kwargs...) +function check_point(G::GeneralLinear, p; kwargs...) + mpv = check_point(decorated_manifold(G), p; kwargs...) mpv === nothing || return mpv detp = det(p) if iszero(detp) @@ -37,17 +37,13 @@ function check_manifold_point(G::GeneralLinear, p; kwargs...) end return nothing end -check_manifold_point(::GT, ::Identity{GT}; kwargs...) where {GT<:GeneralLinear} = nothing -function check_manifold_point(G::GeneralLinear, e::Identity; kwargs...) +check_point(::GT, ::Identity{GT}; kwargs...) where {GT<:GeneralLinear} = nothing +function check_point(G::GeneralLinear, e::Identity; kwargs...) return DomainError(e, "The identity element $(e) does not belong to $(G).") end -function check_tangent_vector(G::GeneralLinear, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(G, p; kwargs...) - mpe === nothing || return mpe - end - mpv = check_tangent_vector(decorated_manifold(G), p, X; kwargs...) +function check_vector(G::GeneralLinear, p, X; kwargs...) + mpv = check_vector(decorated_manifold(G), p, X; kwargs...) mpv === nothing || return mpv return nothing end @@ -112,25 +108,41 @@ function exp!(G::GeneralLinear{2}, q, p, X) return q end -flat!(::GeneralLinear, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - -get_coordinates(::GeneralLinear{n,ℝ}, p, X, ::DefaultOrthonormalBasis) where {n} = vec(X) +function get_coordinates( + ::GeneralLinear{n,ℝ}, + p, + X, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) where {n} + return vec(X) +end function get_coordinates!( ::GeneralLinear{n,ℝ}, Xⁱ, p, X, - ::DefaultOrthonormalBasis, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {n} return copyto!(Xⁱ, X) end -function get_vector(::GeneralLinear{n,ℝ}, p, Xⁱ, ::DefaultOrthonormalBasis) where {n} +function get_vector( + ::GeneralLinear{n,ℝ}, + p, + Xⁱ, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) where {n} return reshape(Xⁱ, n, n) end -function get_vector!(::GeneralLinear{n,ℝ}, X, p, Xⁱ, ::DefaultOrthonormalBasis) where {n} +function get_vector!( + ::GeneralLinear{n,ℝ}, + X, + p, + Xⁱ, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) where {n} return copyto!(X, Xⁱ) end @@ -220,8 +232,6 @@ project(::GeneralLinear, p, X) = X project!(::GeneralLinear, q, p) = copyto!(q, p) project!(::GeneralLinear, Y, p, X) = copyto!(Y, X) -sharp!(::GeneralLinear, X::TFVector, p, ΞΎ::CoTFVector) = copyto!(X, ΞΎ) - Base.show(io::IO, ::GeneralLinear{n,𝔽}) where {n,𝔽} = print(io, "GeneralLinear($n, $𝔽)") translate_diff(::GeneralLinear, p, q, X, ::LeftAction) = X diff --git a/src/groups/group.jl b/src/groups/group.jl index 5cb445584b..5d0d852d42 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -33,7 +33,7 @@ abstract type AbstractGroupManifold{𝔽,O<:AbstractGroupOperation,T<:AbstractEm AbstractEmbeddedManifold{𝔽,T} end """ - GroupManifold{𝔽,M<:Manifold{𝔽},O<:AbstractGroupOperation} <: AbstractGroupManifold{𝔽,O} + GroupManifold{𝔽,M<:AbstractManifold{𝔽},O<:AbstractGroupOperation} <: AbstractGroupManifold{𝔽,O} Decorator for a smooth manifold that equips the manifold with a group operation, thus making it a Lie group. See [`AbstractGroupManifold`](@ref) for more details. @@ -44,7 +44,7 @@ Group manifolds by default forward metric-related operations to the wrapped mani GroupManifold(manifold, op) """ -struct GroupManifold{𝔽,M<:Manifold{𝔽},O<:AbstractGroupOperation} <: +struct GroupManifold{𝔽,M<:AbstractManifold{𝔽},O<:AbstractGroupOperation} <: AbstractGroupManifold{𝔽,O,TransparentIsometricEmbedding} manifold::M op::O @@ -59,26 +59,26 @@ const GROUP_MANIFOLD_BASIS_DISAMBIGUATION = [AbstractDecoratorManifold, ValidationManifold, VectorBundle] """ - base_group(M::Manifold) -> AbstractGroupManifold + base_group(M::AbstractManifold) -> AbstractGroupManifold Un-decorate `M` until an `AbstractGroupManifold` is encountered. Return an error if the [`base_manifold`](@ref) is reached without encountering a group. """ base_group(M::AbstractDecoratorManifold) = base_group(decorated_manifold(M)) -function base_group(M::Manifold) +function base_group(M::AbstractManifold) return error("base_group: no base group found.") end base_group(G::AbstractGroupManifold) = G base_manifold(G::GroupManifold) = G.manifold -decorator_group_dispatch(::Manifold) = Val(false) +decorator_group_dispatch(::AbstractManifold) = Val(false) function decorator_group_dispatch(M::AbstractDecoratorManifold) return decorator_group_dispatch(decorated_manifold(M)) end decorator_group_dispatch(::AbstractGroupManifold) = Val(true) -function is_group_decorator(M::Manifold) +function is_group_decorator(M::AbstractManifold) return _extract_val(decorator_group_dispatch(M)) end @@ -86,8 +86,10 @@ default_decorator_dispatch(::AbstractGroupManifold) = Val(false) # piping syntax for decoration if VERSION β‰₯ v"1.3" - (op::AbstractGroupOperation)(M::Manifold) = GroupManifold(M, op) - (::Type{T})(M::Manifold) where {T<:AbstractGroupOperation} = GroupManifold(M, T()) + (op::AbstractGroupOperation)(M::AbstractManifold) = GroupManifold(M, op) + function (::Type{T})(M::AbstractManifold) where {T<:AbstractGroupOperation} + return GroupManifold(M, T()) + end end function decorator_transparent_dispatch( @@ -154,7 +156,7 @@ struct Identity{G<:AbstractGroupManifold,PT} end Identity(M::AbstractDecoratorManifold, p) = Identity(decorated_manifold(M), p) -function Identity(M::Manifold, p) +function Identity(M::AbstractManifold, p) return error("Identity not implemented for manifold $(M) and point $(p).") end @@ -162,7 +164,7 @@ function Base.:(==)(e1::Identity, e2::Identity) return e1.p == e2.p && e1.group == e2.group end -make_identity(M::Manifold, p) = Identity(M, identity(M, p)) +make_identity(M::AbstractManifold, p) = Identity(M, identity(M, p)) Base.show(io::IO, e::Identity) = print(io, "Identity($(e.group), $(e.p))") @@ -178,7 +180,7 @@ Base.isapprox(e::Identity, p; kwargs...) = isapprox(e.group, e, p; kwargs...) Base.isapprox(e::E, ::E; kwargs...) where {E<:Identity} = true function allocate_result( - M::Manifold, + M::AbstractManifold, f::typeof(get_coordinates), e::Identity, X, @@ -195,7 +197,7 @@ function decorator_transparent_dispatch( ) return Val(:parent) end -function allocate_result(M::Manifold, f::typeof(get_vector), e::Identity, Xⁱ) +function allocate_result(M::AbstractManifold, f::typeof(get_vector), e::Identity, Xⁱ) is_group_decorator(M) && return allocate_result(base_group(M), f, e, Xⁱ) return error( "allocate_result not implemented for manifold $(M), function $(f), point $(e), and vector $(Xⁱ).", @@ -257,7 +259,7 @@ function get_vector(M::AbstractGroupManifold, e::Identity, X, B::VeeOrthogonalBa M != e.group && error("On $(M) the identity $(e) does not match to perform get_vector.") return get_vector(decorated_manifold(M), e.p, X, B) end -function get_vector(M::Manifold, e::Identity, X, B::VeeOrthogonalBasis) +function get_vector(M::AbstractManifold, e::Identity, X, B::VeeOrthogonalBasis) M != e.group.manifold && error("On $(M) the identity $(e) does not match to perform get_vector.") return get_vector(M, e.p, X, B) @@ -265,7 +267,7 @@ end for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION eval( quote - @invoke_maker 1 Manifold get_vector( + @invoke_maker 1 AbstractManifold get_vector( M::$MT, e::Identity, X, @@ -278,7 +280,7 @@ function get_vector!(M::AbstractGroupManifold, Y, e::Identity, X, B::VeeOrthogon M != e.group && error("On $(M) the identity $(e) does not match to perform get_vector!") return get_vector!(decorated_manifold(M), Y, e.p, X, B) end -function get_vector!(M::Manifold, Y, e::Identity, X, B::VeeOrthogonalBasis) +function get_vector!(M::AbstractManifold, Y, e::Identity, X, B::VeeOrthogonalBasis) M != e.group.manifold && error("On $(M) the identity $(e) does not match to perform get_vector!") return get_vector!(M, Y, e.p, X, B) @@ -286,7 +288,7 @@ end for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION eval( quote - @invoke_maker 1 Manifold get_vector!( + @invoke_maker 1 AbstractManifold get_vector!( M::$MT, Y, e::Identity, @@ -302,7 +304,7 @@ function get_coordinates(M::AbstractGroupManifold, e::Identity, X, B::VeeOrthogo error("On $(M) the identity $(e) does not match to perform get_coordinates") return get_coordinates(decorated_manifold(M), e.p, X, B) end -function get_coordinates(M::Manifold, e::Identity, X, B::VeeOrthogonalBasis) +function get_coordinates(M::AbstractManifold, e::Identity, X, B::VeeOrthogonalBasis) M != e.group.manifold && error("On $(M) the identity $(e) does not match to perform get_coordinates") return get_coordinates(M, e.p, X, B) @@ -310,7 +312,7 @@ end for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION eval( quote - @invoke_maker 1 Manifold get_coordinates( + @invoke_maker 1 AbstractManifold get_coordinates( M::$MT, e::Identity, X, @@ -331,7 +333,7 @@ function get_coordinates!( error("On $(M) the identity $(e) does not match to perform get_coordinates!") return get_coordinates!(decorated_manifold(M), Y, e.p, X, B) end -function get_coordinates!(M::Manifold, Y, e::Identity, X, B::VeeOrthogonalBasis) +function get_coordinates!(M::AbstractManifold, Y, e::Identity, X, B::VeeOrthogonalBasis) M != e.group.manifold && error("On $(M) the identity $(e) does not match to perform get_coordinates!") return get_coordinates!(M, Y, e.p, X, B) @@ -339,7 +341,7 @@ end for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION eval( quote - @invoke_maker 1 Manifold get_coordinates!( + @invoke_maker 1 AbstractManifold get_coordinates!( M::$MT, Y, e::Identity, @@ -352,7 +354,7 @@ end manifold_dimension(G::GroupManifold) = manifold_dimension(G.manifold) -function check_manifold_point(G::AbstractGroupManifold, e::Identity; kwargs...) +function check_point(G::AbstractGroupManifold, e::Identity; kwargs...) e.group === G && return nothing return DomainError(e, "The identity element $(e) does not belong to $(G).") end diff --git a/src/groups/metric.jl b/src/groups/metric.jl index 8d4dcf91ac..7c100472da 100644 --- a/src/groups/metric.jl +++ b/src/groups/metric.jl @@ -1,5 +1,5 @@ @doc raw""" - InvariantMetric{G<:Metric,D<:ActionDirection} <: Metric + InvariantMetric{G<:AbstractMetric,D<:ActionDirection} <: AbstractMetric Extend a metric on the Lie algebra of an [`AbstractGroupManifold`](@ref) to the whole group via translation in the specified direction. @@ -31,9 +31,9 @@ provided. # Constructor - InvariantMetric(metric::Metric, conv::ActionDirection = LeftAction()) + InvariantMetric(metric::AbstractMetric, conv::ActionDirection = LeftAction()) """ -struct InvariantMetric{G<:Metric,D<:ActionDirection} <: Metric +struct InvariantMetric{G<:AbstractMetric,D<:ActionDirection} <: AbstractMetric metric::G end @@ -41,19 +41,19 @@ function InvariantMetric(metric, conv=LeftAction()) return InvariantMetric{typeof(metric),typeof(conv)}(metric) end -const LeftInvariantMetric{G} = InvariantMetric{G,LeftAction} where {G<:Metric} +const LeftInvariantMetric{G} = InvariantMetric{G,LeftAction} where {G<:AbstractMetric} """ - LeftInvariantMetric(metric::Metric) + LeftInvariantMetric(metric::AbstractMetric) Alias for a left-[`InvariantMetric`](@ref). """ LeftInvariantMetric(metric) = InvariantMetric{typeof(metric),LeftAction}(metric) -const RightInvariantMetric{G} = InvariantMetric{G,RightAction} where {G<:Metric} +const RightInvariantMetric{G} = InvariantMetric{G,RightAction} where {G<:AbstractMetric} """ - RightInvariantMetric(metric::Metric) + RightInvariantMetric(metric::AbstractMetric) Alias for a right-[`InvariantMetric`](@ref). """ @@ -131,14 +131,14 @@ end Return `Val(true)` if the metric on the manifold is bi-invariant, that is, if the metric is both left- and right-invariant (see [`invariant_metric_dispatch`](@ref)). """ -function biinvariant_metric_dispatch(M::Manifold) +function biinvariant_metric_dispatch(M::AbstractManifold) return Val( invariant_metric_dispatch(M, LeftAction()) === Val(true) && invariant_metric_dispatch(M, RightAction()) === Val(true), ) end -has_biinvariant_metric(M::Manifold) = _extract_val(biinvariant_metric_dispatch(M)) +has_biinvariant_metric(M::AbstractManifold) = _extract_val(biinvariant_metric_dispatch(M)) @doc raw""" invariant_metric_dispatch(G::AbstractGroupManifold, conv::ActionDirection) -> Val @@ -166,19 +166,19 @@ function invariant_metric_dispatch(M::MetricManifold, conv::ActionDirection) return Val(false) end function invariant_metric_dispatch( - M::MetricManifold{𝔽,<:Manifold,<:InvariantMetric}, + M::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric}, conv::ActionDirection, ) where {𝔽} direction(metric(M)) === conv && return Val(true) return invoke(invariant_metric_dispatch, Tuple{MetricManifold,typeof(conv)}, M, conv) end -invariant_metric_dispatch(M::Manifold, ::ActionDirection) = Val(false) +invariant_metric_dispatch(M::AbstractManifold, ::ActionDirection) = Val(false) -function has_invariant_metric(M::Manifold, conv::ActionDirection) +function has_invariant_metric(M::AbstractManifold, conv::ActionDirection) return _extract_val(invariant_metric_dispatch(M, conv)) end -function inner(M::MetricManifold{𝔽,<:Manifold,<:InvariantMetric}, p, X, Y) where {𝔽} +function inner(M::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric}, p, X, Y) where {𝔽} imetric = metric(M) conv = direction(imetric) N = MetricManifold(M.manifold, imetric.metric) @@ -188,7 +188,7 @@ function inner(M::MetricManifold{𝔽,<:Manifold,<:InvariantMetric}, p, X, Y) wh end function default_metric_dispatch( - M::MetricManifold{𝔽,<:Manifold,<:InvariantMetric}, + M::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric}, ) where {𝔽} imetric = metric(M) N = MetricManifold(M.manifold, imetric.metric) @@ -217,7 +217,7 @@ function log!( end function LinearAlgebra.norm( - M::MetricManifold{𝔽,<:Manifold,<:InvariantMetric}, + M::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric}, p, X, ) where {𝔽} diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index ec9d9aed15..fc50876b52 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -1,6 +1,6 @@ @doc raw""" RotationAction( - M::Manifold, + M::AbstractManifold, SOn::SpecialOrthogonal, AD::ActionDirection = LeftAction(), ) @@ -8,14 +8,14 @@ Space of actions of the [`SpecialOrthogonal`](@ref) group $\mathrm{SO}(n)$ on a Euclidean-like manifold `M` of dimension `n`. """ -struct RotationAction{TM<:Manifold,TSO<:SpecialOrthogonal,TAD<:ActionDirection} <: +struct RotationAction{TM<:AbstractManifold,TSO<:SpecialOrthogonal,TAD<:ActionDirection} <: AbstractGroupAction{TAD} manifold::TM SOn::TSO end function RotationAction( - M::Manifold, + M::AbstractManifold, SOn::SpecialOrthogonal, ::TAD=LeftAction(), ) where {TAD<:ActionDirection} @@ -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.manifold, p, true) - is_manifold_point(A.manifold, q, true) + is_point(A.manifold, p, true) + is_point(A.manifold, q, true) Xmul = p * transpose(q) F = svd(Xmul) diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 44a7770103..9266cc8f39 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -143,7 +143,7 @@ function get_vector!(G::SemidirectProductGroup, Y, p, X, B::VeeOrthogonalBasis) end eval( quote - @invoke_maker 1 Manifold get_vector!( + @invoke_maker 1 AbstractManifold get_vector!( M::SemidirectProductGroup, Xⁱ, e::Identity, @@ -167,7 +167,7 @@ function get_coordinates!(G::SemidirectProductGroup, Y, p, X, B::VeeOrthogonalBa end eval( quote - @invoke_maker 1 Manifold get_coordinates!( + @invoke_maker 1 AbstractManifold get_coordinates!( M::SemidirectProductGroup, Y, e::Identity, @@ -177,19 +177,19 @@ eval( end, ) -function zero_tangent_vector(G::SemidirectProductGroup, p) - X = allocate_result(G, zero_tangent_vector, p) - zero_tangent_vector!(G, X, p) +function zero_vector(G::SemidirectProductGroup, p) + X = allocate_result(G, zero_vector, p) + zero_vector!(G, X, p) return X end -function zero_tangent_vector!(G::SemidirectProductGroup, X, p) +function zero_vector!(G::SemidirectProductGroup, X, p) M = base_manifold(G) N, H = M.manifolds np, hp = submanifold_components(G, p) nX, hX = submanifold_components(G, X) - zero_tangent_vector!(N, nX, np) - zero_tangent_vector!(H, hX, hp) + zero_vector!(N, nX, np) + zero_vector!(H, hX, hp) return X end diff --git a/src/groups/special_linear.jl b/src/groups/special_linear.jl index 56a0f9225b..10d3f43244 100644 --- a/src/groups/special_linear.jl +++ b/src/groups/special_linear.jl @@ -25,8 +25,8 @@ function allocation_promotion_function(::SpecialLinear{n,β„‚}, f, args::Tuple) w return complex end -function check_manifold_point(G::SpecialLinear{n,𝔽}, p; kwargs...) where {n,𝔽} - mpv = check_manifold_point(Euclidean(n, n; field=𝔽), p; kwargs...) +function check_point(G::SpecialLinear{n,𝔽}, p; kwargs...) where {n,𝔽} + mpv = check_point(Euclidean(n, n; field=𝔽), p; kwargs...) mpv === nothing || return mpv detp = det(p) if !isapprox(detp, 1; kwargs...) @@ -38,18 +38,13 @@ function check_manifold_point(G::SpecialLinear{n,𝔽}, p; kwargs...) where {n, end return nothing end -check_manifold_point(::GT, ::Identity{GT}; kwargs...) where {GT<:SpecialLinear} = nothing -function check_manifold_point(G::SpecialLinear, e::Identity; kwargs...) +check_point(::GT, ::Identity{GT}; kwargs...) where {GT<:SpecialLinear} = nothing +function check_point(G::SpecialLinear, e::Identity; kwargs...) return DomainError(e, "The identity element $(e) does not belong to $(G).") end -function check_tangent_vector(G::SpecialLinear, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(G, p; kwargs...) - mpe === nothing || return mpe - end - mpv = - check_tangent_vector(decorated_manifold(G), p, X; check_base_point=false, kwargs...) +function check_vector(G::SpecialLinear, p, X; kwargs...) + mpv = check_vector(decorated_manifold(G), p, X; kwargs...) mpv === nothing || return mpv trX = tr(inverse_translate_diff(G, p, p, X, LeftAction())) if !isapprox(trX, 0; kwargs...) diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 836538907b..e410f1a2d6 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -1,6 +1,6 @@ @doc raw""" TranslationAction( - M::Manifold, + M::AbstractManifold, Rn::TranslationGroup, AD::ActionDirection = LeftAction(), ) @@ -10,14 +10,14 @@ manifold `M`. The left and right actions are equivalent. """ -struct TranslationAction{TM<:Manifold,TRn<:TranslationGroup,TAD<:ActionDirection} <: +struct TranslationAction{TM<:AbstractManifold,TRn<:TranslationGroup,TAD<:ActionDirection} <: AbstractGroupAction{TAD} manifold::TM Rn::TRn end function TranslationAction( - M::Manifold, + M::AbstractManifold, Rn::TranslationGroup, ::TAD=LeftAction(), ) where {TAD<:ActionDirection} diff --git a/src/manifolds/CenteredMatrices.jl b/src/manifolds/CenteredMatrices.jl index f00c2b8684..1f6757c2e3 100644 --- a/src/manifolds/CenteredMatrices.jl +++ b/src/manifolds/CenteredMatrices.jl @@ -19,7 +19,7 @@ function CenteredMatrices(m::Int, n::Int, field::AbstractNumbers=ℝ) end @doc raw""" - check_manifold_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) + check_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) Check whether the matrix is a valid point on the [`CenteredMatrices`](@ref) `M`, i.e. is an `m`-by-`n` matrix whose columns sum to @@ -27,9 +27,8 @@ zero. The tolerance for the column sums of `p` can be set using `kwargs...`. """ -function check_manifold_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) where {m,n,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) where {m,n,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv if !isapprox(sum(p, dims=1), zeros(1, n); kwargs...) return DomainError( @@ -43,33 +42,20 @@ function check_manifold_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) where end """ - check_tangent_vector(M::CenteredMatrices{m,n,𝔽}, p, X; check_base_point = true, kwargs... ) + check_vector(M::CenteredMatrices{m,n,𝔽}, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the -[`CenteredMatrices`](@ref) `M`, i.e. that `X` is a matrix of size `(m,n)` whose columns +[`CenteredMatrices`](@ref) `M`, i.e. that `X` is a matrix of size `(m, n)` whose columns sum to zero and its values are from the correct [`AbstractNumbers`](@ref). -The optional parameter `check_base_point` indicates, whether to call - [`check_manifold_point`](@ref) for `p`. The tolerance for the column sums of `p` and `X` can be set using `kwargs...`. """ -function check_tangent_vector( - M::CenteredMatrices{m,n,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {m,n,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::CenteredMatrices{m,n,𝔽}, p, X; kwargs...) where {m,n,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, kwargs..., ) mpv === nothing || return mpv diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 0940922021..af3c3a3dae 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -1,5 +1,5 @@ @doc raw""" - CholeskySpace{N} <: Manifold{ℝ} + CholeskySpace{N} <: AbstractManifold{ℝ} The manifold of lower triangular matrices with positive diagonal and a metric based on the cholesky decomposition. The formulae for this manifold @@ -15,19 +15,19 @@ Generate the manifold of $nΓ— n$ lower triangular matrices with positive diagona > Lin, Zenhua: "Riemannian Geometry of Symmetric Positive Definite Matrices via > Cholesky Decomposition", arXiv: [1908.09326](https://arxiv.org/abs/1908.09326). """ -struct CholeskySpace{N} <: Manifold{ℝ} end +struct CholeskySpace{N} <: AbstractManifold{ℝ} end CholeskySpace(n::Int) = CholeskySpace{n}() @doc raw""" - check_manifold_point(M::CholeskySpace, p; kwargs...) + check_point(M::CholeskySpace, p; kwargs...) 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, p; kwargs...) +function check_point(M::CholeskySpace, p; kwargs...) if size(p) != representation_size(M) return DomainError( size(p), @@ -50,19 +50,14 @@ function check_manifold_point(M::CholeskySpace, p; kwargs...) end """ - check_tangent_vector(M::CholeskySpace, p, X; check_base_point = true, kwargs... ) + check_vector(M::CholeskySpace, p, X; kwargs... ) Check whether `v` is a tangent vector to `p` on the [`CholeskySpace`](@ref) `M`, i.e. -after [`check_manifold_point`](@ref)`(M,p)`, `X` has to have the same dimension as `x` +after [`check_point`](@ref)`(M,p)`, `X` has to have the same dimension as `p` and a symmetric matrix. -The optional parameter `check_base_point` indicates whether to call [`check_manifold_point`](@ref) for `p`. The tolerance for the tests can be set using the `kwargs...`. """ -function check_tangent_vector(M::CholeskySpace, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe !== nothing && return mpe - end +function check_vector(M::CholeskySpace, p, X; kwargs...) if size(X) != representation_size(M) return DomainError( size(X), @@ -213,10 +208,10 @@ function vector_transport_to!(::CholeskySpace, Y, p, X, q, ::ParallelTransport) end @doc raw""" - zero_tangent_vector(M::CholeskySpace, p) + zero_vector(M::CholeskySpace, p) Return the zero tangent vector on the [`CholeskySpace`](@ref) `M` at `p`. """ -zero_tangent_vector(::CholeskySpace, ::Any...) +zero_vector(::CholeskySpace, ::Any...) -zero_tangent_vector!(M::CholeskySpace, X, p) = fill!(X, 0) +zero_vector!(M::CholeskySpace, X, p) = fill!(X, 0) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 47c890bbd5..4b59a85252 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -1,5 +1,5 @@ @doc raw""" - Circle{𝔽} <: Manifold{𝔽} + Circle{𝔽} <: AbstractManifold{𝔽} The circle $π•Š^1$ is a manifold here represented by real-valued points in $[-Ο€,Ο€)$ or complex-valued points $z ∈ β„‚$ of absolute value @@ -12,20 +12,20 @@ Generate the `ℝ`-valued Circle represented by angles, which alternatively can be set to use the [`AbstractNumbers`](@ref) `𝔽=β„‚` to obtain the circle represented by `β„‚`-valued circle of unit numbers. """ -struct Circle{𝔽} <: Manifold{𝔽} end +struct Circle{𝔽} <: AbstractManifold{𝔽} end Circle(𝔽::AbstractNumbers=ℝ) = Circle{𝔽}() @doc raw""" - check_manifold_point(M::Circle, p) + check_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 ∈ [-Ο€,Ο€)$. +For the real-valued case, `p` is an angle and hence it checks that $p ∈ [-Ο€,Ο€)$. for the complex-valued case, it is a unit number, $p ∈ β„‚$ with $\lvert p \rvert = 1$. """ -check_manifold_point(::Circle, ::Any...) +check_point(::Circle, ::Any...) -function check_manifold_point(M::Circle{ℝ}, p; kwargs...) +function check_point(M::Circle{ℝ}, p; kwargs...) if !isapprox(sym_rem(p), p; kwargs...) return DomainError( p, @@ -34,7 +34,7 @@ function check_manifold_point(M::Circle{ℝ}, p; kwargs...) end return nothing end -function check_manifold_point(M::Circle{β„‚}, p; kwargs...) +function check_point(M::Circle{β„‚}, p; kwargs...) if !isapprox(sum(abs.(p)), 1.0; kwargs...) return DomainError( abs(p), @@ -45,29 +45,20 @@ function check_manifold_point(M::Circle{β„‚}, p; kwargs...) end """ - check_tangent_vector(M::Circle, p, X; check_base_point, kwargs...) + check_vector(M::Circle, p, X; kwargs...) 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 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. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -check_tangent_vector(::Circle{ℝ}, ::Any...; ::Any...) +check_vector(::Circle{ℝ}, ::Any...; ::Any...) -function check_tangent_vector(M::Circle{ℝ}, p, X; check_base_point=true, kwargs...) - if check_base_point - perr = check_manifold_point(M, p; kwargs...) - return perr # if x is valid all v that are real numbers are valid - end +function check_vector(M::Circle{ℝ}, p, X; kwargs...) return nothing end -function check_tangent_vector(M::Circle{β„‚}, p, X; check_base_point=true, kwargs...) - if check_base_point - perr = check_manifold_point(M, p) - perr === nothing || return perr - end +function check_vector(M::Circle{β„‚}, p, X; kwargs...) if !isapprox(abs(complex_dot(p, X)), 0.0; kwargs...) return DomainError( abs(complex_dot(p, X)), @@ -112,9 +103,9 @@ complex plane. """ exp(::Circle, ::Any...) Base.exp(::Circle{ℝ}, p::Real, X::Real) = sym_rem(p + X) -function Base.exp(M::Circle{β„‚}, x::Number, v::Number) - ΞΈ = norm(M, x, v) - return cos(ΞΈ) * x + usinc(ΞΈ) * v +function Base.exp(M::Circle{β„‚}, p::Number, X::Number) + ΞΈ = norm(M, p, X) + return cos(ΞΈ) * p + usinc(ΞΈ) * X end exp!(::Circle{ℝ}, q, p, X) = (q .= sym_rem(p + X)) @@ -124,26 +115,29 @@ function exp!(M::Circle{β„‚}, q, p, X) return q end -flat(::Circle, ::Number, X::TFVector) = FVector(CotangentSpace, X.data) - -flat!(::Circle, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - -function get_basis(M::Circle{ℝ}, p, B::DiagonalizingOrthonormalBasis) +function get_basis(::Circle{ℝ}, p, B::DiagonalizingOrthonormalBasis) sbv = sign(B.frame_direction[]) vs = @SVector [@SVector [sbv == 0 ? one(sbv) : sbv]] return CachedBasis(B, (@SVector [0]), vs) end -get_coordinates(M::Circle{ℝ}, p, X, B::DefaultOrthonormalBasis) = X + +get_coordinates(::Circle{ℝ}, p, X, ::AbstractBasis{<:Any,TangentSpaceType}) = X +get_coordinates(::Circle{ℝ}, p, X, ::DefaultOrthonormalBasis{<:Any,TangentSpaceType}) = X function get_coordinates(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) sbv = sign(B.frame_direction[]) - return X .* (sbv == 0 ? 1 : sbv) + return X .* (sbv == 0 ? one(sbv) : sbv) end """ get_coordinates(M::Circle{β„‚}, p, X, B::DefaultOrthonormalBasis) Return tangent vector coordinates in the Lie algebra of the [`Circle`](@ref). """ -function get_coordinates(M::Circle{β„‚}, p, X, B::DefaultOrthonormalBasis) +function get_coordinates( + ::Circle{β„‚}, + p, + X, + ::DefaultOrthonormalBasis{<:Any,TangentSpaceType}, +) X, p = X[1], p[1] Xⁱ = imag(X) * real(p) - real(X) * imag(p) return @SVector [Xⁱ] @@ -151,7 +145,7 @@ end eval( quote - @invoke_maker 1 Manifold get_coordinates( + @invoke_maker 1 AbstractManifold get_coordinates( M::Circle, e::Identity, X, @@ -160,7 +154,13 @@ eval( end, ) -function get_coordinates!(M::Circle, Y::AbstractArray, p, X, B::DefaultOrthonormalBasis) +function get_coordinates!( + M::Circle, + Y::AbstractArray, + p, + X, + B::DefaultOrthonormalBasis{<:Any,TangentSpaceType}, +) Y[] = get_coordinates(M, p, X, B)[] return Y end @@ -177,7 +177,7 @@ end eval( quote - @invoke_maker 1 Manifold get_coordinates!( + @invoke_maker 1 AbstractManifold get_coordinates!( M::Circle, Y::AbstractArray, p, @@ -187,27 +187,50 @@ eval( end, ) -get_vector(::Circle{ℝ}, p, X, ::AbstractBasis) = X -function get_vector(M::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) +get_vector(::Circle{ℝ}, p, X, ::AbstractBasis{ℝ,TangentSpaceType}) = X +get_vector(::Circle{ℝ}, p, X, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) = X +function get_vector(::Circle{ℝ}, p, X, B::DiagonalizingOrthonormalBasis) sbv = sign(B.frame_direction[]) - return X .* (sbv == 0 ? 1 : sbv) + return X .* (sbv == 0 ? one(sbv) : sbv) end """ get_vector(M::Circle{β„‚}, p, X, B::DefaultOrthonormalBasis) Return tangent vector from the coordinates in the Lie algebra of the [`Circle`](@ref). """ -get_vector(M::Circle{β„‚}, p, X, B::AbstractBasis) = @SVector [1im * X[1] * p[1]] +function get_vector(::Circle{β„‚}, p, X, ::AbstractBasis{<:Any,TangentSpaceType}) + @SVector [1im * X[1] * p[1]] +end +eval( + quote + @invoke_maker 4 AbstractBasis get_vector( + M::Circle{β„‚}, + p, + X, + B::DefaultOrthonormalBasis{<:Any,TangentSpaceType}, + ) + end, +) -function get_vector!(M::Circle, Y::AbstractArray, p, X, B::AbstractBasis) - Y[] = get_vector(M, p, X, B)[] - return Y +for BT in [AbstractBasis{<:Any,TangentSpaceType}] + eval(quote + function get_vector!(::Circle{ℝ}, Y::AbstractArray, p, X, ::$BT) + Y[] = X[] + return Y + end + end) + eval(quote + function get_vector!(::Circle{β„‚}, Y::AbstractArray, p, X, ::$BT) + Y[] = 1im * X[1] * p[1] + return Y + end + end) end -for BT in ManifoldsBase.DISAMBIGUATION_BASIS_TYPES +for BT in ManifoldsBase.DISAMBIGUATION_BASIS_TYPES, CT in [Circle, Circle{ℝ}, Circle{β„‚}] eval( quote @invoke_maker 5 $(supertype(BT)) get_vector!( - M::Circle, + M::$CT, Y::AbstractArray, p, X, @@ -228,7 +251,7 @@ injectivity_radius(::Circle, ::Any) = Ο€ injectivity_radius(::Circle, ::Any, ::ExponentialRetraction) = Ο€ eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::Circle, rm::AbstractRetractionMethod, ) @@ -259,11 +282,11 @@ inner(::Circle, ::Any...) @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()) +function inverse_retract(M::Circle, p::Number, q::Number) + return inverse_retract(M, p, q, LogarithmicInverseRetraction()) end -function inverse_retract(M::Circle, x::Number, y::Number, ::LogarithmicInverseRetraction) - return log(M, x, y) +function inverse_retract(M::Circle, p::Number, q::Number, ::LogarithmicInverseRetraction) + return log(M, p, q) end @doc raw""" @@ -320,7 +343,7 @@ manifold_dimension(::Circle) = 1 @doc raw""" mean(M::Circle{ℝ}, x::AbstractVector[, w::AbstractWeights]) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` of points on +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` of points on the [`Circle`](@ref) $π•Š^1$, reprsented by real numbers, i.e. the angular mean ````math \operatorname{atan}\Bigl( \sum_{i=1}^n w_i\sin(x_i), \sum_{i=1}^n w_i\sin(x_i) \Bigr). @@ -341,7 +364,7 @@ end @doc raw""" mean(M::Circle{β„‚}, x::AbstractVector[, w::AbstractWeights]) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` of points on +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` of points on the [`Circle`](@ref) $π•Š^1$, reprsented by complex numbers, i.e. embedded in the complex plade. Comuting the sum ````math @@ -418,10 +441,6 @@ retract(M::Circle, p, q, m::ExponentialRetraction) = exp(M, p, q) representation_size(::Circle) = () -sharp(::Circle, p::Number, ΞΎ::CoTFVector) = FVector(TangentSpace, ΞΎ.data) - -sharp!(::Circle, X::TFVector, p, ΞΎ::CoTFVector) = copyto!(X, ΞΎ) - Base.show(io::IO, ::Circle{𝔽}) where {𝔽} = print(io, "Circle($(𝔽))") @doc raw""" @@ -491,5 +510,5 @@ function vector_transport_direction( return vector_transport_to(M, p, X, q, m) end -zero_tangent_vector(::Circle, p::Number) = zero(p) -zero_tangent_vector!(::Circle, X, p) = fill!(X, 0) +zero_vector(::Circle, p::Number) = zero(p) +zero_vector!(::Circle, X, p) = fill!(X, 0) diff --git a/src/manifolds/Elliptope.jl b/src/manifolds/Elliptope.jl index 218e021035..6d92f5046b 100644 --- a/src/manifolds/Elliptope.jl +++ b/src/manifolds/Elliptope.jl @@ -51,7 +51,7 @@ struct Elliptope{N,K} <: AbstractEmbeddedManifold{ℝ,DefaultIsometricEmbeddingT Elliptope(n::Int, k::Int) = Elliptope{n,k}() @doc raw""" - check_manifold_point(M::Elliptope, q; kwargs...) + check_point(M::Elliptope, q; kwargs...) checks, whether `q` is a valid reprsentation of a point $p=qq^{\mathrm{T}}$ on the [`Elliptope`](@ref) `M`, i.e. is a matrix @@ -60,9 +60,8 @@ Since by construction $p$ is symmetric, this is not explicitly checked. Since $p$ is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ -function check_manifold_point(M::Elliptope{N,K}, q; kwargs...) where {N,K} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) +function check_point(M::Elliptope{N,K}, q; kwargs...) where {N,K} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) mpv === nothing || return mpv row_norms_sq = sum(abs2, q; dims=2) if !all(isapprox.(row_norms_sq, 1.0; kwargs...)) @@ -75,34 +74,23 @@ function check_manifold_point(M::Elliptope{N,K}, q; kwargs...) where {N,K} end @doc raw""" - check_tangent_vector(M::Elliptope, q, Y; check_base_point = true, kwargs... ) + check_vector(M::Elliptope, q, Y; kwargs... ) Check whether $X = qY^{\mathrm{T}} + Yq^{\mathrm{T}}$ is a tangent vector to $p=qq^{\mathrm{T}}$ on the [`Elliptope`](@ref) `M`, -i.e. atfer [`check_manifold_point`](@ref) of `q`, `Y` has to be of same dimension as `q` -and a $X$ has to be a symmetric matrix with zero diagonal. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `q`. +i.e. `Y` has to be of same dimension as `q` and a $X$ has to be a symmetric matrix with +zero diagonal. + The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. Note that symmetric of $X$ holds by construction an is not explicitly checked. """ -function check_tangent_vector( - M::Elliptope{N,K}, - q, - Y; - check_base_point=true, - kwargs..., -) where {N,K} - if check_base_point - mpe = check_manifold_point(M, q; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Elliptope{N,K}, q, Y; kwargs...) where {N,K} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(q),typeof(Y)}, M, q, Y; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -192,11 +180,11 @@ function vector_transport_to!(M::Elliptope, Y, p, X, q, ::ProjectionTransport) end @doc raw""" - zero_tangent_vector(M::Elliptope,p) + zero_vector(M::Elliptope,p) returns the zero tangent vector in the tangent space of the symmetric positive definite matrix `p` on the [`Elliptope`](@ref) manifold `M`. """ -zero_tangent_vector(::Elliptope, ::Any...) +zero_vector(::Elliptope, ::Any...) -zero_tangent_vector!(::Elliptope{N,K}, v, ::Any) where {N,K} = fill!(v, 0) +zero_vector!(::Elliptope{N,K}, v, ::Any) where {N,K} = fill!(v, 0) diff --git a/src/manifolds/EssentialManifold.jl b/src/manifolds/EssentialManifold.jl index f478e39a38..3538c514fa 100644 --- a/src/manifolds/EssentialManifold.jl +++ b/src/manifolds/EssentialManifold.jl @@ -50,7 +50,7 @@ unsigned (`is_signed=false`) variant. [^TronDaniilidis2017]: > Tron R.; Daniilidis K.; The Space of Essential Matrices as a Riemannian Quotient - > Manifold. + > AbstractManifold. > SIAM Journal on Imaging Sciences (2017), > DOI: [10.1137/16M1091332](https://doi.org/10.1137/16M1091332), > PDF: [https://www.cis.upenn.edu/~kostas/mypub.dir/tron17siam.pdf](https://www.cis.upenn.edu/~kostas/mypub.dir/tron17siam.pdf). @@ -63,19 +63,19 @@ end EssentialManifold(is_signed::Bool=true) = EssentialManifold(is_signed, Rotations(3)) @doc raw""" - check_manifold_point(M::EssentialManifold, p; kwargs...) + check_point(M::EssentialManifold, p; kwargs...) Check whether the matrix is a valid point on the [`EssentialManifold`](@ref) `M`, i.e. a 2-element array containing SO(3) matrices. """ -function check_manifold_point(M::EssentialManifold, p; kwargs...) +function check_point(M::EssentialManifold, p; kwargs...) if length(p) != 2 return DomainError( length(p), "The point $(p) does not lie on $M, since it does not contain exactly two elements.", ) end - return check_manifold_point( + return check_point( PowerManifold(M.manifold, NestedPowerRepresentation(), 2), p; kwargs..., @@ -83,28 +83,22 @@ function check_manifold_point(M::EssentialManifold, p; kwargs...) end """ - check_tangent_vector(M::EssentialManifold, p, X; check_base_point = true, kwargs... ) + check_vector(M::EssentialManifold, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`EssentialManifold`](@ref) `M`, i.e. `X` has to be a 2-element array of `3`-by-`3` skew-symmetric matrices. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector(M::EssentialManifold, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::EssentialManifold, p, X; kwargs...) if length(X) != 2 return DomainError( length(X), "$(X) is not a tangent vector to the manifold $M, since it does not contain exactly two elements.", ) end - return check_tangent_vector( + return check_vector( PowerManifold(M.manifold, NestedPowerRepresentation(), 2), p, X; - check_base_point=check_base_point, kwargs..., ) end diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index f1b0fad2a0..6a8b269ead 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -1,5 +1,5 @@ @doc raw""" - Euclidean{T<:Tuple,𝔽} <: Manifold{𝔽} + Euclidean{T<:Tuple,𝔽} <: AbstractManifold{𝔽} Euclidean vector space. @@ -25,7 +25,7 @@ The dimension of this space is ``k \dim_ℝ 𝔽``, where ``\dim_ℝ 𝔽`` is t Generate the 1D Euclidean manifold for an `ℝ`-, `β„‚`-valued real- or complex-valued immutable values (in contrast to 1-element arrays from the constructor above). """ -struct Euclidean{N,𝔽} <: Manifold{𝔽} where {N<:Tuple} end +struct Euclidean{N,𝔽} <: AbstractManifold{𝔽} where {N<:Tuple} end function Euclidean(n::Vararg{Int,I}; field::AbstractNumbers=ℝ) where {I} return Euclidean{Tuple{n...},field}() @@ -61,7 +61,7 @@ function allocation_promotion_function( return complex end -function check_manifold_point(M::Euclidean{N,𝔽}, p) where {N,𝔽} +function check_point(M::Euclidean{N,𝔽}, p) where {N,𝔽} if (𝔽 === ℝ) && !(eltype(p) <: Real) return DomainError( eltype(p), @@ -83,17 +83,7 @@ function check_manifold_point(M::Euclidean{N,𝔽}, p) where {N,𝔽} return nothing end -function check_tangent_vector( - M::Euclidean{N,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {N,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Euclidean{N,𝔽}, p, X; kwargs...) where {N,𝔽} if (𝔽 === ℝ) && !(eltype(X) <: Real) return DomainError( eltype(X), @@ -115,7 +105,11 @@ function check_tangent_vector( return nothing end -function det_local_metric(::MetricManifold{𝔽,<:Manifold,EuclideanMetric}, p) where {𝔽} +function det_local_metric( + ::MetricManifold{𝔽,<:AbstractManifold,EuclideanMetric}, + p, + ::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, +) where {𝔽} return one(eltype(p)) end @@ -187,25 +181,15 @@ Base.exp(::Euclidean, p::Number, q::Number) = p + q exp!(::Euclidean, q, p, X) = (q .= p .+ X) -""" - flat(M::Euclidean, p, X) - -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`. -""" -flat(::Euclidean, ::Any...) -function flat(::Euclidean{Tuple{}}, ::Number, X::TFVector) - return FVector(CotangentSpace, X.data) -end - -flat!(::Euclidean, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - -function get_basis(::Euclidean, p, B::DefaultOrthonormalBasis{ℝ}) +function get_basis(::Euclidean, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) vecs = [_euclidean_basis_vector(p, i) for i in eachindex(p)] return CachedBasis(B, vecs) end -function get_basis(::Euclidean{<:Tuple,β„‚}, p, B::DefaultOrthonormalBasis{β„‚}) +function get_basis( + ::Euclidean{<:Tuple,β„‚}, + p, + B::DefaultOrthonormalBasis{β„‚,TangentSpaceType}, +) vecs = [_euclidean_basis_vector(p, i) for i in eachindex(p)] return CachedBasis(B, [vecs; im * vecs]) end @@ -215,7 +199,7 @@ function get_basis(M::Euclidean, p, B::DiagonalizingOrthonormalBasis) return CachedBasis(B, DiagonalizingBasisData(B.frame_direction, eigenvalues, vecs)) end -function get_coordinates!(M::Euclidean, Y, p, X, B::DefaultOrDiagonalizingBasis{ℝ}) +function get_coordinates!(M::Euclidean, Y, p, X, ::DefaultOrDiagonalizingBasis{ℝ}) S = representation_size(M) PS = prod(S) copyto!(Y, reshape(X, PS)) @@ -278,19 +262,44 @@ where ``\cdot^{\mathrm{H}}`` denotes the Hermitian, i.e. complex conjugate trans """ inner(::Euclidean, ::Any...) @inline inner(::Euclidean, p, X, Y) = dot(X, Y) -@inline inner(::MetricManifold{𝔽,<:Manifold,EuclideanMetric}, p, X, Y) where {𝔽} = dot(X, Y) +@inline function inner( + ::MetricManifold{𝔽,<:AbstractManifold,EuclideanMetric}, + p, + X, + Y, +) where {𝔽} + return dot(X, Y) +end -function inverse_local_metric(M::MetricManifold{𝔽,<:Manifold,EuclideanMetric}, p) where {𝔽} - return local_metric(M, p) +function inverse_local_metric( + M::MetricManifold{𝔽,<:AbstractManifold,EuclideanMetric}, + p, + B::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, +) where {𝔽} + return local_metric(M, p, B) +end +function inverse_local_metric( + M::Euclidean, + p, + B::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, +) where {𝔽} + return local_metric(M, p, B) end -inverse_local_metric(M::Euclidean, p) = local_metric(M, p) default_metric_dispatch(::Euclidean, ::EuclideanMetric) = Val(true) -function local_metric(::MetricManifold{𝔽,<:Manifold,EuclideanMetric}, p) where {𝔽} +function local_metric( + ::MetricManifold{𝔽,<:AbstractManifold,EuclideanMetric}, + p, + B::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, +) where {𝔽} return Diagonal(ones(SVector{size(p, 1),eltype(p)})) end -function local_metric(::Euclidean, p) +function local_metric( + ::Euclidean, + p, + B::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, +) where {𝔽} return Diagonal(ones(SVector{size(p, 1),eltype(p)})) end @@ -321,8 +330,9 @@ Base.log(::Euclidean{Tuple{}}, p::Number, q::Number) = q - p log!(::Euclidean, X, p, q) = (X .= q .- p) function log_local_metric_density( - ::MetricManifold{𝔽,<:Manifold,EuclideanMetric}, + ::MetricManifold{𝔽,<:AbstractManifold,EuclideanMetric}, p, + ::InducedBasis{𝔽,TangentSpaceType,<:RetractionAtlas}, ) where {𝔽} return zero(eltype(p)) end @@ -409,7 +419,7 @@ Compute the norm of a tangent vector `X` at `p` on the [`Euclidean`](@ref) in this case, just the (Frobenius) norm of `X`. """ LinearAlgebra.norm(::Euclidean, ::Any, X) = norm(X) -LinearAlgebra.norm(::MetricManifold{ℝ,<:Manifold,EuclideanMetric}, p, X) = norm(X) +LinearAlgebra.norm(::MetricManifold{ℝ,<:AbstractManifold,EuclideanMetric}, p, X) = norm(X) function project!( ::EmbeddedManifold{𝔽,Euclidean{nL,𝔽},Euclidean{mL,𝔽2}}, @@ -474,21 +484,6 @@ function retract(M::Euclidean{Tuple{}}, p::Number, q::Number, ::ExponentialRetra return exp(M, p, q) end -""" - sharp(M::Euclidean, p, ΞΎ) - -Transform the cotangent vector `ΞΎ` at `p` on the [`Euclidean`](@ref) `M` to a tangent vector `X`. -Since cotangent and tangent vectors can directly be identified in the [`Euclidean`](@ref) -case, this yields just the identity. -""" -sharp(::Euclidean, ::Any...) - -function sharp(::Euclidean{Tuple{}}, ::Number, ΞΎ::CoTFVector) - return FVector(TangentSpace, ΞΎ.data) -end - -sharp!(::Euclidean, X::TFVector, p, ΞΎ::CoTFVector) = copyto!(X, ΞΎ) - function Base.show(io::IO, ::Euclidean{N,𝔽}) where {N,𝔽} return print(io, "Euclidean($(join(N.parameters, ", ")); field = $(𝔽))") end @@ -553,12 +548,12 @@ function Statistics.var(::Euclidean, x::AbstractVector{<:Number}, m::Number; kwa end """ - zero_tangent_vector(M::Euclidean, x) + zero_vector(M::Euclidean, x) Return the zero vector in the tangent space of `x` on the [`Euclidean`](@ref) `M`, which here is just a zero filled array the same size as `x`. """ -zero_tangent_vector(::Euclidean, ::Any...) -zero_tangent_vector(::Euclidean{Tuple{}}, p::Number) = zero(p) +zero_vector(::Euclidean, ::Any...) +zero_vector(::Euclidean{Tuple{}}, p::Number) = zero(p) -zero_tangent_vector!(::Euclidean, v, ::Any) = fill!(v, 0) +zero_vector!(::Euclidean, v, ::Any) = fill!(v, 0) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 065cf49bda..0f19d73f74 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - FixedRankMatrices{m,n,k,𝔽} <: Manifold{𝔽} + FixedRankMatrices{m,n,k,𝔽} <: AbstractManifold{𝔽} The manifold of $m Γ— n$ real-valued or complex-valued matrices of fixed rank $k$, i.e. ````math @@ -42,13 +42,13 @@ Generate the manifold of `m`-by-`n` (`field`-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,𝔽} <: Manifold{𝔽} end +struct FixedRankMatrices{M,N,K,𝔽} <: AbstractManifold{𝔽} end function FixedRankMatrices(m::Int, n::Int, k::Int, field::AbstractNumbers=ℝ) return FixedRankMatrices{m,n,k,field}() end @doc raw""" - SVDMPoint <: MPoint + SVDMPoint <: AbstractManifoldPoint A point on a certain manifold, where the data is stored in a svd like fashion, i.e. in the form $USV^\mathrm{H}$, where this structure stores $U$, $S$ and @@ -66,7 +66,8 @@ and accordingly shortened $U$ (columns) and $V^\mathrm{T}$ (rows). * `SVDMPoint(U,S,Vt,k)` for the svd factors to initialize the `SVDMPoint`, stores its svd factors shortened to the best rank $k$ approximation """ -struct SVDMPoint{TU<:AbstractMatrix,TS<:AbstractVector,TVt<:AbstractMatrix} <: MPoint +struct SVDMPoint{TU<:AbstractMatrix,TS<:AbstractVector,TVt<:AbstractMatrix} <: + AbstractManifoldPoint U::TU S::TS Vt::TVt @@ -167,7 +168,7 @@ end #### @doc raw""" - check_manifold_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) + check_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 an `m`-by`n` matrix of @@ -175,7 +176,7 @@ 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}, p; kwargs...) where {m,n,k} +function check_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) where {m,n,k} r = rank(p; kwargs...) s = "The point $(p) does not lie on $(M), " if size(p) != (m, n) @@ -186,11 +187,7 @@ function check_manifold_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) where { end return nothing end -function check_manifold_point( - M::FixedRankMatrices{m,n,k}, - x::SVDMPoint; - kwargs..., -) where {m,n,k} +function check_point(M::FixedRankMatrices{m,n,k}, x::SVDMPoint; kwargs...) where {m,n,k} s = "The point $(x) does not lie on $(M), " if (size(x.U) != (m, k)) || (length(x.S) != k) || (size(x.Vt) != (k, n)) return DomainError( @@ -217,24 +214,18 @@ function check_manifold_point( end @doc raw""" - check_tangent_vector(M:FixedRankMatrices{m,n,k}, p, X; check_base_point = true, kwargs...) + check_vector(M:FixedRankMatrices{m,n,k}, p, X; kwargs...) 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`. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector( +function check_vector( M::FixedRankMatrices{m,n,k}, p::SVDMPoint, X::UMVTVector; - check_base_point=true, kwargs..., ) where {m,n,k} - if check_base_point - c = check_manifold_point(M, p; kwargs...) - c === nothing || return c - end if (size(X.U) != (m, k)) || (size(X.Vt) != (k, n)) || (size(X.M) != (k, k)) return DomainError( cat(size(X.U), size(X.M), size(X.Vt), dims=1), @@ -440,13 +431,13 @@ function Base.copyto!(X::UMVTVector, Y::UMVTVector) end @doc raw""" - zero_tangent_vector(M::FixedRankMatrices, p::SVDMPoint) + zero_vector(M::FixedRankMatrices, p::SVDMPoint) Return a [`UMVTVector`](@ref) representing the zero tangent vector in the tangent space of `p` 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}, p::SVDMPoint) where {m,n,k} +function zero_vector(::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} v = UMVTVector( zeros(eltype(p.U), m, k), zeros(eltype(p.S), k, k), @@ -455,11 +446,7 @@ function zero_tangent_vector(::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m, return v end -function zero_tangent_vector!( - ::FixedRankMatrices{m,n,k}, - X::UMVTVector, - p::SVDMPoint, -) where {m,n,k} +function zero_vector!(::FixedRankMatrices{m,n,k}, X::UMVTVector, p::SVDMPoint) where {m,n,k} X.U .= zeros(eltype(X.U), m, k) X.M .= zeros(eltype(X.M), k, k) X.Vt .= zeros(eltype(X.Vt), k, n) diff --git a/src/manifolds/GeneralizedGrassmann.jl b/src/manifolds/GeneralizedGrassmann.jl index 1cfb4a701e..cd46d6b43b 100644 --- a/src/manifolds/GeneralizedGrassmann.jl +++ b/src/manifolds/GeneralizedGrassmann.jl @@ -58,15 +58,15 @@ function GeneralizedGrassmann( end @doc raw""" - check_manifold_point(M::GeneralizedGrassmann{n,k,𝔽}, p) + check_point(M::GeneralizedGrassmann{n,k,𝔽}, p) Check whether `p` is representing a point on the [`GeneralizedGrassmann`](@ref) `M`, i.e. its a `n`-by-`k` matrix of unitary column vectors with respect to the B inner prudct and of correct `eltype` with respect to `𝔽`. """ -function check_manifold_point(M::GeneralizedGrassmann{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} +function check_point(M::GeneralizedGrassmann{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} mpv = invoke( - check_manifold_point, + check_point, Tuple{typeof(get_embedding(M)),typeof(p)}, get_embedding(M), p; @@ -84,7 +84,7 @@ function check_manifold_point(M::GeneralizedGrassmann{n,k,𝔽}, p; kwargs...) w end @doc raw""" - check_tangent_vector(M::GeneralizedGrassmann{n,k,𝔽}, p, X; check_base_point = true, kwargs...) + check_vector(M::GeneralizedGrassmann{n,k,𝔽}, p, X; kwargs...) Check whether `X` is a tangent vector in the tangent space of `p` on the [`GeneralizedGrassmann`](@ref) `M`, i.e. that `X` is of size and type as well as that @@ -95,26 +95,14 @@ the [`GeneralizedGrassmann`](@ref) `M`, i.e. that `X` is of size and type as wel where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, $\overline{\cdot}$ the (elementwise) complex conjugate, and $0_k$ denotes the $k Γ— k$ zero natrix. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector( - M::GeneralizedGrassmann{n,k,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,k,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::GeneralizedGrassmann{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{typeof(get_embedding(M)),typeof(p),typeof(X)}, get_embedding(M), p, X; - check_base_point=check_base_point, kwargs..., ) mpv === nothing || return mpv @@ -197,7 +185,7 @@ injectivity_radius(::GeneralizedGrassmann, ::Any) = Ο€ / 2 injectivity_radius(::GeneralizedGrassmann, ::Any, ::ExponentialRetraction) = Ο€ / 2 eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::GeneralizedGrassmann, rm::AbstractRetractionMethod, ) @@ -219,7 +207,7 @@ where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian inner(M::GeneralizedGrassmann{n,k}, p, X, Y) where {n,k} = dot(X, M.B * Y) function Base.isapprox(M::GeneralizedGrassmann, p, X, Y; kwargs...) - return isapprox(sqrt(inner(M, p, zero_tangent_vector(M, p), X - Y)), 0; kwargs...) + return isapprox(sqrt(inner(M, p, zero_vector(M, p), X - Y)), 0; kwargs...) end function Base.isapprox(M::GeneralizedGrassmann, p, q; kwargs...) return isapprox(distance(M, p, q), 0.0; kwargs...) @@ -280,7 +268,7 @@ end kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ mean(::GeneralizedGrassmann{n,k} where {n,k}, ::Any...) @@ -378,11 +366,11 @@ function vector_transport_to!(M::GeneralizedGrassmann, Y, p, X, q, ::ProjectionT end @doc raw""" - zero_tangent_vector(M::GeneralizedGrassmann, p) + zero_vector(M::GeneralizedGrassmann, p) Return the zero tangent vector from the tangent space at `p` on the [`GeneralizedGrassmann`](@ref) `M`, which is given by a zero matrix the same size as `p`. """ -zero_tangent_vector(::GeneralizedGrassmann, ::Any...) +zero_vector(::GeneralizedGrassmann, ::Any...) -zero_tangent_vector!(::GeneralizedGrassmann, X, p) = fill!(X, 0) +zero_vector!(::GeneralizedGrassmann, X, p) = fill!(X, 0) diff --git a/src/manifolds/GeneralizedStiefel.jl b/src/manifolds/GeneralizedStiefel.jl index 571d90219a..7aad63d12f 100644 --- a/src/manifolds/GeneralizedStiefel.jl +++ b/src/manifolds/GeneralizedStiefel.jl @@ -24,7 +24,7 @@ T_p\mathcal M = \{ X \in 𝔽^{n Γ— k} : p^{\mathrm{H}}BX + X^{\mathrm{H}}Bp=0_n where $0_k$ is the $k Γ— k$ zero matrix. This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. -several functions like the [`zero_tangent_vector`](@ref) are inherited from the embedding. +several functions like the [`zero_vector`](@ref) are inherited from the embedding. The manifold is named after [Eduard L. Stiefel](https://en.wikipedia.org/wiki/Eduard_Stiefel) (1909–1978). @@ -50,16 +50,15 @@ function GeneralizedStiefel( end @doc raw""" - check_manifold_point(M::GeneralizedStiefel, p; kwargs...) + check_point(M::GeneralizedStiefel, p; kwargs...) Check whether `p` is a valid point on the [`GeneralizedStiefel`](@ref) `M`=$\operatorname{St}(n,k,B)$, i.e. that it has the right [`AbstractNumbers`](@ref) type and $x^{\mathrm{H}}Bx$ 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::GeneralizedStiefel{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::GeneralizedStiefel{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv c = p' * M.B * p if !isapprox(c, one(c); kwargs...) @@ -72,7 +71,7 @@ function check_manifold_point(M::GeneralizedStiefel{n,k,𝔽}, p; kwargs...) whe end @doc raw""" - check_tangent_vector(M::GeneralizedStiefel, p, X; kwargs...) + check_vector(M::GeneralizedStiefel, p, X; kwargs...) Check whether `X` is a valid tangent vector at `p` on the [`GeneralizedStiefel`](@ref) `M`=$\operatorname{St}(n,k,B)$, i.e. the [`AbstractNumbers`](@ref) fits, @@ -80,24 +79,13 @@ Check whether `X` is a valid tangent vector at `p` on the [`GeneralizedStiefel`] it (approximately) holds that $p^{\mathrm{H}}BX + \overline{X^{\mathrm{H}}Bp} = 0$, where `kwargs...` is passed to the `isapprox`. """ -function check_tangent_vector( - M::GeneralizedStiefel{n,k,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,k,B,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::GeneralizedStiefel{n,k,𝔽}, p, X; kwargs...) where {n,k,B,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv diff --git a/src/manifolds/GraphManifold.jl b/src/manifolds/GraphManifold.jl index c1227fe08e..b36fe7c65e 100644 --- a/src/manifolds/GraphManifold.jl +++ b/src/manifolds/GraphManifold.jl @@ -23,12 +23,12 @@ struct VertexManifold <: GraphManifoldType end @doc raw""" GraphManifold{G,𝔽,M,T} <: AbstractPowerManifold{𝔽,M,NestedPowerRepresentation} -Build a manifold, that is a [`PowerManifold`](@ref) of the [`Manifold`](@ref) `M` either on +Build a manifold, that is a [`PowerManifold`](@ref) of the [`AbstractManifold`](@ref) `M` either on the edges or vertices of a graph `G` depending on the [`GraphManifoldType`](@ref) `T`. # Fields * `G` is an `AbstractSimpleGraph` -* `M` is a [`Manifold`](@ref) +* `M` is a [`AbstractManifold`](@ref) """ struct GraphManifold{G<:AbstractGraph,𝔽,TM,T<:GraphManifoldType} <: AbstractPowerManifold{𝔽,TM,NestedPowerRepresentation} @@ -40,29 +40,31 @@ function GraphManifold( g::G, M::TM, ::VertexManifold, -) where {G<:AbstractGraph,𝔽,TM<:Manifold{<:𝔽}} +) where {G<:AbstractGraph,𝔽,TM<:AbstractManifold{<:𝔽}} return GraphManifold{G,𝔽,TM,VertexManifold}(g, M) end function GraphManifold( g::G, M::TM, ::EdgeManifold, -) where {G<:AbstractGraph,𝔽,TM<:Manifold{<:𝔽}} +) where {G<:AbstractGraph,𝔽,TM<:AbstractManifold{<:𝔽}} return GraphManifold{G,𝔽,TM,EdgeManifold}(g, M) end -const EdgeGraphManifold{𝔽} = GraphManifold{<:AbstractGraph,𝔽,<:Manifold{𝔽},EdgeManifold} -const VertexGraphManifold{𝔽} = GraphManifold{<:AbstractGraph,𝔽,<:Manifold{𝔽},VertexManifold} +const EdgeGraphManifold{𝔽} = + GraphManifold{<:AbstractGraph,𝔽,<:AbstractManifold{𝔽},EdgeManifold} +const VertexGraphManifold{𝔽} = + GraphManifold{<:AbstractGraph,𝔽,<:AbstractManifold{𝔽},VertexManifold} @doc raw""" - check_manifold_point(M::GraphManifold, p) + check_point(M::GraphManifold, p) 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`. +passes the [`check_point`](@ref) test for the base manifold `M.manifold`. """ -check_manifold_point(::GraphManifold, ::Any...) -function check_manifold_point(M::VertexGraphManifold, p; kwargs...) +check_point(::GraphManifold, ::Any...) +function check_point(M::VertexGraphManifold, p; kwargs...) if size(p) != (nv(M.graph),) return DomainError( length(p), @@ -70,9 +72,9 @@ function check_manifold_point(M::VertexGraphManifold, p; kwargs...) ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), nv(M.graph)) - return check_manifold_point(PM, p; kwargs...) + return check_point(PM, p; kwargs...) end -function check_manifold_point(M::EdgeGraphManifold, p; kwargs...) +function check_point(M::EdgeGraphManifold, p; kwargs...) if size(p) != (ne(M.graph),) return DomainError( length(p), @@ -80,34 +82,21 @@ function check_manifold_point(M::EdgeGraphManifold, p; kwargs...) ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), ne(M.graph)) - return check_manifold_point(PM, p; kwargs...) + return check_point(PM, p; kwargs...) end @doc raw""" - check_tangent_vector(M::GraphManifold, p, X; check_base_point = true, kwargs...) + check_vector(M::GraphManifold, p, X; kwargs...) 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 entry of `p` passes the -[`check_tangent_vector`](@ref) test for the base manifold `M.manifold`. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. +[`check_vector`](@ref) test for the base manifold `M.manifold`. """ -check_tangent_vector(::GraphManifold, ::Any...) -function check_tangent_vector( - M::VertexGraphManifold, - p, - X; - check_base_point=true, - kwargs..., -) - if check_base_point && size(p) != (nv(M.graph),) - return DomainError( - length(p), - "The number of points in `x` ($(size(p)) does not match the number of nodes in the graph ($(nv(M.graph))).", - ) - end +check_vector(::GraphManifold, ::Any...) +function check_vector(M::VertexGraphManifold, p, X; kwargs...) if size(X) != (nv(M.graph),) return DomainError( length(X), @@ -115,15 +104,9 @@ function check_tangent_vector( ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), nv(M.graph)) - return check_tangent_vector(PM, p, X; check_base_point=check_base_point, kwargs...) + return check_vector(PM, p, X; kwargs...) end -function check_tangent_vector(M::EdgeGraphManifold, p, X; check_base_point=true, kwargs...) - if check_base_point && size(p) != (ne(M.graph),) - return DomainError( - length(p), - "The number of elements in `x` ($(size(p)) does not match the number of edges in the graph ($(ne(M.graph))).", - ) - end +function check_vector(M::EdgeGraphManifold, p, X; kwargs...) if size(X) != (ne(M.graph),) return DomainError( length(X), @@ -131,7 +114,7 @@ function check_tangent_vector(M::EdgeGraphManifold, p, X; check_base_point=true, ) end PM = PowerManifold(M.manifold, NestedPowerRepresentation(), ne(M.graph)) - return check_tangent_vector(PM, p, X; check_base_point=check_base_point, kwargs...) + return check_vector(PM, p, X; kwargs...) end get_iterator(M::EdgeGraphManifold) = 1:ne(M.graph) @@ -149,7 +132,7 @@ If the internal graph is a `SimpleWeightedGraph` the weighted sum of the tangent vectors is computed. """ function incident_log(M::VertexGraphManifold, p) - v = zero_tangent_vector(M, p) + v = zero_vector(M, p) return incident_log!(M, v, p) end @@ -170,7 +153,7 @@ function incident_log!(M::VertexGraphManifold, X, p) return X end function incident_log!( - M::GraphManifold{<:AbstractSimpleWeightedGraph,𝔽,<:Manifold{𝔽},VertexManifold}, + M::GraphManifold{<:AbstractSimpleWeightedGraph,𝔽,<:AbstractManifold{𝔽},VertexManifold}, X, p, ) where {𝔽} @@ -228,7 +211,7 @@ function _show_graph_manifold(io::IO, M; man_desc="", pre="") sg = sprint(show, "text/plain", M.graph, context=io, sizehint=0) sg = replace(sg, '\n' => "\n$(pre)") println(io, pre, sg) - println(io, "Manifold$(man_desc):") + println(io, "AbstractManifold$(man_desc):") sm = sprint(show, "text/plain", M.manifold, context=io, sizehint=0) sm = replace(sm, '\n' => "\n$(pre)") print(io, pre, sm) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 451e71df85..1a660e48dd 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -60,14 +60,13 @@ function allocation_promotion_function(M::Grassmann{n,k,β„‚}, f, args::Tuple) wh end @doc raw""" - check_manifold_point(M::Grassmann{n,k,𝔽}, p) + check_point(M::Grassmann{n,k,𝔽}, p) 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 `𝔽`. """ -function check_manifold_point(M::Grassmann{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::Grassmann{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv c = p' * p if !isapprox(c, one(c); kwargs...) @@ -80,7 +79,7 @@ function check_manifold_point(M::Grassmann{n,k,𝔽}, p; kwargs...) where {n,k, end @doc raw""" - check_tangent_vector(M::Grassmann{n,k,𝔽}, p, X; check_base_point = true, kwargs...) + check_vector(M::Grassmann{n,k,𝔽}, p, X; kwargs...) 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 @@ -91,26 +90,14 @@ 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, $\overline{\cdot}$ the (elementwise) complex conjugate, and $0_k$ the $k Γ— k$ zero matrix. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector( - M::Grassmann{n,k,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,k,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Grassmann{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -191,7 +178,7 @@ injectivity_radius(::Grassmann, ::Any) = Ο€ / 2 injectivity_radius(::Grassmann, ::Any, ::ExponentialRetraction) = Ο€ / 2 eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::Grassmann, rm::AbstractRetractionMethod, ) @@ -246,7 +233,7 @@ inverse_retract(::Grassmann, ::Any, ::Any, ::QRInverseRetraction) inverse_retract!(::Grassmann, X, p, q, ::QRInverseRetraction) = copyto!(X, q / (p' * q) - p) function Base.isapprox(M::Grassmann, p, X, Y; kwargs...) - return isapprox(sqrt(inner(M, p, zero_tangent_vector(M, p), X - Y)), 0; kwargs...) + return isapprox(sqrt(inner(M, p, zero_vector(M, p), X - Y)), 0; kwargs...) end Base.isapprox(M::Grassmann, p, q; kwargs...) = isapprox(distance(M, p, q), 0.0; kwargs...) @@ -303,7 +290,7 @@ manifold_dimension(::Grassmann{n,k,𝔽}) where {n,k,𝔽} = k * (n - k) * real_ kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ mean(::Grassmann{n,k} where {n,k}, ::Any...) @@ -425,11 +412,11 @@ function vector_transport_to!(M::Grassmann, Y, p, X, q, ::ProjectionTransport) end @doc raw""" - zero_tangent_vector(M::Grassmann, p) + zero_vector(M::Grassmann, p) 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_vector(::Grassmann, ::Any...) -zero_tangent_vector!(::Grassmann, X, p) = fill!(X, 0) +zero_vector!(::Grassmann, X, p) = fill!(X, 0) diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index fd91befd66..8e44b43ad8 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -38,14 +38,14 @@ struct Hyperbolic{N} <: AbstractEmbeddedManifold{ℝ,DefaultIsometricEmbeddingTy Hyperbolic(n::Int) = Hyperbolic{n}() @doc raw""" - HyperboloidPoint <: MPoint + HyperboloidPoint <: AbstractManifoldPoint In the Hyperboloid model of the [`Hyperbolic`](@ref) $\mathcal H^n$ points are represented as vectors in $ℝ^{n+1}$ with [`MinkowskiMetric`](@ref) equal to $-1$. This representation is the default, i.e. `AbstractVector`s are assumed to have this repesentation. """ -struct HyperboloidPoint{TValue<:AbstractVector} <: MPoint +struct HyperboloidPoint{TValue<:AbstractVector} <: AbstractManifoldPoint value::TValue end @@ -63,12 +63,12 @@ struct HyperboloidTVector{TValue<:AbstractVector} <: TVector end @doc raw""" - PoincareBallPoint <: MPoint + PoincareBallPoint <: AbstractManifoldPoint A point on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ can be represented as a vector of norm less than one in $\mathbb R^n$. """ -struct PoincareBallPoint{TValue<:AbstractVector} <: MPoint +struct PoincareBallPoint{TValue<:AbstractVector} <: AbstractManifoldPoint value::TValue end @@ -78,17 +78,17 @@ end In the PoincarΓ© ball model of the [`Hyperbolic`](@ref) $\mathcal H^n$ tangent vectors are represented as vectors in $ℝ^{n}$. """ -struct PoincareBallTVector{TValue<:AbstractVector} <: MPoint +struct PoincareBallTVector{TValue<:AbstractVector} <: AbstractManifoldPoint value::TValue end @doc raw""" - PoincareHalfSpacePoint <: MPoint + PoincareHalfSpacePoint <: AbstractManifoldPoint A point on the [`Hyperbolic`](@ref) manifold $\mathcal H^n$ can be represented as a vector in the half plane, i.e. $x ∈ ℝ^n$ with $x_d > 0$. """ -struct PoincareHalfSpacePoint{TValue<:AbstractVector} <: MPoint +struct PoincareHalfSpacePoint{TValue<:AbstractVector} <: AbstractManifoldPoint value::TValue end @@ -191,7 +191,7 @@ for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes) end @doc raw""" - check_manifold_point(M::Hyperbolic, p; kwargs...) + check_point(M::Hyperbolic, p; kwargs...) Check whether `p` is a valid point on the [`Hyperbolic`](@ref) `M`. @@ -205,36 +205,29 @@ less than 1. For the [`PoincareHalfSpacePoint`](@ref) a valid point is a vector from $p ∈ ℝ^n$ with a positive last entry, i.e. $p_n>0$ """ -check_manifold_point(::Hyperbolic, ::Any) +check_point(::Hyperbolic, ::Any) @doc raw""" - check_tangent_vector(M::Hyperbolic{n}, p, X; check_base_point = true, kwargs... ) + check_vector(M::Hyperbolic{n}, p, X; kwargs... ) 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 the same dimension as `p`. -The optional parameter `check_base_point` indicates whether to -call [`check_manifold_point`](@ref) for `p`. The tolerance for the last test can be set -using the `kwargs...`. +after [`check_point`](@ref)`(M,p)`, `X` has to be of the same dimension as `p`. +The tolerance for the last test can be set using the `kwargs...`. For a the hyperboloid model or vectors, `X` has to be orthogonal to `p` with respect to the inner product from the embedding, see [`MinkowskiMetric`](@ref). For a the PoincarΓ© ball as well as the PoincarΓ© half plane model, `X` has to be a vector from $ℝ^{n}$. """ -check_tangent_vector(::Hyperbolic, ::Any, ::Any) +check_vector(::Hyperbolic, ::Any, ::Any) -function check_tangent_vector( +function check_vector( M::Hyperbolic{N}, p, X::Union{PoincareBallTVector,PoincareHalfSpaceTVector}; - check_base_point=true, kwargs..., ) where {N} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end - return check_manifold_point(Euclidean(N), X.value; kwargs...) + return check_point(Euclidean(N), X.value; kwargs...) end # Define self conversions @@ -292,7 +285,7 @@ injectivity_radius(::Hyperbolic, ::Any) = Inf injectivity_radius(::Hyperbolic, ::Any, ::ExponentialRetraction) = Inf eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::Hyperbolic, rm::AbstractRetractionMethod, ) @@ -355,7 +348,7 @@ manifold_dimension(::Hyperbolic{N}) where {N} = N kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` on the +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` on the [`Hyperbolic`](@ref) space using [`CyclicProximalPointEstimation`](@ref). """ mean(::Hyperbolic, ::Any...) @@ -431,6 +424,6 @@ for (P, T) in zip(_HyperbolicPointTypes, _HyperbolicTangentTypes) ).value return Y end - @eval zero_tangent_vector(::Hyperbolic, p::$P) = $T(zero(p.value)) - @eval zero_tangent_vector!(::Hyperbolic, X::$T, ::$P) = fill!(X.value, 0) + @eval zero_vector(::Hyperbolic, p::$P) = $T(zero(p.value)) + @eval zero_vector!(::Hyperbolic, X::$T, ::$P) = fill!(X.value, 0) end diff --git a/src/manifolds/HyperbolicHyperboloid.jl b/src/manifolds/HyperbolicHyperboloid.jl index 7533538e5b..20376fe198 100644 --- a/src/manifolds/HyperbolicHyperboloid.jl +++ b/src/manifolds/HyperbolicHyperboloid.jl @@ -1,7 +1,6 @@ -function check_manifold_point(M::Hyperbolic, p; kwargs...) - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::Hyperbolic, p; kwargs...) + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv if !isapprox(minkowski_metric(p, p), -1.0; kwargs...) return DomainError( @@ -11,22 +10,17 @@ function check_manifold_point(M::Hyperbolic, p; kwargs...) end return nothing end -function check_manifold_point(M::Hyperbolic, p::HyperboloidPoint; kwargs...) - return check_manifold_point(M, p.value; kwargs...) +function check_point(M::Hyperbolic, p::HyperboloidPoint; kwargs...) + return check_point(M, p.value; kwargs...) end -function check_tangent_vector(M::Hyperbolic, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Hyperbolic, p, X; kwargs...) mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -38,13 +32,8 @@ function check_tangent_vector(M::Hyperbolic, p, X; check_base_point=true, kwargs end return nothing end -function check_tangent_vector( - M::Hyperbolic, - p::HyperboloidPoint, - X::HyperboloidTVector; - kwargs..., -) - return check_tangent_vector(M, p.value, X.value; kwargs...) +function check_vector(M::Hyperbolic, p::HyperboloidPoint, X::HyperboloidTVector; kwargs...) + return check_vector(M, p.value, X.value; kwargs...) end function convert(::Type{HyperboloidTVector}, X::T) where {T<:AbstractVector} @@ -239,7 +228,7 @@ function exp!(M::Hyperbolic, q, p, X) return copyto!(q, cosh(vn) * p + sinh(vn) / vn * X) end -function get_basis(M::Hyperbolic, p, B::DefaultOrthonormalBasis) +function get_basis(M::Hyperbolic, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) n = manifold_dimension(M) V = [ _hyperbolize(M, p, [i == k ? one(eltype(p)) : zero(eltype(p)) for k in 1:n]) for @@ -284,7 +273,13 @@ the unit vectors from $ℝ^n$, where $n$ is the manifold dimension of the [`Hype """ get_coordinates(M::Hyperbolic, p, X, B::DefaultOrthonormalBasis) -function get_coordinates!(M::Hyperbolic, c, p, X, B::DefaultOrthonormalBasis) +function get_coordinates!( + M::Hyperbolic, + c, + p, + X, + B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) c = get_coordinates!(M, c, p, X, get_basis(M, p, B)) return c end @@ -302,7 +297,7 @@ the unit vectors from $ℝ^n$, where $n$ is the manifold dimension of the [`Hype """ get_vector(M::Hyperbolic, p, c, ::DefaultOrthonormalBasis) -function get_vector!(M::Hyperbolic, X, p, c, B::DefaultOrthonormalBasis) +function get_vector!(M::Hyperbolic, X, p, c, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) X = get_vector!(M, X, p, c, get_basis(M, p, B)) return X end @@ -357,7 +352,7 @@ function log!(M::Hyperbolic, X, p, q) scp = minkowski_metric(p, q) w = q + scp * p wn = sqrt(max(scp .^ 2 - 1, 0.0)) - wn < eps(eltype(p)) && return zero_tangent_vector!(M, X, p) + wn < eps(eltype(p)) && return zero_vector!(M, X, p) X .= acosh(max(1.0, -scp)) / wn .* w return X end diff --git a/src/manifolds/HyperbolicPoincareBall.jl b/src/manifolds/HyperbolicPoincareBall.jl index b1bf4ab634..651e208648 100644 --- a/src/manifolds/HyperbolicPoincareBall.jl +++ b/src/manifolds/HyperbolicPoincareBall.jl @@ -1,5 +1,5 @@ -function check_manifold_point(M::Hyperbolic{N}, p::PoincareBallPoint; kwargs...) where {N} - mpv = check_manifold_point(Euclidean(N), p.value; kwargs...) +function check_point(M::Hyperbolic{N}, p::PoincareBallPoint; kwargs...) where {N} + mpv = check_point(Euclidean(N), p.value; kwargs...) mpv === nothing || return mpv if !(norm(p.value) < 1) return DomainError( diff --git a/src/manifolds/HyperbolicPoincareHalfspace.jl b/src/manifolds/HyperbolicPoincareHalfspace.jl index dbc4addb7a..06f3cbca2d 100644 --- a/src/manifolds/HyperbolicPoincareHalfspace.jl +++ b/src/manifolds/HyperbolicPoincareHalfspace.jl @@ -1,9 +1,5 @@ -function check_manifold_point( - M::Hyperbolic{N}, - p::PoincareHalfSpacePoint; - kwargs..., -) where {N} - mpv = check_manifold_point(Euclidean(N), p.value; kwargs...) +function check_point(M::Hyperbolic{N}, p::PoincareHalfSpacePoint; kwargs...) where {N} + mpv = check_point(Euclidean(N), p.value; kwargs...) mpv === nothing || return mpv if !(last(p.value) > 0) return DomainError( diff --git a/src/manifolds/Lorentz.jl b/src/manifolds/Lorentz.jl index 0864a20e68..941a573181 100644 --- a/src/manifolds/Lorentz.jl +++ b/src/manifolds/Lorentz.jl @@ -1,11 +1,11 @@ @doc raw""" - LorentzMetric <: Metric + LorentzMetric <: AbstractMetric Abstract type for Lorentz metrics, which have a single time dimension. These metrics assume the spacelike convention with the time dimension being last, giving the signature $(++...+-)$. """ -abstract type LorentzMetric <: Metric end +abstract type LorentzMetric <: AbstractMetric end @doc raw""" MinkowskiMetric <: LorentzMetric diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index ffb6f19d15..b5853be271 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -1,5 +1,5 @@ @doc raw""" - Metric + AbstractMetric Abstract type for the pseudo-Riemannian metric tensor ``g``, a family of smoothly varying inner products on the tangent space. See [`inner`](@ref). @@ -12,21 +12,21 @@ Generate the `MetricManifold` that wraps the manifold `M` with given `metric`. This works for both a variable containing the metric as well as a subtype `T<:Metric`, where a zero parameter constructor `T()` is availabe. """ -abstract type Metric end +abstract type AbstractMetric end # piping syntax for decoration -(metric::Metric)(M::Manifold) = MetricManifold(M, metric) -(::Type{T})(M::Manifold) where {T<:Metric} = MetricManifold(M, T()) +(metric::AbstractMetric)(M::AbstractManifold) = MetricManifold(M, metric) +(::Type{T})(M::AbstractManifold) where {T<:AbstractMetric} = MetricManifold(M, T()) """ - MetricManifold{𝔽,M<:Manifold{𝔽},G<:Metric} <: AbstractDecoratorManifold{𝔽} + MetricManifold{𝔽,M<:AbstractManifold{𝔽},G<:AbstractMetric} <: AbstractDecoratorManifold{𝔽} -Equip a [`Manifold`](@ref) explicitly with a [`Metric`](@ref) `G`. +Equip a [`AbstractManifold`](@ref) explicitly with a [`AbstractMetric`](@ref) `G`. -For a Metric Manifold, by default, assumes, that you implement the linear form +For a Metric AbstractManifold, by default, assumes, that you implement the linear form from [`local_metric`](@ref) in order to evaluate the exponential map. -If the corresponding [`Metric`](@ref) `G` yields closed form formulae for e.g. +If the corresponding [`AbstractMetric`](@ref) `G` yields closed form formulae for e.g. the exponential map and this is implemented directly (without solving the ode), you can of course still implement that directly. @@ -34,30 +34,32 @@ you can of course still implement that directly. MetricManifold(M, G) -Generate the [`Manifold`](@ref) `M` as a manifold with the [`Metric`](@ref) `G`. +Generate the [`AbstractManifold`](@ref) `M` as a manifold with the [`AbstractMetric`](@ref) `G`. """ -struct MetricManifold{𝔽,M<:Manifold{𝔽},G<:Metric} <: AbstractDecoratorManifold{𝔽} +struct MetricManifold{𝔽,M<:AbstractManifold{𝔽},G<:AbstractMetric} <: + AbstractDecoratorManifold{𝔽} manifold::M metric::G end @doc raw""" - RiemannianMetric <: Metric + RiemannianMetric <: AbstractMetric Abstract type for Riemannian metrics, a family of positive definite inner 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 +abstract type RiemannianMetric <: AbstractMetric end @doc raw""" christoffel_symbols_first( - M::Manifold - p; + M::MetricManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend = diff_backend(), ) -Compute the Christoffel symbols of the first kind in local coordinates. +Compute the Christoffel symbols of the first kind in local coordinates of basis `B`. The Christoffel symbols are (in Einstein summation convention) ```math @@ -68,13 +70,14 @@ 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)``. """ -christoffel_symbols_first(::Manifold, ::Any) +christoffel_symbols_first(::AbstractManifold, ::Any, B::AbstractBasis) function christoffel_symbols_first( - M::Manifold, - p; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend=diff_backend(), ) - βˆ‚g = local_metric_jacobian(M, p; backend=backend) + βˆ‚g = local_metric_jacobian(M, p, B; 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]) @@ -82,7 +85,8 @@ function christoffel_symbols_first( end @decorator_transparent_signature christoffel_symbols_first( M::AbstractDecoratorManifold, - p; + p, + B::AbstractBasis; kwargs..., ) function decorator_transparent_dispatch( @@ -95,12 +99,13 @@ end @doc raw""" christoffel_symbols_second( - M::Manifold, - x; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend = diff_backend(), ) -Compute the Christoffel symbols of the second kind in local coordinates. +Compute the Christoffel symbols of the second kind in local coordinates of basis `B`. The Christoffel symbols are (in Einstein summation convention) ````math @@ -111,21 +116,23 @@ 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)``. """ -christoffel_symbols_second(::Manifold, ::Any) +christoffel_symbols_second(::AbstractManifold, ::AbstractBasis, ::Any) function christoffel_symbols_second( - M::Manifold, - p; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend=diff_backend(), ) - Ginv = inverse_local_metric(M, p) - Γ₁ = christoffel_symbols_first(M, p; backend=backend) + Ginv = inverse_local_metric(M, p, B) + Γ₁ = christoffel_symbols_first(M, p, B; backend=backend) Ξ“β‚‚ = allocate(Γ₁) @einsum Ξ“β‚‚[l, i, j] = Ginv[k, l] * Γ₁[i, j, k] return Ξ“β‚‚ end @decorator_transparent_signature christoffel_symbols_second( M::AbstractDecoratorManifold, - p; + p, + B::AbstractBasis; kwargs..., ) function decorator_transparent_dispatch( @@ -138,13 +145,14 @@ end @doc raw""" christoffel_symbols_second_jacobian( - M::Manifold, - p; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend = diff_backend(), ) Get partial derivatives of the Christoffel symbols of the second kind -for manifold `M` at `p` with respect to the coordinates of `p`, i.e. +for manifold `M` at `p` with respect to the coordinates of `B`, i.e. ```math \frac{βˆ‚}{βˆ‚ p^l} Ξ“^{k}_{ij} = Ξ“^{k}_{ij,l}. @@ -152,15 +160,16 @@ for manifold `M` at `p` with respect to the coordinates of `p`, i.e. The dimensions of the resulting multi-dimensional array are ordered ``(i,j,k,l)``. """ -christoffel_symbols_second_jacobian(::Manifold, ::Any) +christoffel_symbols_second_jacobian(::AbstractManifold, ::Any, B::AbstractBasis) function christoffel_symbols_second_jacobian( - M::Manifold, - p; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend=diff_backend(), ) n = size(p, 1) βˆ‚Ξ“ = reshape( - _jacobian(q -> christoffel_symbols_second(M, q; backend=backend), p, backend), + _jacobian(q -> christoffel_symbols_second(M, q, B; backend=backend), p, backend), n, n, n, @@ -170,7 +179,8 @@ function christoffel_symbols_second_jacobian( end @decorator_transparent_signature christoffel_symbols_second_jacobian( M::AbstractDecoratorManifold, - p; + p, + B::AbstractBasis; kwargs..., ) function decorator_transparent_dispatch( @@ -324,18 +334,22 @@ function decorator_transparent_dispatch( end @doc raw""" - det_local_metric(M::Manifold, p) + det_local_metric(M::AbstractManifold, p, B::AbstractBasis) Return the determinant of local matrix representation of the metric tensor ``g``, i.e. of the matrix ``G(p)`` representing the metric in the tangent space at ``p`` with as a matrix. See also [`local_metric`](@ref) """ -det_local_metric(::Manifold, ::Any) -function det_local_metric(M::Manifold, p) - return det(local_metric(M, p)) +det_local_metric(::AbstractManifold, p, ::AbstractBasis) +function det_local_metric(M::AbstractManifold, p, B::AbstractBasis) + return det(local_metric(M, p, B)) end -@decorator_transparent_signature det_local_metric(M::AbstractDecoratorManifold, p) +@decorator_transparent_signature det_local_metric( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis, +) function decorator_transparent_dispatch( ::typeof(det_local_metric), ::MetricManifold, @@ -344,20 +358,30 @@ function decorator_transparent_dispatch( return Val(:parent) end """ - einstein_tensor(M::Manifold, p; backend::AbstractDiffBackend = diff_backend()) + einstein_tensor(M::AbstractManifold, p, B::AbstractBasis; backend::AbstractDiffBackend = diff_backend()) Compute the Einstein tensor of the manifold `M` at the point `p`, see [https://en.wikipedia.org/wiki/Einstein_tensor](https://en.wikipedia.org/wiki/Einstein_tensor) """ -einstein_tensor(::Manifold, ::Any...) -function einstein_tensor(M::Manifold, p; backend::AbstractDiffBackend=diff_backend()) - Ric = ricci_tensor(M, p; backend=backend) - g = local_metric(M, p) - Ginv = inverse_local_metric(M, p) +einstein_tensor(::AbstractManifold, ::Any, ::AbstractBasis) +function einstein_tensor( + M::AbstractManifold, + p, + B::AbstractBasis; + backend::AbstractDiffBackend=diff_backend(), +) + Ric = ricci_tensor(M, p, B; backend=backend) + g = local_metric(M, p, B) + Ginv = inverse_local_metric(M, p, B) S = sum(Ginv .* Ric) G = Ric - g .* S / 2 return G end -@decorator_transparent_signature einstein_tensor(M::AbstractDecoratorManifold, p; kwargs...) +@decorator_transparent_signature einstein_tensor( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis; + kwargs..., +) function decorator_transparent_dispatch( ::typeof(einstein_tensor), ::MetricManifold, @@ -369,10 +393,10 @@ end @doc raw""" exp(N::MetricManifold{M,G}, p, X) -Copute the exponential map on the [`Manifold`](@ref) `M` equipped with the [`Metric`](@ref) `G`. +Copute the exponential map on the [`AbstractManifold`](@ref) `M` equipped with the [`AbstractMetric`](@ref) `G`. If the metric was declared the default metric using [`is_default_metric`](@ref), this method -falls back to `exp(M,p,X)`. +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 @@ -383,7 +407,10 @@ exp(::MetricManifold, ::Any...) @decorator_transparent_fallback function exp!(M::MetricManifold, q, p, X) tspan = (0.0, 1.0) - sol = solve_exp_ode(M, p, X, tspan; dense=false, saveat=[1.0]) + A = get_default_atlas(M) + i = get_chart_index(M, A, p) + B = induced_basis(M, A, i, TangentSpace) + sol = solve_exp_ode(M, p, X, tspan, B; dense=false, saveat=[1.0]) n = length(p) return copyto!(q, sol.u[1][(n + 1):end]) end @@ -392,7 +419,7 @@ end flat(N::MetricManifold{M,G}, p, X::FVector{TangentSpaceType}) Compute the musical isomorphism to transform the tangent vector `X` from the -[`Manifold`](@ref) `M` equipped with [`Metric`](@ref) `G` to a cotangent by +[`AbstractManifold`](@ref) `M` equipped with [`AbstractMetric`](@ref) `G` to a cotangent by computing ````math @@ -408,23 +435,24 @@ flat(::MetricManifold, ::Any...) p, X::TFVector, ) - g = local_metric(M, p) + g = local_metric(M, p, ΞΎ.basis) copyto!(ΞΎ.data, g * X.data) return ΞΎ end """ - gaussian_curvature(M::Manifold, x; backend::AbstractDiffBackend = diff_backend()) + gaussian_curvature(M::AbstractManifold, p, B::AbstractBasis; backend::AbstractDiffBackend = diff_backend()) -Compute the Gaussian curvature of the manifold `M` at the point `x`. +Compute the Gaussian curvature of the manifold `M` at the point `p` using basis `B`. """ -gaussian_curvature(::Manifold, ::Any) -function gaussian_curvature(M::Manifold, p; kwargs...) - return ricci_curvature(M, p; kwargs...) / 2 +gaussian_curvature(::AbstractManifold, ::Any, ::AbstractBasis) +function gaussian_curvature(M::AbstractManifold, p, B::AbstractBasis; kwargs...) + return ricci_curvature(M, p, B; kwargs...) / 2 end @decorator_transparent_signature gaussian_curvature( M::AbstractDecoratorManifold, - p; + p, + B::AbstractBasis; kwargs..., ) function decorator_transparent_dispatch( @@ -452,18 +480,22 @@ function injectivity_radius(M::MetricManifold, p, m::ExponentialRetraction) end @doc raw""" - inverse_local_metric(M::Manifold, p) + inverse_local_metric(M::AbstractcManifold, p, B::AbstractBasis) Return the local matrix representation of the inverse metric (cometric) tensor, usually written ``g^{ij}``. See also [`local_metric`](@ref) """ -inverse_local_metric(::Manifold, ::Any) -function inverse_local_metric(M::Manifold, p) - return inv(local_metric(M, p)) +inverse_local_metric(::AbstractManifold, ::Any, ::AbstractBasis) +function inverse_local_metric(M::AbstractManifold, p, B::AbstractBasis) + return inv(local_metric(M, p, B)) end -@decorator_transparent_signature inverse_local_metric(M::AbstractDecoratorManifold, p) +@decorator_transparent_signature inverse_local_metric( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis, +) function decorator_transparent_dispatch( ::typeof(inverse_local_metric), ::MetricManifold, @@ -475,20 +507,20 @@ end default_decorator_dispatch(M::MetricManifold) = default_metric_dispatch(M) """ - is_default_metric(M,G) + is_default_metric(M, G) -Indicate whether the [`Metric`](@ref) `G` is the default metric for -the [`Manifold`](@ref) `M`. This means that any occurence of +Indicate whether the [`AbstractMetric`](@ref) `G` is the default metric for +the [`AbstractManifold`](@ref) `M`. This means that any occurence of [`MetricManifold`](@ref)(M,G) where `typeof(is_default_metric(M,G)) = true` -falls back to just be called with `M` such that the [`Manifold`](@ref) `M` +falls back to just be called with `M` such that the [`AbstractManifold`](@ref) `M` implicitly has this metric, for example if this was the first one implemented or is the one most commonly assumed to be used. """ -function is_default_metric(M::Manifold, G::Metric) +function is_default_metric(M::AbstractManifold, G::AbstractMetric) return _extract_val(default_metric_dispatch(M, G)) end -default_metric_dispatch(::Manifold, ::Metric) = Val(false) +default_metric_dispatch(::AbstractManifold, ::AbstractMetric) = Val(false) function default_metric_dispatch(M::MetricManifold) return default_metric_dispatch(base_manifold(M), metric(M)) end @@ -496,11 +528,11 @@ end """ is_default_metric(MM::MetricManifold) -Indicate whether the [`Metric`](@ref) `MM.G` is the default metric for -the [`Manifold`](@ref) `MM.manifold,` within the [`MetricManifold`](@ref) `MM`. +Indicate whether the [`AbstractMetric`](@ref) `MM.G` is the default metric for +the [`AbstractManifold`](@ref) `MM.manifold,` within the [`MetricManifold`](@ref) `MM`. This means that any occurence of [`MetricManifold`](@ref)`(MM.manifold, MM.G)` where `is_default_metric(MM.manifold, MM.G)) = true` -falls back to just be called with `MM.manifold,` such that the [`Manifold`](@ref) `MM.manifold` +falls back to just be called with `MM.manifold,` such that the [`AbstractManifold`](@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. """ @@ -512,10 +544,18 @@ function Base.convert(::Type{MetricManifold{𝔽,MT,GT}}, M::MT) where {𝔽,MT, return _convert_with_default(M, GT, default_metric_dispatch(M, GT())) end -function _convert_with_default(M::MT, T::Type{<:Metric}, ::Val{true}) where {MT<:Manifold} +function _convert_with_default( + M::MT, + T::Type{<:AbstractMetric}, + ::Val{true}, +) where {MT<:AbstractManifold} return MetricManifold(M, T()) end -function _convert_with_default(M::MT, T::Type{<:Metric}, ::Val{false}) where {MT<:Manifold} +function _convert_with_default( + M::MT, + T::Type{<:AbstractMetric}, + ::Val{false}, +) where {MT<:AbstractManifold} return error( "Can not convert $(M) to a MetricManifold{$(MT),$(T)}, since $(T) is not the default metric.", ) @@ -525,69 +565,75 @@ end inner(N::MetricManifold{M,G}, p, X, Y) 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 +[`AbstractManifold`](@ref) `M` using the [`AbstractMetric`](@ref) `G`. If `G` is the default 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_p Y⟩, ```` -where ``G_p`` is the loal matrix representation of the [`Metric`](@ref) `G`. +where ``G_p`` is the loal matrix representation of the [`AbstractMetric`](@ref) `G`. """ -inner(::MetricManifold, ::Any) +inner(::MetricManifold, ::Any, ::Any, ::Any) @decorator_transparent_fallback :intransparent function inner( - M::MMT, - p, - X, - Y, -) where {MMT<:MetricManifold} - return dot(X, local_metric(M, p) * Y) -end -function inner( - B::VectorBundleFibers{<:CotangentSpaceType,MMT}, + M::MetricManifold, p, - X, - Y, -) where {MMT<:MetricManifold} - Ginv = inverse_local_metric(B.manifold, p) - return dot(X, Ginv * Y) + X::TFVector, + Y::TFVector, +) + X.basis === Y.basis || + error("calculating inner product of vectors from different bases is not supported") + return dot(X.data, local_metric(M, p, X.basis) * Y.data) end @doc raw""" - local_metric(M::Manifold, p, B) + local_metric(M::AbstractManifold, p, B::AbstractBasis) Return the local matrix representation at the point `p` of the metric tensor ``g`` with respect to the [`AbstractBasis`](@ref) `B` on the [`Manifold`](@ref) `M`, usually written ``g_{ij}``. The matrix has the property that ``g(X, Y)=X^\mathrm{T} [g_{ij}] Y = g_{ij} X^i Y^j``, where the latter expression uses Einstein summation convention. +The metric tensor is such that the formula works for the given [`AbstractBasis`](@ref) `B`. """ -local_metric(::Manifold, ::Any...) -@decorator_transparent_signature local_metric(M::AbstractDecoratorManifold, p; kwargs...) +local_metric(::AbstractManifold, ::Any, ::AbstractBasis) +@decorator_transparent_signature local_metric( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis; + kwargs..., +) function decorator_transparent_dispatch(::typeof(local_metric), ::MetricManifold, args...) return Val(:parent) end @doc raw""" local_metric_jacobian( - M::Manifold, - p; + M::AbstractManifold, + p, + B::AbstractBasis; backend::AbstractDiffBackend = diff_backend(), ) -Get partial derivatives of the local metric of `M` at `p` with respect to the +Get partial derivatives of the local metric of `M` at `p` in basis `B` with respect to the coordinates of `p`, ``\frac{βˆ‚}{βˆ‚ p^k} g_{ij} = g_{ij,k}``. The dimensions of the resulting multi-dimensional array are ordered ``(i,j,k)``. """ -local_metric_jacobian(::Manifold, ::Any) -function local_metric_jacobian(M::Manifold, p; backend::AbstractDiffBackend=diff_backend()) +local_metric_jacobian(::AbstractManifold, ::Any, B::AbstractBasis) +function local_metric_jacobian( + M::AbstractManifold, + p, + B::AbstractBasis; + backend::AbstractDiffBackend=diff_backend(), +) n = size(p, 1) - βˆ‚g = reshape(_jacobian(q -> local_metric(M, q), p, backend), n, n, n) + βˆ‚g = reshape(_jacobian(q -> local_metric(M, q, B), p, backend), n, n, n) return βˆ‚g end @decorator_transparent_signature local_metric_jacobian( M::AbstractDecoratorManifold, - p; + p, + B::AbstractBasis; kwargs..., ) function decorator_transparent_dispatch( @@ -601,25 +647,29 @@ end @doc raw""" log(N::MetricManifold{M,G}, p, q) -Copute the logarithmic map on the [`Manifold`](@ref) `M` equipped with the [`Metric`](@ref) `G`. +Copute the logarithmic map on the [`AbstractManifold`](@ref) `M` equipped with the [`AbstractMetric`](@ref) `G`. If the metric was declared the default metric using [`is_default_metric`](@ref), this method falls back to `log(M,p,q)`. Otherwise, you have to provide an implementation for the non-default -[`Metric`](@ref) `G` metric within its [`MetricManifold`](@ref)`{M,G}`. +[`AbstractMetric`](@ref) `G` metric within its [`MetricManifold`](@ref)`{M,G}`. """ log(::MetricManifold, ::Any...) @doc raw""" - log_local_metric_density(M::Manifold, p) + log_local_metric_density(M::AbstractManifold, p, B::AbstractBasis) Return the natural logarithm of the metric density ``ρ`` of `M` at `p`, which -is given by ``ρ = \log \sqrt{|\det [g_{ij}]|}``. +is given by ``ρ = \log \sqrt{|\det [g_{ij}]|}`` for the metric tensor expressed in basis `B`. """ -log_local_metric_density(::Manifold, ::Any) -function log_local_metric_density(M::Manifold, p) - return log(abs(det_local_metric(M, p))) / 2 +log_local_metric_density(::AbstractManifold, ::Any, ::AbstractBasis) +function log_local_metric_density(M::AbstractManifold, p, B::AbstractBasis) + return log(abs(det_local_metric(M, p, B))) / 2 end -@decorator_transparent_signature log_local_metric_density(M::AbstractDecoratorManifold, p) +@decorator_transparent_signature log_local_metric_density( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis, +) function decorator_transparent_dispatch( ::typeof(log_local_metric_density), ::MetricManifold, @@ -640,18 +690,28 @@ function metric(M::MetricManifold) end """ - ricci_curvature(M::Manifold, p; backend::AbstractDiffBackend = diff_backend()) + ricci_curvature(M::AbstractManifold, p, B::AbstractBasis; backend::AbstractDiffBackend = diff_backend()) -Compute the Ricci scalar curvature of the manifold `M` at the point `p`. +Compute the Ricci scalar curvature of the manifold `M` at the point `p` using basis `B`. """ -ricci_curvature(::Manifold, ::Any) -function ricci_curvature(M::Manifold, p; backend::AbstractDiffBackend=diff_backend()) - Ginv = inverse_local_metric(M, p) - Ric = ricci_tensor(M, p; backend=backend) +ricci_curvature(::AbstractManifold, ::Any, ::AbstractBasis) +function ricci_curvature( + M::AbstractManifold, + p, + B::AbstractBasis; + backend::AbstractDiffBackend=diff_backend(), +) + Ginv = inverse_local_metric(M, p, B) + Ric = ricci_tensor(M, p, B; backend=backend) S = sum(Ginv .* Ric) return S end -@decorator_transparent_signature ricci_curvature(M::AbstractDecoratorManifold, p; kwargs...) +@decorator_transparent_signature ricci_curvature( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis; + kwargs..., +) function decorator_transparent_dispatch( ::typeof(ricci_curvature), ::MetricManifold, @@ -660,41 +720,57 @@ function decorator_transparent_dispatch( return Val(:parent) end """ - ricci_tensor(M::Manifold, p; backend::AbstractDiffBackend = diff_backend()) + ricci_tensor(M::AbstractManifold, p, B::AbstractBasis; backend::AbstractDiffBackend = diff_backend()) Compute the Ricci tensor, also known as the Ricci curvature tensor, -of the manifold `M` at the point `p`, see [https://en.wikipedia.org/wiki/Ricci_curvature#Introduction_and_local_definition](https://en.wikipedia.org/wiki/Ricci_curvature#Introduction_and_local_definition). +of the manifold `M` at the point `p` using basis `B`, +see [https://en.wikipedia.org/wiki/Ricci_curvature#Introduction_and_local_definition](https://en.wikipedia.org/wiki/Ricci_curvature#Introduction_and_local_definition). """ -ricci_tensor(::Manifold, ::Any) -function ricci_tensor(M::Manifold, p; kwargs...) - R = riemann_tensor(M, p; kwargs...) +ricci_tensor(::AbstractManifold, ::Any, ::AbstractBasis) +function ricci_tensor(M::AbstractManifold, p, B::AbstractBasis; kwargs...) + R = riemann_tensor(M, p, B; kwargs...) n = size(R, 1) Ric = allocate(R, Size(n, n)) @einsum Ric[i, j] = R[l, i, l, j] return Ric end -@decorator_transparent_signature ricci_tensor(M::AbstractDecoratorManifold, p; kwargs...) +@decorator_transparent_signature ricci_tensor( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis; + kwargs..., +) function decorator_transparent_dispatch(::typeof(ricci_tensor), ::MetricManifold, args...) return Val(:parent) end @doc raw""" - riemann_tensor(M::Manifold, p; backend::AbstractDiffBackend = diff_backend()) + riemann_tensor(M::AbstractManifold, p, B::AbstractBasis; backend::AbstractDiffBackend = diff_backend()) Compute the Riemann tensor ``R^l_{ijk}``, also known as the Riemann curvature tensor, at the point `p`. The dimensions of the resulting multi-dimensional array are ordered ``(l,i,j,k)``. """ -riemann_tensor(::Manifold, ::Any...) -function riemann_tensor(M::Manifold, p; backend::AbstractDiffBackend=diff_backend()) +riemann_tensor(::AbstractManifold, ::Any, ::AbstractBasis) +function riemann_tensor( + M::AbstractManifold, + p, + B::AbstractBasis; + backend::AbstractDiffBackend=diff_backend(), +) n = size(p, 1) - Ξ“ = christoffel_symbols_second(M, p; backend=backend) - βˆ‚Ξ“ = christoffel_symbols_second_jacobian(M, p; backend=backend) ./ n + Ξ“ = christoffel_symbols_second(M, p, B; backend=backend) + βˆ‚Ξ“ = christoffel_symbols_second_jacobian(M, p, B; backend=backend) ./ n R = allocate(βˆ‚Ξ“, Size(n, n, n, n)) @einsum R[l, i, j, k] = βˆ‚Ξ“[l, i, k, j] - βˆ‚Ξ“[l, i, j, k] + Ξ“[s, i, k] * Ξ“[l, s, j] - Ξ“[s, i, j] * Ξ“[l, s, k] return R end -@decorator_transparent_signature riemann_tensor(M::AbstractDecoratorManifold, p; kwargs...) +@decorator_transparent_signature riemann_tensor( + M::AbstractDecoratorManifold, + p, + B::AbstractBasis; + kwargs..., +) function decorator_transparent_dispatch(::typeof(riemann_tensor), ::MetricManifold, args...) return Val(:parent) end @@ -703,7 +779,7 @@ end sharp(N::MetricManifold{M,G}, p, ΞΎ::FVector{CotangentSpaceType}) Compute the musical isomorphism to transform the cotangent vector `ΞΎ` from the -[`Manifold`](@ref) `M` equipped with [`Metric`](@ref) `G` to a tangent by +[`AbstractManifold`](@ref) `M` equipped with [`AbstractMetric`](@ref) `G` to a tangent by computing ````math @@ -712,10 +788,10 @@ computing 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) +sharp(::MetricManifold, ::Any, ::CoTFVector) function sharp!(M::N, X::TFVector, p, ΞΎ::CoTFVector) where {N<:MetricManifold} - Ginv = inverse_local_metric(M, p) + Ginv = inverse_local_metric(M, p, X.basis) copyto!(X.data, Ginv * ΞΎ.data) return X end @@ -729,7 +805,8 @@ end M::MetricManifold, p, X, - tspan; + tspan, + B::AbstractBasis; backend::AbstractDiffBackend = diff_backend(), solver = AutoVern9(Rodas5()), kwargs..., @@ -753,13 +830,13 @@ coordinate chart that covers the entire manifold. This excludes coordinates in an embedded space. !!! note - This function only works for Julia 1.1 or greater, when + This function only works when [OrdinaryDiffEq.jl](https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl) is loaded with ```julia using OrdinaryDiffEq ``` """ -function solve_exp_ode(M, p, X, tspan; kwargs...) +function solve_exp_ode(M, p, X, tspan, B::AbstractBasis; kwargs...) return 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.", ) diff --git a/src/manifolds/Multinomial.jl b/src/manifolds/Multinomial.jl index ca620f6645..36f86eef64 100644 --- a/src/manifolds/Multinomial.jl +++ b/src/manifolds/Multinomial.jl @@ -37,57 +37,37 @@ function Base.:^(M::ProbabilitySimplex{N}, m::Int) where {N} end @doc raw""" - check_manifold_point(M::MultinomialMatrices, p) + check_point(M::MultinomialMatrices, p) Checks whether `p` is a valid point on the [`MultinomialMatrices`](@ref)`(m,n)` `M`, i.e. is a matrix of `m` discrete probability distributions as columns from $\mathbb R^{n}$, i.e. each column is a point from [`ProbabilitySimplex`](@ref)`(n-1)`. """ -check_manifold_point(::MultinomialMatrices, ::Any) -function check_manifold_point(M::MultinomialMatrices{n,m}, p; kwargs...) where {n,m} +check_point(::MultinomialMatrices, ::Any) +function check_point(M::MultinomialMatrices{n,m}, p; kwargs...) where {n,m} if size(p) != (n, m) return DomainError( length(p), "The matrix in `p` ($(size(p))) does not match the dimensions of $(M).", ) end - return check_manifold_point(PowerManifold(M.manifold, m), p; kwargs...) + return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @doc raw""" - check_tangent_vector(M::MultinomialMatrices p, X; check_base_point = true, kwargs...) + check_vector(M::MultinomialMatrices p, X; kwargs...) Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialMatrices`](@ref) `M`. This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`ProbabilitySimplex`](@ref). -The optional parameter `check_base_point` indicates, whether to call -[`check_manifold_point`](@ref check_manifold_point(::MultinomialMatrices, ::Any)) for `p`. """ -function check_tangent_vector( - M::MultinomialMatrices{n,m}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,m} - if check_base_point && size(p) != (n, m) - return DomainError( - length(p), - "The matrix `p` ($(size(p))) does not match the dimension of $(M).", - ) - end +function check_vector(M::MultinomialMatrices{n,m}, p, X; kwargs...) where {n,m} if size(X) != (n, m) return DomainError( length(X), - "The matrix `X` ($(size(X))) does not match the dimension of $(M).", + "The matrix `X` ($(size(X))) does not match the required dimension ($(representation_size(M))) for $(M).", ) end - return check_tangent_vector( - PowerManifold(M.manifold, m), - p, - X; - check_base_point=check_base_point, - kwargs..., - ) + return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end get_iterator(::MultinomialMatrices{n,m}) where {n,m} = Base.OneTo(m) diff --git a/src/manifolds/MultinomialDoublyStochastic.jl b/src/manifolds/MultinomialDoublyStochastic.jl index 65fabb69e3..8f549b2425 100644 --- a/src/manifolds/MultinomialDoublyStochastic.jl +++ b/src/manifolds/MultinomialDoublyStochastic.jl @@ -45,7 +45,7 @@ Generate the manifold of matrices $\mathbb R^{nΓ—n}$ that are doubly stochastic [^DouikHassibi2019]: > A. Douik, B. Hassibi: - > Manifold Optimization Over the Set of Doubly Stochastic Matrices: A Second-Order Geometry, + > AbstractManifold Optimization Over the Set of Doubly Stochastic Matrices: A Second-Order Geometry, > IEEE Transactions on Signal Processing 67(22), pp. 5761–5774, 2019. > doi: [10.1109/tsp.2019.2946024](http://doi.org/10.1109/tsp.2019.2946024), > arXiv: [1802.02628](https://arxiv.org/abs/1802.02628). @@ -57,14 +57,13 @@ function MultinomialDoubleStochastic(n::Int) end @doc raw""" - check_manifold_point(M::MultinomialDoubleStochastic, p) + check_point(M::MultinomialDoubleStochastic, p) Checks whether `p` is a valid point on the [`MultinomialDoubleStochastic`](@ref)`(n)` `M`, i.e. is a matrix with positive entries whose rows and columns sum to one. """ -function check_manifold_point(M::MultinomialDoubleStochastic{n}, p; kwargs...) where {n} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::MultinomialDoubleStochastic{n}, p; kwargs...) where {n} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv # positivity and columns are checked in the embedding, we further check r = sum(p, dims=2) @@ -77,33 +76,19 @@ function check_manifold_point(M::MultinomialDoubleStochastic{n}, p; kwargs...) w return nothing end @doc raw""" - check_tangent_vector(M::MultinomialDoubleStochastic p, X; check_base_point = true, kwargs...) + check_vector(M::MultinomialDoubleStochastic p, X; kwargs...) Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialDoubleStochastic`](@ref) `M`. This means, that `p` is valid, that `X` is of correct dimension and sums to zero along any column or row. - -The optional parameter `check_base_point` indicates, whether to call -[`check_manifold_point`](@ref check_manifold_point(::MultinomialDoubleStochastic, ::Any)) for `p`. """ -function check_tangent_vector( - M::MultinomialDoubleStochastic{n}, - p, - X; - check_base_point=true, - kwargs..., -) where {n} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::MultinomialDoubleStochastic{n}, p, X; kwargs...) where {n} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv diff --git a/src/manifolds/MultinomialSymmetric.jl b/src/manifolds/MultinomialSymmetric.jl index 4314cc5d63..d6fd9844be 100644 --- a/src/manifolds/MultinomialSymmetric.jl +++ b/src/manifolds/MultinomialSymmetric.jl @@ -41,7 +41,7 @@ Generate the manifold of matrices $\mathbb R^{nΓ—n}$ that are doubly stochastic [^DouikHassibi2019]: > A. Douik, B. Hassibi: - > Manifold Optimization Over the Set of Doubly Stochastic Matrices: A Second-Order Geometry, + > AbstractManifold Optimization Over the Set of Doubly Stochastic Matrices: A Second-Order Geometry, > IEEE Transactions on Signal Processing 67(22), pp. 5761–5774, 2019. > doi: [10.1109/tsp.2019.2946024](http://doi.org/10.1109/tsp.2019.2946024), > arXiv: [1802.02628](https://arxiv.org/abs/1802.02628). @@ -53,53 +53,38 @@ function MultinomialSymmetric(n::Int) end @doc raw""" - check_manifold_point(M::MultinomialSymmetric, p) + check_point(M::MultinomialSymmetric, p) Checks whether `p` is a valid point on the [`MultinomialSymmetric`](@ref)`(m,n)` `M`, i.e. is a symmetric matrix with positive entries whose rows sum to one. """ -function check_manifold_point(M::MultinomialSymmetric{n}, p; kwargs...) where {n} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::MultinomialSymmetric{n}, p; kwargs...) where {n} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv # the embedding checks for positivity and unit sum columns, by symmetry we would get # the same for the rows, so checking symmetry is the only thing left, we can just use # the corresponding manifold for that - return check_manifold_point(SymmetricMatrices(n, ℝ), p) + return check_point(SymmetricMatrices(n, ℝ), p) end @doc raw""" - check_tangent_vector(M::MultinomialSymmetric p, X; check_base_point = true, kwargs...) + check_vector(M::MultinomialSymmetric p, X; kwargs...) Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialSymmetric`](@ref) `M`. This means, that `p` is valid, that `X` is of correct dimension, symmetric, and sums to zero along any row. - -The optional parameter `check_base_point` indicates, whether to call -[`check_manifold_point`](@ref check_manifold_point(::MultinomialSymmetric, ::Any)) for `p`. """ -function check_tangent_vector( - M::MultinomialSymmetric{n}, - p, - X; - check_base_point=true, - kwargs..., -) where {n} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::MultinomialSymmetric{n}, p, X; kwargs...) where {n} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv # from the embedding we know that columns sum to zero, only symmety is left, i.e. - return check_tangent_vector(SymmetricMatrices(n, ℝ), p, X; check_base_point=false) + return check_vector(SymmetricMatrices(n, ℝ), p, X; kwargs...) end function decorated_manifold(::MultinomialSymmetric{N}) where {N} diff --git a/src/manifolds/Oblique.jl b/src/manifolds/Oblique.jl index 6b8bcfd282..bb7710067b 100644 --- a/src/manifolds/Oblique.jl +++ b/src/manifolds/Oblique.jl @@ -28,56 +28,37 @@ end Base.:^(M::Sphere{N,𝔽}, m::Int) where {N,𝔽} = Oblique{manifold_dimension(M) + 1,m,𝔽,N}(M) @doc raw""" - check_manifold_point(M::Oblique{n,m},p) + check_point(M::Oblique{n,m},p) Checks whether `p` is a valid point on the [`Oblique`](@ref)`{m,n}` `M`, i.e. is a matrix of `m` unit columns from $\mathbb R^{n}$, i.e. each column is a point from [`Sphere`](@ref)`(n-1)`. """ -check_manifold_point(::Oblique, ::Any) -function check_manifold_point(M::Oblique{n,m}, p; kwargs...) where {n,m} +check_point(::Oblique, ::Any) +function check_point(M::Oblique{n,m}, p; kwargs...) where {n,m} if size(p) != (n, m) return DomainError( length(p), "The matrix in `p` ($(size(p))) does not match the dimension of $(M).", ) end - return check_manifold_point(PowerManifold(M.manifold, m), p; kwargs...) + return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @doc raw""" - check_tangent_vector(M::Oblique p, X; check_base_point = true, kwargs...) + check_vector(M::Oblique p, X; kwargs...) Checks whether `X` is a valid tangent vector to `p` on the [`Oblique`](@ref) `M`. This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`Sphere`](@ref). -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector( - M::Oblique{n,m}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,m} - if check_base_point && size(p) != (n, m) - return DomainError( - length(p), - "The matrix `p` ($(size(p))) does not match the dimension of $(M).", - ) - end +function check_vector(M::Oblique{n,m}, p, X; kwargs...) where {n,m} if size(X) != (n, m) return DomainError( length(X), - "The matrix `X` ($(size(X))) does not match the dimension of $(M).", + "The matrix `X` ($(size(X))) does not match the required dimension ($(representation_size(M))) for $(M).", ) end - return check_tangent_vector( - PowerManifold(M.manifold, m), - p, - X; - check_base_point=check_base_point, - kwargs..., - ) + return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end get_iterator(::Oblique{n,m}) where {n,m} = Base.OneTo(m) diff --git a/src/manifolds/PositiveNumbers.jl b/src/manifolds/PositiveNumbers.jl index eb15326940..05f16c3c05 100644 --- a/src/manifolds/PositiveNumbers.jl +++ b/src/manifolds/PositiveNumbers.jl @@ -1,5 +1,5 @@ @doc raw""" - PositiveNumbers <: Manifold{ℝ} + PositiveNumbers <: AbstractManifold{ℝ} The hyperbolic manifold of positive numbers $H^1$ is a the hyperbolic manifold represented by just positive numbers. @@ -12,7 +12,7 @@ Generate the `ℝ`-valued hyperbolic model represented by positive positive numb To use this with arrays (1-element arrays), please use [`SymmetricPositiveDefinite`](@ref)`(1)`. """ -struct PositiveNumbers <: Manifold{ℝ} end +struct PositiveNumbers <: AbstractManifold{ℝ} end """ PositiveVectors(n) @@ -39,11 +39,11 @@ This manifold is modeled as a [`PowerManifold`](@ref) of [`PositiveNumbers`](@re PositiveArrays(n::Vararg{Int,I}) where {I} = PositiveNumbers()^(n) @doc raw""" - check_manifold_point(M::PositiveNumbers, p) + check_point(M::PositiveNumbers, p) Check whether `p` is a point on the [`PositiveNumbers`](@ref) `M`, i.e. $p>0$. """ -function check_manifold_point(M::PositiveNumbers, p; kwargs...) +function check_point(M::PositiveNumbers, p; kwargs...) if any(p .<= 0.0) return DomainError( p, @@ -54,7 +54,7 @@ function check_manifold_point(M::PositiveNumbers, p; kwargs...) end """ - check_tangent_vector(M::PositiveNumbers, p, X; check_base_point, kwargs...) + check_vector(M::PositiveNumbers, p, X; kwargs...) Check whether `X` is a tangent vector in the tangent space of `p` on the [`PositiveNumbers`](@ref) `M`. @@ -62,11 +62,7 @@ For the real-valued case represented by positive numbers, all `X` are valid, sin For the complex-valued case `X` [...] """ -function check_tangent_vector(M::PositiveNumbers, p, X; check_base_point=true, kwargs...) - if check_base_point - perr = check_manifold_point(M, p; kwargs...) - return perr # if x is valid all v that are real numbers are valid - end +function check_vector(M::PositiveNumbers, p, X; kwargs...) return nothing end @@ -97,10 +93,6 @@ Base.exp(::PositiveNumbers, p::Real, X::Real) = p * exp(X / p) exp!(::PositiveNumbers, q, p, X) = (q .= p .* exp.(X ./ p)) -flat(::PositiveNumbers, ::Real, X::TFVector) = FVector(CotangentSpace, X.data) - -flat!(::PositiveNumbers, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - @doc raw""" injectivity_radius(M::PositiveNumbers[, p]) @@ -112,7 +104,7 @@ injectivity_radius(::PositiveNumbers, ::Any) = Inf injectivity_radius(::PositiveNumbers, ::Any, ::ExponentialRetraction) = Inf eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::PositiveNumbers, rm::AbstractRetractionMethod, ) @@ -183,10 +175,6 @@ retract(M::PositiveNumbers, p, q, ::ExponentialRetraction) = exp(M, p, q) representation_size(::PositiveNumbers) = () -sharp(::PositiveNumbers, ::Real, ΞΎ::CoTFVector) = FVector(TangentSpace, ΞΎ.data) - -sharp!(::PositiveNumbers, X::TFVector, p, ΞΎ::CoTFVector) = copyto!(X, ΞΎ) - Base.show(io::IO, ::PositiveNumbers) = print(io, "PositiveNumbers()") function Base.show( @@ -235,5 +223,5 @@ function vector_transport_direction( return vector_transport_to(M, p, X, q, m) end -zero_tangent_vector(::PositiveNumbers, p::Real) = zero(p) -zero_tangent_vector!(::PositiveNumbers, X, p) = fill!(X, 0) +zero_vector(::PositiveNumbers, p::Real) = zero(p) +zero_vector!(::PositiveNumbers, X, p) = fill!(X, 0) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index f8e7addc28..a7e6693ba1 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -12,15 +12,15 @@ direction. struct ArrayPowerRepresentation <: AbstractPowerRepresentation end @doc raw""" - PowerMetric <: Metric + PowerMetric <: AbstractMetric -Represent the [`Metric`](@ref) on an [`AbstractPowerManifold`](@ref), i.e. the inner +Represent the [`AbstractMetric`](@ref) on an [`AbstractPowerManifold`](@ref), i.e. the inner product on the tangent space is the sum of the inner product of each elements tangent space of the power manifold. """ -struct PowerMetric <: Metric end +struct PowerMetric <: AbstractMetric end -function PowerManifold(M::Manifold{𝔽}, size::Integer...) where {𝔽} +function PowerManifold(M::AbstractManifold{𝔽}, size::Integer...) where {𝔽} return PowerManifold{𝔽,typeof(M),Tuple{size...},ArrayPowerRepresentation}(M) end @@ -55,35 +55,14 @@ struct PowerFVectorDistribution{ end const PowerManifoldMultidimensional = - AbstractPowerManifold{𝔽,<:Manifold{𝔽},ArrayPowerRepresentation} where {𝔽} + AbstractPowerManifold{𝔽,<:AbstractManifold{𝔽},ArrayPowerRepresentation} where {𝔽} -Base.:^(M::Manifold, n) = PowerManifold(M, n...) - -function allocate_result(M::PowerManifoldNested, f::typeof(flat), w::TFVector, x) - alloc = [allocate(_access_nested(w.data, i)) for i in get_iterator(M)] - return FVector(CotangentSpace, alloc) -end -function allocate_result(M::PowerManifoldNested, f::typeof(sharp), w::CoTFVector, x) - alloc = [allocate(_access_nested(w.data, i)) for i in get_iterator(M)] - return FVector(TangentSpace, alloc) -end +Base.:^(M::AbstractManifold, n) = PowerManifold(M, n...) default_metric_dispatch(::AbstractPowerManifold, ::PowerMetric) = Val(true) -function det_local_metric( - M::MetricManifold{PowerMetric,𝔽,<:AbstractPowerManifold{𝔽}}, - p::AbstractArray, -) where {𝔽} - 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, p, i)) - end - return result -end - @doc raw""" - flat(M::AbstractPowerManifold, p, X::FVector{TangentSpaceType}) + flat(M::AbstractPowerManifold, p, X) 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. @@ -91,14 +70,15 @@ This can be done elementwise for each entry of `X` (and `p`). """ flat(::AbstractPowerManifold, ::Any...) -function flat!(M::AbstractPowerManifold, ΞΎ::CoTFVector, p, X::TFVector) +function flat!(M::AbstractPowerManifold, ΞΎ::RieszRepresenterCotangentVector, p, X) rep_size = representation_size(M.manifold) for i in get_iterator(M) + p_i = _read(M, rep_size, p, i) flat!( M.manifold, - FVector(CotangentSpace, _write(M, rep_size, ΞΎ.data, i)), - _read(M, rep_size, p, i), - FVector(TangentSpace, _read(M, rep_size, X.data, i)), + RieszRepresenterCotangentVector(M.manifold, p_i, _write(M, rep_size, ΞΎ.X, i)), + p_i, + _read(M, rep_size, X, i), ) end return ΞΎ @@ -186,12 +166,12 @@ Base.@propagate_inbounds @inline function _read( return x[i...] end -function representation_size(M::PowerManifold{𝔽,<:Manifold,TSize}) where {𝔽,TSize} +function representation_size(M::PowerManifold{𝔽,<:AbstractManifold,TSize}) where {𝔽,TSize} return (representation_size(M.manifold)..., size_to_tuple(TSize)...) end @doc raw""" - sharp(M::AbstractPowerManifold, p, ΞΎ::FVector{CotangentSpaceType}) + sharp(M::AbstractPowerManifold, p, ΞΎ::RieszRepresenterCotangentVector) Use the musical isomorphism to transform the cotangent vector `ΞΎ` from the tangent space at `p` on an [`AbstractPowerManifold`](@ref) `M` to a tangent vector. @@ -199,14 +179,15 @@ This can be done elementwise for every entry of `ΞΎ` (and `p`). """ sharp(::AbstractPowerManifold, ::Any...) -function sharp!(M::AbstractPowerManifold, X::TFVector, p, ΞΎ::CoTFVector) +function sharp!(M::AbstractPowerManifold, X, p, ΞΎ::RieszRepresenterCotangentVector) rep_size = representation_size(M.manifold) for i in get_iterator(M) + p_i = _read(M, rep_size, p, i) sharp!( M.manifold, - FVector(TangentSpace, _write(M, rep_size, X.data, i)), - _read(M, rep_size, p, i), - FVector(CotangentSpace, _read(M, rep_size, ΞΎ.data, i)), + _write(M, rep_size, X, i), + p_i, + RieszRepresenterCotangentVector(M.manifold, p_i, _read(M, rep_size, ΞΎ.X, i)), ) end return X diff --git a/src/manifolds/ProbabilitySimplex.jl b/src/manifolds/ProbabilitySimplex.jl index 51c5ebdf70..bd2ae1983d 100644 --- a/src/manifolds/ProbabilitySimplex.jl +++ b/src/manifolds/ProbabilitySimplex.jl @@ -48,7 +48,7 @@ Describes an inverse retraction that is based on the softmax function. struct SoftmaxInverseRetraction <: AbstractInverseRetractionMethod end """ - FisherRaoMetric <: Metric + FisherRaoMetric <: AbstractMetric The Fisher-Rao metric or Fisher information metric is a particular Riemannian metric which can be defined on a smooth statistical manifold, i.e., a smooth manifold whose points are @@ -56,18 +56,18 @@ probability measures defined on a common probability space. See for example the [`ProbabilitySimplex`](@ref). """ -struct FisherRaoMetric <: Metric end +struct FisherRaoMetric <: AbstractMetric end """ - check_manifold_point(M::ProbabilitySimplex, p; kwargs...) + check_point(M::ProbabilitySimplex, p; kwargs...) Check whether `p` is a valid point on the [`ProbabilitySimplex`](@ref) `M`, i.e. is a point in the embedding with positive entries that sum to one The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::ProbabilitySimplex, p; kwargs...) +function check_point(M::ProbabilitySimplex, p; kwargs...) mpv = invoke( - check_manifold_point, + check_point, Tuple{(typeof(get_embedding(M))),typeof(p)}, get_embedding(M), p; @@ -90,27 +90,20 @@ function check_manifold_point(M::ProbabilitySimplex, p; kwargs...) end """ - check_tangent_vector(M::ProbabilitySimplex, p, X; check_base_point = true, kwargs... ) + check_vector(M::ProbabilitySimplex, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`ProbabilitySimplex`](@ref) `M`, i.e. -after [`check_manifold_point`](@ref check_manifold_point(::ProbabilitySimplex, ::Any))`(M,p)`, +after [`check_point`](@ref check_point(::ProbabilitySimplex, ::Any))`(M,p)`, `X` has to be of same dimension as `p` and its elements have to sum to one. -The optional parameter `check_base_point` indicates, whether to call -[`check_manifold_point`](@ref check_manifold_point(::ProbabilitySimplex, ::Any)) for `p` or not. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::ProbabilitySimplex, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::ProbabilitySimplex, p, X; kwargs...) mpv = invoke( - check_tangent_vector, + check_vector, Tuple{typeof(get_embedding(M)),typeof(p),typeof(X)}, get_embedding(M), p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -189,7 +182,7 @@ injectivity_radius(M::ProbabilitySimplex, ::SoftmaxRetraction) = 0 injectivity_radius(M::ProbabilitySimplex, ::ExponentialRetraction) = 0 eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::ProbabilitySimplex, rm::AbstractRetractionMethod, ) @@ -280,7 +273,7 @@ manifold_dimension(::ProbabilitySimplex{n}) where {n} = n kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolation`](@ref). """ mean(::ProbabilitySimplex, ::Any...) @@ -361,11 +354,11 @@ function Base.show(io::IO, ::ProbabilitySimplex{n}) where {n} end @doc raw""" - zero_tangent_vector(M::ProbabilitySimplex,p) + zero_vector(M::ProbabilitySimplex,p) returns the zero tangent vector in the tangent space of the point `p` from the [`ProbabilitySimplex`](@ref) `M`, i.e. its representation by the zero vector in the embedding. """ -zero_tangent_vector(::ProbabilitySimplex, ::Any) +zero_vector(::ProbabilitySimplex, ::Any) -zero_tangent_vector!(M::ProbabilitySimplex, v, p) = fill!(v, 0) +zero_vector!(M::ProbabilitySimplex, v, p) = fill!(v, 0) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index e42683096f..7aef4331d9 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,5 +1,5 @@ @doc raw""" - ProductManifold{𝔽,TM<:Tuple} <: Manifold{𝔽} + ProductManifold{𝔽,TM<:Tuple} <: AbstractManifold{𝔽} Product manifold $M_1 Γ— M_2 Γ— … Γ— M_n$ with product geometry. @@ -11,11 +11,11 @@ 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`. """ -struct ProductManifold{𝔽,TM<:Tuple} <: Manifold{𝔽} +struct ProductManifold{𝔽,TM<:Tuple} <: AbstractManifold{𝔽} manifolds::TM end -function ProductManifold(manifolds::Manifold...) +function ProductManifold(manifolds::AbstractManifold...) 𝔽 = ManifoldsBase._unify_number_systems((number_system.(manifolds))...) return ProductManifold{𝔽,typeof(manifolds)}(manifolds) end @@ -33,8 +33,11 @@ ProductManifold() = throw(MethodError("No method matching ProductManifold().")) const PRODUCT_BASIS_LIST = [ VeeOrthogonalBasis, DefaultBasis, + DefaultBasis{<:Any,TangentSpaceType}, DefaultOrthogonalBasis, + DefaultOrthogonalBasis{<:Any,TangentSpaceType}, DefaultOrthonormalBasis, + DefaultOrthonormalBasis{<:Any,TangentSpaceType}, ProjectedOrthonormalBasis{:gram_schmidt,ℝ}, ProjectedOrthonormalBasis{:svd,ℝ}, ] @@ -51,11 +54,11 @@ end const PRODUCT_BASIS_LIST_CACHED = [CachedBasis] """ - ProductMetric <: Metric + ProductMetric <: AbstractMetric A type to represent the product of metrics for a [`ProductManifold`](@ref). """ -struct ProductMetric <: Metric end +struct ProductMetric <: AbstractMetric end """ ProductFVectorDistribution([type::VectorBundleFibers], [x], distrs...) @@ -133,7 +136,7 @@ function ProductVectorTransport(methods::AbstractVectorTransportMethod...) end """ - check_manifold_point(M::ProductManifold, p; kwargs...) + check_point(M::ProductManifold, p; kwargs...) Check whether `p` is a valid point on the [`ProductManifold`](@ref) `M`. If `p` is not a point on `M` a [`CompositeManifoldError`](@ref) consisting of all error messages of the @@ -141,13 +144,9 @@ components, for which the tests fail is returned. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point( - M::ProductManifold, - p::Union{ProductRepr,ProductArray}; - kwargs..., -) +function check_point(M::ProductManifold, p::Union{ProductRepr,ProductArray}; kwargs...) ts = ziptuples(Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p)) - e = [(t[1], check_manifold_point(t[2:end]...; kwargs...)) for t in ts] + e = [(t[1], check_point(t[2:end]...; kwargs...)) for t in ts] errors = filter((x) -> !(x[2] === nothing), e) cerr = [ComponentManifoldError(er...) for er in errors] (length(errors) > 1) && return CompositeManifoldError(cerr) @@ -156,34 +155,28 @@ function check_manifold_point( end """ - check_tangent_vector(M::ProductManifold, p, X; check_base_point = true, kwargs... ) + check_vector(M::ProductManifold, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`ProductManifold`](@ref) -`M`, i.e. after [`check_manifold_point`](@ref)`(M, p)`, and all projections to -base manifolds must be respective tangent vectors. -If `X` is not a tangent vector to `p` on `M` a [`CompositeManifoldError`](@ref) consisting of all error -messages of the components, for which the tests fail is returned. +`M`, i.e. all projections to base manifolds must be respective tangent vectors. +If `X` is not a tangent vector to `p` on `M` a [`CompositeManifoldError`](@ref) consisting +of all error messages of the components, for which the tests fail is returned. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector( +function check_vector( M::ProductManifold, p::Union{ProductRepr,ProductArray}, X::Union{ProductRepr,ProductArray}; - check_base_point=true, kwargs..., ) - if check_base_point - perr = check_manifold_point(M, p; kwargs...) - perr === nothing || return perr - end ts = ziptuples( Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p), submanifold_components(M, X), ) - e = [(t[1], check_tangent_vector(t[2:end]...; kwargs...)) for t in ts] + e = [(t[1], check_vector(t[2:end]...; kwargs...)) for t in ts] errors = filter(x -> !(x[2] === nothing), e) cerr = [ComponentManifoldError(er...) for er in errors] (length(errors) > 1) && return CompositeManifoldError(cerr) @@ -210,7 +203,7 @@ end cross(M,N) cross(M1, M2, M3,...) -Return the [`ProductManifold`](@ref) For two [`Manifold`](@ref)s `M` and `N`, +Return the [`ProductManifold`](@ref) For two [`AbstractManifold`](@ref)s `M` and `N`, where for the case that one of them is a [`ProductManifold`](@ref) itself, the other is either prepended (if `N` is a product) or appenden (if `M`) is. If both are product manifold, they are combined into one product manifold, @@ -219,26 +212,18 @@ keeping the order. For the case that more than one is a product manifold of these is build with the same approach as above """ -cross(::Manifold...) -LinearAlgebra.cross(M1::Manifold, M2::Manifold) = ProductManifold(M1, M2) -function LinearAlgebra.cross(M1::ProductManifold, M2::Manifold) +cross(::AbstractManifold...) +LinearAlgebra.cross(M1::AbstractManifold, M2::AbstractManifold) = ProductManifold(M1, M2) +function LinearAlgebra.cross(M1::ProductManifold, M2::AbstractManifold) return ProductManifold(M1.manifolds..., M2) end -function LinearAlgebra.cross(M1::Manifold, M2::ProductManifold) +function LinearAlgebra.cross(M1::AbstractManifold, M2::ProductManifold) return ProductManifold(M1, M2.manifolds...) end function LinearAlgebra.cross(M1::ProductManifold, M2::ProductManifold) return ProductManifold(M1.manifolds..., M2.manifolds...) end -function det_local_metric( - M::MetricManifold{ProductMetric,𝔽,ProductManifold{𝔽}}, - p::ProductArray, -) where {𝔽} - dets = map(det_local_metric, M.manifolds, submanifold_components(M, p)) - return prod(dets) -end - @doc raw""" distance(M::ProductManifold, p, q) @@ -297,19 +282,12 @@ entry in `p`) separately. """ flat(::ProductManifold, ::Any...) -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, p, B::AbstractBasis) parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(p))) return CachedBasis(B, ProductBasisData(parts)) end function get_basis(M::ProductManifold, p, B::CachedBasis) - return invoke(get_basis, Tuple{Manifold,Any,CachedBasis}, M, p, B) + return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) end function get_basis(M::ProductManifold, p, B::DiagonalizingOrthonormalBasis) vs = map( @@ -365,7 +343,7 @@ for BT in PRODUCT_BASIS_LIST_CACHED end eval( quote - @invoke_maker 1 Manifold get_coordinates( + @invoke_maker 1 AbstractManifold get_coordinates( M::ProductManifold, e::Identity, X, @@ -453,7 +431,7 @@ for BT in PRODUCT_BASIS_LIST end eval( quote - @invoke_maker 1 Manifold get_coordinates!( + @invoke_maker 1 AbstractManifold get_coordinates!( M::ProductManifold, Y, e::Identity, @@ -494,7 +472,7 @@ eval( ) eval( quote - @invoke_maker 1 Manifold get_vector( + @invoke_maker 1 AbstractManifold get_vector( M::ProductManifold, e::Identity, X, @@ -572,7 +550,7 @@ function get_vector!( end eval( quote - @invoke_maker 1 Manifold get_vector!( + @invoke_maker 1 AbstractManifold get_vector!( M::ProductManifold, Xⁱ, e::Identity, @@ -603,7 +581,7 @@ function get_vectors( N = number_of_components(M) xparts = submanifold_components(p) BVs = map(t -> get_vectors(t...), ziptuples(M.manifolds, xparts, B.data.parts)) - zero_tvs = map(t -> zero_tangent_vector(t...), ziptuples(M.manifolds, xparts)) + zero_tvs = map(t -> zero_vector(t...), ziptuples(M.manifolds, xparts)) vs = typeof(ProductRepr(zero_tvs...))[] for i in 1:N, k in 1:length(BVs[i]) push!(vs, ProductRepr(zero_tvs[1:(i - 1)]..., BVs[i][k], zero_tvs[(i + 1):end]...)) @@ -824,7 +802,7 @@ number_of_components(M::ProductManifold{𝔽,<:NTuple{N,Any}}) where {𝔽,N} = function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - p::Union{AbstractArray,MPoint,ProductRepr}, + p::Union{AbstractArray,AbstractManifoldPoint,ProductRepr}, distributions::FVectorDistribution..., ) return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( @@ -963,7 +941,7 @@ end """ set_component!(M::ProductManifold, q, p, i) -Set the `i`th component of a point `q` on a [`ProductManifold`](@ref) `M` to `p`, where `p` is a point on the [`Manifold`](@ref) this factor of the product manifold consists of. +Set the `i`th component of a point `q` on a [`ProductManifold`](@ref) `M` to `p`, where `p` is a point on the [`AbstractManifold`](@ref) this factor of the product manifold consists of. """ function set_component!(M::ProductManifold, q, p, i) return copyto!(submanifold_component(M, q, i), p) @@ -994,14 +972,7 @@ This can be done elementwise for every entry of `ΞΎ` (and `p`) separately """ sharp(::ProductManifold, ::Any...) -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="") +function _show_submanifold(io::IO, M::AbstractManifold; pre="") sx = sprint(show, "text/plain", M, context=io, sizehint=0) if occursin('\n', sx) sx = sprint(show, M, context=io, sizehint=0) @@ -1166,9 +1137,9 @@ function vector_transport_to!(M::ProductManifold, Y, p, X, q, m::ProductVectorTr return Y end -function zero_tangent_vector!(M::ProductManifold, X, p) +function zero_vector!(M::ProductManifold, X, p) map( - zero_tangent_vector!, + zero_vector!, M.manifolds, submanifold_components(M, X), submanifold_components(M, p), diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index c1559b5a6c..c5b98880c2 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -87,15 +87,15 @@ function allocation_promotion_function(::AbstractProjectiveSpace{β„‚}, f, args:: end @doc raw""" - check_manifold_point(M::AbstractProjectiveSpace, p; kwargs...) + check_point(M::AbstractProjectiveSpace, p; kwargs...) Check whether `p` is a valid point on the [`AbstractProjectiveSpace`](@ref) `M`, i.e. that it has the same size as elements of the embedding and has unit Frobenius norm. The tolerance for the norm check can be set using the `kwargs...`. """ -function check_manifold_point(M::AbstractProjectiveSpace, p; kwargs...) +function check_point(M::AbstractProjectiveSpace, p; kwargs...) mpv = invoke( - check_manifold_point, + check_point, Tuple{(typeof(get_embedding(M))),typeof(p)}, get_embedding(M), p; @@ -111,34 +111,21 @@ function check_manifold_point(M::AbstractProjectiveSpace, p; kwargs...) return nothing end -@doc doc""" - check_tangent_vector(M::AbstractProjectiveSpace, p, X; check_base_point = true, kwargs... ) +@doc raw""" + check_vector(M::AbstractProjectiveSpace, p, X; kwargs... ) Check whether `X` is a tangent vector in the tangent space of `p` on the [`AbstractProjectiveSpace`](@ref) `M`, i.e. that `X` has the same size as elements of the tangent space of the embedding and that the Frobenius inner product $⟨p, X⟩_{\mathrm{F}} = 0$. -The optional parameter `check_base_point` indicates whether to call -[`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector( - M::AbstractProjectiveSpace, - p, - X; - check_base_point=true, - kwargs..., -) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::AbstractProjectiveSpace, p, X; kwargs...) mpv = invoke( - check_tangent_vector, + check_vector, Tuple{typeof(get_embedding(M)),typeof(p),typeof(X)}, get_embedding(M), p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -179,8 +166,6 @@ function exp!(M::AbstractProjectiveSpace, q, p, X) return q end -flat!(::AbstractProjectiveSpace, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - function get_basis(::ProjectiveSpace{n,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) where {n} return get_basis(Sphere{n,ℝ}(), p, B) end @@ -207,7 +192,7 @@ function get_coordinates!( Y, p, X, - ::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {𝔽} n = div(manifold_dimension(M), real_dimension(𝔽)) z = p[1] @@ -242,7 +227,7 @@ function get_vector!( Y, p, X, - ::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {𝔽} n = div(manifold_dimension(M), real_dimension(𝔽)) z = p[1] @@ -261,7 +246,7 @@ injectivity_radius(::AbstractProjectiveSpace, ::Any) = Ο€ / 2 injectivity_radius(::AbstractProjectiveSpace, ::Any, ::ExponentialRetraction) = Ο€ / 2 eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::AbstractProjectiveSpace, rm::AbstractRetractionMethod, ) @@ -369,7 +354,7 @@ end kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of points in vector `x` +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of points in vector `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ mean(::AbstractProjectiveSpace, ::Any...) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index c6f1232bd5..e1a96cf0b7 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,5 +1,5 @@ @doc raw""" - Rotations{N} <: Manifold{ℝ} + Rotations{N} <: AbstractManifold{ℝ} The special orthogonal manifold $\mathrm{SO}(n)$ represented by $n Γ— n$ real-valued orthogonal matrices with determinant $+1$ is the manifold of `Rotations`, @@ -11,7 +11,7 @@ since these matrices represent all rotations of points in $ℝ^n$. Generate the $\mathrm{SO}(n) \subset ℝ^{n Γ— n}$ """ -struct Rotations{N} <: Manifold{ℝ} end +struct Rotations{N} <: AbstractManifold{ℝ} end Rotations(n::Int) = Rotations{n}() @@ -61,14 +61,14 @@ function angles_4d_skew_sym_matrix(A) end """ - check_manifold_point(M, p; kwargs...) + check_point(M, p; kwargs...) 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}, p; kwargs...) where {N} +function check_point(M::Rotations{N}, p; kwargs...) where {N} if size(p) != (N, N) return DomainError( size(p), @@ -88,26 +88,15 @@ function check_manifold_point(M::Rotations{N}, p; kwargs...) where {N} end """ - check_tangent_vector(M, p, X; check_base_point = true, kwargs... ) + check_vector(M, p, X; kwargs... ) 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 +space `M`, i.e. after [`check_point`](@ref)`(M,p)`, `X` has to be of same dimension and orthogonal to `p`. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector( - M::Rotations{N}, - p, - X; - check_base_point=true, - kwargs..., -) where {N} - if check_base_point - perr = check_manifold_point(M, p) - perr === nothing || return perr - end - return check_manifold_point(SkewSymmetricMatrices(N), X) +function check_vector(M::Rotations{N}, p, X; kwargs...) where {N} + return check_point(SkewSymmetricMatrices(N), X; kwargs...) end @doc raw""" @@ -238,8 +227,6 @@ function exp!(M::Rotations{4}, q, p, X) return copyto!(q, p * pinvq) end -flat!(M::Rotations, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - @doc raw""" get_coordinates(M::Rotations, p, X) @@ -257,13 +244,25 @@ For $\mathrm{SO}(n)$ where $n β‰₯ 4$, the additional elements of $X^i$ are $X^{j (j - 3)/2 + k + 1} = X_{jk}$, for $j ∈ [4,n], k ∈ [1,j)$. """ get_coordinates(::Rotations, ::Any...) -get_coordinates(::Rotations{2}, p, X, ::DefaultOrthogonalBasis) = [X[2]] +get_coordinates(::Rotations{2}, p, X, ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}) = [X[2]] -function get_coordinates!(::Rotations{2}, Xⁱ, p, X, ::DefaultOrthogonalBasis) +function get_coordinates!( + ::Rotations{2}, + Xⁱ, + p, + X, + ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, +) Xⁱ[1] = X[2] return Xⁱ end -function get_coordinates!(M::Rotations{N}, Xⁱ, p, X, B::DefaultOrthogonalBasis) where {N} +function get_coordinates!( + M::Rotations{N}, + Xⁱ, + p, + X, + ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, +) where {N} @inbounds begin Xⁱ[1] = X[3, 2] Xⁱ[2] = X[1, 3] @@ -277,7 +276,13 @@ function get_coordinates!(M::Rotations{N}, Xⁱ, p, X, B::DefaultOrthogonalBasis end return Xⁱ end -function get_coordinates!(M::Rotations{N}, Xⁱ, p, X, ::DefaultOrthonormalBasis) where {N} +function get_coordinates!( + M::Rotations{N}, + Xⁱ, + p, + X, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) where {N} T = Base.promote_eltype(p, X) get_coordinates!(M, Xⁱ, p, X, DefaultOrthogonalBasis()) Xⁱ .*= sqrt(T(2)) @@ -285,7 +290,7 @@ function get_coordinates!(M::Rotations{N}, Xⁱ, p, X, ::DefaultOrthonormalBasis end @doc raw""" - get_vector(M::Rotations, p, Xⁱ, B:: DefaultOrthogonalBasis) + get_vector(M::Rotations, p, Xⁱ, B::DefaultOrthogonalBasis) Convert the unique tangent vector components `Xⁱ` at point `p` on [`Rotations`](@ref) group $\mathrm{SO}(n)$ to the matrix representation $X$ of the tangent vector. See @@ -293,10 +298,22 @@ group $\mathrm{SO}(n)$ to the matrix representation $X$ of the tangent vector. S """ get_vector(::Rotations, ::Any...) -function get_vector!(M::Rotations{2}, X, p, Xⁱ, B::DefaultOrthogonalBasis) +function get_vector!( + M::Rotations{2}, + X, + p, + Xⁱ, + B::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, +) return get_vector!(M, X, p, Xⁱ[1], B) end -function get_vector!(::Rotations{2}, X, p, Xⁱ::Real, ::DefaultOrthogonalBasis) +function get_vector!( + M::Rotations{2}, + X, + p, + Xⁱ::Real, + ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, +) @assert length(X) == 4 @inbounds begin X[1] = 0 @@ -306,7 +323,13 @@ function get_vector!(::Rotations{2}, X, p, Xⁱ::Real, ::DefaultOrthogonalBasis) end return X end -function get_vector!(M::Rotations{N}, X, p, Xⁱ, ::DefaultOrthogonalBasis) where {N} +function get_vector!( + M::Rotations{N}, + X, + p, + Xⁱ, + ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, +) where {N} @assert size(X) == (N, N) @assert length(Xⁱ) == manifold_dimension(M) @inbounds begin @@ -331,7 +354,7 @@ function get_vector!(M::Rotations{N}, X, p, Xⁱ, ::DefaultOrthogonalBasis) wher end return X end -function get_vector!(M::Rotations, X, p, Xⁱ, B::DefaultOrthonormalBasis) +function get_vector!(M::Rotations, X, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) T = Base.promote_eltype(p, X) get_vector!(M, X, p, Xⁱ, DefaultOrthogonalBasis()) X ./= sqrt(T(2)) @@ -357,7 +380,7 @@ injectivity_radius(::Rotations) = Ο€ * sqrt(2.0) injectivity_radius(::Rotations, ::ExponentialRetraction) = Ο€ * sqrt(2.0) eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::Rotations, rm::AbstractRetractionMethod, ) @@ -523,7 +546,7 @@ manifold_dimension(::Rotations{N}) where {N} = div(N * (N - 1), 2) kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ mean(::Rotations, ::Any) @@ -628,8 +651,6 @@ $\mathrm{SO}(n)$ it's `(n,n)`. """ @generated representation_size(::Rotations{N}) where {N} = (N, N) -sharp!(M::Rotations, X::TFVector, p, ΞΎ::CoTFVector) = copyto!(X, ΞΎ) - function Random.rand( rng::AbstractRNG, d::NormalRotationDistribution{TResult,Rotations{N}}, @@ -706,11 +727,11 @@ Base.show(io::IO, ::Rotations{N}) where {N} = print(io, "Rotations($(N))") Distributions.support(d::NormalRotationDistribution) = MPointSupport(d.manifold) @doc raw""" - zero_tangent_vector(M::Rotations, p) + zero_vector(M::Rotations, p) 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, p) = zero(p) +zero_vector(M::Rotations, p) = zero(p) -zero_tangent_vector!(M::Rotations, X, p) = fill!(X, 0) +zero_vector!(M::Rotations, X, p) = fill!(X, 0) diff --git a/src/manifolds/SkewHermitian.jl b/src/manifolds/SkewHermitian.jl index 44dd713c9c..67c4d80db1 100644 --- a/src/manifolds/SkewHermitian.jl +++ b/src/manifolds/SkewHermitian.jl @@ -1,7 +1,7 @@ @doc raw""" SkewHermitianMatrices{n,𝔽} <: AbstractEmbeddedManifold{𝔽,TransparentIsometricEmbedding} -The [`Manifold`](@ref) $ \operatorname{SkewHerm}(n)$ consisting of the real- or +The [`AbstractManifold`](@ref) $ \operatorname{SkewHerm}(n)$ consisting of the real- or complex-valued skew-hermitian matrices of size ``n Γ— n``, i.e. the set ````math @@ -53,7 +53,7 @@ function allocation_promotion_function( end @doc raw""" - check_manifold_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) + check_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) Check whether `p` is a valid manifold point on the [`SkewHermitianMatrices`](@ref) `M`, i.e. whether `p` is a skew-hermitian matrix of size `(n,n)` with values from the corresponding @@ -61,8 +61,8 @@ whether `p` is a skew-hermitian matrix of size `(n,n)` with values from the corr The tolerance for the skew-symmetry of `p` can be set using `kwargs...`. """ -function check_manifold_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} - mpv = check_manifold_point(decorated_manifold(M), p; kwargs...) +function check_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} + mpv = check_point(decorated_manifold(M), p; kwargs...) mpv === nothing || return mpv if !isapprox(p, -p'; kwargs...) return DomainError( @@ -74,27 +74,15 @@ function check_manifold_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) wh end """ - check_tangent_vector(M::SkewHermitianMatrices{n}, p, X; check_base_point = true, kwargs... ) + check_vector(M::SkewHermitianMatrices{n}, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SkewHermitianMatrices`](@ref) `M`, i.e. `X` must be a skew-hermitian matrix of size `(n,n)` and its values have to be from the correct [`AbstractNumbers`](@ref). -The optional parameter `check_base_point` indicates, whether to call - [`check_manifold_point`](@ref) for `p`. The tolerance for the skew-symmetry of `p` and `X` can be set using `kwargs...`. """ -function check_tangent_vector( - M::SkewHermitianMatrices, - p, - X; - check_base_point=true, - kwargs..., -) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end - return check_manifold_point(M, X; kwargs...) # manifold is its own tangent space +function check_vector(M::SkewHermitianMatrices, p, X; kwargs...) + return check_point(M, X; kwargs...) # manifold is its own tangent space end decorated_manifold(M::SkewHermitianMatrices{N,𝔽}) where {N,𝔽} = Euclidean(N, N; field=𝔽) @@ -110,7 +98,7 @@ function get_coordinates!( Y, p, X, - B::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(Y) == (dim,) @@ -128,7 +116,7 @@ function get_coordinates!( Y, p, X, - B::DefaultOrthonormalBasis{β„‚}, + ::DefaultOrthonormalBasis{β„‚,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(Y) == (dim,) @@ -153,7 +141,7 @@ function get_vector!( Y, p, X, - B::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(X) == (dim,) @@ -174,7 +162,7 @@ function get_vector!( Y, p, X, - B::DefaultOrthonormalBasis{β„‚}, + ::DefaultOrthonormalBasis{β„‚,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(X) == (dim,) diff --git a/src/manifolds/Spectrahedron.jl b/src/manifolds/Spectrahedron.jl index 3aaafb89e4..53289fccc8 100644 --- a/src/manifolds/Spectrahedron.jl +++ b/src/manifolds/Spectrahedron.jl @@ -54,7 +54,7 @@ struct Spectrahedron{N,K} <: AbstractEmbeddedManifold{ℝ,DefaultIsometricEmbedd Spectrahedron(n::Int, k::Int) = Spectrahedron{n,k}() @doc raw""" - check_manifold_point(M::Spectrahedron, q; kwargs...) + check_point(M::Spectrahedron, q; kwargs...) checks, whether `q` is a valid reprsentation of a point $p=qq^{\mathrm{T}}$ on the [`Spectrahedron`](@ref) `M`, i.e. is a matrix @@ -64,9 +64,8 @@ Since by construction $p$ is symmetric, this is not explicitly checked. Since $p$ is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ -function check_manifold_point(M::Spectrahedron{N,K}, q; kwargs...) where {N,K} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) +function check_point(M::Spectrahedron{N,K}, q; kwargs...) where {N,K} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) mpv === nothing || return mpv fro_n = norm(q) if !isapprox(fro_n, 1.0; kwargs...) @@ -79,34 +78,22 @@ function check_manifold_point(M::Spectrahedron{N,K}, q; kwargs...) where {N,K} end @doc raw""" - check_tangent_vector(M::Spectrahedron, q, Y; check_base_point = true, kwargs...) + check_vector(M::Spectrahedron, q, Y; kwargs...) Check whether $X = qY^{\mathrm{T}} + Yq^{\mathrm{T}}$ is a tangent vector to $p=qq^{\mathrm{T}}$ on the [`Spectrahedron`](@ref) `M`, -i.e. atfer [`check_manifold_point`](@ref) of `q`, `Y` has to be of same dimension as `q` +i.e. atfer [`check_point`](@ref) of `q`, `Y` has to be of same dimension as `q` and a $X$ has to be a symmetric matrix with trace. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `q`. The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. Note that symmetry of $X$ holds by construction and is not explicitly checked. """ -function check_tangent_vector( - M::Spectrahedron{N,K}, - q, - Y; - check_base_point=true, - kwargs..., -) where {N,K} - if check_base_point - mpe = check_manifold_point(M, q; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Spectrahedron{N,K}, q, Y; kwargs...) where {N,K} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(q),typeof(Y)}, M, q, Y; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -197,11 +184,11 @@ function vector_transport_to!(M::Spectrahedron, Y, p, X, q, ::ProjectionTranspor end @doc raw""" - zero_tangent_vector(M::Spectrahedron,p) + zero_vector(M::Spectrahedron,p) returns the zero tangent vector in the tangent space of the symmetric positive definite matrix `p` on the [`Spectrahedron`](@ref) manifold `M`. """ -zero_tangent_vector(::Spectrahedron, ::Any...) +zero_vector(::Spectrahedron, ::Any...) -zero_tangent_vector!(::Spectrahedron{N,K}, v, ::Any) where {N,K} = fill!(v, 0) +zero_vector!(::Spectrahedron{N,K}, v, ::Any) where {N,K} = fill!(v, 0) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 10a0d76369..844265ec10 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -36,7 +36,7 @@ are equivalent to $π•Š^3$, though with different default representations. This manifold is modeled as a special case of the more general case, i.e. as an embedded manifold to the [`Euclidean`](@ref), and several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product -and the [`zero_tangent_vector`](@ref zero_tangent_vector(::Euclidean, ::Any...)) are inherited from the embedding. +and the [`zero_vector`](@ref zero_vector(::Euclidean, ::Any...)) are inherited from the embedding. # Constructor @@ -76,7 +76,7 @@ embedding $𝔽^{n_1, n_2, …, n_i}$. This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product and the -[`zero_tangent_vector`](@ref zero_tangent_vector(::Euclidean, ::Any...)) are inherited from the embedding. +[`zero_vector`](@ref zero_vector(::Euclidean, ::Any...)) are inherited from the embedding. # Constructor @@ -90,15 +90,15 @@ function ArraySphere(n::Vararg{Int,I}; field::AbstractNumbers=ℝ) where {I} end """ - check_manifold_point(M::AbstractSphere, p; kwargs...) + check_point(M::AbstractSphere, p; kwargs...) Check whether `p` is a valid point on the [`AbstractSphere`](@ref) `M`, i.e. is a point in the embedding of unit length. The tolerance for the last test can be set using the `kwargs...`. """ -function check_manifold_point(M::AbstractSphere, p; kwargs...) +function check_point(M::AbstractSphere, p; kwargs...) mpv = invoke( - check_manifold_point, + check_point, Tuple{(typeof(get_embedding(M))),typeof(p)}, get_embedding(M), p; @@ -115,27 +115,20 @@ function check_manifold_point(M::AbstractSphere, p; kwargs...) end """ - check_tangent_vector(M::AbstractSphere, p, X; check_base_point = true, kwargs... ) + check_vector(M::AbstractSphere, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`AbstractSphere`](@ref) `M`, i.e. -after [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p` +after [`check_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p` and orthogonal to `p`. -The optional parameter `check_base_point` indicates, whether to call -[`check_manifold_point`](@ref) for `p` or not. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector(M::AbstractSphere, p, X; check_base_point=true, kwargs...) - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::AbstractSphere, p, X; kwargs...) mpv = invoke( - check_tangent_vector, + check_vector, Tuple{typeof(get_embedding(M)),typeof(p),typeof(X)}, get_embedding(M), p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -186,8 +179,6 @@ function exp!(M::AbstractSphere, q, p, X) return q end -flat!(M::AbstractSphere, ΞΎ::CoTFVector, p, X::TFVector) = copyto!(ΞΎ, X) - function get_basis(M::Sphere{n,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) where {n} A = zeros(n + 1, n + 1) A[1, :] = transpose(p) @@ -218,7 +209,13 @@ denotes the Frobenius inner product, the formula for $Y$ is """ get_coordinates(::AbstractSphere{ℝ}, p, X, ::DefaultOrthonormalBasis) -function get_coordinates!(M::AbstractSphere{ℝ}, Y, p, X, ::DefaultOrthonormalBasis{ℝ}) +function get_coordinates!( + M::AbstractSphere{ℝ}, + Y, + p, + X, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) n = manifold_dimension(M) p1 = p[1] cosΞΈ = abs(p1) @@ -245,7 +242,13 @@ Y = X - q\frac{2 \left\langle q, \begin{pmatrix}0 \\ X\end{pmatrix}\right\rangle """ get_vector(::AbstractSphere{ℝ}, p, X, ::DefaultOrthonormalBasis) -function get_vector!(M::AbstractSphere{ℝ}, Y, p, X, ::DefaultOrthonormalBasis{ℝ}) +function get_vector!( + M::AbstractSphere{ℝ}, + Y, + p, + X, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, +) n = manifold_dimension(M) p1 = p[1] cosΞΈ = abs(p1) @@ -276,7 +279,7 @@ injectivity_radius(::AbstractSphere, ::Any, ::ExponentialRetraction) = Ο€ injectivity_radius(::AbstractSphere, ::Any, ::ProjectionRetraction) = Ο€ / 2 eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::AbstractSphere, rm::AbstractRetractionMethod, ) @@ -351,7 +354,7 @@ manifold_dimension(M::AbstractSphere) = manifold_dimension(get_embedding(M)) - 1 kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ mean(::AbstractSphere, ::Any...) @@ -461,3 +464,103 @@ function vector_transport_to!(::AbstractSphere, Y, p, X, q, ::ParallelTransport) Y .= X .- m .* factor return Y end + +""" + StereographicAtlas() + +The stereographic atlas of ``S^n`` with two charts: one with the singular +point (-1, 0, ..., 0) (called `:north`) and one with the singular +point (1, 0, ..., 0) (called `:south`). +""" +struct StereographicAtlas <: AbstractAtlas{ℝ} end + +function get_chart_index(::Sphere{n,ℝ}, ::StereographicAtlas, p) where {n} + if p[1] < 0 + return :south + else + return :north + end +end + +function get_point_coordinates!( + ::Sphere{n,ℝ}, + x, + ::StereographicAtlas, + i::Symbol, + p, +) where {n} + if i === :north + return x .= p[2:end] ./ (1 + p[1]) + else + return x .= p[2:end] ./ (1 - p[1]) + end +end + +function get_point!(::Sphere{n,ℝ}, p, ::StereographicAtlas, i::Symbol, x) where {n} + xnorm2 = dot(x, x) + if i === :north + p[1] = (1 - xnorm2) / (xnorm2 + 1) + else + p[1] = (xnorm2 - 1) / (xnorm2 + 1) + end + p[2:end] .= 2 * x / (xnorm2 + 1) + return p +end + +function get_coordinates!( + ::Sphere{n,ℝ}, + Y, + p, + X, + B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, +) where {n} + if B.i === :north + for i in 1:n + Y[i] = X[i + 1] / (1 + p[1]) - X[1] * p[i + 1] / (1 + p[1])^2 + end + else + for i in 1:n + Y[i] = X[i + 1] / (-1 + p[1]) - X[1] * p[i + 1] / (-1 + p[1])^2 + end + end + return Y +end + +function get_vector!( + M::Sphere{n,ℝ}, + Y, + p, + X, + B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, +) where {n} + a = get_point_coordinates(M, B.A, B.i, p) + mult = inv(1 + dot(a, a))^2 + + Y[1] = 0 + for j in 1:n + Y[1] -= 4 * a[j] * mult * X[j] + end + for i in 2:(n + 1) + Y[i] = 0 + for j in 1:n + if i == j + 1 + Y[i] += 2 * (1 + dot(a, a) - 2 * a[i - 1]^2) * mult * X[j] + else + Y[i] -= 4 * a[i - 1] * a[j] * mult * X[j] + end + end + if B.i === :south + Y[i] *= -1 + end + end + return Y +end + +function local_metric( + M::Sphere{n,ℝ}, + p, + B::InducedBasis{ℝ,TangentSpaceType,StereographicAtlas,Symbol}, +) where {n} + a = get_point_coordinates(M, B.A, B.i, p) + return (4 / (1 + dot(a, a))^2) * I +end diff --git a/src/manifolds/SphereSymmetricMatrices.jl b/src/manifolds/SphereSymmetricMatrices.jl index c5accd2c73..96f4d40a85 100644 --- a/src/manifolds/SphereSymmetricMatrices.jl +++ b/src/manifolds/SphereSymmetricMatrices.jl @@ -1,7 +1,7 @@ @doc raw""" SphereSymmetricMatrices{n,𝔽} <: AbstractEmbeddedManifold{ℝ,TransparentIsometricEmbedding} -The [`Manifold`](@ref) consisting of the $n Γ— n$ symmetric matrices +The [`AbstractManifold`](@ref) consisting of the $n Γ— n$ symmetric matrices of unit Frobenius norm, i.e. ````math \mathcal{S}_{\text{sym}} :=\bigl\{p ∈ 𝔽^{n Γ— n}\ \big|\ p^{\mathrm{H}} = p, \lVert p \rVert = 1 \bigr\}, @@ -22,16 +22,15 @@ function SphereSymmetricMatrices(n::Int, field::AbstractNumbers=ℝ) end @doc raw""" - check_manifold_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) + check_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) Check whether the matrix is a valid point on the [`SphereSymmetricMatrices`](@ref) `M`, i.e. is an `n`-by-`n` symmetric matrix of unit Frobenius norm. The tolerance for the symmetry of `p` can be set using `kwargs...`. """ -function check_manifold_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv if !isapprox(norm(p - p'), 0.0; kwargs...) return DomainError( @@ -43,34 +42,21 @@ function check_manifold_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) end """ - check_tangent_vector(M::SphereSymmetricMatrices{n,𝔽}, p, X; check_base_point = true, kwargs... ) + check_vector(M::SphereSymmetricMatrices{n,𝔽}, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SphereSymmetricMatrices`](@ref) `M`, i.e. `X` has to be a symmetric matrix of size `(n,n)` of unit Frobenius norm. -The optional parameter `check_base_point` indicates, whether to call - [`check_manifold_point`](@ref) for `p`. The tolerance for the symmetry of `p` and `X` can be set using `kwargs...`. """ -function check_tangent_vector( - M::SphereSymmetricMatrices{n,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::SphereSymmetricMatrices{n,𝔽}, p, X; kwargs...) where {n,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index a59e65ff8f..a2afeaa2b7 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -21,7 +21,7 @@ where $0_k$ is the $k Γ— k$ zero matrix and $\overline{\cdot}$ the (elementwise) This manifold is modeled as an embedded manifold to the [`Euclidean`](@ref), i.e. several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product and the -[`zero_tangent_vector`](@ref zero_tangent_vector(::Euclidean, ::Any...)) are inherited from the embedding. +[`zero_vector`](@ref zero_vector(::Euclidean, ::Any...)) are inherited from the embedding. The manifold is named after [Eduard L. Stiefel](https://en.wikipedia.org/wiki/Eduard_Stiefel) (1909–1978). @@ -61,15 +61,14 @@ function allocation_promotion_function(::Stiefel{n,k,β„‚}, ::Any, ::Tuple) where end @doc raw""" - check_manifold_point(M::Stiefel, p; kwargs...) + check_point(M::Stiefel, p; kwargs...) Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. that it has the right [`AbstractNumbers`](@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,𝔽}, p; kwargs...) where {n,k,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::Stiefel{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv c = p' * p if !isapprox(c, one(c); kwargs...) @@ -82,33 +81,21 @@ function check_manifold_point(M::Stiefel{n,k,𝔽}, p; kwargs...) where {n,k, end @doc raw""" - check_tangent_vector(M::Stiefel, p, X; check_base_point = true, kwargs...) + check_vector(M::Stiefel, p, X; kwargs...) Checks whether `X` is a valid tangent vector at `p` on the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. the [`AbstractNumbers`](@ref) fits and it (approximately) holds that $p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0$, where $\cdot^{\mathrm{H}}$ denotes the Hermitian and $\overline{\cdot}$ the (elementwise) complex conjugate. -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. The settings for approximately can be set with `kwargs...`. """ -function check_tangent_vector( - M::Stiefel{n,k,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,k,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::Stiefel{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -157,7 +144,7 @@ in [^KanekoFioriTanaka2013]. [^KanekoFioriTanaka2013]: > T. Kaneko, S. Fiori, T. Tanaka: "Empirical Arithmetic Averaging over the - > Compact Stiefel Manifold", IEEE Transactions on Signal Processing, 2013, + > Compact Stiefel AbstractManifold", 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, ::QRInverseRetraction) @@ -260,7 +247,7 @@ function inverse_retract!(M::Stiefel{n,k}, X, p, q, ::QRInverseRetraction) where end function Base.isapprox(M::Stiefel, p, X, Y; kwargs...) - return isapprox(sqrt(inner(M, p, zero_tangent_vector(M, p), X - Y)), 0; kwargs...) + return isapprox(sqrt(inner(M, p, zero_vector(M, p), X - Y)), 0; kwargs...) end Base.isapprox(::Stiefel, p, q; kwargs...) = isapprox(norm(p - q), 0; kwargs...) diff --git a/src/manifolds/StiefelCanonicalMetric.jl b/src/manifolds/StiefelCanonicalMetric.jl index ac986edbba..fe981c83b0 100644 --- a/src/manifolds/StiefelCanonicalMetric.jl +++ b/src/manifolds/StiefelCanonicalMetric.jl @@ -1,5 +1,5 @@ @doc raw""" - CanonicalMetric <: Metric + CanonicalMetric <: AbstractMetric The Canonical Metric refers to a metric for the [`Stiefel`](@ref) manifold, see[^EdelmanAriasSmith1998]. diff --git a/src/manifolds/StiefelEuclideanMetric.jl b/src/manifolds/StiefelEuclideanMetric.jl index 2065da231d..9b604fc36a 100644 --- a/src/manifolds/StiefelEuclideanMetric.jl +++ b/src/manifolds/StiefelEuclideanMetric.jl @@ -82,7 +82,7 @@ end function get_vector!(M::Stiefel{n,k,ℝ}, X, p, c, B::DefaultOrthonormalBasis{ℝ}) where {n,k} V = get_vectors(M, p, B) - zero_tangent_vector!(M, X, p) + zero_vector!(M, X, p) length(c) < length(V) && error( "Coordinate vector too short. Excpected $(length(V)), but only got $(length(c)) entries.", ) diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index c9170f801d..1829c8570a 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -1,7 +1,7 @@ @doc raw""" SymmetricMatrices{n,𝔽} <: AbstractEmbeddedManifold{𝔽,TransparentIsometricEmbedding} -The [`Manifold`](@ref) $ \operatorname{Sym}(n)$ consisting of the real- or complex-valued +The [`AbstractManifold`](@ref) $ \operatorname{Sym}(n)$ consisting of the real- or complex-valued symmetric matrices of size $n Γ— n$, i.e. the set ````math @@ -36,7 +36,7 @@ function allocation_promotion_function( end @doc raw""" - check_manifold_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) + check_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) 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 @@ -44,9 +44,8 @@ whether `p` is a symmetric matrix of size `(n,n)` with values from the correspon The tolerance for the symmetry of `p` can be set using `kwargs...`. """ -function check_manifold_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv if !isapprox(norm(p - p'), 0.0; kwargs...) return DomainError( @@ -58,33 +57,21 @@ function check_manifold_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) where end """ - check_tangent_vector(M::SymmetricMatrices{n,𝔽}, p, X; check_base_point = true, kwargs... ) + check_vector(M::SymmetricMatrices{n,𝔽}, 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 size `(n,n)` and its values have to be from the correct [`AbstractNumbers`](@ref). -The optional parameter `check_base_point` indicates, whether to call - [`check_manifold_point`](@ref) for `p`. -The tolerance for the symmetry of `p` and `X` can be set using `kwargs...`. + +The tolerance for the symmetry of `X` can be set using `kwargs...`. """ -function check_tangent_vector( - M::SymmetricMatrices{n,𝔽}, - p, - X; - check_base_point=true, - kwargs..., -) where {n,𝔽} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::SymmetricMatrices{n,𝔽}, p, X; kwargs...) where {n,𝔽} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -110,7 +97,7 @@ function get_coordinates!( Y, p, X, - B::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(Y) == (dim,) @@ -129,7 +116,7 @@ function get_coordinates!( Y, p, X, - B::DefaultOrthonormalBasis{β„‚}, + ::DefaultOrthonormalBasis{β„‚,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(Y) == (dim,) @@ -153,7 +140,7 @@ function get_vector!( Y, p, X, - B::DefaultOrthonormalBasis{ℝ}, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(X) == (dim,) @@ -172,7 +159,7 @@ function get_vector!( Y, p, X, - B::DefaultOrthonormalBasis{β„‚}, + ::DefaultOrthonormalBasis{β„‚,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(X) == (dim,) diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index a9daffe529..62d250fac6 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -21,15 +21,14 @@ struct SymmetricPositiveDefinite{N} <: AbstractEmbeddedManifold{ℝ,DefaultEmbed SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}() @doc raw""" - check_manifold_point(M::SymmetricPositiveDefinite, p; kwargs...) + check_point(M::SymmetricPositiveDefinite, p; kwargs...) 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}, p; kwargs...) where {N} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) +function check_point(M::SymmetricPositiveDefinite{N}, p; kwargs...) where {N} + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...) mpv === nothing || return mpv if !isapprox(norm(p - transpose(p)), 0.0; kwargs...) return DomainError( @@ -47,33 +46,21 @@ function check_manifold_point(M::SymmetricPositiveDefinite{N}, p; kwargs...) whe end """ - check_tangent_vector(M::SymmetricPositiveDefinite, p, X; check_base_point = true, kwargs... ) + check_vector(M::SymmetricPositiveDefinite, p, X; kwargs... ) 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` +i.e. atfer [`check_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 optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_tangent_vector( - M::SymmetricPositiveDefinite{N}, - p, - X; - check_base_point=true, - kwargs..., -) where {N} - if check_base_point - mpe = check_manifold_point(M, p; kwargs...) - mpe === nothing || return mpe - end +function check_vector(M::SymmetricPositiveDefinite{N}, p, X; kwargs...) where {N} mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(p),typeof(X)}, M, p, X; - check_base_point=false, # already checked above kwargs..., ) mpv === nothing || return mpv @@ -105,7 +92,7 @@ injectivity_radius(::SymmetricPositiveDefinite, ::Any) = Inf injectivity_radius(::SymmetricPositiveDefinite, ::Any, ::ExponentialRetraction) = Inf eval( quote - @invoke_maker 1 Manifold injectivity_radius( + @invoke_maker 1 AbstractManifold injectivity_radius( M::SymmetricPositiveDefinite, rm::AbstractRetractionMethod, ) @@ -134,7 +121,7 @@ end kwargs..., ) -Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using +Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolation`](@ref). """ mean(::SymmetricPositiveDefinite, ::Any) @@ -163,11 +150,11 @@ function Base.show(io::IO, ::SymmetricPositiveDefinite{N}) where {N} end @doc raw""" - zero_tangent_vector(M::SymmetricPositiveDefinite,x) + zero_vector(M::SymmetricPositiveDefinite,x) returns the zero tangent vector in the tangent space of the symmetric positive definite matrix `x` on the [`SymmetricPositiveDefinite`](@ref) manifold `M`. """ -zero_tangent_vector(::SymmetricPositiveDefinite, ::Any) +zero_vector(::SymmetricPositiveDefinite, ::Any) -zero_tangent_vector!(::SymmetricPositiveDefinite, v, ::Any) = fill!(v, 0) +zero_vector!(::SymmetricPositiveDefinite{N}, v, ::Any) where {N} = fill!(v, 0) diff --git a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl index f71a01550e..6765b0788b 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLinearAffine.jl @@ -1,5 +1,5 @@ @doc raw""" - LinearAffineMetric <: Metric + LinearAffineMetric <: AbstractMetric The linear affine metric is the metric for symmetric positive definite matrices, that employs matrix logarithms and exponentials, which yields a linear and affine metric. @@ -95,7 +95,7 @@ function get_coordinates!( Y, p, X, - ::DefaultOrthonormalBasis, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(Y) == (dim,) @@ -115,7 +115,7 @@ function get_vector!( Y, p, X, - ::DefaultOrthonormalBasis, + ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) where {N} dim = manifold_dimension(M) @assert size(X) == (div(N * (N + 1), 2),) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 11143b8422..c8d9cb7451 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -1,5 +1,5 @@ @doc raw""" - LogCholeskyMetric <: Metric + LogCholeskyMetric <: RiemannianMetric The Log-Cholesky metric imposes a metric based on the Cholesky decomposition as introduced by [^Lin2019]. diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index 5e4a8d160b..a0a1f7bc9a 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -1,5 +1,5 @@ @doc raw""" - LogEuclideanMetric <: Metric + LogEuclideanMetric <: RiemannianMetric The LogEuclidean Metric consists of the Euclidean metric applied to all elements after mapping them into the Lie Algebra, i.e. performing a matrix logarithm beforehand. diff --git a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl index 1d8bf5f5a7..08c8b14860 100644 --- a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl +++ b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl @@ -1,7 +1,7 @@ @doc raw""" SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽} <: AbstractEmbeddedManifold{𝔽,DefaultIsometricEmbeddingType} -The [`Manifold`](@ref) $ \operatorname{SPS}_k(n)$ consisting of the real- or complex-valued +The [`AbstractManifold`](@ref) $ \operatorname{SPS}_k(n)$ consisting of the real- or complex-valued symmetric positive semidefinite matrices of size $n Γ— n$ and rank $k$, i.e. the set ````math @@ -48,7 +48,7 @@ over the `field` of real numbers `ℝ` or complex numbers `β„‚`. > arXiv: [0807.4423](http://arxiv.org/abs/0807.4423). [^MassartAbsil2020]: > Massart, E., Absil, P.-A.: - > "Quotient Geometry with Simple Geodesics for the Manifold of Fixed-Rank Positive-Semidefinite Matrices", + > "Quotient Geometry with Simple Geodesics for the AbstractManifold of Fixed-Rank Positive-Semidefinite Matrices", > SIAM Journal on Matrix Analysis and Applications (41)1, pp. 171–198, 2020. > doi: [10.1137/18m1231389](https://doi.org/10.1137/18m1231389), > preprint: [sites.uclouvain.be/absil/2018.06](https://sites.uclouvain.be/absil/2018.06). @@ -61,7 +61,7 @@ function SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractN end @doc raw""" - check_manifold_point(M::SymmetricPositiveSemidefiniteFixedRank{n,𝔽}, q; kwargs...) + check_point(M::SymmetricPositiveSemidefiniteFixedRank{n,𝔽}, q; kwargs...) Check whether `q` is a valid manifold point on the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) `M`, i.e. whether `p=q*q'` is a symmetric matrix of size `(n,n)` with values from the corresponding @@ -69,13 +69,12 @@ whether `p=q*q'` is a symmetric matrix of size `(n,n)` with values from the corr The symmetry of `p` is not explicitly checked since by using `q` p is symmetric by construction. The tolerance for the symmetry of `p` can and the rank of `q*q'` be set using `kwargs...`. """ -function check_manifold_point( +function check_point( M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, q; kwargs..., ) where {n,k,𝔽} - mpv = - invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) + mpv = invoke(check_point, Tuple{supertype(typeof(M)),typeof(q)}, M, q; kwargs...) mpv === nothing || return mpv p = q * q' r = rank(p * p'; kwargs...) @@ -89,33 +88,25 @@ function check_manifold_point( end """ - check_tangent_vector(M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, p, X; check_base_point = true, kwargs... ) + check_vector(M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SymmetricPositiveSemidefiniteFixedRank`](@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 optional parameter `check_base_point` indicates, whether to call - [`check_manifold_point`](@ref) for `p`. -The tolerance for the symmetry of `p` and `X` can be set using `kwargs...`. +The tolerance for the symmetry of `X` can be set using `kwargs...`. """ -function check_tangent_vector( +function check_vector( M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, q, Y; - check_base_point=true, kwargs..., ) where {n,k,𝔽} - if check_base_point - mpe = check_manifold_point(M, q; kwargs...) - mpe === nothing || return mpe - end mpv = invoke( - check_tangent_vector, + check_vector, Tuple{supertype(typeof(M)),typeof(q),typeof(Y)}, M, q, Y; - check_base_point=false, # already checked above kwargs..., ) return mpv @@ -263,13 +254,13 @@ function vector_transport_to!( end @doc raw""" - zero_tangent_vector(M::SymmetricPositiveSemidefiniteFixedRank, p) + zero_vector(M::SymmetricPositiveSemidefiniteFixedRank, p) returns the zero tangent vector in the tangent space of the symmetric positive definite matrix `p` on the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) manifold `M`. """ -zero_tangent_vector(::SymmetricPositiveSemidefiniteFixedRank, ::Any...) +zero_vector(::SymmetricPositiveSemidefiniteFixedRank, ::Any...) -function zero_tangent_vector!(::SymmetricPositiveSemidefiniteFixedRank, v, ::Any) +function zero_vector!(::SymmetricPositiveSemidefiniteFixedRank, v, ::Any) return fill!(v, 0) end diff --git a/src/manifolds/Torus.jl b/src/manifolds/Torus.jl index d1babdfe7a..a766513326 100644 --- a/src/manifolds/Torus.jl +++ b/src/manifolds/Torus.jl @@ -15,52 +15,39 @@ Torus(n::Int) = Torus{n}(Circle()) Base.:^(M::Circle, n::Int) = Torus{n}(M) @doc raw""" - check_manifold_point(M::Torus{n},p) + check_point(M::Torus{n},p) 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}, p; kwargs...) where {N} +check_point(::Torus, ::Any) +function check_point(M::Torus{N}, p; kwargs...) where {N} if length(p) != N return DomainError( 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), p; kwargs...) + return check_point(PowerManifold(M.manifold, N), p; kwargs...) end @doc raw""" - check_tangent_vector(M::Torus{n}, p, X; check_base_point = true, kwargs...) + check_vector(M::Torus{n}, p, X; kwargs...) 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). -The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`. """ -function check_tangent_vector(M::Torus{N}, p, X; check_base_point=true, kwargs...) where {N} - if check_base_point && length(p) != N - return DomainError( - length(p), - "The number of elements in `p` ($(length(p))) does not match the dimension of the torus ($(N)).", - ) - end +function check_vector(M::Torus{N}, p, X; kwargs...) where {N} if length(X) != N return DomainError( length(X), "The number of elements in `X` ($(length(X))) does not match the dimension of the torus ($(N)).", ) end - return check_tangent_vector( - PowerManifold(M.manifold, N), - p, - X; - check_base_point=check_base_point, - kwargs..., - ) + return check_vector(PowerManifold(M.manifold, N), p, X; kwargs...) end -get_iterator(M::Torus{N}) where {N} = 1:N +get_iterator(::Torus{N}) where {N} = 1:N @generated manifold_dimension(::Torus{N}) where {N} = N diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index b66e4c5998..75f04c2d00 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -1,38 +1,3 @@ -""" - VectorSpaceType - -Abstract type for tangent spaces, cotangent spaces, their tensor products, -exterior products, etc. - -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!(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(fiber)}) where fiber`](@ref). - -Optionally: -* inner product via `inner` (used to provide Riemannian metric on vector - bundles), -* [`flat`](@ref) and [`sharp`](@ref), -* `norm` (by default uses `inner`), -* [`project`](@ref) (for embedded vector spaces), -* [`representation_size`](@ref) (if support for [`ProductArray`](@ref) is desired), -* broadcasting for basic operations. -""" -abstract type VectorSpaceType end - -struct TangentSpaceType <: VectorSpaceType end - -struct CotangentSpaceType <: VectorSpaceType end - -TCoTSpaceType = Union{TangentSpaceType,CotangentSpaceType} - -const TangentSpace = TangentSpaceType() -const CotangentSpace = CotangentSpaceType() """ TensorProductType(spaces::VectorSpaceType...) @@ -47,7 +12,7 @@ end TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) """ - VectorBundleFibers(fiber::VectorSpaceType, M::Manifold) + VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) Type representing a family of vector spaces (fibers) of a vector bundle over `M` with vector spaces of type `fiber`. In contrast with `VectorBundle`, operations @@ -57,26 +22,27 @@ 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} +struct VectorBundleFibers{TVS<:VectorSpaceType,TM<:AbstractManifold} fiber::TVS manifold::TM end -const TangentBundleFibers{M} = VectorBundleFibers{TangentSpaceType,M} where {M<:Manifold} +const TangentBundleFibers{M} = + VectorBundleFibers{TangentSpaceType,M} where {M<:AbstractManifold} -TangentBundleFibers(M::Manifold) = VectorBundleFibers(TangentSpace, M) +TangentBundleFibers(M::AbstractManifold) = VectorBundleFibers(TangentSpace, M) const CotangentBundleFibers{M} = - VectorBundleFibers{CotangentSpaceType,M} where {M<:Manifold} + VectorBundleFibers{CotangentSpaceType,M} where {M<:AbstractManifold} -CotangentBundleFibers(M::Manifold) = VectorBundleFibers(CotangentSpace, M) +CotangentBundleFibers(M::AbstractManifold) = VectorBundleFibers(CotangentSpace, M) """ VectorSpaceAtPoint{ 𝔽, - TFiber<:VectorBundleFibers{<:VectorSpaceType,<:Manifold{𝔽}}, + TFiber<:VectorBundleFibers{<:VectorSpaceType,<:AbstractManifold{𝔽}}, TX, - } <: Manifold{𝔽} + } <: AbstractManifold{𝔽} A vector space at a point `p` on the manifold. This is modelled using [`VectorBundleFibers`](@ref) with only a vector-like part @@ -93,41 +59,43 @@ the manifold `fiber.manifold`. """ struct VectorSpaceAtPoint{ 𝔽, - TFiber<:VectorBundleFibers{<:VectorSpaceType,<:Manifold{𝔽}}, + TFiber<:VectorBundleFibers{<:VectorSpaceType,<:AbstractManifold{𝔽}}, TX, -} <: Manifold{𝔽} +} <: AbstractManifold{𝔽} fiber::TFiber point::TX end const TangentSpaceAtPoint{M} = - VectorSpaceAtPoint{𝔽,TangentBundleFibers{M}} where {𝔽,M<:Manifold{𝔽}} + VectorSpaceAtPoint{𝔽,TangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} """ - TangentSpaceAtPoint(M::Manifold, p) + TangentSpaceAtPoint(M::AbstractManifold, p) Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent -space at `p` on the [`Manifold`](@ref) `M`. +space at `p` on the [`AbstractManifold`](@ref) `M`. """ -TangentSpaceAtPoint(M::Manifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) +TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) """ - TangentSpace(M::Manifold, p) + TangentSpace(M::AbstractManifold, p) -Return a [`TangentSpaceAtPoint`](@ref) representing tangent space at `p` on the [`Manifold`](@ref) `M`. +Return a [`TangentSpaceAtPoint`](@ref) representing tangent space at `p` on the [`AbstractManifold`](@ref) `M`. """ -TangentSpace(M::Manifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) +TangentSpace(M::AbstractManifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) const CotangentSpaceAtPoint{M} = - VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:Manifold{𝔽}} + VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} """ - CotangentSpaceAtPoint(M::Manifold, p) + CotangentSpaceAtPoint(M::AbstractManifold, p) Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent space at `p`. """ -CotangentSpaceAtPoint(M::Manifold, p) = VectorSpaceAtPoint(CotangentBundleFibers(M), p) +function CotangentSpaceAtPoint(M::AbstractManifold, p) + return VectorSpaceAtPoint(CotangentBundleFibers(M), p) +end """ VectorBundleVectorTransport( @@ -147,20 +115,20 @@ struct VectorBundleVectorTransport{ end """ - VectorBundle{𝔽,TVS<:VectorSpaceType,TM<:Manifold{𝔽}} <: Manifold{𝔽} + VectorBundle{𝔽,TVS<:VectorSpaceType,TM<:AbstractManifold{𝔽}} <: AbstractManifold{𝔽} -Vector bundle on a [`Manifold`](@ref) `M` of type [`VectorSpaceType`](@ref). +Vector bundle on a [`AbstractManifold`](@ref) `M` of type [`VectorSpaceType`](@ref). # Constructor - VectorBundle(M::Manifold, type::VectorSpaceType) + VectorBundle(M::AbstractManifold, type::VectorSpaceType) """ struct VectorBundle{ 𝔽, TVS<:VectorSpaceType, - TM<:Manifold{𝔽}, + TM<:AbstractManifold{𝔽}, TVT<:VectorBundleVectorTransport, -} <: Manifold{𝔽} +} <: AbstractManifold{𝔽} type::TVS manifold::TM fiber::VectorBundleFibers{TVS,TM} @@ -171,60 +139,36 @@ function VectorBundle( fiber::TVS, M::TM, vtm::VectorBundleVectorTransport, -) where {TVS<:VectorSpaceType,TM<:Manifold{𝔽}} where {𝔽} +) where {TVS<:VectorSpaceType,TM<:AbstractManifold{𝔽}} where {𝔽} return VectorBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, VectorBundleFibers(fiber, M), vtm) end -function VectorBundle(fiber::VectorSpaceType, M::Manifold) +function VectorBundle(fiber::VectorSpaceType, M::AbstractManifold) vtmm = vector_bundle_transport(fiber, M) vtbm = VectorBundleVectorTransport(vtmm, vtmm) return VectorBundle(fiber, M, vtbm) end -const TangentBundle{𝔽,M} = VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:Manifold{𝔽}} +const TangentBundle{𝔽,M} = + VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} -TangentBundle(M::Manifold) = VectorBundle(TangentSpace, M) -function TangentBundle(M::Manifold, vtm::VectorBundleVectorTransport) +TangentBundle(M::AbstractManifold) = VectorBundle(TangentSpace, M) +function TangentBundle(M::AbstractManifold, vtm::VectorBundleVectorTransport) return VectorBundle(TangentSpace, M, vtm) end -const CotangentBundle{𝔽,M} = VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:Manifold{𝔽}} +const CotangentBundle{𝔽,M} = + VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} -CotangentBundle(M::Manifold) = VectorBundle(CotangentSpace, M) -function CotangentBundle(M::Manifold, vtm::VectorBundleVectorTransport) +CotangentBundle(M::AbstractManifold) = VectorBundle(CotangentSpace, M) +function CotangentBundle(M::AbstractManifold, vtm::VectorBundleVectorTransport) return VectorBundle(CotangentSpace, M, vtm) end -""" - FVector(type::VectorSpaceType, data) - -Decorator indicating that the vector `data` is from a fiber of a vector bundle -of type `type`. -""" -struct FVector{TType<:VectorSpaceType,TData} - type::TType - data::TData -end - -const TFVector = FVector{TangentSpaceType} -const CoTFVector = FVector{CotangentSpaceType} - struct VectorBundleBasisData{BBasis<:CachedBasis,TBasis<:CachedBasis} base_basis::BBasis vec_basis::TBasis end -Base.:+(X::FVector, Y::FVector) = FVector(X.type, X.data + Y.data) - -Base.:-(X::FVector, Y::FVector) = FVector(X.type, X.data - Y.data) -Base.:-(X::FVector) = FVector(X.type, -X.data) - -Base.:*(a::Number, X::FVector) = FVector(X.type, a * X.data) - -function Base.copyto!(X::FVector, Y::FVector) - copyto!(X.data, Y.data) - return X -end - base_manifold(B::VectorBundleFibers) = base_manifold(B.manifold) base_manifold(B::VectorSpaceAtPoint) = base_manifold(B.fiber) base_manifold(B::VectorBundle) = base_manifold(B.manifold) @@ -285,11 +229,6 @@ function distance(M::TangentSpaceAtPoint, p, q) return norm(M.fiber.manifold, M.point, q - p) end -function number_eltype(::Type{FVector{TType,TData}}) where {TType<:VectorSpaceType,TData} - return number_eltype(TData) -end -number_eltype(v::FVector) = number_eltype(v.data) - function embed!(M::TangentSpaceAtPoint, q, p) return embed!(M.fiber.manifold, q, M.point, p) end @@ -342,36 +281,6 @@ function exp!(M::TangentSpaceAtPoint, q, p, X) return q end -@doc raw""" - flat(M::Manifold, p, X::FVector) - -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 -$β™­ : T\mathcal M β†’ T^{*}\mathcal M$ -""" -function flat(M::Manifold, p, X::FVector) - ΞΎ = allocate_result(M, flat, X, p) - return flat!(M, ΞΎ, p, X) -end - -function flat!(M::Manifold, ΞΎ::FVector, p, X::FVector) - return error( - "flat! not implemented for vector bundle fibers space " * - "of type $(typeof(M)), vector of type $(typeof(ΞΎ)), point of " * - "type $(typeof(p)) and vector of type $(typeof(X)).", - ) -end - -@decorator_transparent_signature flat!( - M::AbstractDecoratorManifold, - ΞΎ::CoTFVector, - p, - X::TFVector, -) - function get_basis(M::VectorBundle, p, B::AbstractBasis) xp1 = submanifold_component(p, Val(1)) base_basis = get_basis(M.manifold, xp1, B) @@ -379,10 +288,16 @@ function get_basis(M::VectorBundle, p, B::AbstractBasis) return CachedBasis(B, VectorBundleBasisData(base_basis, vec_basis)) end function get_basis(M::VectorBundle, p, B::CachedBasis) - return invoke(get_basis, Tuple{Manifold,Any,CachedBasis}, M, p, B) + return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) end function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) - return invoke(get_basis, Tuple{Manifold,Any,CachedBasis}, M.fiber.manifold, M.point, B) + return invoke( + get_basis, + Tuple{AbstractManifold,Any,CachedBasis}, + M.fiber.manifold, + M.point, + B, + ) end function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) @@ -396,20 +311,27 @@ end for BT in [ DefaultOrthonormalBasis, + DefaultOrthonormalBasis{<:Any,TangentSpaceType}, ProjectedOrthonormalBasis{:gram_schmidt,ℝ}, ProjectedOrthonormalBasis{:svd,ℝ}, ] eval(quote @invoke_maker 3 AbstractBasis get_basis(M::VectorBundle, p, B::$BT) end) - eval(quote - @invoke_maker 3 AbstractBasis get_basis(M::TangentSpaceAtPoint, p, B::$BT) - end) + eval( + quote + @invoke_maker 3 AbstractBasis{<:Any,TangentSpaceType} get_basis( + M::TangentSpaceAtPoint, + p, + B::$BT, + ) + end, + ) end -function get_basis(M::TangentBundleFibers, p, B::AbstractBasis) +function get_basis(M::TangentBundleFibers, p, B::AbstractBasis{<:Any,TangentSpaceType}) return get_basis(M.manifold, p, B) end -function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis) +function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) return get_basis(M.fiber.manifold, M.point, B) end @@ -456,7 +378,7 @@ for BT in [ ) eval( quote - @invoke_maker 5 AbstractBasis get_coordinates!( + @invoke_maker 5 AbstractBasis{<:Any,TangentSpaceType} get_coordinates!( M::TangentSpaceAtPoint, Y, p, @@ -480,7 +402,7 @@ function get_coordinates!( Y, p, X, - B::ManifoldsBase.all_uncached_bases, + B::ManifoldsBase.all_uncached_bases{TangentSpaceType}, ) return get_coordinates!(M.manifold, Y, p, X, B) end @@ -489,7 +411,7 @@ function get_coordinates!( Y, p, X, - B::ManifoldsBase.all_uncached_bases, + B::ManifoldsBase.all_uncached_bases{TangentSpaceType}, ) return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) end @@ -526,10 +448,22 @@ function get_vector!( ) return Y end -function get_vector!(M::TangentBundleFibers, Y, p, X, B::ManifoldsBase.all_uncached_bases) +function get_vector!( + M::TangentBundleFibers, + Y, + p, + X, + B::ManifoldsBase.all_uncached_bases{TangentSpaceType}, +) return get_vector!(M.manifold, Y, p, X, B) end -function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::ManifoldsBase.all_uncached_bases) +function get_vector!( + M::TangentSpaceAtPoint, + Y, + p, + X, + B::ManifoldsBase.all_uncached_bases{TangentSpaceType}, +) return get_vector!(M.fiber.manifold, Y, M.point, X, B) end for BT in [ @@ -542,7 +476,7 @@ for BT in [ ] eval( quote - @invoke_maker 5 AbstractBasis get_vector!( + @invoke_maker 5 AbstractBasis{<:Any,TangentSpaceType} get_vector!( M::TangentSpaceAtPoint, Y, p, @@ -562,7 +496,7 @@ function get_vectors( B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, ) where {𝔽} xp1 = submanifold_component(p, Val(1)) - zero_m = zero_tangent_vector(M.manifold, xp1) + zero_m = zero_vector(M.manifold, xp1) zero_f = zero_vector(M.fiber, xp1) vs = typeof(ProductRepr(zero_m, zero_f))[] for bv in get_vectors(M.manifold, xp1, B.data.base_basis) @@ -581,8 +515,6 @@ function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) return get_vectors(M.fiber.manifold, M.point, B) end -Base.@propagate_inbounds Base.getindex(x::FVector, i) = getindex(x.data, i) - """ getindex(p::ProductRepr, M::VectorBundle, s::Symbol) p[M::VectorBundle, s] @@ -618,12 +550,7 @@ function inner(B::VectorBundleFibers, p, X, Y) end inner(B::VectorBundleFibers{<:TangentSpaceType}, p, X, Y) = inner(B.manifold, p, X, Y) function inner(B::VectorBundleFibers{<:CotangentSpaceType}, p, X, Y) - return inner( - B.manifold, - p, - sharp(B.manifold, p, FVector(CotangentSpace, X)).data, - sharp(B.manifold, p, FVector(CotangentSpace, Y)).data, - ) + return inner(B.manifold, p, sharp(B.manifold, p, X), sharp(B.manifold, p, Y)) end @doc raw""" inner(B::VectorBundle, p, X, Y) @@ -833,8 +760,6 @@ function project!(B::VectorBundleFibers, Y, p, X) ) end -Base.@propagate_inbounds Base.setindex!(x::FVector, val, i) = setindex!(x.data, val, i) - """ setindex!(p::ProductRepr, val, M::VectorBundle, s::Symbol) p[M::VectorBundle, s] = val @@ -868,38 +793,6 @@ function representation_size(B::TangentSpaceAtPoint) return representation_size(B.fiber.manifold) end -@doc raw""" - sharp(M::Manifold, p, ΞΎ::FVector) - -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 -$β™― : T^{*}\mathcal M β†’ T\mathcal M$ -""" -function sharp(M::Manifold, p, ΞΎ::FVector) - X = allocate_result(M, sharp, ΞΎ, p) - return sharp!(M, X, p, ΞΎ) -end - -function sharp!(M::Manifold, X::FVector, p, ΞΎ::FVector) - return error( - "sharp! not implemented for vector bundle fibers space " * - "of type $(typeof(M)), vector of type $(typeof(X)), point of " * - "type $(typeof(p)) and vector of type $(typeof(ΞΎ)).", - ) -end - -@decorator_transparent_signature sharp!( - M::AbstractDecoratorManifold, - X::TFVector, - p, - ΞΎ::CoTFVector, -) - -Base.show(io::IO, ::TangentSpaceType) = print(io, "TangentSpace") -Base.show(io::IO, ::CotangentSpaceType) = print(io, "CotangentSpace") function Base.show(io::IO, tpt::TensorProductType) return print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") end @@ -929,9 +822,6 @@ Base.show(io::IO, vb::VectorBundle) = print(io, "VectorBundle($(vb.type), $(vb.m Base.show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") Base.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)) - """ allocate_result(B::VectorBundleFibers, f, x...) @@ -944,12 +834,6 @@ function allocate_result(B::VectorBundleFibers, f, x...) T = allocate_result_type(B, f, x) return allocate(x[1], T) end -function allocate_result(M::Manifold, ::typeof(flat), w::TFVector, x) - return FVector(CotangentSpace, allocate(w.data)) -end -function allocate_result(M::Manifold, ::typeof(sharp), w::CoTFVector, x) - return FVector(TangentSpace, allocate(w.data)) -end """ allocate_result_type(B::VectorBundleFibers, f, args::NTuple{N,Any}) where N @@ -962,42 +846,23 @@ function allocate_result_type(::VectorBundleFibers, f, args::NTuple{N,Any}) wher return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) end -Base.size(x::FVector) = size(x.data) - -function submanifold_component(M::Manifold, x::FVector, i::Val) - return submanifold_component(M, x.data, i) -end -submanifold_component(x::FVector, i::Val) = submanifold_component(x.data, i) - -submanifold_components(M::Manifold, x::FVector) = submanifold_components(M, x.data) -submanifold_components(x::FVector) = submanifold_components(x.data) - """ - vector_bundle_transport(fiber::VectorSpaceType, M::Manifold) + vector_bundle_transport(fiber::VectorSpaceType, M::AbstractManifold) Determine the vector tranport used for [`exp`](@ref exp(::VectorBundle, ::Any...)) and [`log`](@ref log(::VectorBundle, ::Any...)) maps on a vector bundle with vector space type `fiber` and manifold `M`. """ -vector_bundle_transport(::VectorSpaceType, ::Manifold) = ParallelTransport() +vector_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() -""" - vector_space_dimension(B::VectorBundleFibers) - -Dimension of the vector space of type `B`. -""" function vector_space_dimension(B::VectorBundleFibers) - return error( - "vector_space_dimension not implemented for vector space family $(typeof(B)).", - ) -end -function vector_space_dimension(B::VectorBundleFibers{<:TCoTSpaceType}) - return manifold_dimension(B.manifold) + return vector_space_dimension(B.manifold, B.fiber) end -function vector_space_dimension(B::VectorBundleFibers{<:TensorProductType}) + +function vector_space_dimension(M::AbstractManifold, V::TensorProductType) dim = 1 - for space in B.fiber.spaces - dim *= vector_space_dimension(VectorBundleFibers(space, B.manifold)) + for space in V.spaces + dim *= vector_space_dimension(M, space) end return dim end @@ -1043,14 +908,20 @@ function vector_transport_to!( ) return vector_transport_to!(M, Y, p, X, q, VectorBundleVectorTransport(m, m)) end -@invoke_maker 6 AbstractVectorTransportMethod vector_transport_to!( - M::TangentBundle, - Y, - p, - X, - q, - m::PoleLadderTransport, -) +for VT in ManifoldsBase.VECTOR_TRANSPORT_DISAMBIGUATION + eval( + quote + @invoke_maker 6 AbstractVectorTransportMethod vector_transport_to!( + M::TangentBundle, + Y, + p, + X, + q, + B::$VT, + ) + end, + ) +end function vector_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) return copyto!(Y, X) end @@ -1081,11 +952,11 @@ function zero_vector!(B::VectorBundleFibers, X, p) ) end function zero_vector!(B::VectorBundleFibers{<:TangentSpaceType}, X, p) - return zero_tangent_vector!(B.manifold, X, p) + return zero_vector!(B.manifold, X, p) end @doc raw""" - zero_tangent_vector(B::VectorBundle, p) + zero_vector(B::VectorBundle, p) Zero tangent vector at point `p` from the vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ @@ -1102,23 +973,23 @@ $\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ 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...) +zero_vector(::VectorBundle, ::Any...) @doc raw""" - zero_tangent_vector(M::TangentSpaceAtPoint, p) + zero_vector(M::TangentSpaceAtPoint, p) Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector at point `M.point`. """ -zero_tangent_vector(::TangentSpaceAtPoint, ::Any...) +zero_vector(::TangentSpaceAtPoint, ::Any...) -function zero_tangent_vector!(B::VectorBundle, X, p) +function zero_vector!(B::VectorBundle, X, p) xp, Vp = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) - zero_tangent_vector!(B.manifold, VXM, xp) + zero_vector!(B.manifold, VXM, xp) zero_vector!(B.fiber, VXF, Vp) return X end -function zero_tangent_vector!(M::TangentSpaceAtPoint, X, p) - return zero_tangent_vector!(M.fiber.manifold, X, M.point) +function zero_vector!(M::TangentSpaceAtPoint, X, p) + return zero_vector!(M.fiber.manifold, X, M.point) end diff --git a/src/nlsolve.jl b/src/nlsolve.jl index 092d28146e..bc5ca0ec69 100644 --- a/src/nlsolve.jl +++ b/src/nlsolve.jl @@ -1,5 +1,5 @@ function _inverse_retract_nlsolve( - M::Manifold, + M::AbstractManifold, p, q, retraction, diff --git a/src/ode.jl b/src/ode.jl index fab86d5ef0..48433b05fc 100644 --- a/src/ode.jl +++ b/src/ode.jl @@ -2,7 +2,8 @@ function solve_exp_ode( M::MetricManifold, x, v, - tspan; + tspan, + B::AbstractBasis; solver=AutoVern9(Rodas5()), backend=diff_backend(), kwargs..., @@ -20,7 +21,7 @@ function solve_exp_ode( x = u[ix] ddx = allocate(u, Size(n)) du = allocate(u) - Ξ“ = christoffel_symbols_second(M, x; backend=backend) + Ξ“ = christoffel_symbols_second(M, x, B; backend=backend) @einsum ddx[k] = -Ξ“[k, i, j] * dx[i] * dx[j] du[iv] .= ddx du[ix] .= dx diff --git a/src/product_representations.jl b/src/product_representations.jl index 28e6d5bacb..e680314223 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -33,7 +33,7 @@ function make_reshape(::ArrayReshaper, ::Type{Size}, data) where {Size} end """ - ShapeSpecification(reshapers, manifolds::Manifold...) + ShapeSpecification(reshapers, manifolds::AbstractManifold...) A structure for specifying array size and offset information for linear storage of points and tangent vectors on the product manifold of `manifolds`. @@ -79,7 +79,7 @@ struct ShapeSpecification{TRanges,TSizes,TReshapers} reshapers::TReshapers end -function ShapeSpecification(reshapers, manifolds::Manifold...) +function ShapeSpecification(reshapers, manifolds::AbstractManifold...) sizes = map(m -> representation_size(m), manifolds) lengths = map(prod, sizes) ranges = UnitRange{Int64}[] @@ -158,6 +158,10 @@ function ProductArray( end ProductArray(M::ShapeSpecification, data) = ProductArray(typeof(M), data, M.reshapers) +function Base.copy(x::ProductArray{TShape}) where {TShape} + return ProductArray(TShape, copy(x.data), x.reshapers) +end + @doc raw""" prod_point(M::ShapeSpecification, pts...) @@ -189,29 +193,29 @@ function prod_point(M::ShapeSpecification, pts...) end @doc raw""" - submanifold_component(M::Manifold, p, i::Integer) - submanifold_component(M::Manifold, p, ::Val(i)) where {i} + submanifold_component(M::AbstractManifold, p, i::Integer) + submanifold_component(M::AbstractManifold, p, ::Val(i)) where {i} submanifold_component(p, i::Integer) submanifold_component(p, ::Val(i)) where {i} Project the product array `p` on `M` to its `i`th component. A new array is returned. """ submanifold_component(::Any...) -@inline function submanifold_component(M::Manifold, p, i::Integer) +@inline function submanifold_component(M::AbstractManifold, p, i::Integer) return submanifold_component(M, p, Val(i)) end -@inline submanifold_component(M::Manifold, p, i::Val) = submanifold_component(p, i) +@inline submanifold_component(M::AbstractManifold, p, i::Val) = submanifold_component(p, i) @inline submanifold_component(p, ::Val{I}) where {I} = p.parts[I] @inline submanifold_component(p, i::Integer) = submanifold_component(p, Val(i)) @doc raw""" - submanifold_components(M::Manifold, p) + submanifold_components(M::AbstractManifold, p) submanifold_components(p) Get the projected components of `p` on the submanifolds of `M`. The components are returned in a Tuple. """ submanifold_components(::Any...) -@inline submanifold_components(M::Manifold, p) = submanifold_components(p) +@inline submanifold_components(M::AbstractManifold, p) = submanifold_components(p) @inline submanifold_components(p) = p.parts function Base.BroadcastStyle( diff --git a/src/projected_distribution.jl b/src/projected_distribution.jl index 6f8fd5969c..e96a3dc5ce 100644 --- a/src/projected_distribution.jl +++ b/src/projected_distribution.jl @@ -1,11 +1,11 @@ """ - ProjectedPointDistribution(M::Manifold, d, proj!, p) + ProjectedPointDistribution(M::AbstractManifold, d, proj!, p) Generates a random point in ambient space of `M` and projects it to `M` using function `proj!`. Generated arrays are of type `TResult`, which can be specified by providing the `p` argument. """ -struct ProjectedPointDistribution{TResult,TM<:Manifold,TD<:Distribution,TProj} <: +struct ProjectedPointDistribution{TResult,TM<:AbstractManifold,TD<:Distribution,TProj} <: MPointDistribution{TM} manifold::TM distribution::TD @@ -13,7 +13,7 @@ struct ProjectedPointDistribution{TResult,TM<:Manifold,TD<:Distribution,TProj} < end function ProjectedPointDistribution( - M::Manifold, + M::AbstractManifold, d::Distribution, proj!, ::TResult, @@ -26,12 +26,12 @@ function ProjectedPointDistribution( end """ - projected_distribution(M::Manifold, d, [p=rand(d)]) + projected_distribution(M::AbstractManifold, d, [p=rand(d)]) Wrap the standard distribution `d` into a manifold-valued distribution. Generated points will be of similar type to `p`. By default, the type is not changed. """ -function projected_distribution(M::Manifold, d, p=rand(d)) +function projected_distribution(M::AbstractManifold, d, p=rand(d)) return ProjectedPointDistribution(M, d, project!, p) end @@ -119,7 +119,7 @@ end Normal distribution in ambient space with standard deviation `Οƒ` projected to tangent space at `p`. """ -function normal_tvector_distribution(M::Manifold, p, Οƒ) +function normal_tvector_distribution(M::AbstractManifold, p, Οƒ) d = Distributions.MvNormal(zero(vec(p)), Οƒ) return ProjectedFVectorDistribution(TangentBundleFibers(M), p, d, project!, p) end diff --git a/src/riemannian_diff.jl b/src/riemannian_diff.jl index c4b4e7b1ba..de8654bf98 100644 --- a/src/riemannian_diff.jl +++ b/src/riemannian_diff.jl @@ -8,37 +8,45 @@ an example. abstract type AbstractRiemannianDiffBackend end @doc raw""" - differential(M::Manifold, f, t::Real, backend::AbstractDiffBackend = rdifferential_backend()) + differential(M::AbstractManifold, f, t::Real, backend::AbstractDiffBackend = rdifferential_backend()) Compute the Riemannian differential of a curve $f: ℝ\to M$ on a manifold `M` represented by function `f` at time `t` using the given backend. It is calculated as the tangent vector equal to $\mathrm{d}f_t(t)[1]$. """ -differential(::Manifold, ::Any, ::Real, ::AbstractRiemannianDiffBackend) +differential(::AbstractManifold, ::Any, ::Real, ::AbstractRiemannianDiffBackend) @doc raw""" - gradient(M::Manifold, f, p, backend::AbstractRiemannianDiffBackend = rgradient_backend()) + gradient(M::AbstractManifold, f, p, backend::AbstractRiemannianDiffBackend = rgradient_backend()) Compute the Riemannian gradient $βˆ‡f(p)$ of a real field on manifold `M` represented by function `f` at point `p` using the given backend. """ -gradient(::Manifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend) +gradient(::AbstractManifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend) -function differential!(M::Manifold, f::Any, X, t, backend::AbstractRiemannianDiffBackend) +function differential!( + M::AbstractManifold, + f::Any, + X, + t, + backend::AbstractRiemannianDiffBackend, +) return copyto!(X, differential(M, f, t, backend)) end -function gradient!(M::Manifold, f, X, p, backend::AbstractRiemannianDiffBackend) +function gradient!(M::AbstractManifold, f, X, p, backend::AbstractRiemannianDiffBackend) return copyto!(X, gradient(M, f, p, backend)) end -differential(M::Manifold, f, p) = differential(M, f, p, rdifferential_backend()) +differential(M::AbstractManifold, f, p) = differential(M, f, p, rdifferential_backend()) -differential!(M::Manifold, f, X, p) = differential!(M, f, X, p, rdifferential_backend()) +function differential!(M::AbstractManifold, f, X, p) + return differential!(M, f, X, p, rdifferential_backend()) +end -gradient(M::Manifold, f, p) = gradient(M, f, p, rgradient_backend()) +gradient(M::AbstractManifold, f, p) = gradient(M, f, p, rgradient_backend()) -gradient!(M::Manifold, f, X, p) = gradient!(M, f, X, p, rgradient_backend()) +gradient!(M::AbstractManifold, f, X, p) = gradient!(M, f, X, p, rgradient_backend()) """ RiemannianONBDiffBackend( @@ -66,7 +74,7 @@ struct RiemannianONBDiffBackend{ basis::TBasis end -function differential(M::Manifold, f, t::Real, backend::RiemannianONBDiffBackend) +function differential(M::AbstractManifold, f, t::Real, backend::RiemannianONBDiffBackend) p = f(t) onb_coords = _derivative(zero(number_eltype(p)), backend.diff_backend) do h return get_coordinates( @@ -79,7 +87,13 @@ function differential(M::Manifold, f, t::Real, backend::RiemannianONBDiffBackend return get_vector(M, p, onb_coords, backend.basis) end -function differential!(M::Manifold, f, X, t::Real, backend::RiemannianONBDiffBackend) +function differential!( + M::AbstractManifold, + f, + X, + t::Real, + backend::RiemannianONBDiffBackend, +) p = f(t) onb_coords = _derivative(zero(number_eltype(p)), backend.diff_backend) do h return get_coordinates( @@ -92,16 +106,16 @@ function differential!(M::Manifold, f, X, t::Real, backend::RiemannianONBDiffBac return get_vector!(M, X, p, onb_coords, backend.basis) end -function gradient(M::Manifold, f, p, backend::RiemannianONBDiffBackend) - X = get_coordinates(M, p, zero_tangent_vector(M, p), backend.basis) +function gradient(M::AbstractManifold, f, p, backend::RiemannianONBDiffBackend) + X = get_coordinates(M, p, zero_vector(M, p), backend.basis) onb_coords = _gradient(X, backend.diff_backend) do Y return f(retract(M, p, get_vector(M, p, Y, backend.basis), backend.retraction)) end return get_vector(M, p, onb_coords, backend.basis) end -function gradient!(M::Manifold, f, X, p, backend::RiemannianONBDiffBackend) - X2 = get_coordinates(M, p, zero_tangent_vector(M, p), backend.basis) +function gradient!(M::AbstractManifold, f, X, p, backend::RiemannianONBDiffBackend) + X2 = get_coordinates(M, p, zero_vector(M, p), backend.basis) onb_coords = _gradient(X2, backend.diff_backend) do Y return f(retract(M, p, get_vector(M, p, Y, backend.basis), backend.retraction)) end @@ -132,7 +146,7 @@ globally default differentiation backend for calculating gradients. # See also -[`Manifolds.gradient(::Manifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend)`](@ref) +[`Manifolds.gradient(::AbstractManifold, ::Any, ::Any, ::AbstractRiemannianDiffBackend)`](@ref) """ const _current_rgradient_backend = CurrentRiemannianDiffBackend( RiemannianONBDiffBackend( @@ -216,12 +230,18 @@ struct RiemannianProjectionGradientBackend{TADBackend<:AbstractDiffBackend} <: diff_backend::TADBackend end -function gradient(M::Manifold, f, p, backend::RiemannianProjectionGradientBackend) +function gradient(M::AbstractManifold, f, p, backend::RiemannianProjectionGradientBackend) amb_grad = _gradient(f, p, backend.diff_backend) return project(M, p, amb_grad) end -function gradient!(M::Manifold, f, X, p, backend::RiemannianProjectionGradientBackend) +function gradient!( + M::AbstractManifold, + f, + X, + p, + backend::RiemannianProjectionGradientBackend, +) amb_grad = embed(M, p, X) _gradient!(f, amb_grad, p, backend.diff_backend) return project!(M, X, p, amb_grad) diff --git a/src/statistics.jl b/src/statistics.jl index 199e7ece34..4a2e2ae0ab 100644 --- a/src/statistics.jl +++ b/src/statistics.jl @@ -56,7 +56,7 @@ t_k &= \frac{w_k}{\sum_{i=1}^k w_i}\\ where $x_k$ are points, $w_k$ are weights, $ΞΌ_k$ is the $k$th estimate of the mean, and $Ξ³_x(y; t)$ is the point at time $t$ along the -[`shortest_geodesic`](@ref shortest_geodesic(::Manifold, ::Any, ::Any, ::Real)) +[`shortest_geodesic`](@ref shortest_geodesic(::AbstractManifold, ::Any, ::Any, ::Real)) 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. @@ -143,10 +143,10 @@ function Base.show(io::IO, method::GeodesicInterpolationWithinRadius) end @doc raw""" - mean(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) + mean(M::AbstractManifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) Compute the (optionally weighted) Riemannian center of mass also known as -Karcher mean of the vector `x` of points on the [`Manifold`](@ref) `M`, defined +Karcher mean of the vector `x` of points on the [`AbstractManifold`](@ref) `M`, defined as the point that satisfies the minimizer ````math \argmin_{y ∈ \mathcal M} \frac{1}{2 \sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{\mathcal M}^2(y,x_i), @@ -155,7 +155,7 @@ where $\mathrm{d}_{\mathcal M}$ denotes the Riemannian [`distance`](@ref). In the general case, the [`GradientDescentEstimation`](@ref) is used to compute the mean. mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::AbstractEstimationMethod; @@ -165,7 +165,7 @@ In the general case, the [`GradientDescentEstimation`](@ref) is used to compute Compute the mean using the specified `method`. mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::GradientDescentEstimation; @@ -208,9 +208,9 @@ The algorithm is further described in[^Afsari2013]. > Communications on Pure Applied Mathematics (1977), 30, pp. 509–541. > doi [10.1002/cpa.3160300502](https://doi.org/10.1002/cpa.3160300502) """ -mean(::Manifold, ::Any...) +mean(::AbstractManifold, ::Any...) function Statistics.mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, method::AbstractEstimationMethod...; kwargs..., @@ -219,7 +219,7 @@ function Statistics.mean( return mean!(M, y, x, method...; kwargs...) end function Statistics.mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractVector, method::AbstractEstimationMethod...; @@ -230,9 +230,9 @@ function Statistics.mean( end @doc raw""" - mean!(M::Manifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) + mean!(M::AbstractManifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) mean!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, [w::AbstractWeights,] @@ -240,11 +240,11 @@ end kwargs..., ) -Compute the [`mean`](@ref mean(::Manifold, args...)) in-place in `y`. +Compute the [`mean`](@ref mean(::AbstractManifold, args...)) in-place in `y`. """ -mean!(::Manifold, ::Any...) +mean!(::AbstractManifold, ::Any...) function Statistics.mean!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, method::AbstractEstimationMethod...; @@ -253,11 +253,17 @@ function Statistics.mean!( w = _unit_weights(length(x)) return mean!(M, y, x, w, method...; kwargs...) end -function Statistics.mean!(M::Manifold, y, x::AbstractVector, w::AbstractVector; kwargs...) +function Statistics.mean!( + M::AbstractManifold, + y, + x::AbstractVector, + w::AbstractVector; + kwargs..., +) return mean!(M, y, x, w, GradientDescentEstimation(); kwargs...) end function Statistics.mean!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, w::AbstractVector, @@ -278,7 +284,7 @@ function Statistics.mean!( end copyto!(y, p0) yold = allocate_result(M, mean, y) - v = zero_tangent_vector(M, y) + v = zero_vector(M, y) vtmp = copy(v) Ξ± = w ./ cumsum(w) for i in 1:stop_iter @@ -297,7 +303,7 @@ end """ mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::GeodesicInterpolation; @@ -317,10 +323,10 @@ points are considered for computing the mean. Optionally, pass `retraction` and `inverse_retraction` method types to specify the (inverse) retraction. """ -mean(::Manifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation) +mean(::AbstractManifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation) function Statistics.mean!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, w::AbstractVector, @@ -344,7 +350,7 @@ function Statistics.mean!( s = w[j] copyto!(q, x[j]) end - v = zero_tangent_vector(M, q) + v = zero_vector(M, q) ytmp = allocate_result(M, mean, q) @inbounds for i in 2:n j = order[i] @@ -359,7 +365,7 @@ end """ mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::GeodesicInterpolationWithinRadius; @@ -369,18 +375,18 @@ end Estimate the Riemannian center of mass of `x` using [`GeodesicInterpolationWithinRadius`](@ref). -See [`mean`](@ref mean(::Manifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation)) +See [`mean`](@ref mean(::AbstractManifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation)) for a description of `kwargs`. """ Statistics.mean( - ::Manifold, + ::AbstractManifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolationWithinRadius, ) function Statistics.mean!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, w::AbstractVector, @@ -399,7 +405,7 @@ function Statistics.mean!( return q end function Statistics.mean!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, w::AbstractVector, @@ -421,7 +427,7 @@ function Statistics.mean!( copyto!(q, p0) yold = allocate_result(M, mean, q) ytmp = copy(yold) - X = zero_tangent_vector(M, q) + X = zero_vector(M, q) wv = convert(AbstractVector, w) ./ sum(w) for i in 1:stop_iter Ξ» = 0.5 / i @@ -464,7 +470,7 @@ end """ mean( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::ExtrinsicEstimation; @@ -477,13 +483,18 @@ the result back. You can specify an `extrinsic_method` to specify which mean estimation method to use in the embedding, which defaults to [`GeodesicInterpolation`](@ref). -See [`mean`](@ref mean(::Manifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation)) +See [`mean`](@ref mean(::AbstractManifold, ::AbstractVector, ::AbstractVector, ::GeodesicInterpolation)) for a description of the remaining `kwargs`. """ -Statistics.mean(::Manifold, ::AbstractVector, ::AbstractVector, ::ExtrinsicEstimation) +Statistics.mean( + ::AbstractManifold, + ::AbstractVector, + ::AbstractVector, + ::ExtrinsicEstimation, +) function Statistics.mean!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, w::AbstractVector, @@ -498,9 +509,9 @@ function Statistics.mean!( end @doc raw""" - median(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) + median(M::AbstractManifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::AbstractEstimationMethod; @@ -508,7 +519,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 +[`AbstractManifold`](@ref) `M`, defined as the point that satisfies the minimizer ````math \argmin_{y ∈ \mathcal M} \frac{1}{\sum_{i=1}^n w_i} \sum_{i=1}^n w_i\mathrm{d}_{\mathcal M}(y,x_i), ```` @@ -520,11 +531,11 @@ median. However, this default may be overloaded for specific manifolds. Compute the median using the specified `method`. """ -Statistics.median(::Manifold, ::Any...) +Statistics.median(::AbstractManifold, ::Any...) """ median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::CyclicProximalPointEstimation; @@ -555,7 +566,7 @@ The algorithm is further described in [^BačÑk2014]. > arxiv: [1210.2145](https://arxiv.org/abs/1210.2145) """ Statistics.median( - ::Manifold, + ::AbstractManifold, ::AbstractVector, ::AbstractVector, ::CyclicProximalPointEstimation, @@ -563,7 +574,7 @@ Statistics.median( """ median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::ExtrinsicEstimation; @@ -576,14 +587,19 @@ in the embedding and projecting the result back. You can specify an `extrinsic_method` to specify which median estimation method to use in the embedding, which defaults to [`CyclicProximalPointEstimation`](@ref). -See [`median`](@ref median(::Manifold, ::AbstractVector, ::AbstractVector, ::CyclicProximalPointEstimation)) +See [`median`](@ref median(::AbstractManifold, ::AbstractVector, ::AbstractVector, ::CyclicProximalPointEstimation)) for a description of `kwargs`. """ -Statistics.median(::Manifold, ::AbstractVector, ::AbstractVector, ::ExtrinsicEstimation) +Statistics.median( + ::AbstractManifold, + ::AbstractVector, + ::AbstractVector, + ::ExtrinsicEstimation, +) @doc raw""" median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, [w::AbstractWeights,] method::WeiszfeldEstimation; @@ -636,10 +652,15 @@ respectively. > 2008 IEEE Conference on Computer Vision and Pattern Recognition, > doi: [10.1109/CVPR.2008.4587747](https://doi.org/10.1109/CVPR.2008.4587747), """ -Statistics.median(::Manifold, ::AbstractVector, ::AbstractVector, ::WeiszfeldEstimation) +Statistics.median( + ::AbstractManifold, + ::AbstractVector, + ::AbstractVector, + ::WeiszfeldEstimation, +) function Statistics.median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, method::AbstractEstimationMethod...; kwargs..., @@ -648,7 +669,7 @@ function Statistics.median( return median!(M, y, x, method...; kwargs...) end function Statistics.median( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractVector, method::AbstractEstimationMethod...; @@ -659,9 +680,9 @@ function Statistics.median( end @doc raw""" - median!(M::Manifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) + median!(M::AbstractManifold, y, x::AbstractVector[, w::AbstractWeights]; kwargs...) median!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, [w::AbstractWeights,] @@ -671,9 +692,9 @@ end computes the [`median`](@ref) in-place in `y`. """ -median!(::Manifold, ::Any...) +median!(::AbstractManifold, ::Any...) function Statistics.median!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, method::AbstractEstimationMethod...; @@ -682,11 +703,17 @@ function Statistics.median!( w = _unit_weights(length(x)) return median!(M, q, x, w, method...; kwargs...) end -function Statistics.median!(M::Manifold, y, x::AbstractVector, w::AbstractVector; kwargs...) +function Statistics.median!( + M::AbstractManifold, + y, + x::AbstractVector, + w::AbstractVector; + kwargs..., +) return median!(M, y, x, w, CyclicProximalPointEstimation(); kwargs...) end function Statistics.median!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, w::AbstractVector, @@ -708,7 +735,7 @@ function Statistics.median!( copyto!(q, p0) yold = allocate_result(M, median, q) ytmp = copy(yold) - v = zero_tangent_vector(M, q) + v = zero_vector(M, q) wv = convert(AbstractVector, w) ./ sum(w) for i in 1:stop_iter Ξ» = 0.5 / i @@ -725,7 +752,7 @@ function Statistics.median!( end function Statistics.median!( - M::Manifold, + M::AbstractManifold, y, x::AbstractVector, w::AbstractVector, @@ -740,7 +767,7 @@ function Statistics.median!( end function Statistics.median!( - M::Manifold, + M::AbstractManifold, q, x::AbstractVector, w::AbstractVector, @@ -764,14 +791,14 @@ function Statistics.median!( yold = allocate_result(M, median, q) ytmp = copy(yold) d = zeros(n) - v = zero_tangent_vector(M, q) + v = zero_vector(M, q) wv = convert(AbstractVector, w) ./ sum(w) for i in 1:stop_iter d .= [distance(M, q, xi) for xi in x] # compute distances # compute new weights / exclude points xi=q d .= [di > 0 ? wi / di : zero(typeof(wi / di)) for (di, wi) in zip(d, w)] copyto!(yold, q) - zero_tangent_vector!(M, v, q) + zero_vector!(M, v, q) for j in 1:n @inbounds v .+= d[j] * inverse_retract(M, q, x[j], inverse_retraction) end @@ -812,7 +839,7 @@ end var(M, x, w::AbstractWeights, m=mean(M, x, w); corrected=false) compute the (optionally weighted) variance of a `Vector` `x` of `n` data points -on the [`Manifold`](@ref) `M`, i.e. +on the [`AbstractManifold`](@ref) `M`, i.e. ````math \frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m), @@ -823,9 +850,9 @@ The mean of `x` can be specified as `m`, and the corrected variance can be activated by setting `corrected=true`. All further `kwargs...` are passed to the computation of the mean (if that is not provided). """ -var(::Manifold, ::Any) +var(::AbstractManifold, ::Any) function Statistics.var( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractWeights, m; @@ -838,22 +865,29 @@ function Statistics.var( c = StatsBase.varcorrection(w, corrected) return c * s end -function Statistics.var(M::Manifold, x::AbstractVector, m; corrected::Bool=true) +function Statistics.var(M::AbstractManifold, x::AbstractVector, m; corrected::Bool=true) n = length(x) w = _unit_weights(n) return var(M, x, w, m; corrected=corrected) end -function Statistics.var(M::Manifold, x::AbstractVector, w::AbstractWeights; kwargs...) +function Statistics.var( + M::AbstractManifold, + x::AbstractVector, + w::AbstractWeights; + kwargs..., +) return mean_and_var(M, x, w; kwargs...)[2] end -Statistics.var(M::Manifold, x::AbstractVector; kwargs...) = mean_and_var(M, x; kwargs...)[2] +function Statistics.var(M::AbstractManifold, x::AbstractVector; kwargs...) + return mean_and_var(M, x; kwargs...)[2] +end @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...) compute the optionally weighted standard deviation of a `Vector` `x` of `n` data -points on the [`Manifold`](@ref) `M`, i.e. +points on the [`AbstractManifold`](@ref) `M`, i.e. ````math \sqrt{\frac{1}{c} \sum_{i=1}^n w_i d_{\mathcal M}^2 (x_i,m)}, @@ -863,16 +897,16 @@ where `c` is a correction term, see The mean of `x` can be specified as `m`, and the corrected variance can be activated by setting `corrected=true`. """ -Statistics.std(M::Manifold, args...; kwargs...) = sqrt(var(M, args...; kwargs...)) +Statistics.std(M::AbstractManifold, args...; kwargs...) = sqrt(var(M, args...; kwargs...)) @doc raw""" - mean_and_var(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, var) + mean_and_var(M::AbstractManifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, var) -Compute the [`mean`](@ref mean(::Manifold, args...)) and the [`var`](@ref)iance +Compute the [`mean`](@ref mean(::AbstractManifold, args...)) and the [`var`](@ref)iance simultaneously. See those functions for a description of the arguments. mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector [w::AbstractWeights,] method::AbstractEstimationMethod; @@ -880,12 +914,12 @@ simultaneously. See those functions for a description of the arguments. ) -> (mean, var) Use the `method` for simultaneously computing the mean and variance. To use -a mean-specific method, call [`mean`](@ref mean(::Manifold, args...)) and then +a mean-specific method, call [`mean`](@ref mean(::AbstractManifold, args...)) and then [`var`](@ref). """ -mean_and_var(M::Manifold, ::Any...) +mean_and_var(M::AbstractManifold, ::Any...) function StatsBase.mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractWeights, method::AbstractEstimationMethod...; @@ -897,7 +931,7 @@ function StatsBase.mean_and_var( return m, v end function StatsBase.mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector, method::AbstractEstimationMethod...; corrected=true, @@ -910,7 +944,7 @@ end @doc raw""" mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector [w::AbstractWeights,] method::GeodesicInterpolation; @@ -935,7 +969,7 @@ interpolation method. to give accurate results except on [`Euclidean`](@ref). """ function StatsBase.mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractWeights, ::GeodesicInterpolation; @@ -959,7 +993,7 @@ function StatsBase.mean_and_var( s = w[j] y = copy(x[j]) end - v = zero_tangent_vector(M, y) + v = zero_vector(M, y) Mβ‚‚ = zero(number_eltype(v)) ytmp = allocate_result(M, mean, y) @inbounds for i in 2:n @@ -980,7 +1014,7 @@ end """ mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector [w::AbstractWeights,] method::GeodesicInterpolationWithinRadius; @@ -991,11 +1025,11 @@ Use repeated weighted geodesic interpolation to estimate the mean. Simultaneously, use a Welford-like recursion to estimate the variance. See [`GeodesicInterpolationWithinRadius`](@ref) and -[`mean_and_var`](@ref mean_and_var(::Manifold, ::AbstractVector, ::AbstractWeights, ::GeodesicInterpolation)) +[`mean_and_var`](@ref mean_and_var(::AbstractManifold, ::AbstractVector, ::AbstractWeights, ::GeodesicInterpolation)) for more information. """ function StatsBase.mean_and_var( - M::Manifold, + M::AbstractManifold, x::AbstractVector, w::AbstractWeights, method::GeodesicInterpolationWithinRadius; @@ -1025,13 +1059,13 @@ function StatsBase.mean_and_var( end @doc raw""" - mean_and_std(M::Manifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, std) + mean_and_std(M::AbstractManifold, x::AbstractVector[, w::AbstractWeights]; kwargs...) -> (mean, std) -Compute the [`mean`](@ref mean(::Manifold, args...)) and the standard deviation +Compute the [`mean`](@ref mean(::AbstractManifold, args...)) and the standard deviation [`std`](@ref) simultaneously. mean_and_std( - M::Manifold, + M::AbstractManifold, x::AbstractVector [w::AbstractWeights,] method::AbstractEstimationMethod; @@ -1039,23 +1073,23 @@ Compute the [`mean`](@ref mean(::Manifold, args...)) and the standard deviation ) -> (mean, var) Use the `method` for simultaneously computing the mean and standard deviation. -To use a mean-specific method, call [`mean`](@ref mean(::Manifold, args...)) and +To use a mean-specific method, call [`mean`](@ref mean(::AbstractManifold, args...)) and then [`std`](@ref). """ -function StatsBase.mean_and_std(M::Manifold, args...; kwargs...) +function StatsBase.mean_and_std(M::AbstractManifold, args...; kwargs...) m, v = mean_and_var(M, args...; kwargs...) return m, sqrt(v) end """ - moment(M::Manifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) + moment(M::AbstractManifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) Compute the `k`th central moment of points in `x` on manifold `M`. Optionally provide weights `w` and/or a precomputed -[`mean`](@ref mean(::Manifold, args...)). +[`mean`](@ref mean(::AbstractManifold, args...)). """ function StatsBase.moment( - M::Manifold, + M::AbstractManifold, x::AbstractVector, k::Int, w::AbstractWeights, @@ -1066,45 +1100,45 @@ function StatsBase.moment( end return s / sum(w) end -function StatsBase.moment(M::Manifold, x::AbstractVector, k::Int, m=mean(M, x)) +function StatsBase.moment(M::AbstractManifold, x::AbstractVector, k::Int, m=mean(M, x)) w = _unit_weights(length(x)) return moment(M, x, k, w, m) end """ - skewness(M::Manifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) + skewness(M::AbstractManifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) Compute the standardized skewness of points in `x` on manifold `M`. Optionally provide weights `w` and/or a precomputed -[`mean`](@ref mean(::Manifold, args...)) `m`. +[`mean`](@ref mean(::AbstractManifold, args...)) `m`. """ -function StatsBase.skewness(M::Manifold, x::AbstractVector, w::AbstractWeights) +function StatsBase.skewness(M::AbstractManifold, x::AbstractVector, w::AbstractWeights) m, s = mean_and_std(M, x, w; corrected=false) return moment(M, x, 3, w, m) / s^3 end -function StatsBase.skewness(M::Manifold, x::AbstractVector, w::AbstractWeights, m) +function StatsBase.skewness(M::AbstractManifold, x::AbstractVector, w::AbstractWeights, m) return moment(M, x, 3, w, m) / std(M, x, w, m; corrected=false)^3 end -function StatsBase.skewness(M::Manifold, x::AbstractVector, args...) +function StatsBase.skewness(M::AbstractManifold, x::AbstractVector, args...) w = _unit_weights(length(x)) return skewness(M, x, w, args...) end """ - kurtosis(M::Manifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) + kurtosis(M::AbstractManifold, x::AbstractVector, k::Int[, w::AbstractWeights], m=mean(M, x[, w])) Compute the excess kurtosis of points in `x` on manifold `M`. Optionally provide weights `w` and/or a precomputed -[`mean`](@ref mean(::Manifold, args...)) `m`. +[`mean`](@ref mean(::AbstractManifold, args...)) `m`. """ -function StatsBase.kurtosis(M::Manifold, x::AbstractVector, w::AbstractWeights) +function StatsBase.kurtosis(M::AbstractManifold, x::AbstractVector, w::AbstractWeights) m, v = mean_and_var(M, x, w; corrected=false) return moment(M, x, 4, w, m) / v^2 - 3 end -function StatsBase.kurtosis(M::Manifold, x::AbstractVector, w::AbstractWeights, m) +function StatsBase.kurtosis(M::AbstractManifold, x::AbstractVector, w::AbstractWeights, m) return moment(M, x, 4, w, m) / var(M, x, w, m; corrected=false)^2 - 3 end -function StatsBase.kurtosis(M::Manifold, x::AbstractVector, args...) +function StatsBase.kurtosis(M::AbstractManifold, x::AbstractVector, args...) w = _unit_weights(length(x)) return kurtosis(M, x, w, args...) end diff --git a/src/tests/tests_forwarddiff.jl b/src/tests/tests_forwarddiff.jl index 510ab955f1..4f0bb112fc 100644 --- a/src/tests/tests_forwarddiff.jl +++ b/src/tests/tests_forwarddiff.jl @@ -1,5 +1,5 @@ -function ManifoldTests.test_forwarddiff(M::Manifold, pts, tv) +function ManifoldTests.test_forwarddiff(M::AbstractManifold, pts, tv) return for (p, X) in zip(pts, tv) exp_f(t) = distance(M, p, exp(M, p, t[1] * X)) d12 = norm(M, p, X) diff --git a/src/tests/tests_general.jl b/src/tests/tests_general.jl index 54eefb4b32..29c495dc9a 100644 --- a/src/tests/tests_general.jl +++ b/src/tests/tests_general.jl @@ -1,6 +1,6 @@ """ test_manifold( - M::Manifold, + M::AbstractManifold, pts::AbstractVector; args, ) @@ -27,8 +27,8 @@ that lie on it (contained in `pts`). [`manifold_dimension`](@ref). - `inverse_retraction_methods = []`: inverse retraction methods that will be tested. - `is_mutating = true`: whether mutating variants of functions should be tested. -- `is_point_atol_multiplier = 0`: determines atol of `is_manifold_point` checks. -- `is_tangent_atol_multiplier = 0`: determines atol of `is_tangent_vector` checks. +- `is_point_atol_multiplier = 0`: determines atol of `is_point` checks. +- `is_tangent_atol_multiplier = 0`: determines atol of `is_vector` checks. - `mid_point12 = test_exp_log ? shortest_geodesic(M, pts[1], pts[2], 0.5) : nothing`: if not `nothing`, then check that `mid_point(M, pts[1], pts[2])` is approximately equal to `mid_point12`. This is by default set to `nothing` if `text_exp_log` is set to false. @@ -41,6 +41,7 @@ that lie on it (contained in `pts`). - `retraction_rtol_multiplier = 1`: change the relative tolerance of (inverse) retraction tests (1 use default). This is deactivated if the `exp_log_atol_multiplier` is nonzero. - `retraction_methods = []`: retraction methods that will be tested. +- `test_atlases = []`: Vector or tuple of atlases that should be tested. - `test_exp_log = true`: if true, checkthat [`exp`](@ref) is the inverse of [`log`](@ref). - `test_forward_diff = true`: if true, automatic differentiation using ForwardDiff is tested. @@ -70,7 +71,7 @@ that lie on it (contained in `pts`). to check the `direction` variant of vector transport """ function ManifoldTests.test_manifold( - M::Manifold, + M::AbstractManifold, pts::AbstractVector; basis_has_specialized_diagonalizing_get=false, basis_types_to_from=(), @@ -84,12 +85,14 @@ function ManifoldTests.test_manifold( is_mutating=true, is_point_atol_multiplier=0, is_tangent_atol_multiplier=0, + musical_isomorphism_bases=[], point_distributions=[], projection_atol_multiplier=0, rand_tvector_atol_multiplier=0, retraction_atol_multiplier=0, retraction_methods=[], retraction_rtol_multiplier=1, + test_atlases=(), test_exp_log=true, test_forward_diff=true, test_is_tangent=true, @@ -123,7 +126,7 @@ function ManifoldTests.test_manifold( # get a default tangent vector for every of the three tangent spaces n = length(pts) if default_inverse_retraction_method === nothing - tv = [zero_tangent_vector(M, pts[i]) for i in 1:n] # no other available + tv = [zero_vector(M, pts[i]) for i in 1:n] # no other available else tv = [ inverse_retract( @@ -170,22 +173,22 @@ function ManifoldTests.test_manifold( end end - Test.@testset "is_manifold_point" begin + Test.@testset "is_point" begin for pt in pts atol = is_point_atol_multiplier * ManifoldTests.find_eps(pt) - Test.@test is_manifold_point(M, pt; atol=atol) - Test.@test check_manifold_point(M, pt; atol=atol) === nothing + Test.@test is_point(M, pt; atol=atol) + Test.@test check_point(M, pt; atol=atol) === nothing end end - test_is_tangent && Test.@testset "is_tangent_vector" begin + test_is_tangent && Test.@testset "is_vector" begin for (p, X) in zip(pts, tv) atol = is_tangent_atol_multiplier * ManifoldTests.find_eps(p) - if !(check_tangent_vector(M, p, X; atol=atol) === nothing) - print(check_tangent_vector(M, p, X; atol=atol)) + if !(check_vector(M, p, X; atol=atol) === nothing) + print(check_vector(M, p, X; atol=atol)) end - Test.@test is_tangent_vector(M, p, X; atol=atol) - Test.@test check_tangent_vector(M, p, X; atol=atol) === nothing + Test.@test is_vector(M, p, X; atol=atol) + Test.@test check_vector(M, p, X; atol=atol) === nothing end end @@ -212,14 +215,14 @@ function ManifoldTests.test_manifold( else Test.@test isapprox(M, pts[1], exp(M, pts[2], X2); atol=atolp1p2, rtol=rtolp1p2) end - Test.@test is_manifold_point(M, exp(M, pts[1], X1); atol=atolp1p2, rtol=rtolp1p2) + Test.@test is_point(M, exp(M, pts[1], X1); atol=atolp1p2, rtol=rtolp1p2) Test.@test isapprox(M, pts[1], exp(M, pts[1], X1, 0); atol=atolp1p2, rtol=rtolp1p2) for p in pts epsx = ManifoldTests.find_eps(p) Test.@test isapprox( M, p, - zero_tangent_vector(M, p), + zero_vector(M, p), log(M, p, p); atol=epsx * exp_log_atol_multiplier, rtol=exp_log_atol_multiplier == 0.0 ? @@ -228,7 +231,7 @@ function ManifoldTests.test_manifold( Test.@test isapprox( M, p, - zero_tangent_vector(M, p), + zero_vector(M, p), inverse_retract(M, p, p); atol=epsx * exp_log_atol_multiplier, rtol=exp_log_atol_multiplier == 0.0 ? @@ -237,11 +240,11 @@ function ManifoldTests.test_manifold( end atolp1 = exp_log_atol_multiplier * ManifoldTests.find_eps(pts[1]) if is_mutating - zero_tangent_vector!(M, X1, pts[1]) + zero_vector!(M, X1, pts[1]) else - X1 = zero_tangent_vector(M, pts[1]) + X1 = zero_vector(M, pts[1]) end - Test.@test isapprox(M, pts[1], X1, zero_tangent_vector(M, pts[1]); atol=atolp1) + Test.@test isapprox(M, pts[1], X1, zero_vector(M, pts[1]); atol=atolp1) if is_mutating log!(M, X1, pts[1], pts[2]) else @@ -266,7 +269,7 @@ function ManifoldTests.test_manifold( for (p, X) in zip(pts, tv) epsx = ManifoldTests.find_eps(p) for retr_method in retraction_methods - Test.@test is_manifold_point(M, retract(M, p, X, retr_method)) + Test.@test is_point(M, retract(M, p, X, retr_method)) Test.@test isapprox( M, p, @@ -281,7 +284,7 @@ function ManifoldTests.test_manifold( else new_pt = retract(M, p, X, retr_method) end - Test.@test is_manifold_point(M, new_pt) + Test.@test is_point(M, new_pt) end end for p in pts @@ -290,7 +293,7 @@ function ManifoldTests.test_manifold( Test.@test isapprox( M, p, - zero_tangent_vector(M, p), + zero_vector(M, p), inverse_retract(M, p, p, inv_retr_method); atol=epsx * retraction_atol_multiplier, rtol=retraction_atol_multiplier == 0 ? @@ -300,14 +303,35 @@ function ManifoldTests.test_manifold( end end + Test.@testset "atlases" begin + if !isempty(test_atlases) + Test.@test get_default_atlas(M) isa AbstractAtlas{ℝ} + end + for A in test_atlases + i = get_chart_index(M, A, pts[1]) + a = get_point_coordinates(M, A, i, pts[1]) + Test.@test isa(a, AbstractVector) + Test.@test length(a) == manifold_dimension(M) + Test.@test isapprox(M, pts[1], get_point(M, A, i, a)) + if is_mutating + get_point_coordinates!(M, a, A, i, pts[2]) + Test.@test a β‰ˆ get_point_coordinates(M, A, i, pts[2]) + + q = allocate(pts[1]) + get_point!(M, q, A, i, a) + Test.@test isapprox(M, pts[2], q) + end + end + end + test_vector_spaces && Test.@testset "vector spaces tests" begin for p in pts - X = zero_tangent_vector(M, p) + X = zero_vector(M, p) mts = Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M) Test.@test isapprox(M, p, X, zero_vector(mts, p)) if is_mutating zero_vector!(mts, X, p) - Test.@test isapprox(M, p, X, zero_tangent_vector(M, p)) + Test.@test isapprox(M, p, X, zero_vector(M, p)) end end end @@ -318,7 +342,7 @@ function ManifoldTests.test_manifold( M, p, 0 * X, - zero_tangent_vector(M, p); + zero_vector(M, p); atol=ManifoldTests.find_eps(pts[1]), ) Test.@test isapprox(M, p, 2 * X, X + X) @@ -382,8 +406,8 @@ function ManifoldTests.test_manifold( test_default_vector_transport && Test.@testset "default vector transport" begin v1t1 = vector_transport_to(M, pts[1], X1, pts32) v1t2 = vector_transport_direction(M, pts[1], X1, X2) - Test.@test is_tangent_vector(M, pts32, v1t1; atol=tvatol) - Test.@test is_tangent_vector(M, pts32, v1t2; atol=tvatol) + Test.@test is_vector(M, pts32, v1t1; atol=tvatol) + Test.@test is_vector(M, pts32, v1t2; atol=tvatol) Test.@test isapprox(M, pts32, v1t1, v1t2) Test.@test isapprox(M, pts[1], vector_transport_to(M, pts[1], X1, pts[1]), X1) @@ -411,10 +435,8 @@ function ManifoldTests.test_manifold( pts32 = retract(M, pts[1], X2, rtr_m) test_to && (v1t1 = vector_transport_to(M, pts[1], X1, pts32, vtm)) test_dir && (v1t2 = vector_transport_direction(M, pts[1], X1, X2, vtm)) - test_to && - Test.@test is_tangent_vector(M, pts32, v1t1, true; atol=tvatol) - test_dir && - Test.@test is_tangent_vector(M, pts32, v1t2, true; atol=tvatol) + test_to && Test.@test is_vector(M, pts32, v1t1, true; atol=tvatol) + test_dir && Test.@test is_vector(M, pts32, v1t2, true; atol=tvatol) (test_to && test_dir) && Test.@test isapprox(M, pts32, v1t1, v1t2) test_to && Test.@test isapprox( M, @@ -425,13 +447,7 @@ function ManifoldTests.test_manifold( test_dir && Test.@test isapprox( M, pts[1], - vector_transport_direction( - M, - pts[1], - X1, - zero_tangent_vector(M, pts[1]), - vtm, - ), + vector_transport_direction(M, pts[1], X1, zero_vector(M, pts[1]), vtm), X1, ) @@ -580,12 +596,29 @@ function ManifoldTests.test_manifold( if default_inverse_retraction_method !== nothing tv_m = inverse_retract(M, pts[1], pts[2], default_inverse_retraction_method) else - tv_m = zero_tangent_vector(M, pts[1]) + tv_m = zero_vector(M, pts[1]) end - ctv_m = flat(M, pts[1], FVector(TangentSpace, tv_m)) - Test.@test ctv_m.type == CotangentSpace + ctv_m = flat(M, pts[1], tv_m) + Test.@test ctv_m(tv_m) β‰ˆ norm(M, pts[1], tv_m)^2 tv_m_back = sharp(M, pts[1], ctv_m) - Test.@test tv_m_back.type == TangentSpace + Test.@test isapprox(M, pts[1], tv_m, tv_m_back) + + if is_mutating + ctv_m_s = allocate(ctv_m) + flat!(M, ctv_m_s, pts[1], tv_m) + Test.@test ctv_m_s(tv_m) β‰ˆ ctv_m(tv_m) + tv_m_s_back = allocate(tv_m_back) + sharp!(M, tv_m_s_back, pts[1], ctv_m_s) + Test.@test isapprox(M, pts[1], tv_m, tv_m_s_back) + end + + for basis in musical_isomorphism_bases + tv_m_f = ManifoldsBase.TFVector(get_coordinates(M, pts[1], tv_m, basis), basis) + ctv_m_f = flat(M, pts[1], tv_m_f) + Test.@test isa(ctv_m_f, CoTFVector) + tv_m_f_back = sharp(M, pts[1], ctv_m_f) + Test.@test isapprox(tv_m_f.data, tv_m_f_back.data) + end end Test.@testset "number_eltype" begin @@ -604,9 +637,9 @@ function ManifoldTests.test_manifold( X2 = allocate(X) if default_inverse_retraction_method === nothing - X3 = zero_tangent_vector(M, p) + X3 = zero_vector(M, p) copyto!(X2, X3) - Test.@test isapprox(M, p, X2, zero_tangent_vector(M, p)) + Test.@test isapprox(M, p, X2, zero_vector(M, p)) else q = retract(M, p, X, default_retraction_method) X3 = inverse_retract(M, p, q, default_inverse_retraction_method) @@ -627,10 +660,10 @@ function ManifoldTests.test_manifold( for pd in point_distributions Test.@test Manifolds.support(pd) isa Manifolds.MPointSupport{typeof(M)} for _ in 1:10 - Test.@test is_manifold_point(M, rand(pd)) + Test.@test is_point(M, rand(pd)) if test_mutating_rand rand!(pd, prand) - Test.@test is_manifold_point(M, prand) + Test.@test is_point(M, prand) end end end @@ -644,7 +677,7 @@ function ManifoldTests.test_manifold( for _ in 1:10 randtv = rand(tvd) atol = rand_tvector_atol_multiplier * ManifoldTests.find_eps(randtv) - Test.@test is_tangent_vector(M, supp.point, randtv; atol=atol) + Test.@test is_vector(M, supp.point, randtv; atol=atol) end end end diff --git a/src/tests/tests_group.jl b/src/tests/tests_group.jl index 84b36a6f3a..dde72a2920 100644 --- a/src/tests/tests_group.jl +++ b/src/tests/tests_group.jl @@ -38,7 +38,7 @@ function ManifoldTests.test_group( Test.@testset "Closed" begin for g1 in g_pts, g2 in g_pts g3 = compose(G, g1, g2) - Test.@test is_manifold_point(G, g3, true; atol=atol) + Test.@test is_point(G, g3, true; atol=atol) end end @@ -199,14 +199,14 @@ function ManifoldTests.test_group( translate_diff(G, g_pts[2], g_pts[1], X, LeftAction()); atol=atol, ) - Test.@test is_tangent_vector( + Test.@test is_vector( G, g12, translate_diff(G, g_pts[2], g_pts[1], X, LeftAction()), true; atol=atol, ) - RightAction() in diff_convs && Test.@test is_tangent_vector( + RightAction() in diff_convs && Test.@test is_vector( G, g21, translate_diff(G, g_pts[2], g_pts[1], X, RightAction()), @@ -299,7 +299,7 @@ function ManifoldTests.test_group( Test.@testset "v = log(exp(v))" begin for v in ve_pts g = group_exp(G, v) - Test.@test is_manifold_point(G, g; atol=atol) + Test.@test is_point(G, g; atol=atol) v2 = group_log(G, g) Test.@test isapprox(G, make_identity(G, g_pts[1]), v2, v; atol=atol) end @@ -308,7 +308,7 @@ function ManifoldTests.test_group( for v in ve_pts g = allocate(g_pts[1]) Test.@test group_exp!(G, g, v) === g - Test.@test is_manifold_point(G, g; atol=atol) + Test.@test is_point(G, g; atol=atol) Test.@test isapprox(G, g, group_exp(G, v); atol=atol) v2 = allocate(v) Test.@test group_log!(G, v2, g) === v2 @@ -345,7 +345,7 @@ function ManifoldTests.test_group( v_pts[1], Manifolds.GroupExponentialRetraction(conv...), ) - Test.@test is_manifold_point(G, y; atol=atol) + Test.@test is_point(G, y; atol=atol) v2 = inverse_retract( G, g_pts[1], @@ -365,7 +365,7 @@ function ManifoldTests.test_group( v_pts[1], Manifolds.GroupExponentialRetraction(conv...), ) === y - Test.@test is_manifold_point(G, y; atol=atol) + Test.@test is_point(G, y; atol=atol) v2 = allocate(v_pts[1]) Test.@test inverse_retract!( G, @@ -464,13 +464,13 @@ function ManifoldTests.test_action( Test.@testset "over actions" begin for a1 in a_pts, a2 in a_pts a3 = compose(A, a1, a2) - Test.@test is_manifold_point(G, a3, true; atol=atol) + Test.@test is_point(G, a3, true; atol=atol) end end Test.@testset "over g-manifold" begin for a in a_pts, m in m_pts - Test.@test is_manifold_point(M, apply(A, a, m), true; atol=atol) - Test.@test is_manifold_point(M, inverse_apply(A, a, m), true; atol=atol) + Test.@test is_point(M, apply(A, a, m), true; atol=atol) + Test.@test is_point(M, inverse_apply(A, a, m), true; atol=atol) end end end @@ -595,8 +595,8 @@ function ManifoldTests.test_action( for a in a_pts am, av = apply(A, a, m), apply_diff(A, a, m, v) ainvm, ainvv = inverse_apply(A, a, m), inverse_apply_diff(A, a, m, v) - Test.@test is_tangent_vector(M, am, av, true; atol=atol) - Test.@test is_tangent_vector(M, ainvm, ainvv, true; atol=atol) + Test.@test is_vector(M, am, av, true; atol=atol) + Test.@test is_vector(M, ainvm, ainvv, true; atol=atol) end a12 = compose(A, a_pts[1], a_pts[2]) @@ -618,8 +618,8 @@ function ManifoldTests.test_action( ainvm = inverse_apply(A, a, m) ainvv = allocate(v) Test.@test inverse_apply_diff!(A, ainvv, a, m, v) === ainvv - Test.@test is_tangent_vector(M, am, av, true; atol=atol) - Test.@test is_tangent_vector(M, ainvm, ainvv, true; atol=atol) + Test.@test is_vector(M, am, av, true; atol=atol) + Test.@test is_vector(M, ainvm, ainvv, true; atol=atol) end a12 = compose(A, a_pts[1], a_pts[2]) diff --git a/src/tests/tests_reversediff.jl b/src/tests/tests_reversediff.jl index 2d4d3ff1fe..91130a66e4 100644 --- a/src/tests/tests_reversediff.jl +++ b/src/tests/tests_reversediff.jl @@ -1,5 +1,5 @@ -function ManifoldTests.test_reversediff(M::Manifold, pts, tv) +function ManifoldTests.test_reversediff(M::AbstractManifold, pts, tv) return for (p, X) in zip(pts, tv) exp_f(t) = distance(M, p, exp(M, p, t[1] * X)) d12 = norm(M, p, X) diff --git a/test/approx_inverse_retraction.jl b/test/approx_inverse_retraction.jl index 78909b3964..5402874aa2 100644 --- a/test/approx_inverse_retraction.jl +++ b/test/approx_inverse_retraction.jl @@ -37,7 +37,7 @@ include("utils.jl") retr_method = ExponentialRetraction() inv_retr_method = NLsolveInverseRetraction(retr_method) X = inverse_retract(M, p, q, inv_retr_method) - @test is_tangent_vector(M, p, X) + @test is_vector(M, p, X) @test X isa Vector{Float64} @test X β‰ˆ q - p end @@ -52,7 +52,7 @@ include("utils.jl") inv_retr_method = NLsolveInverseRetraction(ProjectionRetraction(), X0; project_point=true) X = inverse_retract(M, p, q, inv_retr_method) - @test is_tangent_vector(M, p, X; atol=1e-9) + @test is_vector(M, p, X; atol=1e-9) @test X β‰ˆ X_exp @test_throws OutOfInjectivityRadiusError inverse_retract( M, @@ -71,7 +71,7 @@ include("utils.jl") inv_retr_method = NLsolveInverseRetraction(ExponentialRetraction(); project_point=true) X = inverse_retract(M, p, q, inv_retr_method) - @test is_tangent_vector(M, p, X; atol=1e-8) + @test is_vector(M, p, X; atol=1e-8) @test X β‰ˆ X_exp end end diff --git a/test/centered_matrices.jl b/test/centered_matrices.jl index 714d1d1029..f74c213e6d 100644 --- a/test/centered_matrices.jl +++ b/test/centered_matrices.jl @@ -13,14 +13,14 @@ include("utils.jl") @test representation_size(M) == (3, 2) @test base_manifold(M) === M @test typeof(get_embedding(M)) === Euclidean{Tuple{3,2},ℝ} - @test check_manifold_point(M, A) === nothing - @test_throws DomainError is_manifold_point(M, B, true) - @test_throws DomainError is_manifold_point(M, C, true) - @test_throws DomainError is_manifold_point(M, D, true) - @test check_tangent_vector(M, A, A) === nothing - @test_throws DomainError is_tangent_vector(M, A, D, true) - @test_throws DomainError is_tangent_vector(M, D, A, true) - @test_throws DomainError is_tangent_vector(M, A, B, true) + @test check_point(M, A) === nothing + @test_throws DomainError is_point(M, B, true) + @test_throws DomainError is_point(M, C, true) + @test_throws DomainError is_point(M, D, true) + @test check_vector(M, A, A) === nothing + @test_throws DomainError is_vector(M, A, D, true) + @test_throws DomainError is_vector(M, D, A, true) + @test_throws DomainError is_vector(M, A, B, true) @test manifold_dimension(M) == 4 @test A == project!(M, A, A) @test A == project(M, A, A) diff --git a/test/cholesky_space.jl b/test/cholesky_space.jl index 1ba2927b4a..e3367f057c 100644 --- a/test/cholesky_space.jl +++ b/test/cholesky_space.jl @@ -34,24 +34,24 @@ include("utils.jl") exp_log_atol_multiplier=8.0, ) end - @testset "Test Error cases in is_manifold_point and is_tangent_vector" begin + @testset "Test Error cases in is_point and is_vector" begin pt1f = zeros(2, 3) # wrong size pt2f = [1.0 0.0 0.0; 0.0 -1.0 0.0; 0.0 0.0 1.0] # nonpos diag pt3f = [2.0 0.0 1.0; 0.0 1.0 0.0; 0.0 0.0 4.0] # no lower and nonsym pt4 = [2.0 0.0 0.0; 1.0 2.0 0.0; 0.0 0.0 4.0] - @test !is_manifold_point(M, pt1f) - @test_throws DomainError is_manifold_point(M, pt1f, true) - @test !is_manifold_point(M, pt2f) - @test_throws DomainError is_manifold_point(M, pt2f, true) - @test !is_manifold_point(M, pt3f) - @test_throws DomainError is_manifold_point(M, pt3f, true) - @test is_manifold_point(M, pt4) - @test !is_tangent_vector(M, pt3f, pt1f) - @test_throws DomainError is_tangent_vector(M, pt3f, pt1f, true) - @test !is_tangent_vector(M, pt4, pt1f) - @test_throws DomainError is_tangent_vector(M, pt4, pt1f, true) - @test !is_tangent_vector(M, pt4, pt3f) - @test_throws DomainError is_tangent_vector(M, pt4, pt3f, true) - @test is_tangent_vector(M, pt4, pt2f) + @test !is_point(M, pt1f) + @test_throws DomainError is_point(M, pt1f, true) + @test !is_point(M, pt2f) + @test_throws DomainError is_point(M, pt2f, true) + @test !is_point(M, pt3f) + @test_throws DomainError is_point(M, pt3f, true) + @test is_point(M, pt4) + @test !is_vector(M, pt3f, pt1f) + @test_throws DomainError is_vector(M, pt3f, pt1f, true) + @test !is_vector(M, pt4, pt1f) + @test_throws DomainError is_vector(M, pt4, pt1f, true) + @test !is_vector(M, pt4, pt3f) + @test_throws DomainError is_vector(M, pt4, pt3f, true) + @test is_vector(M, pt4, pt2f) end end diff --git a/test/circle.jl b/test/circle.jl index 15a0632800..edd74326e4 100644 --- a/test/circle.jl +++ b/test/circle.jl @@ -1,16 +1,18 @@ include("utils.jl") +using Manifolds: TFVector, CoTFVector + @testset "Circle" begin M = Circle() @testset "Real Circle Basics" begin @test repr(M) == "Circle(ℝ)" @test representation_size(M) == () @test manifold_dimension(M) == 1 - @test !is_manifold_point(M, 9.0) - @test_throws DomainError is_manifold_point(M, 9.0, true) - @test !is_tangent_vector(M, 9.0, 0.0) - @test_throws DomainError is_tangent_vector(M, 9.0, 0.0, true) - @test is_tangent_vector(M, 0.0, 0.0; check_base_point=false) + @test !is_point(M, 9.0) + @test_throws DomainError is_point(M, 9.0, true) + @test !is_vector(M, 9.0, 0.0) + @test_throws DomainError is_vector(M, 9.0, 0.0, true) + @test is_vector(M, 0.0, 0.0) @test get_coordinates(M, Ref(0.0), Ref(2.0), DefaultOrthonormalBasis())[] β‰ˆ 2.0 @test get_coordinates( M, @@ -54,8 +56,18 @@ include("utils.jl") )[] β‰ˆ 2.0 @test number_of_coordinates(M, DiagonalizingOrthonormalBasis(Ref(-1.0))) == 1 @test number_of_coordinates(M, DefaultOrthonormalBasis()) == 1 - @test flat(M, 0.0, FVector(TangentSpace, 1.0)) == FVector(CotangentSpace, 1.0) - @test sharp(M, 0.0, FVector(CotangentSpace, 1.0)) == FVector(TangentSpace, 1.0) + rrcv = Manifolds.RieszRepresenterCotangentVector(M, 0.0, 1.0) + @test flat(M, 0.0, 1.0) == rrcv + @test sharp(M, 0.0, rrcv) == 1.0 + B_cot = Manifolds.dual_basis(M, 0.0, DefaultOrthonormalBasis()) + @test get_coordinates(M, 0.0, rrcv, B_cot) β‰ˆ 1.0 + @test get_vector(M, 0.0, 1.0, B_cot) isa Manifolds.RieszRepresenterCotangentVector + a = fill(NaN) + get_coordinates!(M, a, 0.0, rrcv, B_cot) + @test a[] β‰ˆ 1.0 + rrcv2 = Manifolds.RieszRepresenterCotangentVector(M, fill(0.0), fill(-10.0)) + get_vector!(M, rrcv2, 0.0, 1.0, B_cot) + @test isapprox(rrcv.X, rrcv2.X[]) @test vector_transport_to(M, 0.0, 1.0, 1.0, ParallelTransport()) == 1.0 @test retract(M, 0.0, 1.0) == exp(M, 0.0, 1.0) @test injectivity_radius(M) β‰ˆ Ο€ @@ -76,8 +88,8 @@ include("utils.jl") x = SVector(0.0) log!(M, v, x, SVector(Ο€ / 4)) @test norm(M, x, v) β‰ˆ Ο€ / 4 - @test is_tangent_vector(M, x, v) - @test is_tangent_vector(M, [], v; check_base_point=false) + @test is_vector(M, x, v) + @test is_vector(M, [], v) @test project(M, 1.0) == 1.0 x = MVector(0.0) project!(M, x, x) @@ -136,25 +148,24 @@ include("utils.jl") @test repr(Mc) == "Circle(β„‚)" @test representation_size(Mc) == () @test manifold_dimension(Mc) == 1 - @test is_tangent_vector(Mc, 1im, 0.0) - @test is_manifold_point(Mc, 1im) - @test !is_manifold_point(Mc, 1 + 1im) - @test_throws DomainError is_manifold_point(Mc, 1 + 1im, true) - @test !is_tangent_vector(Mc, 1 + 1im, 0.0) - @test_throws DomainError is_tangent_vector(Mc, 1 + 1im, 0.0, true) - @test !is_tangent_vector(Mc, 1im, 2im) - @test_throws DomainError is_tangent_vector(Mc, 1im, 2im, true) - @test flat(Mc, 0.0 + 0.0im, FVector(TangentSpace, 1.0im)) == - FVector(CotangentSpace, 1.0im) - @test sharp(Mc, 0.0 + 0.0im, FVector(CotangentSpace, 1.0im)) == - FVector(TangentSpace, 1.0im) + @test is_vector(Mc, 1im, 0.0) + @test is_point(Mc, 1im) + @test !is_point(Mc, 1 + 1im) + @test_throws DomainError is_point(Mc, 1 + 1im, true) + @test !is_vector(Mc, 1 + 1im, 0.0) + @test_throws DomainError is_vector(Mc, 1 + 1im, 0.0, true) + @test !is_vector(Mc, 1im, 2im) + @test_throws DomainError is_vector(Mc, 1im, 2im, true) + rrcv = Manifolds.RieszRepresenterCotangentVector(Mc, 0.0 + 0.0im, 1.0im) + @test flat(Mc, 0.0 + 0.0im, 1.0im) == rrcv + @test sharp(Mc, 0.0 + 0.0im, rrcv) == 1.0im @test norm(Mc, 1.0, log(Mc, 1.0, -1.0)) β‰ˆ Ο€ - @test is_tangent_vector(Mc, 1.0, log(Mc, 1.0, -1.0)) + @test is_vector(Mc, 1.0, log(Mc, 1.0, -1.0)) v = MVector(0.0 + 0.0im) x = SVector(1.0 + 0.0im) log!(Mc, v, x, SVector(-1.0 + 0.0im)) @test norm(Mc, SVector(1.0), v) β‰ˆ Ο€ - @test is_tangent_vector(Mc, x, v) + @test is_vector(Mc, x, v) @test project(Mc, 1.0) == 1.0 project(Mc, 1 / sqrt(2.0) + 1 / sqrt(2.0) * im) == 1 / sqrt(2.0) + 1 / sqrt(2.0) * im diff --git a/test/elliptope.jl b/test/elliptope.jl index 32bc478318..9ede6ed304 100644 --- a/test/elliptope.jl +++ b/test/elliptope.jl @@ -7,14 +7,14 @@ include("utils.jl") @test get_embedding(M) == Euclidean(4, 2) @test representation_size(M) == (4, 2) q = [1.0 0.0; 0.0 1.0; 1/sqrt(2) -1/sqrt(2); 1/sqrt(2) 1/sqrt(2)] - @test is_manifold_point(M, q, true; atol=10^-15) + @test is_point(M, q, true; atol=10^-15) @test base_manifold(M) === M qN = [2.0 0.0; 0.0 1.0; 1/sqrt(2) -1/sqrt(2); 1/sqrt(2) 1/sqrt(2)] - @test_throws DomainError is_manifold_point(M, qN, true) + @test_throws DomainError is_point(M, qN, true) Y = [0.0 1.0; 1.0 0.0; 0.0 0.0; 0.0 0.0] - @test is_tangent_vector(M, q, Y, true; check_base_point=false) + @test is_vector(M, q, Y, true) YN = [0.1 1.0; 1.0 0.1; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_tangent_vector(M, q, YN, true; check_base_point=false) + @test_throws DomainError is_vector(M, q, YN, true) qE = similar(q) embed!(M, qE, q) qE2 = embed(M, q) @@ -22,7 +22,7 @@ include("utils.jl") @test qE2 == q q2 = [4.0/5 3.0/5; 3.0/5.0 -4.0/5.0; 1.0 0.0; 0.0 1.0] q3 = [12.0/13.0 5.0/13.0; 1.0 0.0; -12.0/13.0 5.0/13.0; 0.0 1.0] - @test is_tangent_vector( + @test is_vector( M, q2, vector_transport_to(M, q, Y, q2, ProjectionTransport()); diff --git a/test/essential_manifold.jl b/test/essential_manifold.jl index 58c61cf83f..970e6f94c8 100644 --- a/test/essential_manifold.jl +++ b/test/essential_manifold.jl @@ -19,24 +19,24 @@ include("utils.jl") np1 = [r1, nr] np2 = [nr, nr] np3 = [r1, r2, r3] - @test !is_manifold_point(M, r1) - @test_throws DomainError is_manifold_point(M, r1, true) - @test_throws DomainError is_manifold_point(M, np3, true) - @test is_manifold_point(M, p1) - @test_throws ComponentManifoldError is_manifold_point(M, np1, true) - @test_throws CompositeManifoldError is_manifold_point(M, np2, true) - @test !is_tangent_vector(M, p1, 0.0) - @test_throws DomainError is_tangent_vector( + @test !is_point(M, r1) + @test_throws DomainError is_point(M, r1, true) + @test_throws DomainError is_point(M, np3, true) + @test is_point(M, p1) + @test_throws ComponentManifoldError is_point(M, np1, true) + @test_throws CompositeManifoldError is_point(M, np2, true) + @test !is_vector(M, p1, 0.0) + @test_throws DomainError is_vector( M, p1, [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], true, ) - @test !is_tangent_vector(M, np1, [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]) - @test !is_tangent_vector(M, p1, p2) + @test !is_vector(M, np1, [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]) + @test !is_vector(M, p1, p2) # projection test - @test is_tangent_vector(M, p1, project(M, p1, log(M, p1, p2))) - @test is_tangent_vector(M, p1, project(M, p1, log(M, p2, p1))) + @test is_vector(M, p1, project(M, p1, log(M, p1, p2))) + @test is_vector(M, p1, project(M, p1, log(M, p2, p1))) end @testset "Signed Essential" begin test_manifold( diff --git a/test/euclidean.jl b/test/euclidean.jl index a7ba3eaff3..86146adafd 100644 --- a/test/euclidean.jl +++ b/test/euclidean.jl @@ -1,5 +1,7 @@ include("utils.jl") +using Manifolds: induced_basis + @testset "Euclidean" begin E = Euclidean(3) Ec = Euclidean(3; field=β„‚) @@ -12,8 +14,10 @@ include("utils.jl") @test is_default_metric(E, Manifolds.EuclideanMetric()) @test Manifolds.default_metric_dispatch(E, Manifolds.EuclideanMetric()) === Val{true}() p = zeros(3) - @test det_local_metric(EM, p) == one(eltype(p)) - @test log_local_metric_density(EM, p) == zero(eltype(p)) + A = Manifolds.RetractionAtlas() + B = induced_basis(EM, A, p, TangentSpace) + @test det_local_metric(EM, p, B) == one(eltype(p)) + @test log_local_metric_density(EM, p, B) == zero(eltype(p)) @test project!(E, p, p) == p @test embed!(E, p, p) == p @test manifold_dimension(Ec) == 2 * manifold_dimension(E) @@ -25,15 +29,15 @@ include("utils.jl") @test embed(E, p, X) == X # real manifold does not allow complex values - @test_throws DomainError is_manifold_point(Ec, [:a, :b, :b], true) - @test_throws DomainError is_manifold_point(E, [1.0, 1.0im, 0.0], true) - @test_throws DomainError is_manifold_point(E, [1], true) - @test_throws DomainError is_tangent_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_tangent_vector(E, [1.0, 1.0im, 0.0], [1.0, 1.0, 0.0], true) # real manifold does not allow complex values - @test_throws DomainError is_tangent_vector(E, [1], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_tangent_vector(E, [0.0, 0.0, 0.0], [1.0], true) - @test_throws DomainError is_tangent_vector(E, [0.0, 0.0, 0.0], [1.0, 0.0, 1.0im], true) - @test_throws DomainError is_tangent_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c], true) + @test_throws DomainError is_point(Ec, [:a, :b, :b], true) + @test_throws DomainError is_point(E, [1.0, 1.0im, 0.0], true) + @test_throws DomainError is_point(E, [1], true) + @test_throws DomainError is_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(E, [1.0, 1.0im, 0.0], [1.0, 1.0, 0.0], true) # real manifold does not allow complex values + @test_throws DomainError is_vector(E, [1], [1.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0], true) + @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0, 0.0, 1.0im], true) + @test_throws DomainError is_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c], true) @test E^2 === Euclidean(3, 2) @test ^(E, 2) === Euclidean(3, 2) @@ -215,7 +219,7 @@ include("utils.jl") qT = zeros(ComplexF64, 3, 4) qT[1:3, 1:3] .= 1.0 q = embed(O, p) - @test is_manifold_point(N, q) + @test is_point(N, q) @test q == qT q2 = zeros(ComplexF64, 3, 4) embed!(O, q2, p) @@ -225,25 +229,48 @@ include("utils.jl") @testset "Euclidean metric tests" begin M = Euclidean(2) p = zeros(2) - C1 = christoffel_symbols_first(M, p) + A = Manifolds.get_default_atlas(M) + i = Manifolds.get_chart_index(M, A, p) + B = Manifolds.induced_basis(M, A, i, TangentSpace) + C1 = christoffel_symbols_first(M, p, B) @test size(C1) == (2, 2, 2) @test norm(C1) β‰ˆ 0.0 atol = 1e-13 - C2 = christoffel_symbols_second(M, p) + C2 = christoffel_symbols_second(M, p, B) @test size(C2) == (2, 2, 2) @test norm(C2) β‰ˆ 0.0 atol = 1e-13 - C2j = christoffel_symbols_second_jacobian(M, p) + C2j = christoffel_symbols_second_jacobian(M, p, B) @test size(C2j) == (2, 2, 2, 2) @test norm(C2j) β‰ˆ 0.0 atol = 1e-16 - @test einstein_tensor(M, p) == zeros(2, 2) - @test ricci_curvature(M, p) β‰ˆ 0 atol = 1e-16 - RC = ricci_tensor(M, p) + @test einstein_tensor(M, p, B) == zeros(2, 2) + @test ricci_curvature(M, p, B) β‰ˆ 0 atol = 1e-16 + RC = ricci_tensor(M, p, B) @test size(RC) == (2, 2) @test norm(RC) β‰ˆ 0.0 atol = 1e-16 - @test local_metric(M, p) == Diagonal(ones(2)) - @test inverse_local_metric(M, p) == Diagonal(ones(2)) - @test det_local_metric(M, p) == 1 - RT = riemann_tensor(M, p) + @test local_metric(M, p, B) == Diagonal(ones(2)) + @test inverse_local_metric(M, p, B) == Diagonal(ones(2)) + @test det_local_metric(M, p, B) == 1 + RT = riemann_tensor(M, p, B) @test size(RT) == (2, 2, 2, 2) @test norm(RT) β‰ˆ 0.0 atol = 1e-16 end + @testset "Induced Basis and local metric for EuclideanMetric" begin + struct DefaultManifold <: AbstractManifold{ℝ} end + p = zeros(3) + M = DefaultManifold() + TpM = TangentSpace(M, p) + B = induced_basis(M, Manifolds.get_default_atlas(M), p, TangentSpace) + MM = MetricManifold(M, EuclideanMetric()) + @test local_metric(MM, p, B) == Diagonal(ones(3)) + @test inverse_local_metric(MM, p, B) == Diagonal(ones(3)) + @test det_local_metric(MM, p, B) == 1.0 + DB1 = dual_basis(MM, p, B) + @test DB1 isa InducedBasis + @test DB1.vs isa ManifoldsBase.CotangentSpaceType + DB2 = induced_basis(M, Manifolds.get_default_atlas(M), p, CotangentSpace) + @test DB2 isa InducedBasis + @test DB2.vs isa ManifoldsBase.CotangentSpaceType + DDB = dual_basis(MM, p, DB2) + @test DDB isa InducedBasis + @test DDB.vs isa ManifoldsBase.TangentSpaceType + end end diff --git a/test/fixed_rank.jl b/test/fixed_rank.jl index 5efa51fece..c96b7fa98d 100644 --- a/test/fixed_rank.jl +++ b/test/fixed_rank.jl @@ -48,43 +48,24 @@ include("utils.jl") @test representation_size(Mc) == (3, 2) @test manifold_dimension(M) == 6 @test manifold_dimension(Mc) == 12 - @test !is_manifold_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2)) - @test_throws DomainError is_manifold_point( - M, - SVDMPoint([1.0 0.0; 0.0 0.0], 2), - true, - ) - @test is_manifold_point(M2, x2) + @test !is_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2)) + @test_throws DomainError is_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), true) + @test is_point(M2, x2) - @test !is_tangent_vector( + @test !is_vector( M, SVDMPoint([1.0 0.0; 0.0 1.0; 0.0 0.0]), UMVTVector(zeros(2, 1), zeros(1, 2), zeros(2, 2)), ) - @test !is_tangent_vector(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), v) - @test_throws DomainError is_tangent_vector( - M, - SVDMPoint([1.0 0.0; 0.0 0.0], 2), - v, - true, - ) - @test !is_tangent_vector(M, x, UMVTVector(x.U, v.M, x.Vt, 2)) - @test_throws DomainError is_tangent_vector( - M, - x, - UMVTVector(x.U, v.M, x.Vt, 2), - true, - ) - @test !is_tangent_vector(M, x, UMVTVector(v.U, v.M, x.Vt, 2)) - @test_throws DomainError is_tangent_vector( - M, - x, - UMVTVector(v.U, v.M, x.Vt, 2), - true, - ) + @test !is_vector(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), v) + @test_throws DomainError is_vector(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), v, true) + @test !is_vector(M, x, UMVTVector(x.U, v.M, x.Vt, 2)) + @test_throws DomainError is_vector(M, x, UMVTVector(x.U, v.M, x.Vt, 2), true) + @test !is_vector(M, x, UMVTVector(v.U, v.M, x.Vt, 2)) + @test_throws DomainError is_vector(M, x, UMVTVector(v.U, v.M, x.Vt, 2), true) - @test is_manifold_point(M, x) - @test is_tangent_vector(M, x, v) + @test is_point(M, x) + @test is_vector(M, x, v) end types = [[Matrix{Float64}, Vector{Float64}, Matrix{Float64}]] TEST_FLOAT32 && push!(types, [Matrix{Float32}, Vector{Float32}, Matrix{Float32}]) @@ -98,9 +79,9 @@ include("utils.jl") push!(pts, SVDMPoint(convert.(T, [p.U, p.S, p.Vt])...)) end for p in pts - @test is_manifold_point(M, p) + @test is_point(M, p) end - @testset "SVD MPoint Basics" begin + @testset "SVD AbstractManifoldPoint Basics" begin s = svd(x.U * Diagonal(x.S) * x.Vt) x2 = SVDMPoint(s) x3 = SVDMPoint(s.U, s.S, s.Vt) @@ -126,27 +107,19 @@ include("utils.jl") @test y2.Vt == y3.Vt @test y2 == y3 - @test is_manifold_point(M, x) + @test is_point(M, x) xM = x.U * Diagonal(x.S) * x.Vt - @test is_manifold_point(M, xM) - @test !is_manifold_point(M, xM[1:2, :]) - @test_throws DomainError is_manifold_point(M, xM[1:2, :], true) - @test_throws DomainError is_manifold_point( - FixedRankMatrices(3, 2, 1), - x, - true, - ) - @test_throws DomainError is_manifold_point( - FixedRankMatrices(3, 2, 1), - xM, - true, - ) + @test is_point(M, xM) + @test !is_point(M, xM[1:2, :]) + @test_throws DomainError is_point(M, xM[1:2, :], true) + @test_throws DomainError is_point(FixedRankMatrices(3, 2, 1), x, true) + @test_throws DomainError is_point(FixedRankMatrices(3, 2, 1), xM, true) xF1 = SVDMPoint(2 * x.U, x.S, x.Vt) - @test !is_manifold_point(M, xF1) - @test_throws DomainError is_manifold_point(M, xF1, true) + @test !is_point(M, xF1) + @test_throws DomainError is_point(M, xF1, true) xF2 = SVDMPoint(x.U, x.S, 2 * x.Vt) - @test !is_manifold_point(M, xF2) - @test_throws DomainError is_manifold_point(M, xF2, true) + @test !is_point(M, xF2) + @test_throws DomainError is_point(M, xF2, true) # copyto yC = allocate(y) @@ -168,7 +141,7 @@ include("utils.jl") w = UMVTVector(v.U, v.M, v.Vt) @test v == w w = allocate(v, number_eltype(v)) - zero_tangent_vector!(M, w, x) + zero_vector!(M, w, x) oneP = SVDMPoint(one(zeros(3, 3)), ones(2), one(zeros(2, 2)), 2) @test oneP == one(x) oneV = UMVTVector(one(zeros(3, 3)), one(zeros(2, 2)), one(zeros(2, 2)), 2) diff --git a/test/generalized_grassmann.jl b/test/generalized_grassmann.jl index e82973aae2..3f194fdbed 100644 --- a/test/generalized_grassmann.jl +++ b/test/generalized_grassmann.jl @@ -11,20 +11,11 @@ include("utils.jl") @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 2 @test base_manifold(M) === M - @test_throws DomainError is_manifold_point(M, [1.0, 0.0, 0.0, 0.0], true) - @test_throws DomainError is_manifold_point( - M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, - ) - @test !is_tangent_vector(M, x, [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_tangent_vector(M, x, [0.0, 0.0, 1.0, 0.0], true) - @test_throws DomainError is_tangent_vector( - M, - x, - 1 * im * zero_tangent_vector(M, x), - true, - ) + @test_throws DomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test !is_vector(M, x, [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(M, x, 1 * im * zero_vector(M, x), true) @test injectivity_radius(M) == Ο€ / 2 @test injectivity_radius(M, ExponentialRetraction()) == Ο€ / 2 @test injectivity_radius(M, x) == Ο€ / 2 @@ -38,7 +29,7 @@ include("utils.jl") embed!(M, y, x) @test y == z a = [1.0 0.0; 0.0 2.0; 0.0 0.0] - @test !is_manifold_point(M, a) + @test !is_point(M, a) b = similar(a) c = project(M, a) @test c == x @@ -59,8 +50,8 @@ include("utils.jl") @test inner(M, x, X, Y) == 0 y = retract(M, x, X) z = retract(M, x, Y) - @test is_manifold_point(M, y) - @test is_manifold_point(M, z) + @test is_point(M, y) + @test is_point(M, z) @test retract(M, x, X) == exp(M, x, X) a = project(M, x + X) @@ -74,10 +65,10 @@ include("utils.jl") @test vector_transport_to(M, x, X, y, ProjectionTransport()) == project(M, y, X) @testset "Type $T" for T in types pts = convert.(T, [x, y, z]) - @test !is_manifold_point(M, 2 * x) - @test_throws DomainError !is_manifold_point(M, 2 * x, true) - @test !is_tangent_vector(M, x, y) - @test_throws DomainError is_tangent_vector(M, x, y, true) + @test !is_point(M, 2 * x) + @test_throws DomainError !is_point(M, 2 * x, true) + @test !is_vector(M, x, y) + @test_throws DomainError is_vector(M, x, y, true) test_manifold( M, pts, @@ -107,13 +98,13 @@ include("utils.jl") "GeneralizedGrassmann(3, 2, [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0], β„‚)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) x = [1.0 0.0; 0.0 0.5; 0.0 0.0] x = [1im 0.0; 0.0 0.5im; 0.0 0.0] - @test is_manifold_point(M, x) - @test !is_manifold_point(M, 2 * x) + @test is_point(M, x) + @test !is_point(M, 2 * x) end end diff --git a/test/generalized_stiefel.jl b/test/generalized_stiefel.jl index a18f493dc3..b23ca02023 100644 --- a/test/generalized_stiefel.jl +++ b/test/generalized_stiefel.jl @@ -11,20 +11,11 @@ include("utils.jl") @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 3 @test base_manifold(M) === M - @test_throws DomainError is_manifold_point(M, [1.0, 0.0, 0.0, 0.0], true) - @test_throws DomainError is_manifold_point( - M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, - ) - @test !is_tangent_vector(M, x, [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_tangent_vector(M, x, [0.0, 0.0, 1.0, 0.0], true) - @test_throws DomainError is_tangent_vector( - M, - x, - 1 * im * zero_tangent_vector(M, x), - true, - ) + @test_throws DomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test !is_vector(M, x, [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(M, x, 1 * im * zero_vector(M, x), true) end @testset "Embedding and Projection" begin y = similar(x) @@ -33,7 +24,7 @@ include("utils.jl") embed!(M, y, x) @test y == z a = [1.0 0.0; 0.0 2.0; 0.0 0.0] - @test !is_manifold_point(M, a) + @test !is_point(M, a) b = similar(a) c = project(M, a) @test c == x @@ -54,8 +45,8 @@ include("utils.jl") @test inner(M, x, X, Y) == 0 y = retract(M, x, X) z = retract(M, x, Y) - @test is_manifold_point(M, y) - @test is_manifold_point(M, z) + @test is_point(M, y) + @test is_point(M, z) a = project(M, x + X) b = retract(M, x, X) c = retract(M, x, X, ProjectionRetraction()) @@ -69,10 +60,10 @@ include("utils.jl") @test vector_transport_to(M, x, X, y, ProjectionTransport()) == project(M, y, X) @testset "Type $T" for T in types pts = convert.(T, [x, y, z]) - @test !is_manifold_point(M, 2 * x) - @test_throws DomainError !is_manifold_point(M, 2 * x, true) - @test !is_tangent_vector(M, x, y) - @test_throws DomainError is_tangent_vector(M, x, y, true) + @test !is_point(M, 2 * x) + @test_throws DomainError !is_point(M, 2 * x, true) + @test !is_vector(M, x, y) + @test_throws DomainError is_vector(M, x, y, true) test_manifold( M, pts, @@ -104,13 +95,13 @@ include("utils.jl") "GeneralizedStiefel(3, 2, [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0], β„‚)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 8 - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) x = [1.0 0.0; 0.0 0.5; 0.0 0.0] x = [1im 0.0; 0.0 0.5im; 0.0 0.0] - @test is_manifold_point(M, x) - @test !is_manifold_point(M, 2 * x) + @test is_point(M, x) + @test !is_point(M, 2 * x) end end diff --git a/test/graph.jl b/test/graph.jl index 8e5c1613c8..4cc7852746 100644 --- a/test/graph.jl +++ b/test/graph.jl @@ -1,6 +1,6 @@ include("utils.jl") -@testset "Graph Manifold" begin +@testset "Graph AbstractManifold" begin M = Euclidean(2) x = [[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]] y = [[4.0, 5.0], [6.0, 7.0], [8.0, 9.0]] @@ -13,14 +13,14 @@ include("utils.jl") @test manifold_dimension(N) == manifold_dimension(M) * nv(G) @test manifold_dimension(GraphManifold(G, M, EdgeManifold())) == manifold_dimension(M) * ne(G) - @test is_manifold_point(N, x) - @test !is_manifold_point(N, [x..., [0.0, 0.0]]) # an entry too much - @test_throws DomainError is_manifold_point(N, [x..., [0.0, 0.0]], true) - @test is_tangent_vector(N, x, log(N, x, y)) - @test !is_tangent_vector(N, x[1:2], log(N, x, y)) - @test_throws DomainError is_tangent_vector(N, x[1:2], log(N, x, y), true) - @test !is_tangent_vector(N, x[1:2], log(N, x, y)[1:2]) - @test_throws DomainError is_tangent_vector(N, x, log(N, x, y)[1:2], true) + @test is_point(N, x) + @test !is_point(N, [x..., [0.0, 0.0]]) # an entry too much + @test_throws DomainError is_point(N, [x..., [0.0, 0.0]], true) + @test is_vector(N, x, log(N, x, y)) + @test !is_vector(N, x[1:2], log(N, x, y)) + @test_throws DomainError is_vector(N, x[1:2], log(N, x, y), true) + @test !is_vector(N, x[1:2], log(N, x, y)[1:2]) + @test_throws DomainError is_vector(N, x, log(N, x, y)[1:2], true) @test incident_log(N, x) == [x[2] - x[1], x[1] - x[2] + x[3] - x[2], x[2] - x[3]] pts = [x, y, z] @@ -34,18 +34,18 @@ include("utils.jl") GraphManifold Graph: {3, 2} undirected simple Int64 graph - Manifold on vertices: + AbstractManifold on vertices: Euclidean(2; field = ℝ)""" NE = GraphManifold(G, M, EdgeManifold()) - @test is_manifold_point(NE, x[1:2]) - @test !is_manifold_point(NE, x) # an entry too much - @test_throws DomainError is_manifold_point(NE, x, true) - @test is_tangent_vector(NE, x[1:2], log(N, x, y)[1:2]) - @test !is_tangent_vector(NE, x, log(N, x, y)) - @test_throws DomainError is_tangent_vector(NE, x, log(N, x, y), true) - @test !is_tangent_vector(N, x[1:2], log(N, x, y)) - @test_throws DomainError is_tangent_vector(NE, x[1:2], log(N, x, y), true) + @test is_point(NE, x[1:2]) + @test !is_point(NE, x) # an entry too much + @test_throws DomainError is_point(NE, x, true) + @test is_vector(NE, x[1:2], log(N, x, y)[1:2]) + @test !is_vector(NE, x, log(N, x, y)) + @test_throws DomainError is_vector(NE, x, log(N, x, y), true) + @test !is_vector(N, x[1:2], log(N, x, y)) + @test_throws DomainError is_vector(NE, x[1:2], log(N, x, y), true) test_manifold( NE, @@ -57,7 +57,7 @@ include("utils.jl") GraphManifold Graph: {3, 2} undirected simple Int64 graph - Manifold on edges: + AbstractManifold on edges: Euclidean(2; field = ℝ)""" G2 = SimpleDiGraph(3) diff --git a/test/grassmann.jl b/test/grassmann.jl index cf25c9a2be..6b2219f1d1 100644 --- a/test/grassmann.jl +++ b/test/grassmann.jl @@ -7,37 +7,33 @@ include("utils.jl") @test repr(M) == "Grassmann(3, 2, ℝ)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 2 - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_manifold_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) - @test_throws DomainError is_tangent_vector( + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws DomainError is_vector( M, [2.0 0.0; 0.0 1.0; 0.0 0.0], zeros(3, 2), true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], ones(3, 2), true, ) - @test is_manifold_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], true) - @test_throws DomainError is_manifold_point( - M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, - ) - @test is_tangent_vector( + @test is_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws DomainError is_point(M, 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - zero_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), + zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - 1im * zero_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), + 1im * zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), true, ) @test injectivity_radius(M) == Ο€ / 2 @@ -110,7 +106,7 @@ include("utils.jl") v = [0.0 0.0; 0.0 0.0; 0.0 1.0] y = exp(M, x, v) @test vector_transport_to(M, x, v, y, ProjectionTransport()) == project(M, y, v) - @test is_tangent_vector( + @test is_vector( M, y, vector_transport_to(M, x, v, y, ProjectionTransport()), @@ -126,28 +122,28 @@ include("utils.jl") @test repr(M) == "Grassmann(3, 2, β„‚)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) @test Manifolds.allocation_promotion_function(M, exp!, (1,)) == complex - @test_throws DomainError is_manifold_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws DomainError is_vector( M, [2.0 0.0; 0.0 1.0; 0.0 0.0], zeros(3, 2), true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], ones(3, 2), true, ) - @test is_tangent_vector( + @test is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - 1im * zero_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), + 1im * zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), ) - @test is_manifold_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]) + @test is_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]) @test injectivity_radius(M) == Ο€ / 2 end types = [Matrix{ComplexF64}] @@ -194,10 +190,10 @@ include("utils.jl") @testset "Complex and conjugate" begin G = Grassmann(3, 1, β„‚) p = reshape([im, 0.0, 0.0], 3, 1) - @test is_manifold_point(G, p) + @test is_point(G, p) X = reshape([-0.5; 0.5; 0], 3, 1) - @test_throws DomainError is_tangent_vector(G, p, X, true) + @test_throws DomainError is_vector(G, p, X, true) Y = project(G, p, X) - @test is_tangent_vector(G, p, Y) + @test is_vector(G, p, Y) end end diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 94da76e632..3f0fb79656 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -73,25 +73,25 @@ using NLsolve @testset "Real" begin G = GeneralLinear(3) - @test_throws DomainError is_manifold_point(G, randn(2, 3), true) - @test_throws DomainError is_manifold_point(G, randn(2, 2), true) - @test_throws DomainError is_manifold_point(G, randn(ComplexF64, 3, 3), true) - @test_throws DomainError is_manifold_point(G, zeros(3, 3), true) - @test_throws DomainError is_manifold_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point(G, randn(2, 3), true) + @test_throws DomainError is_point(G, randn(2, 2), true) + @test_throws DomainError is_point(G, randn(ComplexF64, 3, 3), true) + @test_throws DomainError is_point(G, zeros(3, 3), true) + @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) + @test_throws DomainError is_point( G, make_identity(GeneralLinear(2), ones(2, 2)), true, ) - @test is_manifold_point(G, Float64[0 0 1; 0 1 1; 1 1 1], true) - @test is_manifold_point(G, make_identity(G, ones(3, 3)), true) - @test_throws DomainError is_tangent_vector( + @test is_point(G, Float64[0 0 1; 0 1 1; 1 1 1], true) + @test is_point(G, make_identity(G, ones(3, 3)), true) + @test_throws DomainError is_vector( G, Float64[0 1 1; 0 1 1; 1 0 0], randn(3, 3), true, ) - @test is_tangent_vector(G, Float64[0 0 1; 0 1 1; 1 1 1], randn(3, 3), true) + @test is_vector(G, Float64[0 0 1; 0 1 1; 1 1 1], randn(3, 3), true) types = [Matrix{Float64}] pts = [ @@ -145,25 +145,25 @@ using NLsolve @testset "Complex" begin G = GeneralLinear(2, β„‚) - @test_throws DomainError is_manifold_point(G, randn(ComplexF64, 2, 3), true) - @test_throws DomainError is_manifold_point(G, randn(ComplexF64, 3, 3), true) - @test_throws DomainError is_manifold_point(G, zeros(2, 2), true) - @test_throws DomainError is_manifold_point(G, ComplexF64[1 im; 1 im], true) - @test is_manifold_point(G, ComplexF64[1 1; im 1], true) - @test is_manifold_point(G, make_identity(G, ones(ComplexF64, 2, 2)), true) - @test_throws DomainError is_manifold_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point(G, randn(ComplexF64, 2, 3), true) + @test_throws DomainError is_point(G, randn(ComplexF64, 3, 3), true) + @test_throws DomainError is_point(G, zeros(2, 2), true) + @test_throws DomainError is_point(G, ComplexF64[1 im; 1 im], true) + @test is_point(G, ComplexF64[1 1; im 1], true) + @test is_point(G, make_identity(G, ones(ComplexF64, 2, 2)), true) + @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) + @test_throws DomainError is_point( G, make_identity(GeneralLinear(3), ones(3, 3)), true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( G, ComplexF64[im im; im im], randn(ComplexF64, 2, 2), true, ) - @test is_tangent_vector(G, ComplexF64[1 im; im im], randn(ComplexF64, 2, 2), true) + @test is_vector(G, ComplexF64[1 im; im im], randn(ComplexF64, 2, 2), true) types = [Matrix{ComplexF64}] pts = [ diff --git a/test/groups/group_utils.jl b/test/groups/group_utils.jl index b4814e6f1a..26f68d0575 100644 --- a/test/groups/group_utils.jl +++ b/test/groups/group_utils.jl @@ -1,6 +1,6 @@ struct NotImplementedOperation <: AbstractGroupOperation end -struct NotImplementedManifold <: Manifold{ℝ} end +struct NotImplementedManifold <: AbstractManifold{ℝ} end struct NotImplementedGroupDecorator{M} <: AbstractDecoratorManifold{ℝ} manifold::M diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index 01aabb0d77..c77c933cc3 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -15,7 +15,7 @@ include("group_utils.jl") eg = Identity(G, [0.0, 0.0]) @test repr(eg) === "Identity($(G), $([0.0, 0.0]))" @test number_eltype(eg) == Bool - @test is_manifold_point(G, eg) # identity transparent + @test is_point(G, eg) # identity transparent p = similar(x) copyto!(p, eg) @test p == eg.p @@ -176,7 +176,7 @@ include("group_utils.jl") G = GroupManifold(NotImplementedManifold(), Manifolds.AdditionOperation()) test_group(G, [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], [], [[1.0, 2.0]]) - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point( G, Identity( GroupManifold(NotImplementedManifold(), NotImplementedOperation()), diff --git a/test/groups/metric.jl b/test/groups/metric.jl index 736e961550..0741c34724 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -4,40 +4,43 @@ include("group_utils.jl") using OrdinaryDiffEq import Manifolds: invariant_metric_dispatch, default_metric_dispatch, local_metric -struct TestInvariantMetricBase <: Metric end +struct TestInvariantMetricBase <: AbstractMetric end function local_metric( - ::MetricManifold{𝔽,<:Manifold,TestInvariantMetricBase}, + ::MetricManifold{𝔽,<:AbstractManifold,TestInvariantMetricBase}, ::Identity, + ::DefaultOrthonormalBasis, ) where {𝔽} return Diagonal([1.0, 2.0, 3.0]) end function local_metric( - ::MetricManifold{𝔽,<:Manifold,<:InvariantMetric{TestInvariantMetricBase}}, + ::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric{TestInvariantMetricBase}}, p, + ::DefaultOrthonormalBasis, ) where {𝔽} return Diagonal([1.0, 2.0, 3.0]) end -struct TestBiInvariantMetricBase <: Metric end +struct TestBiInvariantMetricBase <: AbstractMetric end function invariant_metric_dispatch( - ::MetricManifold{𝔽,<:Manifold,<:InvariantMetric{TestBiInvariantMetricBase}}, + ::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric{TestBiInvariantMetricBase}}, ::ActionDirection, ) where {𝔽} return Val(true) end function local_metric( - ::MetricManifold{𝔽,<:Manifold,<:TestBiInvariantMetricBase}, + ::MetricManifold{𝔽,<:AbstractManifold,<:TestBiInvariantMetricBase}, ::Identity, + ::DefaultOrthonormalBasis, ) where {𝔽} return Diagonal(0.4I, 3) end -struct TestInvariantMetricManifold <: Manifold{ℝ} end +struct TestInvariantMetricManifold <: AbstractManifold{ℝ} end -struct TestDefaultInvariantMetricManifold <: Manifold{ℝ} end +struct TestDefaultInvariantMetricManifold <: AbstractManifold{ℝ} end function default_metric_dispatch( ::MetricManifold{ @@ -98,16 +101,22 @@ invariant_metric_dispatch(::TestDefaultInvariantMetricManifold, ::RightAction) = @testset "inner/norm" begin SO3 = SpecialOrthogonal(3) p = exp(hat(SO3, Identity(SO3, e), [1.0, 2.0, 3.0])) - X = hat(SO3, Identity(SO3, e), [2.0, 3.0, 4.0]) - Y = hat(SO3, Identity(SO3, e), [3.0, 4.0, 1.0]) + + B = DefaultOrthonormalBasis() + + fX = ManifoldsBase.TFVector([2.0, 3.0, 4.0], B) + fY = ManifoldsBase.TFVector([3.0, 4.0, 1.0], B) + X = hat(SO3, Identity(SO3, e), fX.data) + Y = hat(SO3, Identity(SO3, e), fY.data) G = MetricManifold(SO3, lmetric) - @test inner(G, p, X, Y) β‰ˆ dot(X, Diagonal([1.0, 2.0, 3.0]) * Y) - @test norm(G, p, X) β‰ˆ sqrt(inner(G, p, X, X)) + @test inner(G, p, fX, fY) β‰ˆ dot(fX.data, Diagonal([1.0, 2.0, 3.0]) * fY.data) + @test norm(G, p, fX) β‰ˆ sqrt(inner(G, p, fX, fX)) G = MetricManifold(SO3, rmetric) - @test inner(G, p, X, Y) β‰ˆ dot(p * X * p', Diagonal([1.0, 2.0, 3.0]) * p * Y * p') - @test norm(G, p, X) β‰ˆ sqrt(inner(G, p, X, X)) + @test_broken inner(G, p, fX, fY) β‰ˆ + dot(p * X * p', Diagonal([1.0, 2.0, 3.0]) * p * Y * p') + @test_broken norm(G, p, fX) β‰ˆ sqrt(inner(G, p, fX, fX)) end @testset "log/exp bi-invariant" begin @@ -129,7 +138,7 @@ invariant_metric_dispatch(::TestDefaultInvariantMetricManifold, ::RightAction) = T3 = TranslationGroup(3) p = [1.0, 2.0, 3.0] X = [3.0, 5.0, 6.0] - @test isapprox(T3, exp(MetricManifold(T3, lmetric), p, X), p .+ X) - @test isapprox(T3, exp(MetricManifold(T3, rmetric), p, X), p .+ X) + @test_broken isapprox(T3, exp(MetricManifold(T3, lmetric), p, X), p .+ X) + @test_broken isapprox(T3, exp(MetricManifold(T3, rmetric), p, X), p .+ X) end end diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index a1222c6551..f006ad0523 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -24,9 +24,9 @@ include("group_utils.jl") v_pts = [Manifolds.prod_point(shape_se, tuple_v...)] X = log(G, pts[1], pts[1]) - Y = zero_tangent_vector(G, pts[1]) - Z = Manifolds.allocate_result(G, zero_tangent_vector, pts[1]) - Z = zero_tangent_vector!(M, Z, pts[1]) + Y = zero_vector(G, pts[1]) + Z = Manifolds.allocate_result(G, zero_vector, pts[1]) + Z = zero_vector!(M, Z, pts[1]) @test norm(G, pts[1], X) β‰ˆ 0 @test norm(G, pts[1], Y) β‰ˆ 0 @test norm(G, pts[1], Z) β‰ˆ 0 diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index 3125a02bc8..3e10ed3a2e 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -48,33 +48,33 @@ using NLsolve @testset "Real" begin G = SpecialLinear(3) - @test_throws DomainError is_manifold_point(G, randn(2, 3), true) - @test_throws DomainError is_manifold_point(G, Float64[2 1; 1 1], true) - @test_throws DomainError is_manifold_point(G, [1 0 im; im 0 0; 0 -1 0], true) - @test_throws DomainError is_manifold_point(G, zeros(3, 3), true) - @test_throws DomainError is_manifold_point(G, Float64[1 3 3; 1 1 2; 1 2 3], true) - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point(G, randn(2, 3), true) + @test_throws DomainError is_point(G, Float64[2 1; 1 1], true) + @test_throws DomainError is_point(G, [1 0 im; im 0 0; 0 -1 0], true) + @test_throws DomainError is_point(G, zeros(3, 3), true) + @test_throws DomainError is_point(G, Float64[1 3 3; 1 1 2; 1 2 3], true) + @test_throws DomainError is_point( G, make_identity(SpecialLinear(2), ones(2, 2)), true, ) - @test is_manifold_point(G, Float64[1 1 1; 2 2 1; 2 3 3], true) - @test is_manifold_point(G, make_identity(G, ones(3, 3)), true) - @test_throws DomainError is_tangent_vector( + @test is_point(G, Float64[1 1 1; 2 2 1; 2 3 3], true) + @test is_point(G, make_identity(G, ones(3, 3)), true) + @test_throws DomainError is_vector( G, Float64[2 3 2; 3 1 2; 1 1 1], randn(3, 3), true; atol=1e-6, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( G, Float64[2 1 2; 3 2 2; 2 2 1], Float64[2 1 -1; 2 2 1; 1 1 -1], true; atol=1e-6, ) - @test is_tangent_vector( + @test is_vector( G, Float64[2 1 2; 3 2 2; 2 2 1], Float64[-1 -1 -1; 1 -1 2; -1 -1 2], @@ -126,15 +126,15 @@ using NLsolve @testset "project" begin p = randn(3, 3) - @test !is_manifold_point(G, p) + @test !is_point(G, p) q = project(G, p) - @test is_manifold_point(G, q) + @test is_point(G, q) @test project(G, q) β‰ˆ q X = randn(3, 3) - @test !is_tangent_vector(G, q, X; atol=1e-6) + @test !is_vector(G, q, X; atol=1e-6) Y = project(G, q, X) - @test is_tangent_vector(G, q, Y; atol=1e-6) + @test is_vector(G, q, Y; atol=1e-6) @test project(G, q, Y) β‰ˆ Y end end @@ -142,36 +142,32 @@ using NLsolve @testset "Complex" begin G = SpecialLinear(2, β„‚) - @test_throws DomainError is_manifold_point(G, randn(ComplexF64, 2, 3), true) - @test_throws DomainError is_manifold_point(G, randn(2, 2), true) - @test_throws DomainError is_manifold_point( - G, - ComplexF64[1 0 im; im 0 0; 0 -1 0], - true, - ) - @test_throws DomainError is_manifold_point(G, ComplexF64[1 im; im 1], true) - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point(G, randn(ComplexF64, 2, 3), true) + @test_throws DomainError is_point(G, randn(2, 2), true) + @test_throws DomainError is_point(G, ComplexF64[1 0 im; im 0 0; 0 -1 0], true) + @test_throws DomainError is_point(G, ComplexF64[1 im; im 1], true) + @test_throws DomainError is_point( G, make_identity(SpecialLinear(2), ones(ComplexF64, 2, 2)), true, ) - @test is_manifold_point(G, ComplexF64[im 1; -2 im], true) - @test is_manifold_point(G, make_identity(G, ones(3, 3)), true) - @test_throws DomainError is_tangent_vector( + @test is_point(G, ComplexF64[im 1; -2 im], true) + @test is_point(G, make_identity(G, ones(3, 3)), true) + @test_throws DomainError is_vector( G, ComplexF64[-1+im -1; -im 1], ComplexF64[1-im 1+im; 1 -1+im], true; atol=1e-6, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( G, ComplexF64[1 1+im; -1+im -1], ComplexF64[1-im -1-im; -im im], true; atol=1e-6, ) - @test is_tangent_vector( + @test is_vector( G, ComplexF64[1 1+im; -1+im -1], ComplexF64[1-im 1+im; 1 -1+im], @@ -226,15 +222,15 @@ using NLsolve @testset "project" begin p = randn(ComplexF64, 2, 2) - @test !is_manifold_point(G, p) + @test !is_point(G, p) q = project(G, p) - @test is_manifold_point(G, q) + @test is_point(G, q) @test project(G, q) β‰ˆ q X = randn(ComplexF64, 2, 2) - @test !is_tangent_vector(G, q, X; atol=1e-6) + @test !is_vector(G, q, X; atol=1e-6) Y = project(G, q, X) - @test is_tangent_vector(G, q, Y; atol=1e-6) + @test is_vector(G, q, Y; atol=1e-6) @test project(G, q, Y) β‰ˆ Y end end diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index 7e25a9474b..a115df2e82 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -54,7 +54,7 @@ include("group_utils.jl") @test (@inferred Manifolds.decorator_group_dispatch(DM)) === Val(true) @test Manifolds.is_group_decorator(DM) @test base_group(DM) === G - @test_throws DomainError is_manifold_point( + @test_throws DomainError is_point( DM, make_identity(TranslationGroup(3), [1, 2, 3]), true, @@ -89,6 +89,7 @@ include("group_utils.jl") exp_log_atol_multiplier=20, retraction_atol_multiplier=12, is_tangent_atol_multiplier=1.2, + test_atlases=(Manifolds.RetractionAtlas(),), ) @test injectivity_radius(G) == injectivity_radius(M) diff --git a/test/hyperbolic.jl b/test/hyperbolic.jl index 2ad3102d13..328a18c202 100644 --- a/test/hyperbolic.jl +++ b/test/hyperbolic.jl @@ -13,25 +13,15 @@ include("utils.jl") @test isinf(injectivity_radius(M, ExponentialRetraction())) @test isinf(injectivity_radius(M, [0.0, 0.0, 1.0])) @test isinf(injectivity_radius(M, [0.0, 0.0, 1.0], ExponentialRetraction())) - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_manifold_point(M, [2.0, 0.0, 0.0], true) - @test !is_manifold_point(M, [2.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test !is_point(M, [2.0, 0.0, 0.0]) + @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) @test Manifolds.default_metric_dispatch(M, MinkowskiMetric()) === Val{true}() - @test_throws DomainError is_tangent_vector( - M, - [1.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - true, - ) - @test !is_tangent_vector(M, [0.0, 0.0, 1.0], [1.0, 0.0, 1.0]) - @test_throws DomainError is_tangent_vector( - M, - [0.0, 0.0, 1.0], - [1.0, 0.0, 1.0], - true, - ) + @test_throws DomainError is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], true) + @test !is_vector(M, [0.0, 0.0, 1.0], [1.0, 0.0, 1.0]) + @test_throws DomainError is_vector(M, [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], true) @test is_default_metric(M, MinkowskiMetric()) @test Manifolds.default_metric_dispatch(M, MinkowskiMetric()) === Val{true}() @test manifold_dimension(M) == 2 @@ -70,35 +60,23 @@ include("utils.jl") @test convert(HyperboloidTVector, X).value == XH.value @test convert(AbstractVector, XH) == X @test convert(HyperboloidPoint, p).value == pH.value - is_manifold_point(M, pH) + is_point(M, pH) pB = convert(PoincareBallPoint, p) @test pB.value == convert(PoincareBallPoint, pH).value - @test is_manifold_point(M, pB) + @test is_point(M, pB) @test convert(AbstractVector, pB) == p # convert back yields again p @test convert(HyperboloidPoint, pB).value == pH.value - @test_throws DomainError is_manifold_point( - M, - PoincareBallPoint([0.9, 0.0, 0.0]), - true, - ) - @test_throws DomainError is_manifold_point(M, PoincareBallPoint([1.0, 0.0]), true) + @test_throws DomainError is_point(M, PoincareBallPoint([0.9, 0.0, 0.0]), true) + @test_throws DomainError is_point(M, PoincareBallPoint([1.0, 0.0]), true) - @test is_tangent_vector(M, pB, PoincareBallTVector([2.0, 2.0])) + @test is_vector(M, pB, PoincareBallTVector([2.0, 2.0])) pS = convert(PoincareHalfSpacePoint, p) pS2 = convert(PoincareHalfSpacePoint, pB) pS3 = convert(PoincareHalfSpacePoint, pH) - @test_throws DomainError is_manifold_point( - M, - PoincareHalfSpacePoint([0.0, 0.0, 1.0]), - true, - ) - @test_throws DomainError is_manifold_point( - M, - PoincareHalfSpacePoint([0.0, -1.0]), - true, - ) + @test_throws DomainError is_point(M, PoincareHalfSpacePoint([0.0, 0.0, 1.0]), true) + @test_throws DomainError is_point(M, PoincareHalfSpacePoint([0.0, -1.0]), true) @test pS.value == pS2.value @test pS.value == pS3.value @@ -235,7 +213,7 @@ include("utils.jl") B = get_basis(M, p, DefaultOrthonormalBasis()) V = get_vectors(M, p, B) for v in V - @test is_tangent_vector(M, p, v, true) + @test is_vector(M, p, v, true) for b in [DefaultOrthonormalBasis(), DiagonalizingOrthonormalBasis(V[1])] @test isapprox(M, p, v, get_vector(M, p, get_coordinates(M, p, v, b), b)) end @@ -244,7 +222,7 @@ include("utils.jl") @test inner(M, p, v, w) β‰ˆ (v == w ? 1 : 0) end X = 0.5 * V[1] + 1.0 .* V[2] - @test is_tangent_vector(M, p, X) + @test is_vector(M, p, X) c = get_coordinates(M, p, X, B) @test c β‰ˆ [0.5, 1.0] B2 = DiagonalizingOrthonormalBasis(X) diff --git a/test/lorentz.jl b/test/lorentz.jl new file mode 100644 index 0000000000..68be747c3f --- /dev/null +++ b/test/lorentz.jl @@ -0,0 +1,7 @@ +include("utils.jl") + +@testset "Minkowski Metric" begin + N = Euclidean(3) + M = MetricManifold(N, MinkowskiMetric()) + @test local_metric(M, zeros(3)) == Diagonal([1.0, 1.0, -1.0]) +end diff --git a/test/metric.jl b/test/metric.jl index bd8a593bca..a1e9e02acf 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -1,48 +1,60 @@ using FiniteDifferences, ForwardDiff using LinearAlgebra: I using StatsBase: AbstractWeights, pweights -import Manifolds: mean!, median! +import Manifolds: mean!, median!, InducedBasis, induced_basis, get_chart_index include("utils.jl") -struct TestEuclidean{N} <: Manifold{ℝ} end -struct TestEuclideanMetric <: Metric end +struct TestEuclidean{N} <: AbstractManifold{ℝ} end +struct TestEuclideanMetric <: AbstractMetric end Manifolds.manifold_dimension(::TestEuclidean{N}) where {N} = N function Manifolds.local_metric( M::MetricManifold{ℝ,<:TestEuclidean,<:TestEuclideanMetric}, - x, + ::Any, + ::InducedBasis, +) + return Diagonal(1.0:manifold_dimension(M)) +end +function Manifolds.local_metric( + M::MetricManifold{ℝ,<:TestEuclidean,<:TestEuclideanMetric}, + ::Any, + ::InducedBasis, ) return Diagonal(1.0:manifold_dimension(M)) end -struct TestSphere{N,T} <: Manifold{ℝ} +struct TestSphere{N,T} <: AbstractManifold{ℝ} r::T end -struct TestSphericalMetric <: Metric end +struct TestSphericalMetric <: AbstractMetric end Manifolds.manifold_dimension(::TestSphere{N}) where {N} = N -function Manifolds.local_metric(M::MetricManifold{ℝ,<:TestSphere,<:TestSphericalMetric}, x) +function Manifolds.local_metric( + M::MetricManifold{ℝ,<:TestSphere,<:TestSphericalMetric}, + p, + ::InducedBasis, +) r = base_manifold(M).r - d = allocate(x) + d = allocate(p) d[1] = r^2 - d[2] = d[1] * sin(x[1])^2 + d[2] = d[1] * sin(p[1])^2 return Diagonal(d) end sph_to_cart(ΞΈ, Ο•) = [cos(Ο•) * sin(ΞΈ), sin(Ο•) * sin(ΞΈ), cos(ΞΈ)] -struct BaseManifold{N} <: Manifold{ℝ} end -struct BaseManifoldMetric{M} <: Metric end -struct DefaultBaseManifoldMetric <: Metric end -struct NotImplementedMetric <: Metric end +struct BaseManifold{N} <: AbstractManifold{ℝ} end +struct BaseManifoldMetric{M} <: AbstractMetric end +struct DefaultBaseManifoldMetric <: AbstractMetric end +struct NotImplementedMetric <: AbstractMetric end Manifolds.manifold_dimension(::BaseManifold{N}) where {N} = N -Manifolds.inner(::BaseManifold, x, v, w) = 2 * dot(v, w) -Manifolds.exp!(::BaseManifold, y, x, v) = y .= x + 2 * v -Manifolds.log!(::BaseManifold, v, x, y) = v .= (y - x) / 2 -Manifolds.project!(::BaseManifold, w, x, v) = w .= 2 .* v -Manifolds.project!(::BaseManifold, y, x) = (y .= x) +Manifolds.inner(::BaseManifold, p, X, Y) = 2 * dot(X, Y) +Manifolds.exp!(::BaseManifold, q, p, X) = q .= p + 2 * X +Manifolds.log!(::BaseManifold, Y, p, q) = Y .= (q - p) / 2 +Manifolds.project!(::BaseManifold, Y, p, X) = Y .= 2 .* X +Manifolds.project!(::BaseManifold, q, p) = (q .= p) Manifolds.injectivity_radius(::BaseManifold) = Inf Manifolds.injectivity_radius(::BaseManifold, ::Any) = Inf Manifolds.injectivity_radius(::BaseManifold, ::AbstractRetractionMethod) = Inf @@ -51,32 +63,53 @@ Manifolds.injectivity_radius(::BaseManifold, ::Any, ::AbstractRetractionMethod) Manifolds.injectivity_radius(::BaseManifold, ::Any, ::ExponentialRetraction) = Inf function Manifolds.local_metric( ::MetricManifold{ℝ,BaseManifold{N},BaseManifoldMetric{N}}, - x, + p, + ::InducedBasis, ) where {N} - return 2 * one(x * x') + return 2 * one(p * p') end function Manifolds.exp!( M::MetricManifold{ℝ,BaseManifold{N},BaseManifoldMetric{N}}, - y, - x, - v, + q, + p, + X, +) where {N} + return exp!(base_manifold(M), q, p, X) +end +function Manifolds.vector_transport_to!(::BaseManifold, Y, p, X, q, ::ParallelTransport) + return (Y .= X) +end +function Manifolds.get_basis( + ::BaseManifold{N}, + p, + B::DefaultOrthonormalBasis{<:Any,ManifoldsBase.TangentSpaceType}, ) where {N} - return exp!(base_manifold(M), y, x, v) + return CachedBasis(B, [(Matrix{eltype(p)}(I, N, N)[:, i]) for i in 1:N]) end -function Manifolds.vector_transport_to!(::BaseManifold, vto, x, v, y, ::ParallelTransport) - return (vto .= v) +function Manifolds.get_coordinates!( + ::BaseManifold, + Y, + p, + X, + ::DefaultOrthonormalBasis{<:Any,ManifoldsBase.TangentSpaceType}, +) + return Y .= X end -function Manifolds.get_basis(::BaseManifold{N}, x, B::DefaultOrthonormalBasis) where {N} - return CachedBasis(B, [(Matrix{eltype(x)}(I, N, N)[:, i]) for i in 1:N]) +function Manifolds.get_vector!( + ::BaseManifold, + Y, + p, + X, + ::DefaultOrthonormalBasis{<:Any,ManifoldsBase.TangentSpaceType}, +) + return Y .= X end -Manifolds.get_coordinates!(::BaseManifold, Y, p, X, ::DefaultOrthonormalBasis) = (Y .= X) -Manifolds.get_vector!(::BaseManifold, Y, p, X, ::DefaultOrthonormalBasis) = (Y .= X) Manifolds.default_metric_dispatch(::BaseManifold, ::DefaultBaseManifoldMetric) = Val(true) function Manifolds.projected_distribution(M::BaseManifold, d) return ProjectedPointDistribution(M, d, project!, rand(d)) end -function Manifolds.projected_distribution(M::BaseManifold, d, x) - return ProjectedPointDistribution(M, d, project!, x) +function Manifolds.projected_distribution(M::BaseManifold, d, p) + return ProjectedPointDistribution(M, d, project!, p) end function Manifolds.mean!(::BaseManifold, y, x::AbstractVector, w::AbstractVector; kwargs...) return fill!(y, 1) @@ -132,7 +165,7 @@ end # some tests failed due to insufficient accuracy for a particularly bad RNG state Random.seed!(42) @testset "Metric Basics" begin - #one for MetricManifold, one for Manifold & Metric + #one for MetricManifold, one for AbstractManifold & Metric @test length(methods(is_default_metric)) == 2 end @@ -141,29 +174,31 @@ end g = TestEuclideanMetric() M = MetricManifold(E, g) - x = [1.0, 2.0, 3.0] - v = [2.0, 3.0, 4.0] - @test_throws ErrorException exp(M, x, v) - if VERSION β‰₯ v"1.1" - using OrdinaryDiffEq - exp(M, x, v) - end + p = [1.0, 2.0, 3.0] + X = [2.0, 3.0, 4.0] + @test_throws ErrorException exp(M, p, X) + using OrdinaryDiffEq + exp(M, p, X) end @testset "Local Metric Error message" begin M = MetricManifold(BaseManifold{2}(), NotImplementedMetric()) - @test_throws MethodError local_metric(M, [3, 4]) + A = Manifolds.get_default_atlas(M) + p = [3, 4] + i = get_chart_index(M, A, p) + + B = induced_basis(M, A, i, TangentSpace) + @test_throws MethodError local_metric(M, p, B) end @testset "scaled Euclidean metric" begin n = 3 E = TestEuclidean{n}() g = TestEuclideanMetric() M = MetricManifold(E, g) + A = Manifolds.get_default_atlas(M) @test repr(M) == "MetricManifold(TestEuclidean{3}(), TestEuclideanMetric())" - if VERSION β‰₯ v"1.3" - @test TestEuclideanMetric()(E) === M - @test TestEuclideanMetric(E) === M - end + @test TestEuclideanMetric()(E) === M + @test TestEuclideanMetric(E) === M G = Diagonal(1.0:n) invG = inv(G) @@ -171,54 +206,67 @@ end @test base_manifold(M) === E @test metric(M) === g - @test_throws MethodError local_metric_jacobian(E, zeros(3)) - @test_throws MethodError christoffel_symbols_second_jacobian(E, zeros(3)) + i_zeros = get_chart_index(M, A, zeros(3)) + B_i_zeros = induced_basis(M, A, i_zeros, TangentSpace) + @test_throws MethodError local_metric_jacobian(E, zeros(3), B_i_zeros) + @test_throws MethodError christoffel_symbols_second_jacobian(E, zeros(3), B_i_zeros) for vtype in (Vector, MVector{n}) - x, v, w = vtype(randn(n)), vtype(randn(n)), vtype(randn(n)) + p, X, Y = vtype(randn(n)), vtype(randn(n)), vtype(randn(n)) + + chart_p = get_chart_index(M, A, p) + B_chart_p = induced_basis(M, A, chart_p, TangentSpace) - @test check_manifold_point(M, x) == check_manifold_point(E, x) - @test check_tangent_vector(M, x, v) == check_tangent_vector(E, x, v) + @test check_point(M, p) == check_point(E, p) + @test check_vector(M, p, X) == check_vector(E, p, X) - @test local_metric(M, x) β‰ˆ G - @test inverse_local_metric(M, x) β‰ˆ invG - @test det_local_metric(M, x) β‰ˆ *(1.0:n...) - @test log_local_metric_density(M, x) β‰ˆ sum(log.(1.0:n)) / 2 - @test inner(M, x, v, w) β‰ˆ dot(v, G * w) atol = 1e-6 - @test norm(M, x, v) β‰ˆ sqrt(dot(v, G * v)) atol = 1e-6 + @test local_metric(M, p, B_chart_p) β‰ˆ G + @test inverse_local_metric(M, p, B_chart_p) β‰ˆ invG + @test det_local_metric(M, p, B_chart_p) β‰ˆ *(1.0:n...) + @test log_local_metric_density(M, p, B_chart_p) β‰ˆ sum(log.(1.0:n)) / 2 + + fX = ManifoldsBase.TFVector(X, B_chart_p) + fY = ManifoldsBase.TFVector(Y, B_chart_p) + @test inner(M, p, fX, fY) β‰ˆ dot(X, G * Y) atol = 1e-6 + @test norm(M, p, fX) β‰ˆ sqrt(dot(X, G * X)) atol = 1e-6 if VERSION β‰₯ v"1.1" T = 0:0.5:10 - @test geodesic(M, x, v, T) β‰ˆ [x + t * v for t in T] atol = 1e-6 + @test geodesic(M, p, X, T) β‰ˆ [p + t * X for t in T] atol = 1e-6 end - @test christoffel_symbols_first(M, x) β‰ˆ zeros(n, n, n) atol = 1e-6 - @test christoffel_symbols_second(M, x) β‰ˆ zeros(n, n, n) atol = 1e-6 - @test riemann_tensor(M, x) β‰ˆ zeros(n, n, n, n) atol = 1e-6 - @test ricci_tensor(M, x) β‰ˆ zeros(n, n) atol = 1e-6 - @test ricci_curvature(M, x) β‰ˆ 0 atol = 1e-6 - @test gaussian_curvature(M, x) β‰ˆ 0 atol = 1e-6 - @test einstein_tensor(M, x) β‰ˆ zeros(n, n) atol = 1e-6 + @test christoffel_symbols_first(M, p, B_chart_p) β‰ˆ zeros(n, n, n) atol = 1e-6 + @test christoffel_symbols_second(M, p, B_chart_p) β‰ˆ zeros(n, n, n) atol = 1e-6 + @test riemann_tensor(M, p, B_chart_p) β‰ˆ zeros(n, n, n, n) atol = 1e-6 + @test ricci_tensor(M, p, B_chart_p) β‰ˆ zeros(n, n) atol = 1e-6 + @test ricci_curvature(M, p, B_chart_p) β‰ˆ 0 atol = 1e-6 + @test gaussian_curvature(M, p, B_chart_p) β‰ˆ 0 atol = 1e-6 + @test einstein_tensor(M, p, B_chart_p) β‰ˆ zeros(n, n) atol = 1e-6 fdm = FiniteDifferencesBackend(forward_fdm(2, 1)) - @test christoffel_symbols_first(M, x; backend=fdm) β‰ˆ zeros(n, n, n) atol = 1e-6 - @test christoffel_symbols_second(M, x; backend=fdm) β‰ˆ zeros(n, n, n) atol = 1e-6 - @test riemann_tensor(M, x; backend=fdm) β‰ˆ zeros(n, n, n, n) atol = 1e-6 - @test ricci_tensor(M, x; backend=fdm) β‰ˆ zeros(n, n) atol = 1e-6 - @test ricci_curvature(M, x; backend=fdm) β‰ˆ 0 atol = 1e-6 - @test gaussian_curvature(M, x; backend=fdm) β‰ˆ 0 atol = 1e-6 - @test einstein_tensor(M, x; backend=fdm) β‰ˆ zeros(n, n) atol = 1e-6 + @test christoffel_symbols_first(M, p, B_chart_p; backend=fdm) β‰ˆ zeros(n, n, n) atol = + 1e-6 + @test christoffel_symbols_second(M, p, B_chart_p; backend=fdm) β‰ˆ zeros(n, n, n) atol = + 1e-6 + @test riemann_tensor(M, p, B_chart_p; backend=fdm) β‰ˆ zeros(n, n, n, n) atol = + 1e-6 + @test ricci_tensor(M, p, B_chart_p; backend=fdm) β‰ˆ zeros(n, n) atol = 1e-6 + @test ricci_curvature(M, p, B_chart_p; backend=fdm) β‰ˆ 0 atol = 1e-6 + @test gaussian_curvature(M, p, B_chart_p; backend=fdm) β‰ˆ 0 atol = 1e-6 + @test einstein_tensor(M, p, B_chart_p; backend=fdm) β‰ˆ zeros(n, n) atol = 1e-6 fwd_diff = Manifolds.ForwardDiffBackend() - @test christoffel_symbols_first(M, x; backend=fwd_diff) β‰ˆ zeros(n, n, n) atol = + @test christoffel_symbols_first(M, p, B_chart_p; backend=fwd_diff) β‰ˆ + zeros(n, n, n) atol = 1e-6 + @test christoffel_symbols_second(M, p, B_chart_p; backend=fwd_diff) β‰ˆ + zeros(n, n, n) atol = 1e-6 + @test riemann_tensor(M, p, B_chart_p; backend=fwd_diff) β‰ˆ zeros(n, n, n, n) atol = 1e-6 - @test christoffel_symbols_second(M, x; backend=fwd_diff) β‰ˆ zeros(n, n, n) atol = + @test ricci_tensor(M, p, B_chart_p; backend=fwd_diff) β‰ˆ zeros(n, n) atol = 1e-6 + @test ricci_curvature(M, p, B_chart_p; backend=fwd_diff) β‰ˆ 0 atol = 1e-6 + @test gaussian_curvature(M, p, B_chart_p; backend=fwd_diff) β‰ˆ 0 atol = 1e-6 + @test einstein_tensor(M, p, B_chart_p; backend=fwd_diff) β‰ˆ zeros(n, n) atol = 1e-6 - @test riemann_tensor(M, x; backend=fwd_diff) β‰ˆ zeros(n, n, n, n) atol = 1e-6 - @test ricci_tensor(M, x; backend=fwd_diff) β‰ˆ zeros(n, n) atol = 1e-6 - @test ricci_curvature(M, x; backend=fwd_diff) β‰ˆ 0 atol = 1e-6 - @test gaussian_curvature(M, x; backend=fwd_diff) β‰ˆ 0 atol = 1e-6 - @test einstein_tensor(M, x; backend=fwd_diff) β‰ˆ zeros(n, n) atol = 1e-6 end end @@ -230,44 +278,49 @@ end S = Manifolds.Sphere(n) g = TestSphericalMetric() M = MetricManifold(Sr, g) + A = Manifolds.get_default_atlas(M) @test manifold_dimension(M) == n @test base_manifold(M) === Sr @test metric(M) === g for vtype in (Vector, MVector{n}) - x = vtype([ΞΈ, Ο•]) + p = vtype([ΞΈ, Ο•]) + chart_p = get_chart_index(M, A, p) + B_p = induced_basis(M, A, chart_p, TangentSpace) G = Diagonal(vtype([1, sin(ΞΈ)^2])) .* r^2 invG = Diagonal(vtype([1, 1 / sin(ΞΈ)^2])) ./ r^2 - v, w = normalize(randn(n)), normalize(randn(n)) - - @test local_metric(M, x) β‰ˆ G atol = 1e-6 - @test inverse_local_metric(M, x) β‰ˆ invG atol = 1e-6 - @test det_local_metric(M, x) β‰ˆ r^4 * sin(ΞΈ)^2 atol = 1e-6 - @test log_local_metric_density(M, x) β‰ˆ 2 * log(r) + log(sin(ΞΈ)) atol = 1e-6 - @test inner(M, x, v, w) β‰ˆ dot(v, G * w) atol = 1e-6 - @test norm(M, x, v) β‰ˆ sqrt(dot(v, G * v)) atol = 1e-6 - - xcart = sph_to_cart(ΞΈ, Ο•) - vcart = [ + X, Y = normalize(randn(n)), normalize(randn(n)) + + @test local_metric(M, p, B_p) β‰ˆ G atol = 1e-6 + @test inverse_local_metric(M, p, B_p) β‰ˆ invG atol = 1e-6 + @test det_local_metric(M, p, B_p) β‰ˆ r^4 * sin(ΞΈ)^2 atol = 1e-6 + @test log_local_metric_density(M, p, B_p) β‰ˆ 2 * log(r) + log(sin(ΞΈ)) atol = 1e-6 + fX = ManifoldsBase.TFVector(X, B_p) + fY = ManifoldsBase.TFVector(Y, B_p) + @test inner(M, p, fX, fY) β‰ˆ dot(X, G * Y) atol = 1e-6 + @test norm(M, p, fX) β‰ˆ sqrt(dot(X, G * X)) atol = 1e-6 + + pcart = sph_to_cart(ΞΈ, Ο•) + Xcart = [ cos(Ο•)*cos(ΞΈ) -sin(Ο•)*sin(ΞΈ) sin(Ο•)*cos(ΞΈ) cos(Ο•)*sin(ΞΈ) -sin(ΞΈ) 0 - ] * v + ] * X - if VERSION β‰₯ v"1.1" && (!Sys.iswindows() || Sys.ARCH == :x86_64) + if !Sys.iswindows() || Sys.ARCH == :x86_64 @testset "numerically integrated geodesics for $vtype" begin T = 0:0.1:1 @test isapprox( - [sph_to_cart(yi...) for yi in geodesic(M, x, v, T)], - geodesic(S, xcart, vcart, T); + [sph_to_cart(yi...) for yi in geodesic(M, p, X, T)], + geodesic(S, pcart, Xcart, T); atol=1e-3, rtol=1e-3, ) end end - Γ₁ = christoffel_symbols_first(M, x) + Γ₁ = christoffel_symbols_first(M, p, B_p) for i in 1:n, j in 1:n, k in 1:n if (i, j, k) == (1, 2, 2) || (i, j, k) == (2, 1, 2) @test Γ₁[i, j, k] β‰ˆ r^2 * cos(ΞΈ) * sin(ΞΈ) atol = 1e-6 @@ -278,7 +331,7 @@ end end end - Ξ“β‚‚ = christoffel_symbols_second(M, x) + Ξ“β‚‚ = christoffel_symbols_second(M, p, B_p) for l in 1:n, i in 1:n, j in 1:n if (l, i, j) == (1, 2, 2) @test Ξ“β‚‚[l, i, j] β‰ˆ -cos(ΞΈ) * sin(ΞΈ) atol = 1e-6 @@ -289,7 +342,7 @@ end end end - R = riemann_tensor(M, x) + R = riemann_tensor(M, p, B_p) for l in 1:n, i in 1:n, j in 1:n, k in 1:n if (l, i, j, k) == (2, 1, 1, 2) @test R[l, i, j, k] β‰ˆ -1 atol = 2e-6 @@ -304,11 +357,11 @@ end end end - @test ricci_tensor(M, x) β‰ˆ G ./ r^2 atol = 2e-6 - @test ricci_curvature(M, x) β‰ˆ 2 / r^2 atol = 2e-6 - @test gaussian_curvature(M, x) β‰ˆ 1 / r^2 atol = 2e-6 - @test einstein_tensor(M, x) β‰ˆ ricci_tensor(M, x) - gaussian_curvature(M, x) .* G atol = - 1e-6 + @test ricci_tensor(M, p, B_p) β‰ˆ G ./ r^2 atol = 2e-6 + @test ricci_curvature(M, p, B_p) β‰ˆ 2 / r^2 atol = 2e-6 + @test gaussian_curvature(M, p, B_p) β‰ˆ 1 / r^2 atol = 2e-6 + @test einstein_tensor(M, p, B_p) β‰ˆ + ricci_tensor(M, p, B_p) - gaussian_curvature(M, p, B_p) .* G atol = 1e-6 end end @@ -316,15 +369,16 @@ end M = BaseManifold{3}() g = BaseManifoldMetric{3}() MM = MetricManifold(M, g) - if VERSION β‰₯ v"1.3" - @test DefaultBaseManifoldMetric(BaseManifold{3}()) === - MetricManifold(BaseManifold{3}(), DefaultBaseManifoldMetric()) - MT = DefaultBaseManifoldMetric() - @test MT(BaseManifold{3}()) === - MetricManifold(BaseManifold{3}(), DefaultBaseManifoldMetric()) - end + + @test DefaultBaseManifoldMetric(BaseManifold{3}()) === + MetricManifold(BaseManifold{3}(), DefaultBaseManifoldMetric()) + MT = DefaultBaseManifoldMetric() + @test MT(BaseManifold{3}()) === + MetricManifold(BaseManifold{3}(), DefaultBaseManifoldMetric()) + g2 = DefaultBaseManifoldMetric() MM2 = MetricManifold(M, g2) + A = Manifolds.get_default_atlas(M) @test (@inferred Manifolds.default_metric_dispatch(MM)) === (@inferred Manifolds.default_metric_dispatch(base_manifold(MM), metric(MM))) @@ -341,44 +395,49 @@ end @test convert(typeof(MM2), M) == MM2 @test_throws ErrorException convert(typeof(MM), M) - x = [0.1, 0.2, 0.4] - v = [0.5, 0.7, 0.11] - w = [0.13, 0.17, 0.19] - y = allocate(x) - - z = allocate(x) - copyto!(MM, z, x) - z2 = allocate(x) - copyto!(M, z2, x) - @test z == z2 - X = zero_tangent_vector(MM, x) + p = [0.1, 0.2, 0.4] + X = [0.5, 0.7, 0.11] + Y = [0.13, 0.17, 0.19] + q = allocate(p) + + p2 = allocate(p) + copyto!(MM, p2, p) + p3 = allocate(p) + copyto!(M, p3, p) + @test p2 == p3 + X = zero_vector(MM, p) Y = allocate(X) - copyto!(MM, Y, x, X) + copyto!(MM, Y, p, X) Y2 = allocate(X) - copyto!(M, Y2, x, X) + copyto!(M, Y2, p, X) @test Y == Y2 - @test inner(M, x, v, w) == 2 * dot(v, w) - @test inner(MM, x, v, w) === inner(M, x, v, w) - @test norm(MM, x, v) === norm(M, x, v) - @test exp(M, x, v) == x + 2 * v - @test exp(MM2, x, v) == exp(M, x, v) - @test exp!(MM, y, x, v) === exp!(M, y, x, v) - @test retract!(MM, y, x, v) === retract!(M, y, x, v) - @test retract!(MM, y, x, v, 1) === retract!(M, y, x, v, 1) + chart_p = get_chart_index(M, A, p) + B_p = induced_basis(M, A, chart_p, TangentSpace) + fX = ManifoldsBase.TFVector(X, B_p) + fY = ManifoldsBase.TFVector(Y, B_p) + + @test inner(M, p, X, Y) == 2 * dot(X, Y) + @test inner(MM, p, fX, fY) === inner(M, p, X, Y) + @test norm(MM, p, fX) === norm(M, p, X) + @test exp(M, p, X) == p + 2 * X + @test exp(MM2, p, X) == exp(M, p, X) + @test exp!(MM, q, p, X) === exp!(M, q, p, X) + @test retract!(MM, q, p, X) === retract!(M, q, p, X) + @test retract!(MM, q, p, X, 1) === retract!(M, q, p, X, 1) # without a definition for the metric from the embedding, no projection possible - @test_throws ErrorException log!(MM, w, x, y) === project!(M, w, x, y) - @test_throws ErrorException project!(MM, w, x, v) === project!(M, w, x, v) - @test_throws ErrorException project!(MM, y, x) === project!(M, y, x) - @test_throws ErrorException vector_transport_to!(MM, w, x, v, y) === - vector_transport_to!(M, w, x, v, y) + @test_throws ErrorException log!(MM, Y, p, q) === project!(M, Y, p, q) + @test_throws ErrorException project!(MM, Y, p, X) === project!(M, Y, p, X) + @test_throws ErrorException project!(MM, q, p) === project!(M, q, p) + @test_throws ErrorException vector_transport_to!(MM, Y, p, X, q) === + vector_transport_to!(M, Y, p, X, q) # without DiffEq, these error - # @test_throws ErrorException exp(MM,x, v, 1:3) - # @test_throws ErrorException exp!(MM, y, x, v) + # @test_throws ErrorException exp(MM,x, X, 1:3) + # @test_throws ErrorException exp!(MM, q, p, X) # these always fall back anyways. - @test zero_tangent_vector!(MM, v, x) === zero_tangent_vector!(M, v, x) + @test zero_vector!(MM, X, p) === zero_vector!(M, X, p) - @test injectivity_radius(MM, x) === injectivity_radius(M, x) + @test injectivity_radius(MM, p) === injectivity_radius(M, p) @test injectivity_radius(MM) === injectivity_radius(M) @test injectivity_radius(MM, ProjectionRetraction()) === injectivity_radius(M, ProjectionRetraction()) @@ -386,42 +445,45 @@ end injectivity_radius(M, ExponentialRetraction()) @test injectivity_radius(MM) === injectivity_radius(M) - @test is_manifold_point(MM, x) === is_manifold_point(M, x) - @test is_tangent_vector(MM, x, v) === is_tangent_vector(M, x, v) + @test is_point(MM, p) === is_point(M, p) + @test is_vector(MM, p, X) === is_vector(M, p, X) - @test_throws MethodError local_metric(MM2, x) - @test_throws MethodError local_metric_jacobian(MM2, x) - @test_throws MethodError christoffel_symbols_second_jacobian(MM2, x) + A = Manifolds.get_default_atlas(MM2) + chart_p = get_chart_index(MM2, A, p) + B_p = induced_basis(MM2, A, chart_p, TangentSpace) + @test_throws MethodError local_metric(MM2, p, B_p) + @test_throws MethodError local_metric_jacobian(MM2, p, B_p) + @test_throws MethodError christoffel_symbols_second_jacobian(MM2, p, B_p) # MM falls back to nondefault error - @test_throws MethodError projected_distribution(MM, 1, x) + @test_throws MethodError projected_distribution(MM, 1, p) @test_throws MethodError projected_distribution(MM, 1) - @test inner(MM2, x, v, w) === inner(M, x, v, w) - @test norm(MM2, x, v) === norm(M, x, v) - @test distance(MM2, x, y) === distance(M, x, y) - @test exp!(MM2, y, x, v) === exp!(M, y, x, v) - @test exp(MM2, x, v) == exp(M, x, v) - @test log!(MM2, v, x, y) === log!(M, v, x, y) - @test log(MM2, x, y) == log(M, x, y) - @test retract!(MM2, y, x, v) === retract!(M, y, x, v) - @test retract!(MM2, y, x, v, 1) === retract!(M, y, x, v, 1) - - @test project!(MM2, y, x) === project!(M, y, x) - @test project!(MM2, w, x, v) === project!(M, w, x, v) - @test vector_transport_to!(MM2, w, x, v, y) == vector_transport_to!(M, w, x, v, y) - @test zero_tangent_vector!(MM2, v, x) === zero_tangent_vector!(M, v, x) - @test injectivity_radius(MM2, x) === injectivity_radius(M, x) + @test inner(MM2, p, X, Y) === inner(M, p, X, Y) + @test norm(MM2, p, X) === norm(M, p, X) + @test distance(MM2, p, q) === distance(M, p, q) + @test exp!(MM2, q, p, X) === exp!(M, q, p, X) + @test exp(MM2, p, X) == exp(M, p, X) + @test log!(MM2, X, p, q) === log!(M, X, p, q) + @test log(MM2, p, q) == log(M, p, q) + @test retract!(MM2, q, p, X) === retract!(M, q, p, X) + @test retract!(MM2, q, p, X, 1) === retract!(M, q, p, X, 1) + + @test project!(MM2, q, p) === project!(M, q, p) + @test project!(MM2, Y, p, X) === project!(M, Y, p, X) + @test vector_transport_to!(MM2, Y, p, X, q) == vector_transport_to!(M, Y, p, X, q) + @test zero_vector!(MM2, X, p) === zero_vector!(M, X, p) + @test injectivity_radius(MM2, p) === injectivity_radius(M, p) @test injectivity_radius(MM2) === injectivity_radius(M) - @test injectivity_radius(MM2, x, ExponentialRetraction()) === - injectivity_radius(M, x, ExponentialRetraction()) + @test injectivity_radius(MM2, p, ExponentialRetraction()) === + injectivity_radius(M, p, ExponentialRetraction()) @test injectivity_radius(MM2, ExponentialRetraction()) === injectivity_radius(M, ExponentialRetraction()) - @test injectivity_radius(MM2, x, ProjectionRetraction()) === - injectivity_radius(M, x, ProjectionRetraction()) + @test injectivity_radius(MM2, p, ProjectionRetraction()) === + injectivity_radius(M, p, ProjectionRetraction()) @test injectivity_radius(MM2, ProjectionRetraction()) === injectivity_radius(M, ProjectionRetraction()) - @test is_manifold_point(MM2, x) === is_manifold_point(M, x) - @test is_tangent_vector(MM2, x, v) === is_tangent_vector(M, x, v) + @test is_point(MM2, p) === is_point(M, p) + @test is_vector(MM2, p, X) === is_vector(M, p, X) a = Manifolds.projected_distribution(M, Distributions.MvNormal(zero(zeros(3)), 1.0)) b = Manifolds.projected_distribution( @@ -430,30 +492,60 @@ end ) @test isapprox(Matrix(a.distribution.Ξ£), Matrix(b.distribution.Ξ£)) @test isapprox(a.distribution.ΞΌ, b.distribution.ΞΌ) - @test get_basis(M, x, DefaultOrthonormalBasis()).data == - get_basis(MM2, x, DefaultOrthonormalBasis()).data - @test_throws ErrorException get_basis(MM, x, DefaultOrthonormalBasis()) - cov = flat(M, x, FVector(TangentSpace, v)) - cow = flat(M, x, FVector(TangentSpace, w)) - @test cov.data β‰ˆ flat(MM, x, FVector(TangentSpace, v)).data + @test get_basis(M, p, DefaultOrthonormalBasis()).data == + get_basis(MM2, p, DefaultOrthonormalBasis()).data + @test_throws ErrorException get_basis(MM, p, DefaultOrthonormalBasis()) + + fX = ManifoldsBase.TFVector(X, B_p) + fY = ManifoldsBase.TFVector(Y, B_p) + coX = flat(M, p, X) + coY = flat(M, p, Y) + cofX = flat(M, p, fX) + cofY = flat(M, p, fY) + @test coX(X) β‰ˆ norm(M, p, X)^2 + @test coY(X) β‰ˆ inner(M, p, X, Y) cotspace = CotangentBundleFibers(M) cotspace2 = CotangentBundleFibers(MM) - @test cov.data β‰ˆ 2 * v - @test inner(M, x, v, w) β‰ˆ inner(cotspace, x, cov.data, cow.data) - @test inner(MM, x, v, w) β‰ˆ inner(cotspace, x, cov.data, cow.data) - @test inner(MM, x, v, w) β‰ˆ inner(cotspace2, x, cov.data, cow.data) - @test sharp(M, x, cov).data β‰ˆ v - - xsample = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] - w = pweights([0.5, 0.5]) + @test coX.X β‰ˆ X + @test inner(M, p, X, Y) β‰ˆ inner(cotspace, p, coX, coY) + @test inner(MM, p, fX, fY) β‰ˆ inner(cotspace, p, coX, coY) + + @test inner(MM, p, fX, fY) β‰ˆ inner(cotspace2, p, cofX, cofY) + @test sharp(M, p, coX) β‰ˆ X + + coMMfX = flat(MM, p, fX) + coMMfY = flat(MM, p, fY) + @test inner(MM, p, fX, fY) β‰ˆ inner(cotspace2, p, coMMfX, coMMfY) + @test isapprox(sharp(MM, p, coMMfX).data, fX.data) + + @testset "Mutating flat/sharp" begin + cofX2 = allocate(cofX) + flat!(M, cofX2, p, fX) + @test isapprox(cofX2.data, cofX.data) + + fX2 = allocate(fX) + sharp!(M, fX2, p, cofX2) + @test isapprox(fX2.data, fX.data) + + cofX2 = allocate(cofX) + flat!(MM, cofX2, p, fX) + @test isapprox(cofX2.data, cofX.data) + + fX2 = allocate(fX) + sharp!(MM, fX2, p, cofX2) + @test isapprox(fX2.data, fX.data) + end + + psample = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] + Y = pweights([0.5, 0.5]) # test despatch with results from above - @test mean(M, xsample, w) β‰ˆ ones(3) - @test mean(MM2, xsample, w) β‰ˆ ones(3) - @test mean(MM, xsample, w) β‰ˆ 3 .* ones(3) + @test mean(M, psample, Y) β‰ˆ ones(3) + @test mean(MM2, psample, Y) β‰ˆ ones(3) + @test mean(MM, psample, Y) β‰ˆ 3 .* ones(3) - @test median(M, xsample, w) β‰ˆ 2 .* ones(3) - @test median(MM2, xsample, w) β‰ˆ 2 * ones(3) - @test median(MM, xsample, w) β‰ˆ 4 .* ones(3) + @test median(M, psample, Y) β‰ˆ 2 .* ones(3) + @test median(MM2, psample, Y) β‰ˆ 2 * ones(3) + @test median(MM, psample, Y) β‰ˆ 4 .* ones(3) end @testset "Metric decorator dispatches" begin diff --git a/test/multinomial_doubly_stochastic.jl b/test/multinomial_doubly_stochastic.jl index 9b36bdc416..df3d612cbb 100644 --- a/test/multinomial_doubly_stochastic.jl +++ b/test/multinomial_doubly_stochastic.jl @@ -6,18 +6,18 @@ include("utils.jl") @test repr(M) == "MultinomialDoubleStochastic(3)" p = ones(3, 3) ./ 3 X = zeros(3, 3) - @test is_manifold_point(M, p) - @test is_tangent_vector(M, p, X) + @test is_point(M, p) + @test is_vector(M, p, X) pf1 = [0.1 0.9 0.1; 0.1 0.9 0.1; 0.1 0.1 0.9] #not sum 1 - @test_throws CompositeManifoldError is_manifold_point(M, pf1, true) + @test_throws CompositeManifoldError is_point(M, pf1, true) pf2r = [0.1 0.9 0.1; 0.8 0.05 0.15; 0.1 0.05 0.75] - @test_throws DomainError is_manifold_point(M, pf2r, true) - @test_throws CompositeManifoldError is_manifold_point(M, pf2r', true) + @test_throws DomainError is_point(M, pf2r, true) + @test_throws CompositeManifoldError is_point(M, pf2r', true) pf3 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] # contains nonpositive entries - @test_throws CompositeManifoldError is_manifold_point(M, pf3, true) + @test_throws CompositeManifoldError is_point(M, pf3, true) Xf2c = [-0.1 0.0 0.1; -0.2 0.1 0.1; 0.2 -0.1 -0.1] #nonzero columns - @test_throws CompositeManifoldError is_tangent_vector(M, p, Xf2c, true) - @test_throws DomainError is_tangent_vector(M, p, Xf2c', true) + @test_throws CompositeManifoldError is_vector(M, p, Xf2c, true) + @test_throws DomainError is_vector(M, p, Xf2c', true) @test representation_size(M) == (3, 3) pE = similar(p) embed!(M, pE, p) @@ -30,7 +30,7 @@ include("utils.jl") p3 = [0.1 0.4 0.5; 0.4 0.5 0.1; 0.5 0.1 0.4] X2 = [-0.1 0.0 0.1; 0.0 0.2 -0.2; 0.1 -0.2 0.1] - @test is_tangent_vector( + @test is_vector( M, p2, vector_transport_to(M, p, X2, p2, ProjectionTransport()); diff --git a/test/multinomial_matrices.jl b/test/multinomial_matrices.jl index cb2f11b130..cefc2c1c74 100644 --- a/test/multinomial_matrices.jl +++ b/test/multinomial_matrices.jl @@ -13,16 +13,22 @@ include("utils.jl") @test Manifolds.get_iterator(ProbabilitySimplex(3)^4) === Base.OneTo(4) p = [2, 0, 0] p2 = [p p] - @test !is_manifold_point(M, p) - @test_throws DomainError is_manifold_point(M, p, true) - @test !is_manifold_point(M, p2) - @test_throws CompositeManifoldError is_manifold_point(M, p2, true) - @test !is_tangent_vector(M, p2, 0.0) - @test_throws DomainError is_tangent_vector(M, p2, [-1.0, 0.0, 0.0], true) - @test !is_tangent_vector(M, p2, [-1.0, 0.0, 0.0]) - @test_throws DomainError is_tangent_vector(M, p, [-1.0, 0.0, 0.0], true) + @test !is_point(M, p) + @test_throws DomainError is_point(M, p, true) + @test !is_point(M, p2) + @test_throws CompositeManifoldError is_point(M, p2, true) + @test !is_vector(M, p2, 0.0) + @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( + M, + p2, + [-1.0, 0.0, 0.0], + true, + ) + @test !is_vector(M, p2, [-1.0, 0.0, 0.0]) + @test_throws DomainError is_vector(M, p, [-1.0, 0.0, 0.0], true) @test injectivity_radius(M) β‰ˆ 0 x = [0.5 0.4 0.1; 0.5 0.4 0.1]' + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0], true) # tangent wrong y = [0.6 0.3 0.1; 0.4 0.5 0.1]' z = [0.3 0.6 0.1; 0.6 0.3 0.1]' test_manifold( diff --git a/test/multinomial_symmetric.jl b/test/multinomial_symmetric.jl index f5f0a9ed4e..978d6fd5df 100644 --- a/test/multinomial_symmetric.jl +++ b/test/multinomial_symmetric.jl @@ -6,18 +6,18 @@ include("utils.jl") @test repr(M) == "MultinomialSymmetric(3)" p = ones(3, 3) ./ 3 X = zeros(3, 3) - @test is_manifold_point(M, p) - @test is_tangent_vector(M, p, X) + @test is_point(M, p) + @test is_vector(M, p, X) pf1 = [0.1 0.9 0.1; 0.1 0.9 0.1; 0.1 0.1 0.9] #not symmetric - @test_throws CompositeManifoldError is_manifold_point(M, pf1, true) + @test_throws CompositeManifoldError is_point(M, pf1, true) pf2 = [0.8 0.1 0.1; 0.1 0.8 0.1; 0.1 0.1 0.9] # cols do not sum to 1 - @test_throws ComponentManifoldError is_manifold_point(M, pf2, true) + @test_throws ComponentManifoldError is_point(M, pf2, true) pf3 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] # contains nonpositive entries - @test_throws CompositeManifoldError is_manifold_point(M, pf3, true) + @test_throws CompositeManifoldError is_point(M, pf3, true) Xf1 = [0.0 1.0 -1.0; 0.0 0.0 0.0; 0.0 0.0 0.0] # not symmetric - @test_throws CompositeManifoldError is_tangent_vector(M, p, Xf1, true) + @test_throws CompositeManifoldError is_vector(M, p, Xf1, true) Xf2 = [0.0 -1.0 0.0; -1.0 0.0 0.0; 0.0 0.0 0.0] # nonzero sums - @test_throws CompositeManifoldError is_tangent_vector(M, p, Xf2, true) + @test_throws CompositeManifoldError is_vector(M, p, Xf2, true) @test representation_size(M) == (3, 3) pE = similar(p) embed!(M, pE, p) @@ -35,7 +35,7 @@ include("utils.jl") p3 = [0.1 0.4 0.5; 0.4 0.5 0.1; 0.5 0.1 0.4] X2 = [-0.1 0.0 0.1; 0.0 0.2 -0.2; 0.1 -0.2 0.1] - @test is_tangent_vector( + @test is_vector( M, p2, vector_transport_to(M, p, X2, p2, ProjectionTransport()); diff --git a/test/notation.jl b/test/notation.jl index 0cfdade542..ea6df530f5 100644 --- a/test/notation.jl +++ b/test/notation.jl @@ -3,11 +3,11 @@ include("utils.jl") @testset "Test Notation" begin M = Sphere(2) p1 = [1.0, 0.0, 0.0] - @test (p1 ∈ M) == is_manifold_point(M, p1) - @test (2 * p1 ∈ M) == is_manifold_point(M, 2 * p1) + @test (p1 ∈ M) == is_point(M, p1) + @test (2 * p1 ∈ M) == is_point(M, 2 * p1) X1 = [0.0, 1.0, 0.0] X2 = [1.0, 0.0, 0.0] TpM = TangentSpaceAtPoint(M, p1) - @test (X1 ∈ TpM) == is_tangent_vector(M, p1, X1) - @test (X2 ∈ TpM) == is_tangent_vector(M, p1, X2) + @test (X1 ∈ TpM) == is_vector(M, p1, X1) + @test (X2 ∈ TpM) == is_vector(M, p1, X2) end diff --git a/test/oblique.jl b/test/oblique.jl index c0027b1037..788f1097ab 100644 --- a/test/oblique.jl +++ b/test/oblique.jl @@ -12,16 +12,22 @@ include("utils.jl") @test manifold_dimension(M) == 4 p = [2, 0, 0] p2 = [p p] - @test !is_manifold_point(M, p) - @test_throws DomainError is_manifold_point(M, p, true) - @test !is_manifold_point(M, p2) - @test_throws CompositeManifoldError is_manifold_point(M, p2, true) - @test !is_tangent_vector(M, p2, 0.0) - @test_throws DomainError is_tangent_vector(M, p2, [0.0, 0.0, 0.0], true) - @test !is_tangent_vector(M, p2, [0.0, 0.0, 0.0]) - @test_throws DomainError is_tangent_vector(M, p, [0.0, 0.0, 0.0], true) + @test !is_point(M, p) + @test_throws DomainError is_point(M, p, true) + @test !is_point(M, p2) + @test_throws CompositeManifoldError is_point(M, p2, true) + @test !is_vector(M, p2, 0.0) + @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( + M, + p2, + [0.0, 0.0, 0.0], + true, + ) + @test !is_vector(M, p2, [0.0, 0.0, 0.0]) + @test_throws DomainError is_vector(M, p, [0.0, 0.0, 0.0], true) # p wrong @test injectivity_radius(M) β‰ˆ Ο€ x = [1.0 0.0 0.0; 1.0 0.0 0.0]' + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0], true) # tangent wrong y = [1.0 0.0 0.0; 1/sqrt(2) 1/sqrt(2) 0.0]' z = [1/sqrt(2) 1/sqrt(2) 0.0; 1.0 0.0 0.0]' basis_types = (DefaultOrthonormalBasis(),) diff --git a/test/positive_numbers.jl b/test/positive_numbers.jl index b94a318383..946740aaa7 100644 --- a/test/positive_numbers.jl +++ b/test/positive_numbers.jl @@ -9,11 +9,9 @@ include("utils.jl") @test repr(PositiveArrays(2, 3, 4)) == "PositiveArrays(2, 3, 4)" @test representation_size(M) == () @test manifold_dimension(M) == 1 - @test !is_manifold_point(M, -1.0) - @test_throws DomainError is_manifold_point(M, -1.0, true) - @test is_tangent_vector(M, 1.0, 0.0; check_base_point=false) - @test flat(M, 1.0, FVector(TangentSpace, 1.0)) == FVector(CotangentSpace, 1.0) - @test sharp(M, 1.0, FVector(CotangentSpace, 1.0)) == FVector(TangentSpace, 1.0) + @test !is_point(M, -1.0) + @test_throws DomainError is_point(M, -1.0, true) + @test is_vector(M, 1.0, 0.0) @test vector_transport_to(M, 1.0, 3.0, 2.0, ParallelTransport()) == 6.0 @test retract(M, 1.0, 1.0) == exp(M, 1.0, 1.0) @test isinf(injectivity_radius(M)) @@ -21,9 +19,9 @@ include("utils.jl") @test isinf(injectivity_radius(M, -2.0, ExponentialRetraction())) @test isinf(injectivity_radius(M, ExponentialRetraction())) @test project(M, 1.5, 1.0) == 1.0 - @test zero_tangent_vector(M, 1.0) == 0.0 + @test zero_vector(M, 1.0) == 0.0 X = similar([1.0]) - zero_tangent_vector!(M, X, 1.0) + zero_vector!(M, X, 1.0) @test X == [0.0] end types = [Float64] diff --git a/test/power_manifold.jl b/test/power_manifold.jl index 3c2b05f32e..469c077214 100644 --- a/test/power_manifold.jl +++ b/test/power_manifold.jl @@ -156,8 +156,8 @@ Random.seed!(42) M = PowerManifold(Sphere(2), NestedPowerRepresentation(), 2) p = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] X = [[0.0, 0.0, 0.0], [0.0, 1.0, 0.0]] - @test_throws ComponentManifoldError is_manifold_point(M, X, true) - @test_throws ComponentManifoldError is_tangent_vector(M, p, X, true) + @test_throws ComponentManifoldError is_point(M, X, true) + @test_throws ComponentManifoldError is_vector(M, p, X, true) end @testset "power vector transport" begin @@ -249,7 +249,7 @@ Random.seed!(42) point_distributions=[power_r1_pt_dist], tvector_distributions=[power_r1_tv_dist], basis_types_to_from=basis_types, - rand_tvector_atol_multiplier=5.0, + rand_tvector_atol_multiplier=8.0, retraction_atol_multiplier=12, is_tangent_atol_multiplier=12.0, exp_log_atol_multiplier=2e2 * prod(power_dimensions(Mr2)), @@ -293,7 +293,7 @@ Random.seed!(42) inverse_retraction_methods=inverse_retraction_methods, point_distributions=[power_r2_pt_dist], tvector_distributions=[power_r2_tv_dist], - rand_tvector_atol_multiplier=5.0, + rand_tvector_atol_multiplier=8.0, retraction_atol_multiplier=12, is_tangent_atol_multiplier=12.0, exp_log_atol_multiplier=4e3 * prod(power_dimensions(Mr2)), @@ -314,7 +314,7 @@ Random.seed!(42) inverse_retraction_methods=inverse_retraction_methods, point_distributions=[power_rn2_pt_dist], tvector_distributions=[power_rn2_tv_dist], - rand_tvector_atol_multiplier=5.0, + rand_tvector_atol_multiplier=8.0, retraction_atol_multiplier=12, is_tangent_atol_multiplier=12.0, exp_log_atol_multiplier=4e3 * prod(power_dimensions(Mrn2)), @@ -381,4 +381,14 @@ Random.seed!(42) is_tangent_atol_multiplier=12.0, ) end + + @testset "Atlas & Induced Basis" begin + M = PowerManifold(Euclidean(2), NestedPowerRepresentation(), 2) + p = [zeros(2), ones(2)] + X = [ones(2), 2 .* ones(2)] + A = RetractionAtlas() + a = get_point_coordinates(M, A, p, p) + p2 = get_point(M, A, p, a) + @test all(p2 .== p) + end end diff --git a/test/probability_simplex.jl b/test/probability_simplex.jl index 8166f2e6d4..f87361f4ad 100644 --- a/test/probability_simplex.jl +++ b/test/probability_simplex.jl @@ -7,16 +7,16 @@ include("utils.jl") q = [0.3, 0.6, 0.1] X = zeros(3) Y = [-0.1, 0.05, 0.05] - @test is_manifold_point(M, p) - @test_throws DomainError is_manifold_point(M, p .+ 1, true) - @test_throws DomainError is_manifold_point(M, [0], true) - @test_throws DomainError is_manifold_point(M, -ones(3), true) + @test is_point(M, p) + @test_throws DomainError is_point(M, p .+ 1, true) + @test_throws DomainError is_point(M, [0], true) + @test_throws DomainError is_point(M, -ones(3), true) @test manifold_dimension(M) == 2 - @test is_tangent_vector(M, p, X) - @test is_tangent_vector(M, p, Y) - @test_throws DomainError is_tangent_vector(M, p .+ 1, X, true) - @test_throws DomainError is_tangent_vector(M, p, zeros(4), true) - @test_throws DomainError is_tangent_vector(M, p, Y .+ 1, true) + @test is_vector(M, p, X) + @test is_vector(M, p, Y) + @test_throws DomainError is_vector(M, p .+ 1, X, true) + @test_throws DomainError is_vector(M, p, zeros(4), true) + @test_throws DomainError is_vector(M, p, Y .+ 1, true) @test Manifolds.default_metric_dispatch(M, Manifolds.FisherRaoMetric()) === Val{true}() @@ -74,7 +74,7 @@ include("utils.jl") X = log(M, q, p) X2 = X + [1, 2, 3] Y = project(M, q, X2) - @test is_tangent_vector(M, q, Y; atol=1e-15) + @test is_vector(M, q, Y; atol=1e-15) @test_throws DomainError project(M, [1, -1, 2]) @test isapprox(M, [0.6, 0.2, 0.2], project(M, [0.3, 0.1, 0.1])) diff --git a/test/product_manifold.jl b/test/product_manifold.jl index 7a7381b0fd..8d14eae573 100644 --- a/test/product_manifold.jl +++ b/test/product_manifold.jl @@ -165,10 +165,10 @@ end X = ProductRepr(X1, X2) pf = ProductRepr(p1, X1) Xf = ProductRepr(X1, p2) - @test is_manifold_point(Mpr, p, true) - @test_throws CompositeManifoldError is_manifold_point(Mpr, X, true) - @test_throws ComponentManifoldError is_tangent_vector(Mpr, pf, X, true) - @test_throws ComponentManifoldError is_tangent_vector(Mpr, p, Xf, true) + @test is_point(Mpr, p, true) + @test_throws CompositeManifoldError is_point(Mpr, X, true) + @test_throws ComponentManifoldError is_vector(Mpr, pf, X, true) + @test_throws ComponentManifoldError is_vector(Mpr, p, Xf, true) end @testset "arithmetic" begin @@ -358,6 +358,7 @@ end pts; test_reverse_diff=isa(T, Vector), test_musical_isomorphisms=true, + musical_isomorphism_bases=[DefaultOrthonormalBasis()], test_injectivity_radius=true, test_project_point=true, test_project_tangent=true, @@ -595,4 +596,14 @@ end @test Manifolds.allocation_promotion_function(Msec, get_vector, ()) === complex @test Manifolds.allocation_promotion_function(Mse, get_vector, ()) === identity end + + @testset "Atlas & Induced Basis" begin + M = ProductManifold(Euclidean(2), Euclidean(2)) + p = ProductRepr(zeros(2), ones(2)) + X = ProductRepr(ones(2), 2 .* ones(2)) + A = RetractionAtlas() + a = get_point_coordinates(M, A, p, p) + p2 = get_point(M, A, p, a) + @test all(p2.parts .== p.parts) + end end diff --git a/test/projective_space.jl b/test/projective_space.jl index 44d69b16e1..1604857d93 100644 --- a/test/projective_space.jl +++ b/test/projective_space.jl @@ -7,17 +7,12 @@ include("utils.jl") @test repr(M) == "ProjectiveSpace(2, ℝ)" @test representation_size(M) == (3,) @test manifold_dimension(M) == 2 - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0]) - @test_throws DomainError is_manifold_point(M, [2.0, 0.0, 0.0], true) - @test !is_manifold_point(M, [2.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) - @test_throws DomainError is_tangent_vector( - M, - [1.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - true, - ) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test !is_point(M, [2.0, 0.0, 0.0]) + @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) + @test_throws DomainError is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], true) @test injectivity_radius(M) == Ο€ / 2 @test injectivity_radius(M, ExponentialRetraction()) == Ο€ / 2 @test injectivity_radius(M, [1.0, 0.0, 0.0]) == Ο€ / 2 @@ -109,19 +104,19 @@ include("utils.jl") @test representation_size(M) == (3,) @test manifold_dimension(M) == 4 @test Manifolds.allocation_promotion_function(M, exp!, (1,)) == complex - @test !is_manifold_point(M, [1.0 + 0im, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 + 0im, 0.0, 0.0, 0.0], [0.0 + 0im, 1.0, 0.0]) - @test_throws DomainError is_manifold_point(M, [1.0, im, 0.0], true) - @test !is_manifold_point(M, [1.0, im, 0.0]) - @test !is_tangent_vector(M, [1.0 + 0im, 0.0, 0.0], [1.0 + 0im, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 + 0im, 0.0, 0.0], [-0.5im, 0.0, 0.0]) - @test_throws DomainError is_tangent_vector( + @test !is_point(M, [1.0 + 0im, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 + 0im, 0.0, 0.0, 0.0], [0.0 + 0im, 1.0, 0.0]) + @test_throws DomainError is_point(M, [1.0, im, 0.0], true) + @test !is_point(M, [1.0, im, 0.0]) + @test !is_vector(M, [1.0 + 0im, 0.0, 0.0], [1.0 + 0im, 0.0, 0.0]) + @test !is_vector(M, [1.0 + 0im, 0.0, 0.0], [-0.5im, 0.0, 0.0]) + @test_throws DomainError is_vector( M, [1.0 + 0im, 0.0, 0.0], [1.0 + 0im, 0.0, 0.0], true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, [1.0 + 0im, 0.0, 0.0], [-0.5im, 0.0, 0.0], @@ -200,31 +195,31 @@ include("utils.jl") @test repr(M) == "ProjectiveSpace(2, ℍ)" @test representation_size(M) == (3,) @test manifold_dimension(M) == 8 - @test !is_manifold_point(M, Quaternion[1.0 + 0im, 0.0, 0.0, 0.0]) - @test !is_tangent_vector( + @test !is_point(M, Quaternion[1.0 + 0im, 0.0, 0.0, 0.0]) + @test !is_vector( M, Quaternion[1.0 + 0im, 0.0, 0.0, 0.0], Quaternion[0.0 + 0im, 1.0, 0.0], ) - @test_throws DomainError is_manifold_point(M, Quaternion[1.0, im, 0.0], true) - @test !is_manifold_point(M, Quaternion[1.0, im, 0.0]) - @test !is_tangent_vector( + @test_throws DomainError is_point(M, Quaternion[1.0, im, 0.0], true) + @test !is_point(M, Quaternion[1.0, im, 0.0]) + @test !is_vector( M, Quaternion[1.0 + 0im, 0.0, 0.0], Quaternion[1.0 + 0im, 0.0, 0.0], ) - @test !is_tangent_vector( + @test !is_vector( M, Quaternion[1.0 + 0im, 0.0, 0.0], Quaternion[-0.5im, 0.0, 0.0], ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, Quaternion[1.0 + 0im, 0.0, 0.0], Quaternion[1.0 + 0im, 0.0, 0.0], true, ) - @test_throws DomainError is_tangent_vector( + @test_throws DomainError is_vector( M, Quaternion[1.0 + 0im, 0.0, 0.0], Quaternion[-0.5im, 0.0, 0.0], @@ -312,10 +307,10 @@ include("utils.jl") @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) - @test is_manifold_point(M, q) + @test is_point(M, q) Y = [1.0 0.0; 0.0 1.1] X = project(M, q, Y) - @test is_tangent_vector(M, q, X) + @test is_vector(M, q, X) M = ArrayProjectiveSpace(2, 2; field=β„‚) @test manifold_dimension(M) == 6 diff --git a/test/rotations.jl b/test/rotations.jl index 9afdcddda5..6d24d0997a 100644 --- a/test/rotations.jl +++ b/test/rotations.jl @@ -160,37 +160,37 @@ include("utils.jl") @test x β‰ˆ exp(SOn, pts[1], v2) end end - @testset "Test Manifold Point and Tangent Vector checks" begin + @testset "Test AbstractManifold Point and Tangent Vector checks" begin M = Manifolds.Rotations(2) for x in [1, [2.0 0.0; 0.0 1.0], [1.0 0.5; 0.0 1.0]] - @test_throws DomainError is_manifold_point(M, x, true) - @test !is_manifold_point(M, x) + @test_throws DomainError is_point(M, x, true) + @test !is_point(M, x) end x = one(zeros(2, 2)) - @test is_manifold_point(M, x) - @test is_manifold_point(M, x, true) + @test is_point(M, x) + @test is_point(M, x, true) for v in [1, [0.0 1.0; 0.0 0.0]] - @test_throws DomainError is_tangent_vector(M, x, v, true) - @test !is_tangent_vector(M, x, v) + @test_throws DomainError is_vector(M, x, v, true) + @test !is_vector(M, x, v) end v = [0.0 1.0; -1.0 0.0] - @test is_tangent_vector(M, x, v) - @test is_tangent_vector(M, x, v, true) + @test is_vector(M, x, v) + @test is_vector(M, x, v, true) end @testset "Project point" begin M = Manifolds.Rotations(2) x = Matrix{Float64}(I, 2, 2) x1 = project(M, x) - @test is_manifold_point(M, x1, true) + @test is_point(M, x1, true) M = Manifolds.Rotations(3) x = collect(reshape(1.0:9.0, (3, 3))) x2 = project(M, x) - @test is_manifold_point(M, x2, true) + @test is_point(M, x2, true) rng = MersenneTwister(44) x3 = project(M, randn(rng, 3, 3)) - @test is_manifold_point(M, x3, true) + @test is_point(M, x3, true) end @testset "Edge cases of Rotations" begin @test_throws OutOfInjectivityRadiusError inverse_retract( diff --git a/test/runtests.jl b/test/runtests.jl index 7a5b037754..d85bce68b1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,7 +11,7 @@ include("utils.jl") @testset "Ambiguities" begin # TODO: reduce the number of ambiguities for Julia 1.6 if VERSION.prerelease == () && !Sys.iswindows() && VERSION < v"1.6.0" - @test length(Test.detect_ambiguities(ManifoldsBase)) <= 18 + @test length(Test.detect_ambiguities(ManifoldsBase)) <= 58 @test length(Test.detect_ambiguities(Manifolds)) == 0 @test length(our_base_ambiguities()) <= 24 else @@ -119,6 +119,7 @@ include("utils.jl") include_test("generalized_stiefel.jl") include_test("grassmann.jl") include_test("hyperbolic.jl") + include_test("lorentz.jl") include_test("multinomial_doubly_stochastic.jl") include_test("multinomial_symmetric.jl") include_test("positive_numbers.jl") diff --git a/test/skewhermitian.jl b/test/skewhermitian.jl index 9cdccb64f7..02fbe1cb6e 100644 --- a/test/skewhermitian.jl +++ b/test/skewhermitian.jl @@ -23,18 +23,18 @@ end @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test typeof(get_embedding(M)) === Euclidean{Tuple{3,3},ℝ} - @test check_manifold_point(M, B_skewsym) === nothing - @test_throws DomainError is_manifold_point(M, A, true) - @test_throws DomainError is_manifold_point(M, C, true) - @test_throws DomainError is_manifold_point(M, D, true) - @test check_tangent_vector(M, B_skewsym, B_skewsym) === nothing - @test_throws DomainError is_tangent_vector(M, B_skewsym, A, true) - @test_throws DomainError is_tangent_vector(M, A, B_skewsym, true) - @test_throws DomainError is_tangent_vector(M, B_skewsym, D, true) - @test_throws DomainError is_tangent_vector( + @test check_point(M, B_skewsym) === nothing + @test_throws DomainError is_point(M, A, true) + @test_throws DomainError is_point(M, C, true) + @test_throws DomainError is_point(M, D, true) + @test check_vector(M, B_skewsym, B_skewsym) === nothing + @test_throws DomainError is_vector(M, B_skewsym, A, true) + @test_throws DomainError is_vector(M, A, B_skewsym, true) + @test_throws DomainError is_vector(M, B_skewsym, D, true) + @test_throws DomainError is_vector( M, B_skewsym, - 1 * im * zero_tangent_vector(M, B_skewsym), + 1 * im * zero_vector(M, B_skewsym), true, ) @test manifold_dimension(M) == 3 diff --git a/test/spectrahedron.jl b/test/spectrahedron.jl index a9dcbfbe4a..b8865f0507 100644 --- a/test/spectrahedron.jl +++ b/test/spectrahedron.jl @@ -8,14 +8,14 @@ include("utils.jl") @test representation_size(M) == (4, 2) q = [1.0 0.0; 0.0 1.0; 1.0 1.0; -1.0 1.0] q = q / norm(q) - @test is_manifold_point(M, q, true) + @test is_point(M, q, true) @test base_manifold(M) === M qN = [2.0 0.0; 0.0 1.0; 1/sqrt(2) -1/sqrt(2); 1/sqrt(2) 1/sqrt(2)] - @test_throws DomainError is_manifold_point(M, qN, true) + @test_throws DomainError is_point(M, qN, true) Y = [0.0 1.0; 1.0 0.0; 0.0 0.0; 0.0 0.0] - @test is_tangent_vector(M, q, Y, true; check_base_point=false) + @test is_vector(M, q, Y, true) YN = [0.1 1.0; 1.0 0.1; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_tangent_vector(M, q, YN, true; check_base_point=false) + @test_throws DomainError is_vector(M, q, YN, true) qE = similar(q) embed!(M, qE, q) qE2 = embed(M, q) @@ -25,7 +25,7 @@ include("utils.jl") q2 = q2 ./ norm(q2) q3 = [12.0/13.0 5.0/13.0; 1.0 0.0; -12.0/13.0 5.0/13.0; 0.0 1.0] q3 = q3 ./ norm(q3) - @test is_tangent_vector( + @test is_vector( M, q2, vector_transport_to(M, q, Y, q2, ProjectionTransport()); diff --git a/test/sphere.jl b/test/sphere.jl index b1c753b4d2..e283f20c70 100644 --- a/test/sphere.jl +++ b/test/sphere.jl @@ -1,5 +1,8 @@ include("utils.jl") +using Manifolds: induced_basis +using ManifoldsBase: TFVector + @testset "Sphere" begin M = Sphere(2) @testset "Sphere Basics" begin @@ -10,17 +13,12 @@ include("utils.jl") @test injectivity_radius(M, ExponentialRetraction()) == Ο€ @test injectivity_radius(M, ProjectionRetraction()) == Ο€ / 2 @test base_manifold(M) === M - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_manifold_point(M, [2.0, 0.0, 0.0], true) - @test !is_manifold_point(M, [2.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) - @test_throws DomainError is_tangent_vector( - M, - [1.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - true, - ) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test !is_point(M, [2.0, 0.0, 0.0]) + @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) + @test_throws DomainError is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], true) @test injectivity_radius(M, [1.0, 0.0, 0.0], ProjectionRetraction()) == Ο€ / 2 end types = [Vector{Float64}] @@ -28,6 +26,7 @@ include("utils.jl") TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) basis_types = (DefaultOrthonormalBasis(), ProjectedOrthonormalBasis(:svd)) + test_atlases = (Manifolds.StereographicAtlas(), Manifolds.RetractionAtlas()) for T in types @testset "Type $T" begin pts = [ @@ -58,6 +57,7 @@ include("utils.jl") retraction_methods=[ProjectionRetraction(), ExponentialRetraction()], inverse_retraction_methods=[ProjectionInverseRetraction()], is_tangent_atol_multiplier=1, + test_atlases=test_atlases, ) @test isapprox(-pts[1], exp(M, pts[1], log(M, pts[1], -pts[1]))) end @@ -101,7 +101,7 @@ include("utils.jl") for i in 1:n vcoord = [j == i for j in 1:n] v = get_vector(M, p, vcoord, B) - @test is_tangent_vector(M, p, v) + @test is_vector(M, p, v) @test get_coordinates(M, p, v, B) β‰ˆ vcoord end end @@ -130,10 +130,10 @@ include("utils.jl") @test representation_size(M) == (3,) p = [1.0, 1.0im, 1.0] q = project(M, p) - @test is_manifold_point(M, q) + @test is_point(M, q) Y = [2.0, 1.0im, 20.0] X = project(M, q, Y) - @test is_tangent_vector(M, q, X, true; atol=10^(-14)) + @test is_vector(M, q, X, true; atol=10^(-14)) end @testset "Quaternion Sphere" begin @@ -143,10 +143,10 @@ include("utils.jl") @test representation_size(M) == (3,) p = [Quaternion(1.0), Quaternion(1.0im), Quaternion(0.0, 0.0, -1.0, 0.0)] q = project(M, p) - @test is_manifold_point(M, q) + @test is_point(M, q) Y = [Quaternion(2.0), Quaternion(1.0im), Quaternion(0.0, 0.0, 20.0, 0.0)] X = project(M, q, Y) - @test is_tangent_vector(M, q, X, true; atol=10^(-14)) + @test is_vector(M, q, X, true; atol=10^(-14)) end @testset "Array Sphere" begin @@ -156,14 +156,61 @@ include("utils.jl") @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) - @test is_manifold_point(M, q) + @test is_point(M, q) Y = [1.0 0.0; 0.0 1.1] X = project(M, q, Y) - @test is_tangent_vector(M, q, X) + @test is_vector(M, q, X) M = ArraySphere(2, 2; field=β„‚) @test repr(M) == "ArraySphere(2, 2; field = β„‚)" @test typeof(get_embedding(M)) === Euclidean{Tuple{2,2},β„‚} @test representation_size(M) == (2, 2) end + + @testset "StereographicAtlas" begin + M = Sphere(2) + A = Manifolds.StereographicAtlas() + p = randn(3) + p ./= norm(p) + for k in [1, -1] + p *= k + i = Manifolds.get_chart_index(M, A, p) + @test i === (p[1] < 0 ? :south : :north) + a = get_point_coordinates(M, A, i, p) + q = get_point(M, A, i, a) + @test isapprox(M, p, q) + + p2 = randn(3) + p2 ./= norm(p2) + p3 = randn(3) + p3 ./= norm(p3) + X2 = log(M, p, p2) + X3 = log(M, p, p3) + B = induced_basis(M, A, i, TangentSpace) + + X2B = get_coordinates(M, p, X2, B) + X3B = get_coordinates(M, p, X3, B) + + @test inner(M, p, X2, X3) β‰ˆ dot(X2B, local_metric(M, p, B) * X3B) + + X2back = get_vector(M, p, X2B, B) + X3back = get_vector(M, p, X3B, B) + @test isapprox(M, p, X2, X2back) + @test isapprox(M, p, X3, X3back) + + @testset "transition_map" begin + other_chart = i === :south ? :north : :south + a_other = Manifolds.transition_map(M, A, i, other_chart, a) + @test isapprox(M, p, get_point(M, A, other_chart, a_other)) + + a_other2 = allocate(a_other) + Manifolds.transition_map!(M, a_other2, A, i, A, other_chart, a) + @test isapprox(M, p, get_point(M, A, other_chart, a_other2)) + + a_other2 = allocate(a_other) + Manifolds.transition_map!(M, a_other2, A, i, other_chart, a) + @test isapprox(M, p, get_point(M, A, other_chart, a_other2)) + end + end + end end diff --git a/test/sphere_symmetric_matrices.jl b/test/sphere_symmetric_matrices.jl index a7d345c177..ddaabe452d 100644 --- a/test/sphere_symmetric_matrices.jl +++ b/test/sphere_symmetric_matrices.jl @@ -15,18 +15,18 @@ include("utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test typeof(get_embedding(M)) === ArraySphere{Tuple{3,3},ℝ} - @test check_manifold_point(M, A) === nothing - @test_throws DomainError is_manifold_point(M, B, true) - @test_throws DomainError is_manifold_point(M, C, true) - @test_throws DomainError is_manifold_point(M, D, true) - @test_throws DomainError is_manifold_point(M, E, true) - @test check_tangent_vector(M, A, zeros(3, 3)) === nothing - @test_throws DomainError is_tangent_vector(M, A, B, true) - @test_throws DomainError is_tangent_vector(M, A, C, true) - @test_throws DomainError is_tangent_vector(M, A, D, true) - @test_throws DomainError is_tangent_vector(M, D, A, true) - @test_throws DomainError is_tangent_vector(M, A, E, true) - @test_throws DomainError is_tangent_vector(M, J, K, true) + @test check_point(M, A) === nothing + @test_throws DomainError is_point(M, B, true) + @test_throws DomainError is_point(M, C, true) + @test_throws DomainError is_point(M, D, true) + @test_throws DomainError is_point(M, E, true) + @test check_vector(M, A, zeros(3, 3)) === nothing + @test_throws DomainError is_vector(M, A, B, true) + @test_throws DomainError is_vector(M, A, C, true) + @test_throws DomainError is_vector(M, A, D, true) + @test_throws DomainError is_vector(M, D, A, true) + @test_throws DomainError is_vector(M, A, E, true) + @test_throws DomainError is_vector(M, J, K, true) @test manifold_dimension(M) == 5 A2 = similar(A) @test A == project!(M, A2, A) diff --git a/test/statistics.jl b/test/statistics.jl index 21863f5aef..7cd37806af 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -2,13 +2,7 @@ include("utils.jl") using StatsBase: AbstractWeights, pweights using Random: GLOBAL_RNG, seed! import ManifoldsBase: - manifold_dimension, - exp!, - log!, - inner, - zero_tangent_vector!, - decorated_manifold, - base_manifold + manifold_dimension, exp!, log!, inner, zero_vector!, decorated_manifold, base_manifold using Manifolds: AbstractEstimationMethod, CyclicProximalPointEstimation, @@ -18,7 +12,7 @@ using Manifolds: WeiszfeldEstimation import Manifolds: mean!, median!, var, mean_and_var -struct TestStatsSphere{N} <: Manifold{ℝ} end +struct TestStatsSphere{N} <: AbstractManifold{ℝ} end TestStatsSphere(N) = TestStatsSphere{N}() manifold_dimension(::TestStatsSphere{N}) where {N} = manifold_dimension(Sphere(N)) function exp!(::TestStatsSphere{N}, w, x, y; kwargs...) where {N} @@ -30,11 +24,11 @@ end function inner(::TestStatsSphere{N}, x, v, w; kwargs...) where {N} return inner(Sphere(N), x, v, w; kwargs...) end -function zero_tangent_vector!(::TestStatsSphere{N}, v, x; kwargs...) where {N} - return zero_tangent_vector!(Sphere(N), v, x; kwargs...) +function zero_vector!(::TestStatsSphere{N}, v, x; kwargs...) where {N} + return zero_vector!(Sphere(N), v, x; kwargs...) end -struct TestStatsEuclidean{N} <: Manifold{ℝ} end +struct TestStatsEuclidean{N} <: AbstractManifold{ℝ} end TestStatsEuclidean(N) = TestStatsEuclidean{N}() manifold_dimension(::TestStatsEuclidean{N}) where {N} = manifold_dimension(Euclidean(N)) function exp!(::TestStatsEuclidean{N}, y, x, v; kwargs...) where {N} @@ -46,8 +40,8 @@ end function inner(::TestStatsEuclidean{N}, x, v, w; kwargs...) where {N} return inner(Euclidean(N), x, v, w; kwargs...) end -function zero_tangent_vector!(::TestStatsEuclidean{N}, v, x; kwargs...) where {N} - return zero_tangent_vector!(Euclidean(N), v, x; kwargs...) +function zero_vector!(::TestStatsEuclidean{N}, v, x; kwargs...) where {N} + return zero_vector!(Euclidean(N), v, x; kwargs...) end struct TestStatsNotImplementedEmbeddedManifold <: @@ -68,7 +62,7 @@ base_manifold(::TestStatsNotImplementedEmbeddedManifold3) = Sphere(2) function test_mean(M, x, yexp=nothing, method...; kwargs...) @testset "mean unweighted" begin y = mean(M, x; kwargs...) - @test is_manifold_point(M, y; atol=10^-9) + @test is_point(M, y; atol=10^-9) if yexp !== nothing @test isapprox(M, y, yexp; atol=10^-7) end @@ -85,7 +79,7 @@ function test_mean(M, x, yexp=nothing, method...; kwargs...) w3 = pweights(2 * ones(n)) y = mean(M, x; kwargs...) for w in (w1, w2, w3) - @test is_manifold_point(M, mean(M, x, w; kwargs...); atol=10^-9) + @test is_point(M, mean(M, x, w; kwargs...); atol=10^-9) @test isapprox(M, mean(M, x, w; kwargs...), y) @test isapprox(M, mean_and_var(M, x, w; kwargs...)[1], y; atol=10^-7) @@ -105,7 +99,7 @@ function test_median( ) @testset "median unweighted$(!isnothing(method) ? " ($method)" : "")" begin y = isnothing(method) ? median(M, x; kwargs...) : median(M, x, method; kwargs...) - @test is_manifold_point(M, y; atol=10^-9) + @test is_point(M, y; atol=10^-9) if yexp !== nothing @test isapprox(M, y, yexp; atol=10^-5) end @@ -119,9 +113,9 @@ function test_median( y = median(M, x; kwargs...) for w in (w1, w2, w3) if isnothing(method) - @test is_manifold_point(M, median(M, x, w; kwargs...); atol=10^-9) + @test is_point(M, median(M, x, w; kwargs...); atol=10^-9) else - @test is_manifold_point(M, median(M, x, w, method; kwargs...); atol=10^-9) + @test is_point(M, median(M, x, w, method; kwargs...); atol=10^-9) end @test isapprox(M, median(M, x, w; kwargs...), y; atol=10^-4) end @@ -268,9 +262,9 @@ function test_moments(M, x) return nothing end -struct TestStatsOverload1 <: Manifold{ℝ} end -struct TestStatsOverload2 <: Manifold{ℝ} end -struct TestStatsOverload3 <: Manifold{ℝ} end +struct TestStatsOverload1 <: AbstractManifold{ℝ} end +struct TestStatsOverload2 <: AbstractManifold{ℝ} end +struct TestStatsOverload3 <: AbstractManifold{ℝ} end struct TestStatsMethod1 <: AbstractEstimationMethod end function mean!( diff --git a/test/stiefel.jl b/test/stiefel.jl index 680027255a..a91ad2cc58 100644 --- a/test/stiefel.jl +++ b/test/stiefel.jl @@ -13,19 +13,10 @@ using Manifolds: default_metric_dispatch @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 3 base_manifold(M) === M - @test_throws DomainError is_manifold_point(M, [1.0, 0.0, 0.0, 0.0], true) - @test_throws DomainError is_manifold_point( - M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, - ) - @test !is_tangent_vector(M, x, [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_tangent_vector( - M, - x, - 1 * im * zero_tangent_vector(M, x), - true, - ) + @test_throws DomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test !is_vector(M, x, [0.0, 0.0, 1.0, 0.0]) + @test_throws DomainError is_vector(M, x, 1 * im * zero_vector(M, x), true) end @testset "Embedding and Projection" begin x = [1.0 0.0; 0.0 1.0; 0.0 0.0] @@ -35,7 +26,7 @@ using Manifolds: default_metric_dispatch embed!(M, y, x) @test y == z a = [1.0 0.0; 0.0 2.0; 0.0 0.0] - @test !is_manifold_point(M, a) + @test !is_point(M, a) b = similar(a) c = project(M, a) @test c == x @@ -110,12 +101,12 @@ using Manifolds: default_metric_dispatch ) pts = convert.(T, [x, y, z]) v = inverse_retract(M, x, y, PolarInverseRetraction()) - @test !is_manifold_point(M, 2 * x) - @test_throws DomainError !is_manifold_point(M, 2 * x, true) - @test !is_tangent_vector(M, 2 * x, v) - @test_throws DomainError !is_tangent_vector(M, 2 * x, v, true) - @test !is_tangent_vector(M, x, y) - @test_throws DomainError is_tangent_vector(M, x, y, true) + @test !is_point(M, 2 * x) + @test_throws DomainError !is_point(M, 2 * x, true) + @test !is_vector(M, 2 * x, v) + @test_throws DomainError !is_vector(M, 2 * x, v, true) + @test !is_vector(M, x, y) + @test_throws DomainError is_vector(M, x, y, true) test_manifold( M, pts, @@ -193,8 +184,8 @@ using Manifolds: default_metric_dispatch @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 8 @test Manifolds.allocation_promotion_function(M, exp!, (1,)) == complex - @test !is_manifold_point(M, [1.0, 0.0, 0.0, 0.0]) - @test !is_tangent_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) + @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) + @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) x = [1.0 0.0; 0.0 1.0; 0.0 0.0] end types = [Matrix{ComplexF64}] @@ -204,12 +195,12 @@ using Manifolds: default_metric_dispatch z = exp(M, x, [0.0 0.0; 0.0 0.0; -1.0 1.0]) pts = convert.(T, [x, y, z]) v = inverse_retract(M, x, y, PolarInverseRetraction()) - @test !is_manifold_point(M, 2 * x) - @test_throws DomainError !is_manifold_point(M, 2 * x, true) - @test !is_tangent_vector(M, 2 * x, v) - @test_throws DomainError !is_tangent_vector(M, 2 * x, v, true) - @test !is_tangent_vector(M, x, y) - @test_throws DomainError is_tangent_vector(M, x, y, true) + @test !is_point(M, 2 * x) + @test_throws DomainError !is_point(M, 2 * x, true) + @test !is_vector(M, 2 * x, v) + @test_throws DomainError !is_vector(M, 2 * x, v, true) + @test !is_vector(M, x, y) + @test_throws DomainError is_vector(M, x, y, true) test_manifold( M, pts, @@ -279,7 +270,7 @@ using Manifolds: default_metric_dispatch @test r1 == PadeRetraction(1) @test repr(r1) == "CayleyRetraction()" q1 = retract(M, p, X, r1) - @test is_manifold_point(M, q1) + @test is_point(M, q1) Y = vector_transport_direction( M, p, @@ -287,7 +278,7 @@ using Manifolds: default_metric_dispatch X, DifferentiatedRetractionVectorTransport(CayleyRetraction()), ) - @test is_tangent_vector(M, q1, Y; atol=10^-15) + @test is_vector(M, q1, Y; atol=10^-15) Y2 = vector_transport_direction( M, p, @@ -295,11 +286,11 @@ using Manifolds: default_metric_dispatch X, DifferentiatedRetractionVectorTransport{CayleyRetraction}(), ) - @test is_tangent_vector(M, q1, Y2; atol=10^-15) + @test is_vector(M, q1, Y2; atol=10^-15) r2 = PadeRetraction(2) @test repr(r2) == "PadeRetraction(2)" q2 = retract(M, p, X, r2) - @test is_manifold_point(M, q2) + @test is_point(M, q2) end @testset "Canonical Metric" begin diff --git a/test/symmetric.jl b/test/symmetric.jl index de02227e44..d0aefc54ff 100644 --- a/test/symmetric.jl +++ b/test/symmetric.jl @@ -17,20 +17,15 @@ include("utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test typeof(get_embedding(M)) === Euclidean{Tuple{3,3},ℝ} - @test check_manifold_point(M, B_sym) === nothing - @test_throws DomainError is_manifold_point(M, A, true) - @test_throws DomainError is_manifold_point(M, C, true) - @test_throws DomainError is_manifold_point(M, D, true) - @test check_tangent_vector(M, B_sym, B_sym) === nothing - @test_throws DomainError is_tangent_vector(M, B_sym, A, true) - @test_throws DomainError is_tangent_vector(M, A, B_sym, true) - @test_throws DomainError is_tangent_vector(M, B_sym, D, true) - @test_throws DomainError is_tangent_vector( - M, - B_sym, - 1 * im * zero_tangent_vector(M, B_sym), - true, - ) + @test check_point(M, B_sym) === nothing + @test_throws DomainError is_point(M, A, true) + @test_throws DomainError is_point(M, C, true) + @test_throws DomainError is_point(M, D, true) + @test check_vector(M, B_sym, B_sym) === nothing + @test_throws DomainError is_vector(M, B_sym, A, true) + @test_throws DomainError is_vector(M, A, B_sym, true) + @test_throws DomainError is_vector(M, B_sym, D, true) + @test_throws DomainError is_vector(M, B_sym, 1 * im * zero_vector(M, B_sym), true) @test manifold_dimension(M) == 6 @test manifold_dimension(M_complex) == 9 @test A_sym2 == project!(M, A_sym, A_sym) diff --git a/test/symmetric_positive_definite.jl b/test/symmetric_positive_definite.jl index fd4ba9a6fc..ced49b9514 100644 --- a/test/symmetric_positive_definite.jl +++ b/test/symmetric_positive_definite.jl @@ -24,10 +24,8 @@ using Manifolds: default_metric_dispatch @test injectivity_radius(M1, one(zeros(3, 3))) == Inf @test injectivity_radius(M1, ExponentialRetraction()) == Inf @test injectivity_radius(M1, one(zeros(3, 3)), ExponentialRetraction()) == Inf - @test zero_tangent_vector(M1, one(zeros(3, 3))) == - zero_tangent_vector(M2, one(zeros(3, 3))) - @test zero_tangent_vector(M1, one(zeros(3, 3))) == - zero_tangent_vector(M3, one(zeros(3, 3))) + @test zero_vector(M1, one(zeros(3, 3))) == zero_vector(M2, one(zeros(3, 3))) + @test zero_vector(M1, one(zeros(3, 3))) == zero_vector(M3, one(zeros(3, 3))) metrics = [M1, M2, M3] types = [Matrix{Float64}] TEST_FLOAT32 && push!(types, Matrix{Float32}) @@ -73,18 +71,18 @@ using Manifolds: default_metric_dispatch is_tangent_atol_multiplier=1, ) end - @testset "Test Error cases in is_manifold_point and is_tangent_vector" begin + @testset "Test Error cases in is_point and is_vector" begin pt1f = zeros(2, 3) # wrong size pt2f = [1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 1.0] # not positive Definite pt3f = [2.0 0.0 1.0; 0.0 1.0 0.0; 0.0 0.0 4.0] # not symmetric pt4 = [2.0 1.0 0.0; 1.0 2.0 0.0; 0.0 0.0 4.0] - @test !is_manifold_point(M, pt1f) - @test !is_manifold_point(M, pt2f) - @test !is_manifold_point(M, pt3f) - @test is_manifold_point(M, pt4) - @test !is_tangent_vector(M, pt4, pt1f) - @test is_tangent_vector(M, pt4, pt2f) - @test !is_tangent_vector(M, pt4, pt3f) + @test !is_point(M, pt1f) + @test !is_point(M, pt2f) + @test !is_point(M, pt3f) + @test is_point(M, pt4) + @test !is_vector(M, pt4, pt1f) + @test is_vector(M, pt4, pt2f) + @test !is_vector(M, pt4, pt3f) end end end @@ -126,18 +124,18 @@ using Manifolds: default_metric_dispatch p3 = A(Ο€ / 6) * [1.0 0.0 0.0; 0.0 2.0 0.0; 0.0 0.0 1] * transpose(A(Ο€ / 6)) X1 = log(M, p1, p3) Y1 = vector_transport_to(M, p1, X1, p2) - @test is_tangent_vector(M, p2, Y1) + @test is_vector(M, p2, Y1) Y2 = vector_transport_to(M, p1, X1, p2, PoleLadderTransport()) - @test is_tangent_vector(M, p2, Y2, atol=10^-16) + @test is_vector(M, p2, Y2, atol=10^-16) Y3 = vector_transport_to(M, p1, X1, p2, SchildsLadderTransport()) - @test is_tangent_vector(M, p2, Y3) + @test is_vector(M, p2, Y3) @test isapprox(M, p2, Y1, Y2) # pole is exact on SPDs, i.e. identical to parallel transport @test norm(M, p1, X1) β‰ˆ norm(M, p2, Y1) # parallel transport is length preserving # test isometry X2 = log(M, p1, p2) Y4 = vector_transport_to(M, p1, X2, p2) @test norm(M, p1, X2) β‰ˆ norm(M, p2, Y4) - @test is_tangent_vector(M, p2, Y4) + @test is_vector(M, p2, Y4) Y5 = vector_transport_to(M, p1, X2, p2, PoleLadderTransport()) @test inner(M, p1, X1, X2) β‰ˆ inner(M, p2, Y1, Y4) # parallel transport isometric @test inner(M, p1, X1, X2) β‰ˆ inner(M, p2, Y2, Y5) # pole ladder transport isometric diff --git a/test/symmetric_positive_semidefinite_fixed_rank.jl b/test/symmetric_positive_semidefinite_fixed_rank.jl index 94b73ca17a..671d467323 100644 --- a/test/symmetric_positive_semidefinite_fixed_rank.jl +++ b/test/symmetric_positive_semidefinite_fixed_rank.jl @@ -6,16 +6,16 @@ include("utils.jl") @test repr(M) == "SymmetricPositiveSemidefiniteFixedRank(4, 2, ℝ)" @test manifold_dimension(M) == 7 q = [1.0 0.0; 0.0 1.0; 0.0 0.0; 0.0 0.0] - @test is_manifold_point(M, q) + @test is_point(M, q) Y = [1.0 0.0; 0.0 0.0; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_manifold_point(M, Y, true) - @test is_tangent_vector(M, q, Y) + @test_throws DomainError is_point(M, Y, true) + @test is_vector(M, q, Y) q2 = [2.0 1.0; 0.0 0.0; 0.0 1.0; 0.0 0.0] q3 = [0.0 0.0; 1.0 0.0; 0.0 1.0; 0.0 0.0] X = log(M, q, q2) X3 = vector_transport_to(M, q, X, q3, ProjectionTransport()) X3t = project(M, q3, X) - @test is_tangent_vector(M, q3, X3) + @test is_vector(M, q3, X3) @test isapprox(M, q3, X3, X3t) types = [Matrix{Float64}] diff --git a/test/torus.jl b/test/torus.jl index e3c18f797b..db8792eccc 100644 --- a/test/torus.jl +++ b/test/torus.jl @@ -10,14 +10,14 @@ include("utils.jl") @test repr(M) == "Torus(2)" @test representation_size(M) == (2,) @test manifold_dimension(M) == 2 - @test !is_manifold_point(M, 9.0) - @test_throws DomainError is_manifold_point(M, 9.0, true) - @test !is_manifold_point(M, [9.0; 9.0]) - @test_throws CompositeManifoldError is_manifold_point(M, [9.0 9.0], true) - @test !is_tangent_vector(M, [9.0; 9.0], 0.0) - @test_throws DomainError is_tangent_vector(M, 9.0, 0.0, true) - @test !is_tangent_vector(M, [9.0; 9.0], [0.0; 0.0]) - @test_throws DomainError is_tangent_vector(M, 9.0, 0.0, true) + @test !is_point(M, 9.0) + @test_throws DomainError is_point(M, 9.0, true) + @test !is_point(M, [9.0; 9.0]) + @test_throws CompositeManifoldError is_point(M, [9.0 9.0], true) + @test !is_vector(M, [9.0; 9.0], 0.0) + @test_throws DomainError is_vector(M, 9.0, 0.0, true) # point false and checked + @test !is_vector(M, [9.0; 9.0], [0.0; 0.0]) + @test_throws DomainError is_vector(M, [0.0, 0.0], 0.0, true) @test injectivity_radius(M) β‰ˆ Ο€ x = [1.0, 2.0] y = [-1.0, 2.0] diff --git a/test/vector_bundle.jl b/test/vector_bundle.jl index 05159f4433..511e0926cc 100644 --- a/test/vector_bundle.jl +++ b/test/vector_bundle.jl @@ -5,53 +5,6 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "Tangent bundle" begin M = Sphere(2) - @testset "FVector" begin - @test sprint(show, TangentSpace) == "TangentSpace" - @test sprint(show, CotangentSpace) == "CotangentSpace" - tvs = ([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) - fv_tvs = map(v -> FVector(TangentSpace, v), tvs) - fv1 = fv_tvs[1] - tv1s = allocate(fv_tvs[1]) - @test isa(tv1s, FVector) - @test size(tv1s) == (3,) - @test tv1s.type == TangentSpace - @test size(tv1s.data) == size(tvs[1]) - @test number_eltype(tv1s) == number_eltype(tvs[1]) - @test number_eltype(tv1s) == number_eltype(typeof(tv1s)) - @test isa(fv1 + fv1, FVector) - @test (fv1 + fv1).type == TangentSpace - @test isa(fv1 - fv1, FVector) - @test (fv1 - fv1).type == TangentSpace - @test isa(-fv1, FVector) - @test (-fv1).type == TangentSpace - @test isa(2 * fv1, FVector) - @test (2 * fv1).type == TangentSpace - @test fv1[1] == tvs[1][1] - - 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], [1 2])) - @test submanifold_component(fv2, 1) == [1, 0, 0] - @test submanifold_component(fv2, 2) == [1 2] - @test (@inferred submanifold_component(fv2, Val(1))) == [1, 0, 0] - @test (@inferred submanifold_component(fv2, Val(2))) == [1 2] - @test submanifold_component(PM, fv2, 1) == [1, 0, 0] - @test submanifold_component(PM, fv2, 2) == [1 2] - @test (@inferred submanifold_component(PM, fv2, Val(1))) == [1, 0, 0] - @test (@inferred submanifold_component(PM, fv2, Val(2))) == [1 2] - @test submanifold_components(PM, fv2) == ([1.0, 0.0, 0.0], [1.0 2.0]) - end - @testset "Nice access to vector bundle components" begin TB = TangentBundle(M) p = ProductRepr([1.0, 0.0, 0.0], [0.0, 2.0, 4.0]) @@ -198,7 +151,7 @@ struct TestVectorSpaceType <: VectorSpaceType end @test_throws ErrorException inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws ErrorException Manifolds.project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws ErrorException zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) - @test_throws ErrorException vector_space_dimension(vbf) + @test_throws MethodError vector_space_dimension(vbf) a = fill(0.0, 6) @test_throws ErrorException get_coordinates!( TangentBundle(M), @@ -216,7 +169,7 @@ struct TestVectorSpaceType <: VectorSpaceType end p2 = ProductRepr([-1.047, -1.047], [0.0, 0.0]) X1 = log(N, p1, p2) @test isapprox(N, p2, exp(N, p1, X1)) - @test is_tangent_vector(N, p2, vector_transport_to(N, p1, X1, p2)) + @test is_vector(N, p2, vector_transport_to(N, p1, X1, p2)) M2 = ProductManifold(Circle(ℝ), Euclidean(2)) N2 = TangentBundle(M2)