# Power manifold

A power manifold is based on a `Manifold`

$\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.

## Example

There are two ways to store the data: in a multidimensional array or in a nested array.

Let's look at an example for both. Let $\mathcal M$ be `Sphere(2)`

the 2-sphere and we want to look at vectors of length 4.

For the default, the `ArrayPowerRepresentation`

, we store the data in a multidimensional array,

```
using Manifolds
M = PowerManifold(Sphere(2), 4)
p = cat([1.0, 0.0, 0.0],
[1/sqrt(2.0), 1/sqrt(2.0), 0.0],
[1/sqrt(2.0), 0.0, 1/sqrt(2.0)],
[0.0, 1.0, 0.0]
,dims=2)
```

3×4 Array{Float64,2}: 1.0 0.707107 0.707107 0.0 0.0 0.707107 0.0 1.0 0.0 0.0 0.707107 0.0

which is a valid point i.e.

`is_manifold_point(M, p)`

true

This can also be used in combination with HybridArrays.jl and StaticArrays.jl, by setting

```
using HybridArrays, StaticArrays
q = HybridArray{Tuple{3,StaticArrays.Dynamic()},Float64,2}(p)
```

3×4 HybridArrays.HybridArray{Tuple{3,StaticArrays.Dynamic()},Float64,2,2,Array{Float64,2}} with indices SOneTo(3)×Base.OneTo(4): 1.0 0.707107 0.707107 0.0 0.0 0.707107 0.0 1.0 0.0 0.0 0.707107 0.0

which is still a valid point on `M`

and `PowerManifold`

works with these, too.

An advantage of this representation is that it is quite efficient, especially when a `HybridArray`

(from the HybridArrays.jl package) is used to represent a point on the power manifold. A disadvantage is not being able to easily identify parts of the multidimensional array that correspond to a single point on the base manifold. Another problem is, that accessing a single point is `p[:, 1]`

which might be unintuitive.

For the `NestedPowerRepresentation`

we can now do

```
using Manifolds
M = PowerManifold(Sphere(2), NestedPowerRepresentation(), 4)
p = [ [1.0, 0.0, 0.0],
[1/sqrt(2.0), 1/sqrt(2.0), 0.0],
[1/sqrt(2.0), 0.0, 1/sqrt(2.0)],
[0.0, 1.0, 0.0],
]
```

4-element Array{Array{Float64,1},1}: [1.0, 0.0, 0.0] [0.7071067811865475, 0.7071067811865475, 0.0] [0.7071067811865475, 0.0, 0.7071067811865475] [0.0, 1.0, 0.0]

which is again a valid point so `is_manifold_point`

`(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]`

.

For accessing points on power manifolds in both representations you can use `get_component`

and `set_component!`

functions. They work work both point representations.

```
using Manifolds
M = PowerManifold(Sphere(2), NestedPowerRepresentation(), 4)
p = [ [1.0, 0.0, 0.0],
[1/sqrt(2.0), 1/sqrt(2.0), 0.0],
[1/sqrt(2.0), 0.0, 1/sqrt(2.0)],
[0.0, 1.0, 0.0],
]
set_component!(M, p, [0.0, 0.0, 1.0], 4)
get_component(M, p, 4)
```

3-element view(::Array{Float64,1}, :) with eltype Float64: 0.0 0.0 1.0

## Types and Functions

`Manifolds.AbstractPowerManifold`

— Type`AbstractPowerManifold{𝔽,M,TPR} <: Manifold{𝔽}`

An abstract `Manifold`

to represent manifolds that are build as powers of another `Manifold`

`M`

with representation type `TPR`

, a subtype of `AbstractPowerRepresentation`

.

`Manifolds.AbstractPowerRepresentation`

— Type`AbstractPowerRepresentation`

An abstract representation type of points and tangent vectors on a power manifold.

`Manifolds.ArrayPowerRepresentation`

— Type`ArrayPowerRepresentation`

Representation of points and tangent vectors on a power manifold using multidimensional arrays where first dimensions are equal to `representation_size`

of the wrapped manifold and the following ones are equal to the number of elements in each direction.

`Torus`

uses this representation.

`Manifolds.InversePowerRetraction`

— Type`InversePowerRetraction{TR<:AbstractInverseRetractionMethod} <: AbstractInverseRetractionMethod`

The `InversePowerRetraction`

avoids ambiguities between dispatching on the `AbstractPowerManifold`

and dispatching on the `AbstractInverseRetractionMethod`

and encapsulates this. This container should only be used in rare cases outside of this package. Usually a subtype of the `AbstractPowerManifold`

should define a way how to treat its `AbstractRetractionMethod`

s.

**Constructor**

`InversePowerRetraction(inverse_retractions::AbstractInverseRetractionMethod...)`

`Manifolds.NestedPowerRepresentation`

— Type`NestedPowerRepresentation`

Representation of points and tangent vectors on a power manifold using arrays of size equal to `TSize`

of a `PowerManifold`

. Each element of such array stores a single point or tangent vector.

`GraphManifold`

uses this representation.

`Manifolds.PowerBasisData`

— Type`PowerBasisData{TB<:AbstractArray}`

Data storage for an array of basis data.

`Manifolds.PowerFVectorDistribution`

— Type`PowerFVectorDistribution([type::VectorBundleFibers], [x], distr)`

Generates a random vector at a `point`

from vector space (a fiber of a tangent bundle) of type `type`

using the power distribution of `distr`

.

Vector space type and `point`

can be automatically inferred from distribution `distr`

.

`Manifolds.PowerManifold`

— Type`PowerManifold{𝔽,TM<:Manifold,TSize<:Tuple,TPR<:AbstractPowerRepresentation} <: AbstractPowerManifold{𝔽,TM}`

The power manifold $\mathcal M^{n_1× n_2 × … × n_d}$ with power geometry `TSize`

statically defines the number of elements along each axis.

For example, a manifold-valued time series would be represented by a power manifold with $d$ equal to 1 and $n_1$ equal to the number of samples. A manifold-valued image (for example in diffusion tensor imaging) would be represented by a two-axis power manifold ($d=2$) with $n_1$ and $n_2$ equal to width and height of the image.

While the size of the manifold is static, points on the power manifold would not be represented by statically-sized arrays. Operations on small power manifolds might be faster if they are represented as `ProductManifold`

.

**Constructor**

```
PowerManifold(M, N_1, N_2, ..., N_d)
PowerManifold(M, NestedPowerRepresentation(), N_1, N_2, ..., N_d)
M^(N_1, N_2, ..., N_d)
```

Generate the power manifold $M^{N_1 × N_2 × … × N_d}$. By default, the `ArrayPowerRepresentation`

of points and tangent vectors is used, although a different one, for example `NestedPowerRepresentation`

, can be given as the second argument to the constructor. When `M`

is a `PowerManifold`

(not any `AbstractPowerManifold`

) itself, given dimensions will be appended to the dimensions already present, for example `PowerManifold(PowerManifold(Sphere(2), 2), 3)`

is equivalent to `PowerManifold(Sphere(2), 2, 3)`

. This feature preserves the representation of the inner power manifold (unless it's explicitly overridden). If you specify `NestedPowerRepresentation()`

, the sizes are not concatenated but you end up with a nested power manifold within a power manifold.

`Manifolds.PowerMetric`

— Type`PowerMetric <: Metric`

Represent the `Metric`

on an `AbstractPowerManifold`

, 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.

`Manifolds.PowerPointDistribution`

— Type`PowerPointDistribution(M::AbstractPowerManifold, distribution)`

Power distribution on manifold `M`

, based on `distribution`

.

`Manifolds.PowerRetraction`

— Type`PowerRetraction{TR<:AbstractRetractionMethod} <: AbstractRetractionMethod`

The `PowerRetraction`

avoids ambiguities between dispatching on the `AbstractPowerManifold`

and dispatching on the `AbstractRetractionMethod`

and encapsulates this. This container should only be used in rare cases outside of this package. Usually a subtype of the `AbstractPowerManifold`

should define a way how to treat its `AbstractRetractionMethod`

s.

**Constructor**

`PowerRetraction(retraction::AbstractRetractionMethod)`

`Manifolds.PowerVectorTransport`

— Type```
PowerVectorTransport{TR<:AbstractVectorTransportMethod} <:
AbstractVectorTransportMethod
```

The `PowerVectorTransport`

avoids ambiguities between dispatching on the `AbstractPowerManifold`

and dispatching on the `AbstractVectorTransportMethod`

and encapsulates this. This container should only be used in rare cases outside of this package. Usually a subtype of the `AbstractPowerManifold`

should define a way how to treat its `AbstractVectorTransportMethod`

s.

**Constructor**

`PowerVectorTransport(method::AbstractVectorTransportMethod)`

`Base.exp`

— Method`exp(M::AbstractPowerManifold, p, X)`

Compute the exponential map from `p`

in direction `X`

on the `AbstractPowerManifold`

`M`

, which can be computed using the base manifolds exponential map elementwise.

`Base.getindex`

— Method```
getindex(p, M::AbstractPowerManifold, i::Union{Integer,Colon,AbstractVector}...)
p[M::AbstractPowerManifold, i...]
```

Access the element(s) at index `[i...]`

of a point `p`

on an `AbstractPowerManifold`

`M`

by linear or multidimensional indexing. See also Array Indexing in Julia.

`Base.log`

— Method`log(M::AbstractPowerManifold, p, q)`

Compute the logarithmic map from `p`

to `q`

on the `AbstractPowerManifold`

`M`

, which can be computed using the base manifolds logarithmic map elementwise.

`Base.setindex!`

— Method```
setindex!(q, p, M::AbstractPowerManifold, i::Union{Integer,Colon,AbstractVector}...)
q[M::AbstractPowerManifold, i...] = p
```

Set the element(s) at index `[i...]`

of a point `q`

on an `AbstractPowerManifold`

`M`

by linear or multidimensional indexing to `q`

. See also Array Indexing in Julia.

`Base.view`

— Method`view(p, M::AbstractPowerManifold, i::Union{Integer,Colon,AbstractVector}...)`

Get the view of the element(s) at index `[i...]`

of a point `p`

on an `AbstractPowerManifold`

`M`

by linear or multidimensional indexing.

`LinearAlgebra.norm`

— Method`norm(M::AbstractPowerManifold, p, X)`

Compute the norm of `X`

from the tangent space of `p`

on an `AbstractPowerManifold`

`M`

, i.e. from the element wise norms the Frobenius norm is computed.

`Manifolds.flat`

— Method`flat(M::AbstractPowerManifold, p, X::FVector{TangentSpaceType})`

use the musical isomorphism to transform the tangent vector `X`

from the tangent space at `p`

on an `AbstractPowerManifold`

`M`

to a cotangent vector. This can be done elementwise for each entry of `X`

(and `p`

).

`Manifolds.get_component`

— Method`get_component(M::AbstractPowerManifold, p, idx...)`

Get the component of a point `p`

on an `AbstractPowerManifold`

`M`

at index `idx`

.

`Manifolds.power_dimensions`

— Method`power_dimensions(M::PowerManifold)`

return the power of `M`

,

`Manifolds.set_component!`

— Method`set_component!(M::AbstractPowerManifold, q, p, idx...)`

Set the component of a point `q`

on an `AbstractPowerManifold`

`M`

at index `idx`

to `p`

, which itself is a point on the `Manifold`

the power manifold is build on.

`Manifolds.sharp`

— Method`sharp(M::AbstractPowerManifold, p, ξ::FVector{CotangentSpaceType})`

Use the musical isomorphism to transform the cotangent vector `ξ`

from the tangent space at `p`

on an `AbstractPowerManifold`

`M`

to a tangent vector. This can be done elementwise for every entry of `ξ`

(and `p`

).

`ManifoldsBase.check_manifold_point`

— Method`check_manifold_point(M::AbstractProductManifold, p; kwargs...)`

Check whether `p`

is a valid point on an `AbstractPowerManifold`

`M`

, i.e. each element of `p`

has to be a valid point on the base manifold. If `p`

is not a point on `M`

a `CompositeManifoldError`

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...`

.

`ManifoldsBase.check_tangent_vector`

— Method`check_tangent_vector(M::AbstractPowerManifold, p, X; check_base_point = true, kwargs... )`

Check whether `X`

is a tangent vector to `p`

an the `AbstractPowerManifold`

`M`

, i.e. atfer `check_manifold_point`

`(M, p)`

, and all projections to base manifolds must be respective tangent vectors. The optional parameter `check_base_point`

indicates, whether to call `check_manifold_point`

for `p`

. If `X`

is not a tangent vector to `p`

on `M`

a `CompositeManifoldError`

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...`

.

`ManifoldsBase.distance`

— Method`distance(M::AbstractPowerManifold, p, q)`

Compute the distance between `q`

and `p`

on an `AbstractPowerManifold`

, i.e. from the element wise distances the Forbenius norm is computed.

`ManifoldsBase.injectivity_radius`

— Method`injectivity_radius(M::AbstractPowerManifold[, p])`

the injectivity radius on an `AbstractPowerManifold`

is for the global case equal to the one of its base manifold. For a given point `p`

it's equal to the minimum of all radii in the array entries.

`ManifoldsBase.inner`

— Method`inner(M::AbstractPowerManifold, p, X, Y)`

Compute the inner product of `X`

and `Y`

from the tangent space at `p`

on an `AbstractPowerManifold`

`M`

, i.e. for each arrays entry the tangent vector entries from `X`

and `Y`

are in the tangent space of the corresponding element from `p`

. The inner product is then the sum of the elementwise inner products.

`ManifoldsBase.inverse_retract`

— Method`inverse_retract(M::AbstractPowerManifold, p, q, m::InversePowerRetraction)`

Compute the inverse retraction from `p`

with respect to `q`

on an `AbstractPowerManifold`

`M`

using an `InversePowerRetraction`

, which by default encapsulates a inverse retraction of the base manifold. Then this method is performed elementwise, so the encapsulated inverse retraction method has to be one that is available on the base `Manifold`

.

`ManifoldsBase.manifold_dimension`

— Method`manifold_dimension(M::PowerManifold)`

Returns the manifold-dimension of an `PowerManifold`

`M`

$=\mathcal N = (\mathcal M)^{n_1,…,n_d}$, i.e. with $n=(n_1,…,n_d)$ the array size of the power manifold and $d_{\mathcal M}$ the dimension of the base manifold $\mathcal M$, the manifold is of dimension

\[\dim(\mathcal N) = \dim(\mathcal M)\prod_{i=1}^d n_i = n_1n_2\cdot…\cdot n_d \dim(\mathcal M).\]

`ManifoldsBase.project`

— Method`project(M::AbstractPowerManifold, p, X)`

Project the point `X`

onto the tangent space at `p`

on the `AbstractPowerManifold`

`M`

by projecting all components.

`ManifoldsBase.project`

— Method`project(M::AbstractPowerManifold, p)`

Project the point `p`

from the embedding onto the `AbstractPowerManifold`

`M`

by projecting all components.

`ManifoldsBase.retract`

— Method`retract(M::AbstractPowerManifold, p, X, method::PowerRetraction)`

Compute the retraction from `p`

with tangent vector `X`

on an `AbstractPowerManifold`

`M`

using a `PowerRetraction`

, which by default encapsulates a retraction of the base manifold. Then this method is performed elementwise, so the encapsulated retraction method has to be one that is available on the base `Manifold`

.

`ManifoldsBase.vector_transport_to`

— Method`vector_transport_to(M::AbstractPowerManifold, p, X, q, method::PowerVectorTransport)`

Compute the vector transport the tangent vector `X`

at `p`

to `q`

on the `PowerManifold`

`M`

using an `PowerVectorTransport`

`m`

. This method is performed elementwise, i.e. the method `m`

has to be implemented on the base manifold.