JuliaManifolds / Manifolds.jl

Manifolds.jl provides a library of manifolds aiming for an easy-to-use and fast implementation.
https://juliamanifolds.github.io/Manifolds.jl
MIT License
368 stars 53 forks source link

Transformation of a manifold #674

Closed Nimrais closed 10 months ago

Nimrais commented 10 months ago

Hello! First, I'd like to express my gratitude for the fantastic package you've provided.

I might have overlooked this in the documentation, but is there a more streamlined method to implement manifolds than the one described here How to implement your own manifold?

Specifically, I'm interested in the linear transformation of the manifold with a non-zero determinant. For instance, if I'm aiming to implement the Negative Numbers manifold, is there a way to leverage or repurpose the Positive Numbers manifold for this?

kellertuer commented 10 months ago

I might have overlooked this in the documentation, but is there a more streamlined method to implement manifolds than the one described here How to implement your own manifold?

I am not 100% sure what you mean by “more streamlined” here? I was hoping that defining a struct (for the manifold) and implementing the functions you need is already a bit streamlined? Maybe we can improve that tutorial. But sure it is not necessarily meant to explain how o build a manifold based on another.

For the NegativeNumbers you could check the file https://github.com/JuliaManifolds/Manifolds.jl/blob/master/src/manifolds/PositiveNumbers.jl adapt the struct in the beginning (line 15) and copy (and adapt) all functions you need from that file?

I do – sadly – not understand what you mean by “the linear transformation of the manifold with a nonzero-determinant” – so you mean GL(n) the manifold of n-by.n manifolds with nonzero determinant? That one we indeed to not have – but then still a bit more information of a linear transform of a manifold would be interesting.

Nimrais commented 10 months ago

Yes, I mean GL(n). It seems, maybe I am wrong, that if I have an implemented manifold X and I want to obtain a manifold $A\text{X}$, where $A \in \text{GL}(n)$, there could be a cheaper way (in terms of the code lines :) ), then to implement manifold $A\text{X}$ from the scratch.

kellertuer commented 10 months ago

Hm, $A\mathcal M$ would not be defined – and if you say you want to do $Ap$ for any $p\in\mathcal M$, what would mean that $p$ is in “vector shape” ? That is conceptually not the case and in code only sometimes. For example the fixed rank manifold is represented by 3 matrices currently within a struct – there $Ap$ does not make sense.

Even if it does make sense, the resulting set $\mathcal N = \{Ap \mid p \in \mathcal M\}$ (in some embedding of the manifold) might often not be a manifold (highly depending on specific $A$ and $\mathcal M$. But if these questions are solved (or you are lazy and ignore them and hope for the best) – sure one could implement what we usually call a “MetaManifold” (a manifold based on one or more manifolds like the product manifold) that defines a few (the ones you need) like this. But for example the first question would already be: which $\mathcal M$ do you have in mind? how does you inner product be defined? What would tangent vectors even look like? But then you could follow the “How to implement a manifold” tutorial where the struct contains a manifold inside and falls back / uses its definitions by itself – and you have your generic manifold.

Do these ideas help? Which manifold do you have in mind to “linearly transform”?

mateuszbaran commented 10 months ago

Hello! There is currently no such functionality. Do you mean to use the pushforward metric on the transformed manifold or the one derived from the embedding after transformation? The first option is relatively simple and I thought about doing it even for nonlinear transformations. The second one is very hard because only a few operations can be generically implemented.

Even if it does make sense, the resulting set (in some embedding of the manifold) might often not be a manifold (highly depending on specific and

I thought being a manifold is invariant to diffeomorphic transformations of the embedding, like the action of GL(n)? Do you have a counterexample?

EDIT: By "generically" I mean without using generic optimization algorithms or differential equation solvers.

kellertuer commented 10 months ago

Well, the first thing is, you need an embedding, then I am not 100% sure, since I do see that one could push forward the metric for sure, so yeah you might be right that the proper embedding might be enough, since GL(n) is a “nice enough transformation”.

Nimrais commented 10 months ago

Hello! There is currently no such functionality. Do you mean to use the pushforward metric on the transformed manifold or the one derived from the embedding after transformation?

Yes, I am interested in the manifold with the pushforward metric.

kellertuer commented 10 months ago

So just to maybe summarise the idea, one could do (just typed not ran or checked)

struct TransformedEmbeddedManifold{F,TA,TM} <: AbstractManifold{F}
    A::TA
    manifold::TM
end

and then defined something like inner(M:: TransformedEmbeddedManifold, p, X, Y) = inner(M.manifold, A\embed(M,p), A\embed(M,p,X), A\embed(M,p,X)) which would “pull back” the points from M to M.manifold (after embedding) – and of course it might be clever to store the matrix not in its array form but maybe its LU decomposition already.

This would still mean you have to follow the roadmap from above, but at least only once for all your transformations ;)

kellertuer commented 10 months ago

I mean if the embedding does not work as you expect and the matrix A maybe does not fit, then this all breaks, but “if all goes well” this would be what you want, right?

Nimrais commented 10 months ago

Yes, indeed.

kellertuer commented 10 months ago

Then, if that is ok with you, maybe try to follow the start I wrote and try to work that out yourself? We are surely here to help if you get stuck :)

Maybe the main thing to remember it to do the embed in between, since usually we assume that it is embedded in some Euclidean space (in the end).

kellertuer commented 10 months ago

By the way, the tutorial on how to implement a manifold moved to ManifoldsBase.jl quite a while back

https://juliamanifolds.github.io/ManifoldsBase.jl/stable/tutorials/implement-a-manifold/

(just because I was confused you linked to a very old version of the docs in the opening post)

mateuszbaran commented 10 months ago

For reference, you can also take a look at implementation of ProbabilitySimplex. Some methods there are implemented using very similar approach to what is needed for pushforward metrics (see e.g. get_coordinates_orthonormal!), though it's more of a pullback from a sphere.

kellertuer commented 10 months ago

On my way to work today I thought about this a bit. I think a disadvantage here might be both numerical stability and computational effort.

Before each and every operation you would either multiply with A or its inverse. Sure we could store that in the struct as well to just have a matrix multiplication. Then, still, if your computational effort is, say, $k$ “manifold operations” you would increase that to $2n^2k$, where n is the dimension of the embedding. You would also do quite a few embed/project operations. These would all introduce rounding errors – especially if $A$ is ill-conditioned.

Would it not be easier to take your data in the beginning – let's call that $p \in \mathcal N$, transform it in the very beginning $q = A^{-1}p \in \mathcal M$ (± projection onto the manifold) and then work on $\mathcal M$ all the time and only transform back (±embed) in the end once?