# Main Design Principles

The interface for a manifold is defined to be as generic as possible, such that applications can be implemented as independently as possible from an actual manifold. This way, algorithms like those from `Manopt.jl`

can be implemented on *arbitrary* manifolds.

The main design criteria for the interface are:

- Aims to also provide
*efficient**global state-free*, both*in-place*and*out-of-place*computations whenever possible. - Provide a high level interface that is easy to use.

Therefore this interface has 3 main features, that we will explain using two (related) concepts, the exponential map that maps a tangent vector $X$ at a point $p$ to a point $q$ or mathematically $\exp_p:T_p\mathcal M \to \mathcal M$ and its generalization, a `retract`

ion $\operatorname{retr}_p$ with the same domain and range.

You do not need to know their exact definition at this point, just that there is *one* exponential map on a Riemannian manifold, and several retractions, where one of them is the exponential map (called `ExponentialRetraction`

for completeness). Every retraction has its own subtype of the `AbstractRetractionMethod`

that uniquely defines it.

The following three design patterns aim to fulfil the criteria from above, while also avoiding ambiguities in multiple dispatch using the dispatch on one argument at a time approach.

## General order of parameters

Since the central element for functions on a manifold is the manifold itself, it should always be the first parameter, even for in-place functions. Then the classical parameters of a function (for example a point and a tangent vector for the retraction) follow and the final part are parameters to further dispatch on, which usually have their defaults.

Besides this order the functions follow the scheme “allocate early”, i.e. to switch to the mutating variant when reasonable, cf. Mutating and allocating functions.

## A 3-Layer architecture for dispatch

The general architecture consists of three layers

- The high level interface for ease of use – and to dispatch on other manifolds.
- The intermediate layer to dispatch on different parameters in the last section, e.g. type of retraction or vector transport.
- The lowest layer for specific manifolds to dispatch on different types of points and tangent vectors. Usually this layer with a specific manifold and no optional parameters.

These three layers are described in more detail in the following. The main motivation to introduce these layers is, that it reduces method ambiguities. It also provides a good structure where to implement extensions to this interface.

### Layer I: The high level interface and ease of use

The highest layer for convenience of decorators. A usual scheme is, that a manifold might assume several things implicitly, for example the default implementation of the sphere $\mathbb S^n$ using unit vectors in $\mathbb R^{n+1}$. The embedding can be explicitly used to avoid re-implementations – the inner product can be “passed on” to its embedding.

To do so, we “decorate” the manifold by making it an `AbstractDecoratorManifold`

and activating the right traits see the tutorial How to Implement a Manifold.

The explicit case of the `EmbeddedManifold`

can be used to distinguish different embeddings of a manifold, but also their dispatch (onto the manifold or its embedding, depending on the type of embedding) happens here.

Note that all other parameters of a function should be as least typed as possible for all parameters besides the manifold. With respect to the dispatch on one argument at a time paradigm, this layer dispatches the *manifold first*. We also stay as abstract as possible, for example on the `AbstractManifold`

level if possible.

If a function has optional positional arguments, (like `retract`

) their default values might be filled/provided on this layer. This layer ends usually in calling the same functions like `retract`

but prefixed with a `_`

to enter Layer II.

Usually only functions from this layer are exported from the interface, since these are the ones one should use for generic implementations. If you implement your own manifold, `import`

the necessary lower layer functions as needed.

### Layer II: An internal dispatch interface for parameters

This layer is an interims layer to dispatch on the (optional/default) parameters of a function. For example the last parameter of retraction: `retract`

determines the type (variant) to be used. The last function in the previous layer calls `_retract`

, which is an internal function. These parameters are usually the last parameters of a function.

On this layer, e.g. for `_retract`

only these last parameters should be typed, the manifold should stay at the `AbstractManifold`

level. The layer dispatches on different functions per existing parameter type (and might pass this one further on, if it has fields). Function definitions on this layer should only be extended when introducing new such parameter types, for example when introducing a new type of a retraction.

The functions from this layer should never be called directly, are hence also not exported and carry the `_`

prefix. They should only be called as the final step in the previous layer.

If the default parameters are not dispatched per type, using `_`

might be skipped. The same holds for functions that do not have these parameters. The following resolution might even be seen as a last step in layer I or the resolution here in layer II.

`exp(M::AbstractManifold, p, X, t::Real) = exp(M, p, t * X)`

When there is no dispatch for different types of the optional parameter (here `t`

), the `_`

might be skipped. One could hence see the last code line as a definition on Layer I that passes directly to Layer III, since there are not parameter to dispatch on.

To close this section, let‘s look at an example. The high level (or Layer I) definition of the retraction is given by

`retract!(M::AbstractManifold, q, p, X, m::AbstractRetractionMethod=default_retraction_method(M, typeof(p))) = _retract!(M, q, p, X, m)`

Note that the convenience function `retract(M, q, p, X, m)`

first allocates a `q`

before calling this function as well.

This level now dispatches on different retraction types `m`

. It usually passes to specific functions implemented in Layer III, here for example

```
_retract!(M::AbstractManifold, q, p, X, m::Exponentialretraction) = exp(M, q, p, X)
_retract!(M::AbstractManifold, q, p, X, m::PolarRetraction) = retract_polar(M, q, p, X)
```

where the `ExponentialRetraction`

is resolved by again calling a function on Layer I (to fill futher default values if these exist). The `PolarRetraction`

is dispatched to `retract_polar!`

, a function on Layer III.

For further details and dispatches, see retractions and inverse retractions for an overview.

The documentation should be attached to the high level functions, since this again fosters ease of use. If you implement a polar retraction, you should write a method of function `retract_polar!`

but the doc string should be attached to `retract(::M, ::P, ::V, ::PolarRetraction)`

for your types `::M, ::P, ::V`

of the manifold, points and vectors, respectively.

To summarize, with respect to the dispatch on one argument at a time paradigm, this layer dispatches the (optional) *parameters second*.

### Layer III: The base layer with focus on implementations

This lower level aims for the actual implementation of the function avoiding ambiguities. It should have as few as possible optional parameters and as concrete as possible types for these.

This means

- the function name should be similar to its high level parent (for example
`retract!`

and`retract_polar!`

above) - The manifold type in method signature should always be as narrow as possible.
- The points/vectors should either be untyped (for the default representation or if there is only one implementation) or provide all type bounds (for second representations or when using
`AbstractManifoldPoint`

and`TVector`

, respectively).

The first step that often happens on this level is memory allocation and calling the in-place function. If faster, it might also implement the function at hand itself.

Usually functions from this layer are not exported, when they have an analogue on the first layer. For example the function `retract_polar!`

`(M, q, p, X)`

is not exported, since when using the interface one would use the `PolarRetraction`

or to be precise call `retract!`

`(M, q, p, X, PolarRetraction())`

. When implementing your own manifold, you have to import functions like these anyway.

To summarize, with respect to the dispatch on one argument at a time paradigm, this layer dispatches the *concrete manifold and point/vector types last*.

## Mutating and allocating functions

Every function, where this is applicable, should provide an in-place and an allocating variant. For example for the exponential map `exp(M, p, X, t)`

returns a *new* point `q`

where the result is computed in. On the other hand `exp!(M, q, p, X, t)`

computes the result in place of `q`

, where the design of the implementation should keep in mind that also `exp!(M, p, p, X, t)`

should correctly overwrite `p`

.

The interface provides a way to determine the allocation type and a result to compute/allocate the resulting memory, such that the default implementation allocating functions, like `exp`

is to allocate the resulting memory and call `exp!`

.

It might be useful to provide two distinct implementations, for example when using AD schemes. The default is meant for ease of use (concerning implementation), since then one has to just implement the in-place variants.

Non-mutating functions in `ManifoldsBase.jl`

are typically implemented using in-place variants after a suitable allocation of memory.

Not that this allocation usually takes place only on Layer III when dispatching on points. Both Layer I and Layer II are usually implemented for both variants in parallel.

### Allocation of new points and vectors

The `allocate`

function behaves like `similar`

for simple representations of points and vectors (for example `Array{Float64}`

). For more complex types, such as nested representations of `PowerManifold`

(see `NestedPowerRepresentation`

), checked types like `ValidationMPoint`

and more it operates differently. While `similar`

only concerns itself with the higher level of nested structures, `allocate`

maps itself through all levels of nesting until a simple array of numbers is reached and then calls `similar`

. The difference can be most easily seen in the following example:

```
julia> x = similar([[1.0], [2.0]])
2-element Array{Array{Float64,1},1}:
#undef
#undef
julia> y = allocate([[1.0], [2.0]])
2-element Array{Array{Float64,1},1}:
[6.90031725726027e-310]
[6.9003678131654e-310]
julia> x[1]
ERROR: UndefRefError: access to undefined reference
Stacktrace:
[1] getindex(::Array{Array{Float64,1},1}, ::Int64) at ./array.jl:744
[2] top-level scope at REPL[12]:1
julia> y[1]
1-element Array{Float64,1}:
6.90031725726027e-310
```

The function `allocate_result`

allocates a correct return value. It takes into account the possibility that different arguments may have different numeric `number_eltype`

types thorough the `allocate_result_type`

function. The most prominent example of the usage of this function is the logarithmic function `log`

when used with typed points. Lets assume on a manifold `M`

the have points of type `P`

and corresponding tangent vector types `V`

. then the logarithmic map has the signature

`log(::M, ::P, ::P)`

but the return type would be $V$, whose internal sizes (fields/arrays) will depend on the concrete type of one of the points. This is accomplished by implementing a method `allocate_result(::M, ::typeof(log), ::P, ::P)`

that returns the concrete variable for the result. This way, even with specific types, one just has to implement `log!`

and the one line for the allocation.

This dispatch from the allocating to the in-place variant happens in Layer I (which changed in ManifoldsBase.jl 0.15), that is, functions like `exp`

or `retract`

allocate their result and call the in-place variant `exp!`

and `retract!`

afterwards, where the ladder passes down to layer III to reach `retract_polar!`

.