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

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

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 mutating 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 a mutating 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.PowerFVectorDistributionType
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.

source
Manifolds.change_metricMethod
change_metric(M::AbstractPowerManifold, ::AbstractMetric, p, X)

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

source
Manifolds.change_representerMethod
change_representer(M::AbstractPowerManifold, ::AbstractMetric, p, X)

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

source
Manifolds.flatMethod
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).

source
Manifolds.sharpMethod
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).

source