JuliaManifolds / LieGroups.jl

LieGroups.jl – WIP
Other
3 stars 0 forks source link

Implement exponentials #7

Open kellertuer opened 5 days ago

kellertuer commented 5 days ago

Both the invariant exponential as well as the lie exponential is what I intend to document and implement next, probably/hopefully beginning of next week after reading more about it.

olivierverdier commented 5 days ago

These are quite different, I would argue.

exp_lie has to be implemented separately for every group.

The invariant exponential is then automatically defined. It is exp_inv in Manifolds.jl. It is also implemented here as exp_group.

kellertuer commented 5 days ago

Yes sure they are, my main point is, whether they should still get the same name – one has an argument less, right?

exp_lie could either be defined with a Lie group and one Lie algebra (vector) – or with a Lie algebra and a vector exp_inv with the Lie group a point and a vector as arguments.

Affie commented 4 days ago

For what groups (excluding semidirect products) will there be a difference between the group exponential exp_lie and the Riemannian exp? I know the docs says: "In general, the group exponential map is distinct from the Riemannian exponential map exp"

EDIT: no need to list it if it's a lot, just trying to understand why there has to be 2 and if the exp_lie can be the default with other metrics/connections added if you need something different.

olivierverdier commented 4 days ago

Yes sure they are, my main point is, whether they should still get the same name – one has an argument less, right?

exp_lie could either be defined with a Lie group and one Lie algebra (vector) – or with a Lie algebra and a vector exp_inv with the Lie group a point and a vector as arguments.

They have different arguments, yes. You could use the same name for both.

exp(G, X) = exp_lie(G, X)
exp(G, g, X) = exp_inv(G, g, X)

I'm not sure it's a great idea, but it's feasible.

What should be crystal clear in the documentation is that you need to implement exp_lie in your new group.

olivierverdier commented 4 days ago

For what groups (excluding semidirect products) will there be a difference between the group exponential exp_lie and the Riemannian exp? I know the docs says: "In general, the group exponential map is distinct from the Riemannian exponential map exp"

EDIT: no need to list it if it's a lot, just trying to understand why there has to be 2 and if the exp_lie can be the default with other metrics/connections added if you need something different.

exp_inv should be the default. It is the exponential of a default connection present on all Lie groups.

On some Lie groups (typically the compact one), that connection happens to be a metric connection, on other groups (typically the non-compact ones), that connection is not metric.

mateuszbaran commented 4 days ago

On some Lie groups (typically the compact one), that connection happens to be a metric connection, on other groups (typically the non-compact ones), that connection is not metric.

There is actually a precise theorem about this: a connected Lie group G admits a biinvariant metric iff (if and only if) it is isomorphic to the Cartesian product of a compact group and a vector space. Theorem 17.9 from here: https://www.cis.upenn.edu/~cis6100/cis610-15-sl17.pdf . Note that this is a very strong restriction. Apart from semidirect products, for example GL(n) doesn't have a biinvariant metric.

EDIT: we should probably add this to the docs to make them more precise.

mateuszbaran commented 4 days ago

Yes sure they are, my main point is, whether they should still get the same name – one has an argument less, right?

I'm not sure it's a great idea, but it's feasible.

IMO it's not a good idea. exp in ManifoldsBase.jl has a variant that accepts scaling factor t for the tangent vector. So something like exp(RealCircle(), 2.0, 3.0) would be ambiguous.

kellertuer commented 4 days ago

Sure exp_inv will be the default exponential map here.

I do not yet understand the ambiguity.

My idea was the following

  1. exp(G, X) (X from the Lie algebra) is the exponential of a Lie group (cf. Sec 9,2 Hilgert Neeb) – this never has a third argument (formerly exp_lie).
  2. exp(G, g, X) (g a point on G) is the exponential map (maybe with a scaling, but that would add a fourth argument (not a third), formerly exp_inv
  3. the old exp which passed down to the manifold would no longer exist in that form.

If this is ambiguous (and I missed something) I would propose to use exp(𝔤,X) for the first, where 𝔤=LieAlgebra(G). This type already does exist, though first my intention was mainly to have that for educational reasons, precision, and completeness.

kellertuer commented 4 days ago

What should be crystal clear in the documentation is that you need to implement exp_lie in your new group.

I do not agree with this too much – one could mention which functions become automatically available once this is defined. If someone goes all the way and implements each and every case of all other functions just not exp, that is also fine. Of course they should never do that. But need? I would maybe strongly recommend to, but there is no need.

mateuszbaran commented 4 days ago

Sure, we currently don't have exp(RealCircle(), 2.0, 3.0) as a scaled exp_lie but that would be a reasonable extension of the existing functionality IMO.

What should be crystal clear in the documentation is that you need to implement exp_lie in your new group.

I do not agree with this too much – one could mention which functions become automatically available once this is defined. If someone goes all the way and implements each and every case of all other functions just not exp, that is also fine. Of course they should never do that. But need? I would maybe strongly recommend to, but there is no need.

Yes, there are a few operations that can be done without exp_lie implemented. But not having it is very limiting. I think recommending having it defined should be enough.

kellertuer commented 4 days ago

Yes, if we would allw for the scaling variant then it becomes ambiguous, that is right. But the more I think about it, the more I like the idea of changing the first parameter of the exp_lie to the Lie algebra, that would remove that ambiguity and be also precise in the sense of specifying the domain of exp_lie as its first parameter.

mateuszbaran commented 4 days ago

Yes, I think changing the first parameter of exp_lie to the Lie algebra would make sense.

Affie commented 4 days ago

After looking at the all the left/right discussions on actions and operations. I was wondering if the same should not be applied to the exponential and log maps. From what I understand so far the left exp (default) would then be the left invariant (metric/connection?) That also means we can have the left and right diff/Jacobians.

kellertuer commented 4 days ago

I think I would make that dependent on the default the corresponding implementation of a Lie group has.

If the default metric/connection is left-invariant, so should the corresponding exp be; vice versa for the right invariant default. Since for now (similar to manifolds) the metric would be something documented in the doc-string of a Lie group (or maybe a trait?) that would determine the exp (exp_invthat is).

mateuszbaran commented 4 days ago

There is no left and right exp, there is one canonical exp.

mateuszbaran commented 4 days ago

Micro Lie theory has left and right oplus because it combines exp with group action which can be left or right.

Affie commented 4 days ago

Micro Lie theory has left and right oplus because it combines exp with group action which can be left or right.

I think that is part of my issue, I understand Micro Lie well enough, but struggle to make the connection to Manifolds.jl and some of the literature. If you look at the implementation of exp_inv it is the same as the oplus except oplus works in coordinates and left/right looks swopped. Thats why I thought it was related. I thought you could use either translate or translate_diff for the same effect.

kellertuer commented 4 days ago

We will discontinue (at least at the current idea of the design) translate because it is doing the same as either compose or apply (with the GroupOperationAction) – but if I understand Mateusz correctly exp_inv with compose should be what you want (where you can of course choose from which side you want to compose something to that).

When we have fixed notation and such here, we can surely also try to help people from other notations to find their way through.

olivierverdier commented 4 days ago

There is actually a precise theorem about this: a connected Lie group G admits a biinvariant metric iff (if and only if) it is isomorphic to the Cartesian product of a compact group and a vector space. Theorem 17.9 from here: https://www.cis.upenn.edu/~cis6100/cis610-15-sl17.pdf . Note that this is a very strong restriction. Apart from semidirect products, for example GL(n) doesn't have a biinvariant metric.

Excellent reference, thank you!

Semidirect products (such as $SE(n)$) are not isomorphic to a product of a compact group and a vector space, so they do not admit a biinvariant metric either, I guess.

You make a big (but probably legit) leap between:

I think it is true, but maybe would deserve some careful consideration.

Affie commented 4 days ago

Maybe I just have the wrong terminology, I just came across groups/connections.jl so maybe rather CartanSchoutenMinus (left-invariant) and CartanSchoutenPlus (right-invariant), but then I also see a LeftInvariantMetric and RightInvariantMetric in the docs.

So let me try again and please tell me where I'm going wrong this time: Following on @kellertuer suggestion of using exp(𝔤,X) We can have something like LieAlgebra(G, CartanSchoutenMinus)

kellertuer commented 4 days ago

That last proposal would be quite different from the approach in Manifolds.jl – programmatically not with respect to the default, which we will change here.

In Manifolds.jl any manifold comes with a default metric in mind and you would use a wrapper (MetricManifold) to change that. So Maybe MetricLieAlgebra(LieAlgebra(G), CartanSchoutenMinus) would fit a bit better. Besides that I agree, that we should find a way to specify a metric (different from the default the Lie group or LieAlgebra to be precise) comes equipped with.

olivierverdier commented 4 days ago

Maybe I just have the wrong terminology, I just came across groups/connections.jl so maybe rather CartanSchoutenMinus (left-invariant) and CartanSchoutenPlus (right-invariant), but then I also see a LeftInvariantMetric and RightInvariantMetric in the docs.

All the (in general infinitely many) Cartan–Schouten connections are invariant. They all give the same geodesics (same exponentials). They only differ by how they parallel transport vectors along a geodesic. Minus/Plus are special because they have zero curvature, and Zero is special because it has zero torsion.

Independently, one can always define a left or right invariant metric. In general, it will give rise to a different connection than the Cartan–Schouten one.

So let me try again and please tell me where I'm going wrong this time: Following on @kellertuer suggestion of using exp(𝔤,X) We can have something like LieAlgebra(G, CartanSchoutenMinus)

That exponential does not depend on any choice of a connection.

But I do like the idea of having Lie algebra types. Something like

exp(alg::LieAlgebra, X) = exp_lie(base_group(alg), X)

For the general exponential, it would make sense to have something like

exp(G, g, X, ConnectionChoice())

(where the default connection is maybe the CartanSchoutenZero() one.)

But that holds for Riemann exponentials in metric manifolds as well, I guess. Something like

exp(M, p, X, MetricConnection(some_metric_structure))
kellertuer commented 4 days ago

But I do like the idea of having Lie algebra types. Something like

exp(alg::LieAlgebra, X) = exp_lie(base_group(alg), X)

I would prefer getting rid of exp_lie alltogether and implement exp(::LieAlgebra{<:LieGroup}, X) instead directly. Maybe one could provide the default the other way around exp(G::LieGroup, X) = exp(LieAlgebra(G), X)) if that does not introduce ambuities, see the discussion above.

For the general exponential, it would make sense to have something like

exp(G, g, X, ConnectionChoice())

(where the default connection is maybe the CartanSchoutenZero() one.)

one disadvantage of this is, that it would be very different from the (mental) model in Manifolds.jl where one would do ConnectionManifold(M, newmetric) to specify a metric (different from the implicit default that comes with M) and use that as the first argument. If the ConnectionChoice() always defaults to something, that would always “ovrerwrite” a metric G already has? Besides that, exp per se is a bit crowded already, so we fight ambiguities already (see the 3 layer design in https://juliamanifolds.github.io/ManifoldsBase.jl/stable/design/#A-3-Layer-architecture-for-dispatch), so code-design-wise this new argument might also be difficult to get realised in an “easy-to-use” way (when implementing Lie groups).

So I personally would prefer to stick to the design in Manifolds.jl though this is a new package, for consistency and avoiding ambiguity-hell. We have been there some time before the 3-layer-design and sticking to that strictly. Ambiguity-hell is no fun.

mateuszbaran commented 4 days ago

You make a big (but probably legit) leap between:

  • the canonical invariant connection comes from a metric
  • there exists a bi-invariant metric

Sure, it's not obvious, but Proposition 17.19 point (3) fills that gap.

I would prefer getting rid of exp_lie alltogether and implement exp(::LieAlgebra{<:LieGroup}, X) instead directly. Maybe one could provide the default the other way around exp(G::LieGroup, X) = exp(LieAlgebra(G), X)) if that does not introduce ambuities, see the discussion above.

I like the idea of implementing exp(::LieAlgebra{<:LieGroup}, X) directly (up to the three-level design).

one disadvantage of this is, that it would be very different from the (mental) model in Manifolds.jl where one would do ConnectionManifold(M, newmetric) to specify a metric (different from the implicit default that comes with M) and use that as the first argument. If the ConnectionChoice() always defaults to something, that would always “ovrerwrite” a metric G already has?

I agree that wrapping a group in a ConnectionManifold or MetricManifold should be the primary way of choosing the structure. Alternatively, we already have a similar syntax with AbstractRetractionMethod. So having retract(M, p, X, MetricRetraction(some_metric_structure)) would be in line with our interface.

Affie commented 3 days ago

I agree that wrapping a group in a ConnectionManifold or MetricManifold should be the primary way of choosing the structure. Alternatively, we already have a similar syntax with AbstractRetractionMethod. So having retract(M, p, X, MetricRetraction(some_metric_structure)) would be in line with our interface.

Maybe something in line with the retract(M, p, X, MetricRetraction(some_metric_structure)) interface then? My reasoning, and correct me where I misunderstand. Is the CG = ConnectionManifold(G) the same group as G? The group has some properties such as a differential manifold, compose, and inverse. So I would say that i should be able to use CG and G together in compose for example. What properties of the underlying manifold is needed for the group? Is the metric one of them? Is the affine connection one?

To give a more concrete example of something similar, that gave me issues in the v0.9 to v0.10 upgrade. For example, we used and dispatched on SpecialEuclidean(3), now that is SpecialEuclidean(3; vectors=HybridTangentRepresentation()). I had to change it everywhere because it is a different type. However, in most of the uses it probably doesn't make a difference as it is just describing the points on the manifold or using compose and inverse or even exp at Identity. So, it would have been nice if I could just change the exp or LieAlgebra type. The same goes for left vs right invariant exp, if I understand correctly, there are cases where right invariance works better such as describing noise in a global reference frame.

mateuszbaran commented 3 days ago

Maybe something in line with the retract(M, p, X, MetricRetraction(some_metric_structure)) interface then? My reasoning, and correct me where I misunderstand. Is the CG = ConnectionManifold(G) the same group as G? The group has some properties such as a differential manifold, compose, and inverse. So I would say that i should be able to use CG and G together in compose for example. What properties of the underlying manifold is needed for the group? Is the metric one of them? Is the affine connection one?

G and CG would have the same group structure, that is the same compose. The difference would be in parallel transport, and potentially exp and log. Each group has a unique exponential map determined by composition and the topological/differential structure (basically charts). Metric is not needed here but is a nice addition, and an affine connection is generally defined based on the group structure, not the other way around.

To give a more concrete example of something similar, that gave me issues in the v0.9 to v0.10 upgrade. For example, we used and dispatched on SpecialEuclidean(3), now that is SpecialEuclidean(3; vectors=HybridTangentRepresentation()). I had to change it everywhere because it is a different type. However, in most of the uses it probably doesn't make a difference as it is just describing the points on the manifold or using compose and inverse or even exp at Identity. So, it would have been nice if I could just change the exp or LieAlgebra type. The same goes for left vs right invariant exp, if I understand correctly, there are cases where right invariance works better such as describing noise in a global reference frame.

Vector representation is only relevant away from Identity. We will think how to move forward here. By the way "left vs right invariant exp" is a bit imprecise. I guess you mean exp derived from the left or right invariant metric?

Affie commented 3 days ago

I guess you mean exp derived from the left or right invariant metric?

Yes I think so, in other words from: “define a metric on the tangent space at identity and extend it to the whole group by left translation”.

kellertuer commented 3 days ago

Another argument would be, that a retraction is not really based on a metric, so that might be misleading.

kellertuer commented 3 days ago

What about the following before we overcomplicate things

The second gets a default implementation using compose and the first function – and this would also be documented like that.. If the same function name (but different first argument) is misleading, we could also use Exp for the first.

“Riemannian” variants can always be accessed using base_manifold(G) as first parameter; same for retractions (from Manifold.jl).

If we notice we need these more general, we can still think about an optional argument after the t or different ways to modify the behaviour by wrappers around G(probably not necessary for 𝔤?). An idea here is, that besides the theoretical discussions for now it might be better to extend the two functions above as soon as we discuss a concrete example, where it is easier to decide how the extension looks like.

PS: I was not that mathematical active today here, but we do have a Logo, for example on the start page.

olivierverdier commented 2 days ago

I agree with the last comment.

The only one thing I tend to dislike is the optional scaling argument: what is its point? Doesn't it add ambiguity? And: is there any reason to ever use exp(G, p, X, t) instead of just exp(G, p, t*X)?

olivierverdier commented 2 days ago

Or then I would just define

exp_scaled(M, p, X, t) = exp(M, p, X, t*X)

to avoid any ambiguity.

mateuszbaran commented 2 days ago

And: is there any reason to ever use exp(G, p, X, t) instead of just exp(G, p, t*X)?

Yes. exp(G, p, X, t) can often be significantly faster than exp(G, p, t * X).

Doesn't it add ambiguity?

There is no ambiguity in the current Manifolds.jl interface regarding exp. Having t only complicates certain extensions of the functionality of exp.

What about the following before we overcomplicate things

I just noticed the Lie algebra is also a manifold with a default metric and thus exp already has a fixed definition. Which is different. Maybe keeping exp_lie is the best solution?

kellertuer commented 2 days ago

For the scaling, that would mainly also be consistent with the current exp from Manifolds.jl.

You are correct, the Lie algebra is a manifold, it is even a vector space. I do not like wxp_lie in a package that is called LieGroups.jl, but then we could maybe do Exp(::LieAlgebra, X [, t]) for the first? Since it is called “exponential function” at least in HilgertNeeb that I am currently reading, if nexessary exp_function would be ok for me as well, but if calling both exp is not reasonable (and I can follow your arguments here), I would prefer Exp.

mateuszbaran commented 2 days ago

I keep getting confused when functions that are not type constructors start with a capital letter so I don't really like Exp. exp_function is fine. It could also be exp_group, expg or expf.

Affie commented 2 days ago

I don't like always having to create Identity(G), but maybe: exp(G, ::Identity, X) to replace exp_lie(G,X)

kellertuer commented 2 days ago

Ah, i do agree, that Expmight be misleading; I will think about the name, maybe exp_group could work.

For the default idea – internally that sounds nice, to provide that implementation as the default, sure, the case g not the identity could still fall back on this and one could document that accordingly. Very interesting idea.

Affie commented 2 days ago

Also, let me also try again with my "left/right actions and operations" idea, please try to look past my inaccuracies , but look at the design concept, but I'll do my best to be more correct. For groups you have the maps: translate, you can call apply(GroupOpperation, ...) action, you can call apply(GroupAction, ...)

We can avoid breaking the Manifolds.jl exp map by using a map in a consistent way throughout LieGroups.jl. Let me give an example, but don't focus on the names and signatures I use yet.

GroupExp = ExpMap or Exp or something representing the Map object GroupExp(G, args...) with args the (insert correct name here to get extend by left/right translation) apply_map = apply or map or eval_map or something representing the evaluation of a generic Map such asGroupExp You can then use it as follows.

apply_map(GroupExp(G), p, X)
# for differential something like
diff_map(GroupExp(G), ...)
# or Jacobians
jac_map(GroupExp(g), ...)

exp(G, p, X) will then be non-breaking and still use the Manifolds.jl way with decorators and so on (* exp(SE(n),...) will have to be fixed then).

kellertuer commented 2 days ago

Can we not mix this please? Yes translate is obsolete. But we have a separate action issue

The change of exp will be breaking since the current default of expGroupManifold,...)is Manifolds.jl passes down to the manifold. One idea of this new package is exactly to focus more on LieGroups and change exactly that default. That change was my main starting point to start this as its own package. Here the Lie groups should be the center and main object and the manifold is a bit more in the background.

also putting map everywhere we could also have stayed with this in Lie and lcalled all functions X_lie(...). I do not follow why exp(XE needs to be fixed then? So that would again be breaking?

Affie commented 2 days ago

Can we not mix this please? Yes translate is obsolete. But we have a separate action issue

Brought apply up to show consistency. And did not propose anything on translate Not X_lie but let me be extra verbose with function names:

apply_the_map_defined_in_the_first_argument(GroupExp(G), p, X)
# for differential something like
compute_the_differential_of_the_map_defined_in_the_first_argumet_wrt_p_in_direction(GroupExp(G), ...)
# or Jacobians
compute_the_jacobian_of_the_map_defined_in_the_first_argumet_wrt_p_with_basis(GroupExp(g), ...)
kellertuer commented 2 days ago

Hm, I am not yet sure apply will really need to be defined for the group exponential. I was hoping apply is restricted to group actions. Introducing that would double some parts maybe? the X_lie names were a reference to what we could do in Manifolds.jl alternatively – since we are here in LieGroups I would not do that here but put those functions into the focus as defaults (that is for example the exp.

compute_the_differential_of_the_map_defined_in_the_first_argumet_wrt_p_in_direction(GroupExp(G), ...) compute_the_jacobian_of_the_map_defined_in_the_first_argumet_wrt_p_with_basis(GroupExp(g), ...)

I do not understand what you are referring here to. The name is a bit too cryptic and map too generic. But I would expect we can have something like exp_diff (maybe even variants base point and argument).

But again. Let‘s keep this thread to both exps; discuss apply in the apply issue – and differentials maybe in a new issue. I personally will more go step by step than everything at once concerning the coding. But if you like to help, feel free to start a PR. My next PR will be the exp one.

Affie commented 2 days ago

The change of exp will be breaking since the current default of expGroupManifold,...)is Manifolds.jl passes down to the manifold. One idea of this new package is exactly to focus more on LieGroups and change exactly that default. That change was my main starting point to start this as its own package. Here the Lie groups should be the center and main object and the manifold is a bit more in the background.

Just figured making the transition easier with a deprecation cycle in LieGroups, so Manifolds to LieGrous transition for exp is non breaking with a deprecation warning. But I think a migration guide will also do and then I guess exp(G, g::Identity, X) and exp(G, g, X) will work.

kellertuer commented 2 days ago

I will try to do a deprecation thingy in Manifolds, yes we discussed that as well as a tutorial here. ut as I mentioned, I would suspect that to only happen in a few month estimating from the current speed I am able to find for coding here.

For me it does not make sense here to release a first version that says: Ah this will break in the next. Someone who then starts with LieGroups (not transitioning) will probably just be confused. It makes much more sense to add deprecations in Manifolds.jl to say “If you now use this, in LieGroups you will have to use this.” and I can do that. sure.

Affie commented 2 days ago

But again. Let‘s keep this thread to both exps; discuss apply in the apply issue – and differentials maybe in a new issue. I personally will more go step by step than everything at once concerning the coding. But if you like to help, feel free to start a PR. My next PR will be the exp one.

I'm more of a big picture person so looking at the other functions already defined as input to implement exponentials to have a consistent API. But you are right it belongs in a different issue.

For me it does not make sense here to release a first version that says: Ah this will break in the next. Someone who then starts with LieGroups (not transitioning) will probably just be confused. It makes much more sense to add deprecations in Manifolds.jl to say “If you now use this, in LieGroups you will have to use this.” and I can do that. sure.

What I mean is there is no exp function in LieGroups.jl and the deprecation warning is in Manifolds.jl and yes, LieGroups.jl might as well stay as a module in manifolds.jl then.

kellertuer commented 2 days ago

implement exponentials to have a consistent API.

Sure that is important – but I do feel there is a “lower level” (like compose and exp) and an upper one like apply (which for example for the group operation action uses compose). We can surely also have an issue about general API consistency.

What I mean is there is no exp function in LieGroups.jl and the deprecation warning is in Manifolds.jl and yes, LieGroups.jl might as well stay as a module in manifolds.jl then.

Well with the PR following this discussion there will be a LieGroup.jl exp function. And no, I do not think it should stay part of Manifolds.jl – the defaults Lie groups should get are too different – for example overwriting the default for exp – that this package in its new form would not fit the overall idea of the Manifolds.jl API.

The current part in Manifolds.jl will get deprecation warnings somehow and then will be removed. If we would keep it, we would have duplicate code. That is not nice to take care of.

Well with the PR that

Affie commented 2 days ago

Sure that is important – but I do feel there is a “lower level” (like compose and exp) and an upper one like apply (which for example for the group operation action uses compose). We can surely also have an issue about general API consistency

Ahh, ok, I thought exp was not on the "lower level". Thats why the "apply"-like option.

kellertuer commented 2 days ago

I would say this is not completely black&white, but the ones actually implemented for a group p will be exp and compose, but probably not apply. My main reason to be against the apply option here is, that in my mind at least apply is for group actions, and exp is not among those.