Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface Documentation rewrite #359

Merged
merged 25 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a52ea35
starts rewriting the interface page.
kellertuer May 16, 2021
01527ec
Merge branch 'master' into kellertuer/documentation-interface-rewrite
kellertuer May 16, 2021
7a8cf70
adapt to new ManifoldsBase file structure.
kellertuer May 16, 2021
7383314
Fix docs.
kellertuer May 16, 2021
b1650d7
fix docs.
kellertuer May 16, 2021
65c4430
add back the implementation using NLsolve since it required Requires.jl
kellertuer May 16, 2021
36b2351
refactor ManifoldsTests to be not a submodule anymore.
kellertuer May 17, 2021
6c6a62b
remove the last parts from the submodule approach
kellertuer May 17, 2021
0329a7d
finish refatcoring to have the tests as separate files included when …
kellertuer May 17, 2021
ea909e3
specify a new anchor.
kellertuer May 17, 2021
d99fa84
start a first circle illustration.
kellertuer May 18, 2021
7dd0ce6
adds the illustrations on the circle.
kellertuer May 18, 2021
f1b0e1f
runs formatter.
kellertuer May 18, 2021
dc2446b
extend documentation.
kellertuer May 21, 2021
b9fa831
Merge branch 'master' into kellertuer/documentation-interface-rewrite
kellertuer May 22, 2021
c4a6726
Further fixes in docs and to the new interface.
kellertuer May 22, 2021
c335db2
Fixes a few last typos.
kellertuer May 22, 2021
a3b8cd1
Move a warning here from ManifoldsBase.
kellertuer May 22, 2021
a9354cc
refine notation.
kellertuer May 23, 2021
3d50323
dependency bump and format.
kellertuer May 23, 2021
fff9d55
Update docs/src/misc/notation.md
kellertuer May 24, 2021
cd4caa5
Fix a warning, add power manifold test.
kellertuer May 24, 2021
3c54c03
fix a typo
kellertuer May 24, 2021
202a596
Apply suggestions from code review
kellertuer May 24, 2021
14030aa
correct push forward notation.
kellertuer May 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ You can also ask your question on [discourse.julialang.org](https://discourse.ju

## How can I file an issue?

If you found a bug or want to propose a feature, we track our issues within the [GitHub repository](https://github.com/JuliaNLSolvers/Manifolds.jl/issues).
If you found a bug or want to propose a feature, we track our issues within the [GitHub repository](https://github.com/JuliaManifolds/Manifolds.jl/issues).

## How can I contribute?

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ FiniteDiff = "2"
FiniteDifferences = "0.12"
HybridArrays = "0.4"
LightGraphs = "1"
ManifoldsBase = "0.11"
ManifoldsBase = "0.11.3"
Plots = "~1.6, =1.10.5"
RecipesBase = "1.1"
Requires = "0.5, 1"
Expand Down
110 changes: 110 additions & 0 deletions assets/projection_illustration.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Manifolds, LinearAlgebra, PGFPlotsX, Colors

out_file = joinpath(
@__DIR__,
"..",
"docs",
"src",
"assets",
"images",
"projection_illustration.png",
)
n = 100

line_width = 1 / 2
light_color = colorant"#BBBBBB"
base_linewidth = line_width
point_color = colorant"#0077BB" #Blue
main_linewidth = 1.25
tangent_color = colorant"#33BBEE" #Cyan
tangent_linewidth = main_linewidth
geo_color = colorant"#009988" #teal
geo_linewidth = 1.25
geo_marker_size = 1.5
projection_color = colorant"#EE7733"
projection_color2 = colorant"#0077BB" #Blue
projection_color3 = colorant"#33BBEE" #Cyan
projection_marker_size = 1.5

C_to_2D(p) = [real(p), imag(p)]

M = Circle(ℂ)

p = (-2 + 1im) / abs(-2 + 1im)
X = (imag(p) - real(p) * im)
q = -1.3 + 1.5im

s1 = project(M, q)
s2 = project(M, p, q)

circle = [[sin(φ), cos(φ)] for φ in range(0, 2π, length=n)]
tp = @pgf Axis({
axis_lines = "none",
axis_equal,
xmin = -1.7,
xmax = 1.7,
ymin = -.25,
ymax = 1.4,
scale = 1.6,
})

plain_opts = @pgf {"no markers", color = light_color, line_width = base_linewidth}

push!(tp, Plot(plain_opts, Coordinates(Tuple.(circle))))
plaint_opts_a = @pgf {"only marks", mark_size = geo_marker_size / 2, color = light_color}
push!(tp, Plot(plaint_opts_a, Coordinates(Tuple.([[0.0, 0.0]]))))

tangent_line2 = [C_to_2D(p - 0.8 * X), C_to_2D(p + 1.1 * X)]
push!(tp, Plot(plain_opts, Coordinates(Tuple.(tangent_line2))))

projection_opts2 = @pgf {
"no markers",
"densely dotted",
mark_size = projection_marker_size,
color = projection_color2,
}
projection_line2 = [C_to_2D(q), C_to_2D(s1)]
push!(tp, Plot(projection_opts2, Coordinates(Tuple.(projection_line2))))
projection_opts2_a =
@pgf {"only marks", mark_size = projection_marker_size, color = projection_color2}
push!(tp, Plot(projection_opts2_a, Coordinates(Tuple.([C_to_2D(s1)]))))

geo_opts2 = @pgf {"only marks", mark_size = geo_marker_size, color = geo_color}
push!(tp, Plot(geo_opts2, Coordinates(Tuple.(C_to_2D.([p, q])))))

projection_opts3 = @pgf {
"no markers",
"densely dotted",
mark_size = projection_marker_size,
color = projection_color3,
}
projection_line3 = [C_to_2D(q), C_to_2D(p + s2)]
push!(tp, Plot(projection_opts3, Coordinates(Tuple.(projection_line3))))
projection_opts3_a =
@pgf {"only marks", mark_size = projection_marker_size, color = projection_color3}
push!(tp, Plot(projection_opts3_a, Coordinates(Tuple.([C_to_2D(p + s2)]))))

push!(
tp,
raw"\node[label ={[label distance=.05cm]below:{\color{gray}$q$}}] at (axis cs:" *
"$(real(q)),$(imag(q))) {};",
)
push!(
tp,
raw"\node[label ={[label distance=.05cm]left:{\color{gray}$p$}}] at (axis cs:" *
"$(real(p)),$(imag(p))) {};",
)
# push!(tp, raw"\node[label ={[label distance=.05cm]right:{$s_1=\mathrm{proj}_{\mathcal C}(q_2)=$\texttt{project(M,q2)}}}] at (axis cs:"*"$(real(s1)),$(imag(s1))) {};")
push!(
tp,
raw"\node[label ={[label distance=.05cm]right:{\color{gray}$s_1=$\texttt{project(M,q)$\in\mathcal C$}}}] at (axis cs:" *
"$(real(s1)),$(imag(s1))) {};",
)
push!(
tp,
raw"\node[label ={[label distance=.05cm]0:{\color{gray}$s_2=$\texttt{project(M,p,q)}$\in T_{p}\mathcal C$}}] at (axis cs:" *
"$(real(p+s2)),$(imag(p+s2))) {};",
)
push!(tp, raw"\node at (axis cs: 0,-.6) {\color{gray}$\mathcal C$};")

pgfsave(out_file, tp, dpi=1200)
104 changes: 104 additions & 0 deletions assets/retraction_illustration.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using Manifolds, LinearAlgebra, PGFPlotsX, Colors

out_file = joinpath(
@__DIR__,
"..",
"docs",
"src",
"assets",
"images",
"retraction_illustration.png",
)
n = 100

line_width = 1 / 2
light_color = colorant"#BBBBBB"
base_linewidth = line_width
point_color = colorant"#0077BB" #Blue
main_linewidth = 1.25
tangent_color = colorant"#33BBEE" #Cyan
tangent_linewidth = main_linewidth
geo_color = colorant"#009988" #teal
geo_linewidth = 1.25
geo_marker_size = 1.5
projection_color = colorant"#EE7733"
projection_color2 = colorant"#0077BB" #Blue
projection_color3 = colorant"#33BBEE" #Cyan
projection_marker_size = 1.5

C_to_2D(p) = [real(p), imag(p)]

M = Circle(ℂ)

p = (1 + 2im) / abs(1 + 2im)
X = imag(p) - real(p) * im
qE = exp(M, p, X)
qP = project(M, p + X)

circle = [[sin(φ), cos(φ)] for φ in range(0, 2π, length=n)]
tp = @pgf Axis({
axis_lines = "none",
axis_equal,
xmin = -1.7,
xmax = 1.7,
ymin = -.25,
ymax = 1.4,
scale = 1.6,
})

plain_opts = @pgf {"no markers", color = light_color, line_width = base_linewidth}

push!(tp, Plot(plain_opts, Coordinates(Tuple.(circle))))
plaint_opts_a = @pgf {"only marks", mark_size = geo_marker_size / 2, color = light_color}
push!(tp, Plot(plaint_opts_a, Coordinates(Tuple.([[0.0, 0.0]]))))

tangent_line = [C_to_2D(p - 0.8 * X), C_to_2D(p + 1.3 * X)]
push!(tp, Plot(plain_opts, Coordinates(Tuple.(tangent_line))))

tangent_opts =
@pgf {"no markers", "->", color = tangent_color, line_width = tangent_linewidth}
tangent_vec = [C_to_2D(p), C_to_2D(p + X)]
push!(tp, Plot(tangent_opts, Coordinates(Tuple.(tangent_vec))))

geo_opts = @pgf {"no markers", "-", color = geo_color, line_width = geo_linewidth}
geo_pts = C_to_2D.(shortest_geodesic(M, p, qE, range(0, 1, length=n)))
push!(tp, Plot(geo_opts, Coordinates(Tuple.(geo_pts))))
geo_opts2 = @pgf {"only marks", mark_size = geo_marker_size, color = geo_color}
push!(tp, Plot(geo_opts2, Coordinates(Tuple.([C_to_2D(p), C_to_2D(qE)]))))

projection_opts = @pgf {
"no markers",
"densely dotted",
color = projection_color,
line_width = base_linewidth,
}
projection_line = [C_to_2D(p + X), C_to_2D(qP)]
push!(tp, Plot(projection_opts, Coordinates(Tuple.(projection_line))))
projection_opts_a =
@pgf {"only marks", mark_size = projection_marker_size, color = projection_color}
push!(tp, Plot(projection_opts_a, Coordinates(Tuple.([C_to_2D(qP)]))))

push!(
tp,
raw"\node[label ={[label distance=.05cm]above:{\color{gray}$p$}}] at (axis cs:" *
"$(real(p)),$(imag(p))) {};",
)
push!(
tp,
raw"\node[label ={[label distance=.1cm]above:{\color{gray}$X\in T_p\mathcal C$}}] at (axis cs:" *
"$(real(p+X/2)),$(imag(p+X/2))) {};",
)
push!(
tp,
raw"\node[label ={[label distance=.05cm]left:{\color{gray}$q'=\mathrm{retr}^{\mathrm{proj}}_pX=\mathrm{proj}_{\mathcal C}(p+X)$}}] at (axis cs:" *
"$(real(qP)),$(imag(qP))) {};",
)
push!(
tp,
raw"\node[label ={[label distance=.05cm]below:{\color{gray}$q=\exp_pX$}}] at (axis cs:" *
"$(real(qE)),$(imag(qE))) {};",
)

push!(tp, raw"\node at (axis cs: 0,-.6) {\color{gray}$\mathcal C$};")

pgfsave(out_file, tp; dpi=1200)
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat]
Documenter = "0.24, 0.25, 0.26"
HybridArrays = "0.4"
ManifoldsBase = "0.11"
ManifoldsBase = "0.11.3"
Plots = "= 1.10.5"
StaticArrays = "1.0"
PyPlot = "2.9"
4 changes: 2 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Plots, RecipesBase, Manifolds, ManifoldsBase, Documenter, PyPlot
# required for loading the Manifolds.ManifoldTests module
# required for loading the manifold tests functios
using Test, ForwardDiff, ReverseDiff
ENV["GKSwstype"] = "100"

Expand All @@ -14,7 +14,7 @@ cp(
makedocs(
# for development, we disable prettyurls
format=Documenter.HTML(prettyurls=false, assets=["assets/favicon.ico"]),
modules=[Manifolds, ManifoldsBase, Manifolds.ManifoldTests],
modules=[Manifolds, ManifoldsBase],
authors="Seth Axen, Mateusz Baran, Ronny Bergmann, and contributors.",
sitename="Manifolds.jl",
pages=[
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docs/src/features/testing.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Testing

Documentation for testing utilities for `Manifolds.jl`. The function [`test_manifold`](@ref Manifolds.ManifoldTests.test_manifold)
Documentation for testing utilities for `Manifolds.jl`. The function [`test_manifold`](@ref Manifolds.test_manifold)
can be used to verify that your manifold correctly implements the `Manifolds.jl`
interface. Similarly [`test_group`](@ref Manifolds.ManifoldTests.test_group) and [`test_action`](@ref Manifolds.ManifoldTests.test_action) can be used to verify implementation of groups and group actions.
interface. Similarly [`test_group`](@ref Manifolds.test_group) and [`test_action`](@ref Manifolds.test_action) can be used to verify implementation of groups and group actions.

```@autodocs
Modules = [Manifolds, Manifolds.ManifoldTests]
Pages = ["tests/ManifoldTests.jl", "tests/tests_general.jl", "tests/tests_group.jl"]
Modules = [Manifolds]
Pages = ["tests/reversediff.jl", "tests/test_forwarddiff.jl","tests/tests_general.jl", "tests/tests_group.jl"]
Order = [:type, :function]
```
102 changes: 100 additions & 2 deletions docs/src/interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,109 @@ Depth = 2

Additionally the [`AbstractDecoratorManifold`](@ref) is provided as well as the [`ValidationManifold`](@ref) as a specific example of such a decorator.

## Types and functions
## [Types and functions](@id interface-types-and-functions)

The following functions are currently available from the interface.
If a manifold that you implement for your own package fits this interface, we happily look forward to a [Pull Request](https://github.com/JuliaManifolds/Manifolds.jl/compare) to add it here.

We would like to highlight a few of the types and functions in the next two sections before listing the remaining types and functions alphabetically.

### The Manifold Type

Besides the most central type, that of a [`AbstractManifold`](@ref) accompanied by [`AbstractManifoldPoint`](@ref) to represent points thereon, note that the point type is meant in a lazy fashion.
kellertuer marked this conversation as resolved.
Show resolved Hide resolved
This is mean as follows: if you implement a new manifold and your points are represented by matrices, vectors or arrays, then it is best to not type the poitns `p` in functions, such that the methods work for example for other array representation types as well.
kellertuer marked this conversation as resolved.
Show resolved Hide resolved
You should subtype your new points on a manifold, if the structure you use is more structured, see for example[`FixedRankMatrices`](@ref).
kellertuer marked this conversation as resolved.
Show resolved Hide resolved
Another reason is, if you want to distinguish (and hence dispatch on) different representation of points on the manifold.
For an example, see the [Hyperbolic](@ref HyperbolicSpace) manifold, which has different models to be represented.

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["maintypes.jl"]
Order = [:type, :function]
```

### The exponential and the logarithmic map and geodesics
kellertuer marked this conversation as resolved.
Show resolved Hide resolved

Geodesics are the generalizations of a straight line to manifolds, i.e. their intrinsic acceleration is zero.
Together with geodesics one also obtains the exponential map and its inverse, the logarithmic map.
Informally speaking, the exponential map takes a vector (think of a direction and a length) at one point and returns another point,
which lies towards this direction at distance of the specified length. The logarithmic map does the inverse, i..e. given two points, what is the vector that “points towards” the other point.
kellertuer marked this conversation as resolved.
Show resolved Hide resolved

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["exp_log_geo.jl"]
Order = [:function]
```

### Retractions and inverse Retractions

The exponential and logarithmic map might be too expensive to evaluate or not be available in a very stable numerical way. Retractions provide a possibly cheap, fast and stable alternative.

The following figure compares the exponential map [`exp`](@ref)`(M, p, X)` on the [`Circle`](@ref)`(ℂ)` (or [`Sphere`](@ref)`(1)` embedded in $ℝ^2$ with one possible retraction, the one based on projections. Note especially that ``\mathrm{dist}(p,q)=\lVert X\rVert_p`` while this is not the case for ``q'``.

![A comparson of the exponential map and a retraction on the Circle.](assets/images/retraction_illustration_600.png)

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["retractions.jl"]
Order = [:function]
```

To distinguish different types of retractions, the last argument of the (inverse) retraction
specifies a type. The following ones are available.

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["retractions.jl"]
Order = [:type]
```

### Projections
mateuszbaran marked this conversation as resolved.
Show resolved Hide resolved

A manifold might be embedded in some space.
Often this is implicitly assumed, for example the complex [`Circle`](@ref) is embedded in the complex plane.
Let‘s keep the circle in mind in the following as a simple example.
For the general case see of explicitly stating an embedding and/or distinguising several, different embeddings, see [Embedded Manifolds](@ref EmbeddedmanifoldSec) below.

To make this a little more concrete, let‘s assume we have a manifold ``\mathcal M`` which is embedded in some manifold ``\mathcal N`` and the image ``i(\mathcal M)`` of the embedding function ``i`` is a closed set (with respect to the topology on ``\mathcal N``). Then we can do two kinds of projections.

To make this concrete in an example for the Circle ``\mathcal M=\mathcal C := \{ p ∈ ℂ | |p| = 1\}``
the embedding can be chosen to be the manifold ``N = ℂ`` and due to our representation of ``\mathcal C`` as complex numbers already, we have ``i(p) = p`` the identity as the embedding function.

1. Given a point ``p∈\mathcal N`` we can look for the closest point on the manifold ``\mathcal M`` formally as

```math
\operatorname*{arg\,min}_{q\in \mathcal M} d_{\mathcal N}(i(q),p)
```

And this resulting ``q`` we call the projection of ``p`` onto the manifold ``\mathcal M``.

2. Given a point ``p∈\mathcal M`` and a vector in ``X\inT_{i(p)}\mathcal N`` in the embedding we can similarly look for the closest point to ``Y∈ T_p\mathcal M`` using the push forward ``i_*`` of the embedding.
kellertuer marked this conversation as resolved.
Show resolved Hide resolved

```math
\operatorname*{arg\,min}_{Y\in T_p\mathcal M} \lVert i_*(p)[Y] - X \rVert_{i(p)}
```

And we call the resulting ``Y`` the projection of ``X`` onto the tangent space ``T_p\mathcal M`` at ``p``.

Let‘s look at the little more concrete example of the complex Circle again.
Here, the closest point of ``p ∈ ℂ`` is just the projection onto the circle, or in other words ``q = \frac{p}{\lvert p \rvert}``. A tangent space ``T_p\mathcal C`` in the embedding is the line orthogonal to a point ``p∈\mathcal C`` through the origin.
This can be better visualized by looking at ``p+T_p\mathcal C`` which is actually the line tangent to ``p``. NOte that this shift does not change the resulting projection relative to the origin of the tangent space.

Here the projection can be computed as the classical projection onto the line, i.e. ``Y = X - ⟨X,p⟩X``.

this is illustrated in the following figure

![An example illustrating the two kinds of projections on the Circle.](assets/images/retraction_illustration_600.png)

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["projections.jl"]
Order = [:function]
```

### Remaining functions

```@autodocs
Modules = [Manifolds, ManifoldsBase]
Pages = ["ManifoldsBase.jl"]
Expand Down Expand Up @@ -172,7 +270,7 @@ Pages = ["ValidationManifold.jl"]
Order = [:macro, :type, :function]
```

## EmbeddedManifold
## [EmbeddedManifold](@id EmbeddedmanifoldSec)

Some manifolds can easily be defined by using a certain embedding.
For example the [`Sphere`](@ref)`(n)` is embedded in [`Euclidean`](@ref)`(n+1)`.
Expand Down
Loading