Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tutorial on how to SE2 #366

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

dehann
Copy link
Contributor

@dehann dehann commented May 22, 2021

Good day,

Please see a new tutorial for SpecialEuclidean(2). There are things I still want to add, but this should be a good baseline point to clean up as needed and merge. I will add more PRs at a later date.

I'd suggest reading the entire tutorial before staring to write up comments (although I would happily take any feedback!). I suspect this tutorial is coming in from a very different angle than much of the existing documentation. So please look at it on the whole first, and feel free to tear it apart thereafter.

In it's current form, this text and structure is meant for someone who picked up Lie group operations as a mechanism for rigid transforms.

Second last note is that PR #355 should probably be merged first, since this one builds upon that. I'm likely to change some of the variable names in this tutorial to be more in line with those in #355, but also to be as closely recognizable to the existing Manifolds.jl code base.

Lastly, I might have found a bug during this and will open an issue on exp for SE(2) separately to clarify. If so, then I have another draft paragraph to add to the end of this tutorial.

cc @Affie who is likely working with this type of code right now.

Thanks for all the help!

Best,
Dehann

@codecov
Copy link

codecov bot commented May 22, 2021

Codecov Report

Merging #366 (fa3eed3) into master (99849e7) will increase coverage by 7.20%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #366      +/-   ##
==========================================
+ Coverage   90.71%   97.92%   +7.20%     
==========================================
  Files          17       76      +59     
  Lines        1012     5915    +4903     
==========================================
+ Hits          918     5792    +4874     
- Misses         94      123      +29     
Impacted Files Coverage Δ
src/ode.jl 100.00% <0.00%> (ø)
src/Manifolds.jl 100.00% <0.00%> (ø)
src/forward_diff.jl 100.00% <0.00%> (ø)
src/SizedAbstractArray.jl
src/Sphere.jl
src/Euclidean.jl
src/PowerManifold.jl
src/Metric.jl
src/Rotations.jl
src/ProjectedDistribution.jl
... and 79 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 99849e7...fa3eed3. Read the comment docs.

@kellertuer kellertuer added the preview docs Add this label if you want to see a PR-preview of the documentation label May 23, 2021

### ProductArray for SpecialEuclidean(2)

As mentioned earlier, there are two data representation formats available for product manifolds. This paragraph will repeat the same steps as the `ProductRepr` paragraph above, but using the `ProductArray` representation instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done some work on a new power manifold representation which should make ProductArray essentially obsolete: #367 . What do you think? That new representation should offer comparable performance and be easier to use, at the price of having to implement non-mutating variants of some operations on manifolds separately from the mutating ones.

Copy link
Contributor Author

@dehann dehann May 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can definitely change the text on this page to whatever is best for Manifolds.jl and to keep the math rigor / utility. I'm 100% good with that :-) Thanks for taking the time to help! I did find ProductRepr a little easier at first, since the shape definitions added another layer to learn on day one.

Perhaps it is worth saying what I struggled with in the beginning, which is why I drafted these pages as they are/were. I was initially very confused trying to pick apart the differences between:

  • Group vs. base_manifold( of the Group ), which do I use because sometimes I get an error, other times not...
  • ProductRepr vs ProductArray, are these the same, is one a coordinate, do they work the same on all Manifolds...
  • What is a ShapeSpecification, and should I worry about Static...
  • What's up with hat vs get_vector...
  • is vee (where textbooks say 'vectorize') the same as get_vector...
  • does get_coordinates take either group or vector (i.e. algebra) elements, and should I always write that myself...
  • are 'coordinates' the same thing as a 'vector' and what does that have to do with shape, and how much does Julia's own ::Array get used for dispatch...
  • if you call exp, does the code know whether you are giving it coordinates or a algebra element...
  • What does Manifolds.jl even define as 'coordinates', I guess it has something to do with the DefaultBasis, maybe...

So hopefully these two new tutorials will help newcomers breeze through these (perhaps silly first order) questions, and quickly get to the harder stuff like "how do we implement and PR a new dispatch for this or that enhancement".

The reason I listed both representations is simply to help others get through the learning curve faster, and help contribute the commits needed towards a consolidated solution. Until a clear design template and "Julian" philosophy about which representation is "better" emerges, I think it will be difficult to say option 'A' vs. option '1' is better. The wide open aperture about data types in the representations (coordinates, vectors, groups) makes this a little harder to pin down in the early days.

I don't yet know the implementation details well enough to see how / where the differences between ProductRepr and ProductArray manifest. I might be a bit more pragmatic in this sense and happy to work with what's available without obstructing progress. In utopia, yes only a single one-size-fits-all representation would be nice. I hope to help contribute some code as I learn more, but that is a longer term thing.

So my suggestion then is perhaps we should move the ProductArray paragraph of this tutorial right to the very end of the page, and simply point out "please see the end of this page for an earlier alternative ProductArray data representation". The ProductArray representation is likely already in use. My feeling is the community will ultimately adopt one approach over the other, and most importantly the rigor/utility should not be lost. Good design and clean-up will result in fast code, but it has to be human readable so that a sizable audience can share the burdens. I think the risk now is the community adopts the "easier" vs the "better" approach. It's probably too soon to try force a deprecation any which way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to help!

You're welcome 🙂 . I'm happy that you find Manifolds.jl useful.

I did find ProductRepr a little easier at first, since the shape definitions added another layer to learn on day one.

I'm really not satisfied with the design ProductArray, and I'm almost sure that we can make ProductRepr cover all possible use cases for product manifolds, so I'd suggest just reporting problems with that approach and not using ProductArray at all.

  • Group vs. base_manifold( of the Group ), which do I use because sometimes I get an error, other times not...

Calling base_manifold should rarely be necessary. Feel free to report any cases where calling an operation on base_manifold works but directly on a group it does not.

  • ProductRepr vs ProductArray, are these the same, is one a coordinate, do they work the same on all Manifolds...

I think you should just stop using ProductArray. It was an idea I had when I doubted ProductRepr could work with broadcasting and power manifolds, but now ProductRepr does work there.

  • What is a ShapeSpecification, and should I worry about Static...

Same as above, you shouldn't use ShapeSpecification.

  • What's up with hat vs get_vector...
  • is vee (where textbooks say 'vectorize') the same as get_vector...

vee and hat were supposed to be aliases to certain variants of get_coordinates and get_vector to make people familiar with them more comfortable but it doesn't seem to work this way... Personally, I never use hat and vee.

  • does get_coordinates take either group or vector (i.e. algebra) elements, and should I always write that myself...

get_coordinates actually takes both a group element and an algebra element. For groups where tangent vectors are represented as tangents at the identity element of the group, it does not matter what point you give there. But note that get_coordinates originates in differential geometry, not Lie theory, and as such it needs to know which point the vector is tangent at.

  • are 'coordinates' the same thing as a 'vector' and what does that have to do with shape, and how much does Julia's own ::Array get used for dispatch...

I don't quite understand the first part of the question, could you rephrase it? I guess it might be confusing that get_coordinates returns a ::Vector (or, sometimes SVector), but the thing you get there is the vector of coordinates, while get_vector returns a tangent vector (or a vector from a fibre of some other vector bundle but maybe let's leave that for another time -- I only mention it to explain why it isn't called get_tangent_vector).

I think we almost never dispatch on Julia's Array, though in many cases we dispatch on AbstractArray.

  • if you call exp, does the code know whether you are giving it coordinates or a algebra element...

exp always expects an element of the tangent space (so an algebra element), it may even silently give a wrong result if given coordinates. We have, though, ValidationManifold for verifying that you give correct arguments to functions in your algorithms.

  • What does Manifolds.jl even define as 'coordinates', I guess it has something to do with the DefaultBasis, maybe...

This might be a decent explanation (by @kellertuer 🙂 ): https://www.youtube.com/watch?v=md-FnDGCh9M&t=1060s . 'Coordinates' in Manifolds.jl generally refer to coordinates of a tangent vector (or f.e. cotangent vector also) in a basis, like in basic linear algebra: https://www.math.tamu.edu/~yvorobet/MATH304-2011A/Lect2-07web.pdf . The catch here is, while you can do lots of linear algebra ignoring the (often stated without appropriate explanation) distinction between elements of vector spaces and their coordinates with respect to a basis, the distinction is absolutely necessary in differential geometry. In Manifolds.jl we usually store tangent vectors as numbers that do not correspond to coefficient in any basis. This probably still needs some better explanation in docs.

So hopefully these two new tutorials will help newcomers breeze through these (perhaps silly first order) questions,

I think these questions are quite good actually, and show the deficiencies in our documentation. I'm not sure, though, where to put some of these answers, for example these regarding coordinates.

The reason I listed both representations is simply to help others get through the learning curve faster, and help contribute the commits needed towards a consolidated solution. Until a clear design template and "Julian" philosophy about which representation is "better" emerges, I think it will be difficult to say option 'A' vs. option '1' is better. The wide open aperture about data types in the representations (coordinates, vectors, groups) makes this a little harder to pin down in the early days.

I don't yet know the implementation details well enough to see how / where the differences between ProductRepr and ProductArray manifest. I might be a bit more pragmatic in this sense and happy to work with what's available without obstructing progress. In utopia, yes only a single one-size-fits-all representation would be nice. I hope to help contribute some code as I learn more, but that is a longer term thing.

So my suggestion then is perhaps we should move the ProductArray paragraph of this tutorial right to the very end of the page, and simply point out "please see the end of this page for an earlier alternative ProductArray data representation". The ProductArray representation is likely already in use. My feeling is the community will ultimately adopt one approach over the other, and most importantly the rigor/utility should not be lost. Good design and clean-up will result in fast code, but it has to be human readable so that a sizable audience can share the burdens. I think the risk now is the community adopts the "easier" vs the "better" approach. It's probably too soon to try force a deprecation any which way.

I'm currently 99% sure ProductArray should be just removed, and maybe I should've pulled the plug earlier. One particular breakthrough in this area is quite recent but I still feel a little embarrassed that you had to go through ProductArray. ProductRepr is definitely easier, and with the recent changes I'm certain it can also always be as fast, if not faster, than ProductArray.

The one thing that remains to be done is changing "can be" to "is" in the last sentence, which requires subverting a little our approach to non-mutating functions -- specifically, we will need to explicitly write some non-mutating methods instead of relying on allocation and mutating variants. I'll write more about it separately, the key thing here is that you should report any performance issues that may be caused by Manifolds.jl -- I'm aware of a lot of places where performance could be improved but I have limited time so I can't fix every potential issue 🙂 .

Finally, thank you for your comments! Also, feel to ask them more often, after reading them it looks like you haven't been asking enough questions when working through Manifolds.jl 🙂 . In any case I and Ronny are available at Julia's slack and zulip if you want some real-time conversation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh! Neat, that even my talk from last year was linked; now you now how I sound and how much German accent my English has ;)

I looked a bit at vee and hat (cf. https://en.wikipedia.org/wiki/3D_rotation_group#Lie_algebra) and it seems that there is one confusion with your question

  • is vee (where textbooks say 'vectorize') the same as get_vector...

First of all – as far as I found hat/vee are used only with rotations and not even with Lie groups in general.

If I understand the wikipedia article corretcly; the hat function takes a set of coordinates $\omega$ and returns an alement from the Lie algebra. So with the identification of every tangent space hat reconstructs a vector from given coefficients in a basis, so hat=get_vector (to be precise https://github.com/JuliaManifolds/ManifoldsBase.jl/blob/194e27e985292e5e1685cf10e7bf27d4af40b9f7/src/bases.jl#L788).
On the other hand, vee(cf also https://juliamanifolds.github.io/Manifolds.jl/stable/interface.html#ManifoldsBase.vee-Tuple{AbstractManifold,Any,Any}) maps a vector that is given in a certain basis (note the Einstein summation on the left) to the coefficients, so it is vee=get_coordinates for the special case of using the VeeOrthogonalBasis (this is also the basis for hats get_vector by the way) … see the one code line https://github.com/JuliaManifolds/ManifoldsBase.jl/blob/194e27e985292e5e1685cf10e7bf27d4af40b9f7/src/bases.jl#L910).
We should maybe be more precise in this description in vee/hat, sure. My main reason for not having done that yet is, that I never used them until now.

Similarly to

  • does get_coordinates take either group or vector (i.e. algebra) elements, and should I always write that myself...

it takes a tangent vector (Lie algebra element) and returns coefficients (coordinates wrt that basis).

Copy link
Contributor Author

@dehann dehann May 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling base_manifold should rarely be necessary. Feel free to report any cases where calling an operation on base_manifold works but directly on a group it does not.

Ah, that helps thanks!

vee and hat were supposed to be aliases to certain variants of get_coordinates and get_vector to make people familiar with them more comfortable but it doesn't seem to work this way... Personally, I never use hat and vee.

Thanks, kellertuer also mentioned that. Hopefully 355 and 366 help newcomers through that faster than I was able to.

get_coordinates actually takes both a group element and an algebra element. For groups where tangent vectors are represented as tangents at the identity element of the group, it does not matter what point you give there. But note that get_coordinates originates in differential geometry, not Lie theory, and as such it needs to know which point the vector is tangent at

Is this written down anywhere in the current docs, I think that should be linked from these tutorial pages -- this is a good piece of info, thanks!

I don't quite understand the first part of the question, could you rephrase it? I guess it might be confusing that get_coordinates returns a ::Vector (or, sometimes SVector), but the thing you get there is the vector of coordinates, while get_vector returns a tangent vector (or a vector from a fibre of some other vector bundle but maybe let's leave that for another time -- I only mention it to explain why it isn't called get_tangent_vector).

Oh all good, that bullet point list above is just a quick list of problems I was not able to easiliy resolve from the documentation when I started. Hence those bullet points are specific things I'm trying to answer with these two new tutorials 355 and 366.

We have, though, ValidationManifold for verifying that you give correct arguments to functions in your algorithms.

  • Ah, should add that in these two new pages somewhere too.

This might be a decent explanation (by kellertuer slightly_smiling_face ):

  • ha, should definitely link that in these docs too. I'm taking this as an opportunity to boil down and capture all the conversations we've had in the past months into the two new shorter tutorials.

I think these questions are quite good actually, and show the deficiencies in our documentation. I'm not sure, though, where to put some of these answers, for example these regarding coordinates.

Great, great, let me chew on where to fit more of these details into the tutorials. I think most of this latest volley of notes should either be linked out to elsewhere at the right place. It should also be okay to add a paragraph or two for additional details.

I think these two new "tutorials" (355 and 366) are all thats needed at this stage. They are specifically about the end-to-end usage of two well known manifolds for newcomers at varying levels of prior knowledge. So the documentation criteria for me on these two pages is to rapidly learn the conventions, with basic do's and don'ts. The additional step then is a reader will either want verification of details if they are an expert (i.e. links to particular functions) and which headings are actually listed here. Another reader might first want to figure out the relation between for e.g. exp and retraction (as we spoke through last week).

I'm currently 99% sure ProductArray should be just removed, and maybe I should've pulled the plug earlier. One particular breakthrough in this area is quite recent but I still feel a little embarrassed that you had to go through ProductArray. ProductRepr is definitely easier, and with the recent changes I'm certain it can also always be as fast, if not faster, than ProductArray.

oh all good, Manifolds.jl is awesome! You guys have been a great help! Just moving to ProductRepr sounds good to me, and then focus effort on building out the required dispatches. I don't know enough of the internals just yet, but I'm slowly getting there.

you should report any performance issues that may be caused by Manifolds.jl -- I'm aware of a lot of places where performance could be improved but I have limited time so I can't fix every potential issue slightly_smiling_face .

All good thanks, we are likely to stumble into a few and have many performance related things to resolve too. Will help however best I can as things come up, at least just document.

If I understand the wikipedia article corretcly; the hat function takes a set of coordinates $\omega$ and returns an alement from the Lie algebra. So with the identification of every tangent space hat reconstructs a vector from given coefficients in a basis, so hat=get_vector

That's how I understand it now too. But not at all how I understood things at first -- I was trudging through runtests.jl to try understand how to do the end-to-end usage. It's much clearer now :-) And the problem I think is really just that first usage example, rather than a listing of function features. It's fine if you know which functions you want, but the problem is which one of the 100's is it... etc. So hopefully 355 and 366 helps.

@dehann
Copy link
Contributor Author

dehann commented May 27, 2021

EDIT: should just mention, I'm documenting converstations related to this tutorial page and thereby understand what are the most important things to add on this tutorial page...

A conversation with @david-m-rosen , @Affie , @pvazteixeira showed we need to be accurate in this documentation on:

  • what is the data representation being used (realization of SE(d) to represent points),
  • what specific metric is being used,
  • what is the convention for the exponential map being used (particular be careful about Lie group exp vs Riemannian exp that corresponds to the metric listed above),

ref


Keeping as breadcrum. I also want to explore / document (pseudo code):

# is p2 is right? ie do i understand exp correct?

p0 = SE2(  [0, 0, 0]  )
p1 = SE2(  [1, 0, pi/2]  )
exp_SE2(p, X) = ....
p2 = exp( p1, [1, 0, pi/2] )

log_SE2(p0, p2) == [1, 1, pi]

exp vs exp_SE2

(This code and test still needs to be refined)

@kellertuer
Copy link
Member

  • what is the data representation being used (realization of SE(d) to represent points),

The usual way we have here is: SE(d) (the manifold type SpecialEuclidean(d) or manifold types in general) are the abstract idea of the manifold, such that manifold_dimension can be implemented. They come with a default idea of points p,q (usually untyped in functions, having AbstractArrays in mind) and a default idea to represent tangent vectors X,Y (on group usually by default represented by elements from the Lie algebra, represented fitting to p,q, also untyped).

For different representations, use subtypes of AbstractManifoldPoint and TVector , respectively. See https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/hyperbolic.html for an example. And sure, if the default is not well documented, that should be improved.

  • what specific metric is being used,

Similar as for points/vectors the default has a metric in mind, otherwise use new AbstractMetric subtypes and the Metricmanifold{M,G} to distinguish, see https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/symmetricpositivedefinite.html#Default-metric:-the-linear-affine-metric fo an example.

  • what is the convention for the exponential map being used (particular be careful about Lie group exp vs Riemannian exp that corresponds to the metric listed above),

exp/log mean the Riemannian https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#The-exponential-and-the-logarithmic-map,-and-geodesics
for the Lie group exponential see group_exp https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/group.html#Manifolds.group_exp-Tuple{AbstractGroupManifold,Vararg{Any,N}%20where%20N}

All these functions (exp/log/group_exp/group_log/retract/...) have as their first argument always the manifold, hence the MetricManifold can also here be used for dispatching on different metrics. Having this in ming, I do not understand your exp_S2? Also p0,p1 would (for the default representation) just be arrays.

For both – different representations and different metric, we would be very happy to extend our code base to more of these; if you are missing a representation or a metric, I‘m happy to help with the structure and even documentation, if you provide the code (that is content within the functions then). :)

@dehann
Copy link
Contributor Author

dehann commented May 27, 2021

Thanks!

I do not understand your exp_S2? Also p0,p1 would (for the default representation) just be arrays.

I just added a piece of pseudo code to remember and illustrate, one mistake I made was the difference between exp and exp_SE2 -- I was trying this in code and were not quite getting the results I expect it yet. Punchline for me is that a bit more documentation is needed on this rigid transform tutorial to show the difference between which metric and which exponential map is used. Even though I was aware, I still stepped in the trap :-P

After the discussion we had today, the interesting thing about SpecialEuclidean(2) is the subtle difference between:

  • ProductManifold T(n) x SO(n) where exp and log are closer to Riemannian metrics, along with what does group_exp mean in this compared; VERSUS
  • The SemidirectProductManifold i.e. SpecialEuclidean(n) = T(n) x| SO(n) and how to use exp and group_exp.
    I know these functions are documented individually, and I'd ilke to showcase some easy examples using them.

@Affie
Copy link
Contributor

Affie commented May 28, 2021

I'm still not following the discussion from @dehann's example and semi-direct product manifolds.

I wrote the pseudo code in julia as I understand it:

julia> using Manifolds

julia> using StaticArrays

julia> M = SpecialEuclidean(2)
SpecialEuclidean(2)

julia> p0 = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0])
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element SVector{2, Float64} with indices SOneTo(2):
   0.0
   0.0
 Component 2 =
  2×2 SMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   1.0  0.0
   0.0  1.0

julia> X1 = hat(M, p0, SA[1., 0, pi/2])
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   0.0     -1.5708
   1.5708   0.0

julia> p1 = exp(M, p0, X1)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   6.12323e-17  -1.0
   1.0           6.12323e-17

julia> X2 = hat(M, p1, SA[1., 0, pi/2])
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   0.0     -1.5708
   1.5708   0.0

julia> p2 = exp(M, p1, X2)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   2.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   -1.0          -1.22465e-16
    1.22465e-16  -1.0

What I understand from the above code is it is just calling exp on all the parts/components.


What will the manifolds.jl equivalent method be for the following composision using the tangent space?

julia> p0 = SA[1.0 0.0 0.0;
               0.0 1.0 0.0;
               0.0 0.0 1.0];

julia>  RT = SA[0.0  -1.0  1.0;
                1.0   0.0  0.0;
                0.0   0.0  1.0];

julia> p1 = RT * p0
3×3 SMatrix{3, 3, Float64, 9} with indices SOneTo(3)×SOneTo(3):
 0.0  -1.0  1.0
 1.0   0.0  0.0
 0.0   0.0  1.0

julia> p2 = RT * p1
3×3 SMatrix{3, 3, Float64, 9} with indices SOneTo(3)×SOneTo(3):
 -1.0   0.0  1.0
  0.0  -1.0  1.0
  0.0   0.0  1.0

@kellertuer
Copy link
Member

What I understand from the above code is it is just calling exp on all the parts/components.
Exactly, since the group manifold is just a decorator: exp/log are not reimplemented for this decorator, so they “pass through” to the underlying product manifold where the (implicit default) metric is the product metric, which decouples.

What will the manifolds.jl equivalent method be for the following composision using the tangent space?

is p0 the same p0 but as one array and then in homogeneous coordinates?
What's RT? a tangent vector at p0? An element from the Lie algebra? Another point on the manifold? Maybe you want to apply the group action (matrix multiplication here)?

For the last question with yes, it's

M = SpecialEuclidean(2)
p = ProductRepr([0.0,0.0], [1.0 0.0;0.0 1.0])
q = ProductRepr([1.0,0.0], [0.0 -1.0;1.0 0.0])

then

julia> r = compose(M,p,X)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element Vector{Float64}:
   1.0
   0.0
 Component 2 =
  2×2 Matrix{Float64}:
   0.0  -1.0
   1.0   0.0

which is what you have, too, see

julia> affine_matrix(M,r)
3×3 Matrix{Float64}:
 0.0  -1.0  1.0
 1.0   0.0  0.0
 0.0   0.0  1.0

(for sure works the same with static arrays, I was just lazy).

@Affie
Copy link
Contributor

Affie commented May 28, 2021

Thanks for all your patience and help while I'm learning more about manifolds and the Manifolds.jl implimentation.

@kellertuer
Copy link
Member

Let me assure you that it is surely not so easy with all these different representations to properly distinguish them (hence all my questions), one has to be very precise there. But great that my explanation helped :)
Especially on Lie groups there is quite a set of operations to get right (group operation, exp/log, group_exp/group_log, ...).


In this section all the details from above are brought together via the action `compose`. Recall the objective for this tutorial is to walk around a rectangle using the `Manifolds.SpecialEuclidean(2)` mechanizations and abstractions. Note that either the `ProductRepr` or `ProductArray` representations will dispatch the same for the code below, but the internal data representation formats will differ.

The fine grain details in this section are perhaps not as straight forward as they first appear, specifically how relative transforms (which are points on the manifold `SpecialEuclidean(2)`) relate to the consecutive positions of the walked rectangle (which are also points on the manifold `SpecialEuclidean(2)`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can achieve a similar result using translate or apply for GroupOperationAction. Especially the last one may be helpful in expressing qualitative difference between the elements of SE(2) corresponding to positions, and elements corresponding to transforms.

Co-authored-by: Mateusz Baran <mateuszbaran89@gmail.com>
@Affie
Copy link
Contributor

Affie commented Jun 4, 2021

I've seen the plus operator (⊕) used in robotics. This example is how I understand it can be used with Manifolds.jl (we should maybe add something like this in this tutorial?).

julia> using Manifolds

julia> using StaticArrays

julia> M = SpecialEuclidean(2);

# Identity element of SE2
julia> I_SE2 = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0]);

julia> p0 = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0]);

julia> X1 = hat(M, p0, SA[1., 0, pi/2]);

julia> p1 = exp(M, p0, X1);

julia> X2 = hat(M, p1, SA[1., 0, -pi/2]);

# right ⊕ : p2 = p1 ⊕ X2
julia> p2 = compose(M, p1, exp(M, I_SE2, X2))
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   1.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   1.0  0.0
   0.0  1.0

# left ⊕  :  p2 = X2 ⊕ p1
julia> p2 = compose(M, exp(M, I_SE2, X2), p1)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
    1.0
   -1.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   1.0  0.0
   0.0  1.0

# exp map on product manifold components
julia> p2 = exp(M, p1, X2)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   2.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   1.0  0.0
   0.0  1.0

What got me: I was expecting p2 = exp(M, p1, X2) to look like p2 = p1 ⊕ X2

I also still need to figure out how group_exp works and fits into this (think I'm missing some fundamentals).

Please let me know what you think.

@kellertuer
Copy link
Member

I've seen the plus operator (⊕) used in robotics.

The question is: How is it defined? Having seen it – does not give (me) the context.
So for me it is unclear “what got you”, since I am missing the context

For exp keep in mind it is the component wise exponential map, so for the matrix component its the exponential map on the rotation matrices, for the R^2 component (of the translation Group thereon) it is just +. This is both exactly, what the exp does there.

@Affie
Copy link
Contributor

Affie commented Jun 4, 2021

Sorry I could have been clearer, it is defined in the code comment:

p2 = X2 ⊕ p1 as
p2 = compose(M, exp(M, I_SE2, X2), p1)

I'm just making a statement of my pitfalls coming from engineering.

For exp keep in mind it is the component wise exponential map, so for the matrix component its the exponential map on the rotation matrices, for the R^2 component (of the translation Group thereon) it is just +. This is both exactly, what the exp does there.

Yes, this is how I now understand it.

@dehann
Copy link
Contributor Author

dehann commented Jun 4, 2021

HI, perhaps I can ask a related question -- I'm trying to understand how to use exp/retract to "walk" through a series of points on a product manifold (in the general sense). I have vector Xa which is defined in the tangent space around p1, and Xb which is defined in the tangent space of p2:

manifoldsWormWalk

neither group_exp or exp for SpecialEuclidean(2) seem to do the operation shown in the figure above...

  • I think the confusion comes from which exp is being used when (and if there might be an implementation gap).
  • Can I use exp/retract to do these local operations, or must I first use exp defined against group identity elements and use then a compose operation?

i.e. retract with vectors local to each point VS compose using group elements is the only answer?

@kellertuer
Copy link
Member

kellertuer commented Jun 4, 2021

You are again a little imprecise here. So if you have p1 as a point on the manifold and Xa is a tangent vector at p1 then sure exp)M,p1,Xa) is a point on the manifold, as is retract(M,p1,Xa,r) if you have the retraction r defined (note that exp is a special case of a retraction). For the definition of a retraction take a look at http://sma.epfl.ch/~nboumal/book/index.html (p. 46, Sec 3.6 or precisely Def 3.41). And exp fulfils some ode additionally to that (Sec 10.2, p. 242, precisely Def 10.13).

Can you explain why you think exp is not doing such an operation? It really does!

What you have to be careful about is whether (for group manifolds/Lie groups) your Xa is a tangent vector or a Lie group element. My text above only holds if it is a tangent vector as you stated! If you have an element from the Lie group you first have to use the differential of the group operation, see for example https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/group.html#Manifolds.translate_diff-Tuple{AbstractGroupManifold,Vararg{Any,N}%20where%20N}

For the group exponential, that is a little different because it has as input an element from the Lie algebra (and not from the tangent space) see https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/group.html#Manifolds.group_exp-Tuple{AbstractGroupManifold,Vararg{Any,N}%20where%20N}

@dehann
Copy link
Contributor Author

dehann commented Jun 6, 2021

Can you explain why you think exp is not doing such an operation? It really does!

@kellertuer , I think we are close to breaking through on this. Here is a minimum example showing my confusion on exp about some random user point p.

M = SpecialEuclidean(2);
p1 = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0]);
Xa = hat(M, p1, SA[1., 0, pi/2]);
p2 = exp(M, p1, Xa);
Xb = hat(M, p2, SA[1., 0, -pi/2]);
p3_test = exp(M, p2, Xb)
# \theta=0.0 (works okay in this example)
@assert isapprox( [1 0; 0 1], p3_test.parts[2])

# but why t=[2,0]?
p3_test.parts[1]' |> println
# should be [1,1]?
@assert isapprox( [1.0,1.0], p3_test.parts[1])

(simplified code and similar to the picture just above)

So if you have p1 as a point on the manifold and Xa is a tangent vector at p1 then sure exp)M,p1,Xa) is a point on the manifold

I would expect this to be point p2, which we can then use with a new vector Xb to "walk" to p3, no? That's what this code snippet is trying to do.

All I can think of is we are using the wrong exp for SpecialEuclidean(2) somehow, maybe but I'm not sure... I checked group_exp(M, X) but that is not defined about a user point (I think it is assuming the group identity element for it's coordinate frame). Perhaps there is still an implementation gap around group_exp, or I'm just missing a major piece of fundamental info. Either way, I think something about this should be added to this tutorial page.

@kellertuer
Copy link
Member

So if you have p1 as a point on the manifold and Xa is a tangent vector at p1 then sure exp)M,p1,Xa) is a point on the manifold

I would expect this to be point p2, which we can then use with a new vector Xb to "walk" to p3, no? That's what this code snippet is trying to do.

Let me phrase that carefully: Sure exp(M,p1,Xa) is some point on the manifold, and sure if at this some point you move somewhere further, then you end up at some third point.

Keep in mind that in the first (R2 component) exp is really just addition! So with that, p1 has [0,0], adding Xa ([1,0]). and Xb(again [1,0]) of course yields [2,0], because that space is - for exp – really just the Euclidean R2.

Why I was asking about more details is exactly: What do you expect, where does this expectation come from? All these details I am missing, so I can just guess and not really help. So with just code without any explanation, I don‘t know why you think it should be [1,1]because all I can see in the Code – it is perfectly fine and computes the correct things for the input you provide and the functions you use.

So again: Can you explain why you think exp is not doing such an operation? It really does!

References, you own computations by hand, something like that is what I would call more detail.

All I can think of is we are using the wrong exp for SpecialEuclidean(2) somehow, maybe but I'm not sure... I checked group_exp(M, X) but that is not defined about a user point (I think it is assuming the group identity element for it's coordinate frame). Perhaps there is still an implementation gap around group_exp, or I'm just missing a major piece of fundamental info. Either way, I think something about this should be added to this tutorial page.

Watt is a “user point” here? Which coordinate frame do you have in mind?

@mateuszbaran
Copy link
Member

I think I know what's going on here. I guess what you want to do is to have an object on a plane with an orientation. Let is start at your p1:

p1 = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0]);

Let us also make the identity element of SE(2) for convenience:

id = ProductRepr(SA[0.0, 0.0], SA[1.0 0.0; 0 1.0])

Now, we want to apply two actions to this object: translation by 1 in the direction it's facing and rotation by pi/2:

a1 = exp(M, id, hat(M, id, SA[1.0, 0, pi/2]))

The other operation is similar but performs rotation by pi/2 in the other direction:

a2 = exp(M, id, hat(M, id, SA[1.0, 0, -pi/2]))

Now, to do the walking you seem to expect (I think?) we need right action of the group operation:

GOA = GroupOperationAction(M, RightAction())

and we can start walking by applying our operations:

julia> u = apply(GOA, a1, p1)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   0.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   6.12323e-17  -1.0
   1.0           6.12323e-17

julia> u2 = apply(GOA, a2, u)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element MVector{2, Float64} with indices SOneTo(2):
   1.0
   1.0
 Component 2 =
  2×2 MMatrix{2, 2, Float64, 4} with indices SOneTo(2)×SOneTo(2):
   1.0  0.0
   0.0  1.0

Does that seem right?

@mateuszbaran
Copy link
Member

You probably need to be a bit more careful in distinguishing elements of SE(2) corresponding to location/orientation of an object and elements of SE(2) that correspond to operations (translations, rotations) of said object.

@dehann
Copy link
Contributor Author

dehann commented Jun 6, 2021

All these details I am missing, so I can just guess

Understood. I think there are two sets of conventions here which makes it tricky to connect. What I'd like to do is use the Manifolds.jl way (which I'm taking as the right way) two write down this "walking from point to point on a general manifold". Think I'm using Manifolds.jl incorrectly.

I don‘t know why you think it should be [1,1] because all I can see in the Code – it is perfectly fine and computes the correct things for the input you provide and the functions you use.

Yes, I'm expecting translation from [1,1] from the code snippet ... This helps me pin down that exp is not doing what I expect, thanks.

want to do is to have an object on a plane with an orientation.

The reason I expect [1,1] is easiest to explain via cartoon avatar on a XY plane, and tracking a yaw angle for the avatar, hence SE(2). Starting from the coordinates for p1 at [x,y,yaw] = [0,0,0], walk to p2 using vector coordinates [1,0,pi/2] (i.e. walk forward and turn 90deg left, assuming z-axis points up). What I called Xa above. Now walk from p2 to p3 with new vector coordinates [1,0,-pi/2], what I called Xb above. Note, "forward" from p2 is actually along the y-axis in the "global coordinates". I'm taking "global coordinates" here as the origin of the XY plane where we started. In "global reference" I would then expect the coordinates of p3 to be [1,1,0].

366_walk

The question though is whether this can be done with exponential maps directly from vectors (attempted here), or whether it can only be done with actions on group elements?

The latest example (#366 (comment)) is still actions using group elements. So, it looks unlikely to me that the idea of "walking" on a plane with orientation (which we represent with elements from SE(2)) can be done using vectors and the exponential map directly (as i tried to show here). It is looking more likely that we have to use compose or actions where we first use exp to get a group element around the identity and then as a second stage use compose / actions to effect the rigid-transform.

Note, the group action / compose version is already in the PR. Thanks, I know this has taken a lot of time. I will try write all this succinctly at the end of this How to SE(2) documentation page.

@kellertuer
Copy link
Member

The answer is already directly above (1) turn your Lie algebra elements (tangent vectors at the identity) into points on the manifold and apply the group action then. That was what Was asking and Mateusz guesses correctly. Your Xa, Xb are tangent vectors at the identity not at p1,p2.

@mateuszbaran
Copy link
Member

The question though is whether this can be done with exponential maps directly from vectors (attempted here), or whether it can only be done with actions on group elements?

The latest example (#366 (comment)) is still actions using group elements. So, it looks unlikely to me that the idea of "walking" on a plane with orientation (which we represent with elements from SE(2)) can be done using vectors and the exponential map directly. It is looking more likely that we have to use compose or actions where we first use exp to get a group element around the identity and then as a second stage use compose / actions to effect the rigid-transform.

After looking at your example, I think group operation actions is the right abstraction here. There are, although, other ways around it. I think I could come up with something relying on differentials of compose/apply, and then using FVector we could add overloads that would let you work on SE(2) with the compact translation-angle representation if you need that but what I wrote above is a much simpler solution. So you don't really have to use exp but this way is easier.

@dehann
Copy link
Contributor Author

dehann commented Jun 6, 2021

Alright, great thanks. Let's then just use groups. I'll add to the end of this new documentation page a note to show that

My next steps are to make sure all the updates are in 355 and 366 so you can merge when happy. Thanks again for all the feedback!

@kellertuer
Copy link
Member

Great, looking forward to the update. I would prefer though, if it was self-contained and that it does not need links to comments/issues.

@kellertuer
Copy link
Member

...you could implement the third way, i.e. on matrix representations (with homogeneous coordinates, i.e. the second one) by just implementing the apply for matrices instead of productrepr and we would then cover all three?

@mateuszbaran
Copy link
Member

...you could implement the third way, i.e. on matrix representations (with homogeneous coordinates, i.e. the second one) by just implementing the apply for matrices instead of productrepr and we would then cover all three?

Yes, that's also an option.

@mateuszbaran
Copy link
Member

I've tried translating their plus and minus to Manifolds.jl and, following Eqs. (25)-(26) it would be roughly:

function right_plus(G::AbstractGroupManifold, p, v)
    X = get_vector(G, identity(G), v, VeeOrthonormalBasis()) # except for SE where they use a different parametrization?
    return compose(G, p, exp(G, identity(G), X) # not yet sure if exp or group_exp
end
function right_minus(G::AbstractGroupManifold, p, q)
    X = log(G, identity(G), compose(G, inv(G, q), p)) # not yet sure if log or group_log
    return get_coordinates(G, identity(G), X, VeeOrthonormalBasis()) # except for SE where they use a different parametrization?
end

I don't understand their parametrization of SE(n) yet though, so there would likely be some additional fun involved but it seems possible to work out.

Anyway, are these plus and minus operations actually convenient for your computation? It very well might be that our abstractions in Manifolds.jl are too closely bound to basic theory and some additional higher-level layer may be helpful, I just don't know because I haven't worked with Lie groups that much.

@Affie
Copy link
Contributor

Affie commented Jun 8, 2021

Anyway, are these plus and minus operations actually convenient for your computation? It very well might be that our abstractions in Manifolds.jl are too closely bound to basic theory and some additional higher-level layer may be helpful, I just don't know because I haven't worked with Lie groups that much.

I think these kinds of operations will fit in the TransformUtils.jl replacement package.

Manifolds.jl's exponential map is this oplus opperator in most cases, for example rotations https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/rotations.html#Base.exp-Tuple{Rotations,Vararg{Any,N}%20where%20N}:
image

@kellertuer
Copy link
Member

Manifolds.jl's exponential map is this oplus opperator in most cases, for example rotations https://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/rotations.html#Base.exp-Tuple{Rotations,Vararg{Any,N}%20where%20N}:
image

On SO(n) you are right, but here on SE(n) note that this is just one component and the other enjoys just a + on R^n.

@mateuszbaran
Copy link
Member

Oplus is the exponential map only when you ignore its parametrization part and work on simple Lie groups or their direct products. On semidirect products is doesn't work like that, and SE(n) is an example.

@Affie
Copy link
Contributor

Affie commented Jun 8, 2021

I mean the idea of the exponential map at any point on the manifold and not only at the identity element (like the group_exp) as in the article.
The small p in
image

I was just trying to say that specialist definitions like the circle plus and diamond plus are not needed in Manifolds.jl in my opinion.

@dehann
Copy link
Contributor Author

dehann commented Jun 8, 2021

are these plus and minus operations actually convenient for your computation? It very well might be that our abstractions in Manifolds.jl are too closely bound to basic theory and some additional higher-level layer may be helpful, I just don't know because I haven't worked with Lie groups that much.

I'm personally wary how ominus is used in robotics literature because the commutativity of operations (or lack thereof) quickly becomes very confusing (left action vs right action). I avoid using ominus entirely, and actually prefer to use log or inversion-style \ with a definition (following the lead from matrix operations, x = A\b). oplus is easier, and following the long discussion here I'm now going adopt the habit of always including a more expansive definition for the specific exp map implied by said oplus.

I was just trying to say that specialist definitions like the circle plus and diamond plus are not needed in Manifolds.jl in my opinion.

I agree that specialist oplus or ominus are not needed here, we can wrap that elsewhere in the TransformUtils.jl replacement. I want to make sure I'm using the right calls to Manifolds.jl.

The briefest of cultural background, the guidance navigation and control community are very familiar with time evolution of rotations (i.e. SO(3)) being written as
\dot{R} = R [\Omega \cross],
and the robotics community quickly adopted
\int_{\delta t} R(t) = R exp(\int_{\delta t} [\Omega(t) \cross])
where cross implies skew-symmetric (i.e. Lie algebra so(3)). So the progression now is to use the same "procedure" beyond rotations, first for SE(2), and then for more generalized manifolds which are rife in robotics. Clearly which exp to use is a big stumbling block, and getting the definitions right will hopefully go a long way to help.

@mateuszbaran
Copy link
Member

A small update: I've opened a PR (#381 ) with Lie bracket and adjoint action. Especially the latter seems to be missing to properly work with rotations and SE(n). I haven't implemented these operations for SE(n) yet (I'm not sure how to do it right) but for rotations it should already work fine and I think it may be a nice addition to #355, since some operations will be doable on Lie algebra representations, and (with some relatively minor work) even their vectorized forms, all in generic Manifolds.jl style interface.

@Affie
Copy link
Contributor

Affie commented Jun 11, 2021

Thanks again for all the help, I'm slowly getting there with the basics of Manifolds.jl.

  1. I have a more general question on the exponential map,
    From a mathematical perspective, is the exponential map on a semi-direct product manifold and a product manifold the same, ie component-wise?

  2. And on retract(G::AbstractGroupManifold, p, X, method::GroupExponentialRetraction)

I'm not completely following the definition in:

````math
\operatorname{retr}_p = τ_p \circ \exp \circ (\mathrm{d}τ_p^{-1})_p,
````
where $\exp$ is the group exponential ([`group_exp`](@ref)), and $(\mathrm{d}τ_p^{-1})_p$ is
the action of the differential of inverse translation $τ_p^{-1}$ evaluated at $p$ (see

I tried to get it to work on SE(n) to help figure it out, but hit errors. Is there maybe an unimplemented function?

julia> p= ProductRepr([0.0, 0.0], [1.0 0.0; 0 1.0]);

julia> X = hat(M, p, [1., 0, pi/2]);

julia> method = Manifolds.GroupExponentialRetraction(RightAction())
Manifolds.GroupExponentialRetraction{RightAction}()

julia> retract(M, p, X, method)
ERROR: translate_diff! not implemented on SpecialEuclidean(2) for arguments ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}}, ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}}, ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}}, ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}} and RightAction.
...

EDIT:

Ok, I just saw LeftAction works:

julia> p= ProductRepr([0.0, 0.0], [1.0 0.0; 0 1.0]);

julia> X = hat(M, p, [1., 0, pi/2]);

julia> method = Manifolds.GroupExponentialRetraction(LeftAction());

julia> retract(M, p, X, method)
ProductRepr with 2 submanifold components:
 Component 1 =
  2-element Vector{Float64}:
   0.6366197723675814
   0.6366197723675813
 Component 2 =
  2×2 Matrix{Float64}:
   6.12323e-17  -1.0
   1.0           6.12323e-17

@sethaxen
Copy link
Member

@sethaxen Did you think before about Lie brackets for SpecialEuclidean?

No, I haven't really thought about Lie brackets for any of the groups, since I haven't had a use for them.

@mateuszbaran
Copy link
Member

  1. I have a more general question on the exponential map,
    From a mathematical perspective, is the exponential map on a semi-direct product manifold and a product manifold the same, ie component-wise?

The Riemannian exponential is the same. Semi-direct product construction only affects group operations. Lie group exponential is a different story, and I still have to learn more about it. I've found this though: https://www.sciencedirect.com/science/article/pii/S0377042709008449 .

I tried to get it to work on SE(n) to help figure it out, but hit errors. Is there maybe an unimplemented function?

Yes, that looks like an unimplemented functions. Sometimes it's hard to find formulas for more complicated cases so we don't always have them, or sometimes things aren't hard but we decide to leave them for later.

@sethaxen Did you think before about Lie brackets for SpecialEuclidean?

No, I haven't really thought about Lie brackets for any of the groups, since I haven't had a use for them.

No problem, they look easy enough to implement 🙂 . Except for special euclidean which I haven't figured out yet.

@mateuszbaran
Copy link
Member

No problem, they look easy enough to implement . Except for special euclidean which I haven't figured out yet.

Hm, it looks like affine_matrix can be thought of as an embedding of SE(n) in GL(n+1). That observation makes things easier.

@sethaxen
Copy link
Member

sethaxen commented Jun 11, 2021

  1. I have a more general question on the exponential map,
    From a mathematical perspective, is the exponential map on a semi-direct product manifold and a product manifold the same, ie component-wise?

The Riemannian exponential is the same. Semi-direct product construction only affects group operations. Lie group exponential is a different story, and I still have to learn more about it. I've found this though: https://www.sciencedirect.com/science/article/pii/S0377042709008449 .

I don't think this is quite true, or at least it needs some qualifications. The Riemannian exponential depends on the choice of metric, while the group exponential depends on the group structure. In general the two exponentials are not at all the same. However, if the chosen metric is bi-invariant with respect to the group operation, then the two become equivalent. So if each subgroup has a bi-invariant metric, you can construct a product metric, that will be bi-invariant wrt the operation of the direct product group but not the semidirect product group.

@sethaxen Did you think before about Lie brackets for SpecialEuclidean?

No, I haven't really thought about Lie brackets for any of the groups, since I haven't had a use for them.

No problem, they look easy enough to implement 🙂 . Except for special euclidean which I haven't figured out yet.

The trick here is that for matrix lie groups, the Lie bracket is lie_bracket(X, Y) = X*Y - Y*X, where X and Y are matrix representations of the tangent vectors. For SE(n), these are the screw matrices with structure X = [Ω w; 0*I 0]. So the Lie bracket is lie_bracket(X₁, X₂) = [lie_bracket(Ω₁, Ω₂) Ω₁w₂-Ω₂w₁; 0*I 0].

@Affie
Copy link
Contributor

Affie commented Jun 11, 2021

Thanks, I'm really learning a lot.

I don't think this is quite true, or at least it needs some qualifications. The Riemannian exponential depends on the choice of metric, while the group exponential depends on the group structure. In general the two exponentials are not at all the same. However, if the chosen metric is bi-invariant with respect to the group operation, then the two become equivalent.

I'm still wrapping my head around it, but this part I understand well enough for now and also saw it in the literature before.

So if each subgroup has a bi-invariant metric, you can construct a product metric, that will be bi-invariant wrt the operation of the direct product group but not the semidirect product group.

Can you perhaps please elaborate on this part (perhaps with SE(n) as an example): but not the semidirect product group.
I tested with SE(n) and as far as I can tell:

  • group_exp and exp are not the same which makes sense as it's not bi-invariant.
  • exp is computed per component so as a product manifold (as far as I can tell SO(n) is bi-invariant).
  • so on my original question with SE(n) in mind, is there a different semidirect product group in this case?

The reason I'm looking into this is the second case in the article I mentioned in #366 (comment):

image

Is this the semidirect product exponent or is it just a way to "move" (lie algebra's exponential map) to a point other than the identity?
I'm quite sure the first one is based on the group_exp and the last one is the product exp used in Manifolds.jl

BTW, We've started with the TransformUtils.jl replacement based on Manifolds.jl here, and Manifolds.jl is really awesome. As soon as we have the basics and integration done, we will be adding some fancy manifolds.

@mateuszbaran
Copy link
Member

I don't think this is quite true, or at least it needs some qualifications. The Riemannian exponential depends on the choice of metric, while the group exponential depends on the group structure. In general the two exponentials are not at all the same. However, if the chosen metric is bi-invariant with respect to the group operation, then the two become equivalent. So if each subgroup has a bi-invariant metric, you can construct a product metric, that will be bi-invariant wrt the operation of the direct product group but not the semidirect product group.

Yes, that's right, I over-simplified it a bit.

The reason I'm looking into this is the second case in the article I mentioned in #366 (comment):

image

Is this the semidirect product exponent or is it just a way to "move" (lie algebra's exponential map) to a point other than the identity?
I'm quite sure the first one is based on the group_exp and the last one is the product exp used in Manifolds.jl

They provide definitions for oplus and ominus (Eqs. (25)-(28)) and it's actually a combination of a few operations. IIUC moving the group exponential to a different point may not be a bad way to understand this operation, and currently I'm leaning towards assuming that they mean the group exponential there.

BTW, We've started with the TransformUtils.jl replacement based on Manifolds.jl here, and Manifolds.jl is really awesome. As soon as we have the basics and integration done, we will be adding some fancy manifolds.

Thanks, that's quite cool 👍 .

@mateuszbaran
Copy link
Member

A small update: now that #381 is done, Manifolds.jl has more abstractions to deal with SE(2) (and other groups as well), see for example adjoint_action. The new things will be even more relevant to SE(3), which turned out to be quite complex.

@dehann
Copy link
Contributor Author

dehann commented Oct 8, 2021

#355 (comment)

@dehann
Copy link
Contributor Author

dehann commented Apr 5, 2022

Hi Folks, I added this Manifolds 101 page in the Caesar.jl Docs:
https://juliarobotics.org/Caesar.jl/latest/concepts/using_manifolds/

Which points to this and sibling tutorial. Thought I'd link it here so long...

@kellertuer
Copy link
Member

Same question here to revive the tutorial – I am working towards Pluto notebook based tutorials, so maybe we could include this in #534 as well?

@kellertuer
Copy link
Member

Is has become silent a bit on these two PR – are there still plans to finish these or should we close them for now and you start anew once there is a need?

@kellertuer
Copy link
Member

This got quite silent and is a bit outdated by now, is it ok if we close this for now and restart when you find time again, @dehan? We could restart this also directly as a Quarto notebook by then.

@mateuszbaran
Copy link
Member

I've started a replacement for this tutorial but I didn't have time to finish it, so I think it doesn't make much sense to continue with this PR. Anyway I will take some inspiration regarding what and how to cover from this PR.

@kellertuer
Copy link
Member

What should we do about this PR? You said you started a replacement? We could still for example close this one?
Or we keep it open to remember to do a replacement?

However, we do have a groups tutorial now, so maybe that is also enough?

@mateuszbaran
Copy link
Member

I'd prefer to keep it open for now as a reminder to finish the replacement 😉

@kellertuer
Copy link
Member

Ok.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
preview docs Add this label if you want to see a PR-preview of the documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants