# Power manifold

A power manifold is based on a `AbstractManifold`

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

There are three available representations for points and vectors on a power manifold:

`ArrayPowerRepresentation`

(the default one), very efficient but only applicable when points on the underlying manifold are represented using plain`AbstractArray`

s.`NestedPowerRepresentation`

, applicable to any manifold. It assumes that points on the underlying manifold are represented using mutable data types.`NestedReplacingPowerRepresentation`

, applicable to any manifold. It does not mutate points on the underlying manifold, replacing them instead when appropriate.

Below are some examples of usage of these representations.

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

`ArrayPowerRepresentation`

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 Matrix{Float64}:
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_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.HybridMatrix{3, StaticArraysCore.Dynamic(), Float64, 2, Matrix{Float64}} 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.

`NestedPowerRepresentation`

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 Vector{Vector{Float64}}:
[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_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 Vector{Float64}:
0.0
0.0
1.0
```

`NestedReplacingPowerRepresentation`

The final representation is the `NestedReplacingPowerRepresentation`

. It is similar to the `NestedPowerRepresentation`

but it does not perform in-place operations on the points on the underlying manifold. The example below uses this representation to store points on a power manifold of the `SpecialEuclidean`

group in-line in an `Vector`

for improved efficiency. When having a mixture of both, i.e. an array structure that is nested (like ´NestedPowerRepresentation) in the sense that the elements of the main vector are immutable, then changing the elements can not be done in an in-place way and hence `NestedReplacingPowerRepresentation`

has to be used.

```
using Manifolds, StaticArrays
R2 = Rotations(2)
G = SpecialEuclidean(2)
N = 5
GN = PowerManifold(G, NestedReplacingPowerRepresentation(), N)
q = [1.0 0.0; 0.0 1.0]
p1 = [ProductRepr(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, i)))) for i in 1:N]
p2 = [ProductRepr(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, -i)))) for i in 1:N]
X = similar(p1);
log!(GN, X, p1, p2)
```

```
5-element Vector{ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}}:
ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}(([0.0, 0.0], [0.0 2.0; -2.0 0.0]))
ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}(([0.0, 0.0], [0.0 -2.2831853071795862; 2.2831853071795862 0.0]))
ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}(([0.0, 0.0], [0.0 -0.28318530717958645; 0.28318530717958645 0.0]))
ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}(([0.0, 0.0], [0.0 1.7168146928204135; -1.7168146928204135 0.0]))
ProductRepr{Tuple{StaticArraysCore.SVector{2, Float64}, StaticArraysCore.SMatrix{2, 2, Float64, 4}}}(([0.0, 0.0], [0.0 -2.566370614359173; 2.566370614359173 0.0]))
```

## Types and Functions

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

— Type`PowerMetric <: AbstractMetric`

Represent the `AbstractMetric`

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

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

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

— Method`sharp(M::AbstractPowerManifold, p, ξ::RieszRepresenterCotangentVector)`

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

— Method`change_metric(M::AbstractPowerManifold, ::AbstractMetric, p, X)`

Since the metric on a power manifold decouples, the change of metric can be done elementwise.

`ManifoldsBase.change_representer`

— Method`change_representer(M::AbstractPowerManifold, ::AbstractMetric, p, X)`

Since the metric on a power manifold decouples, the change of a representer can be done elementwise