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

source
Base.expMethod
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.

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

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

source
ManifoldsBase.check_tangent_vectorMethod
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. The tolerance for the last test can be set using the kwargs....

source
ManifoldsBase.innerMethod
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.

source
ManifoldsBase.inverse_retractMethod
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.

source
ManifoldsBase.manifold_dimensionMethod
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).$
source
ManifoldsBase.retractMethod
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.

source
ManifoldsBase.vector_transport_toMethod
vector_transport_to(M::AbstractPowerManifold, p, X, q, method::PowerVectorTransport)

Compute the vector transport the tangent vector Xat 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.

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

source
Base.viewMethod
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.

source