-
Notifications
You must be signed in to change notification settings - Fork 55
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
base: master
Are you sure you want to change the base?
Conversation
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
|
||
### 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
vsProductArray
, are these the same, is one a coordinate, do they work the same on all Manifolds...- What is a
ShapeSpecification
, and should I worry aboutStatic
... - What's up with
hat
vsget_vector
... - is
vee
(where textbooks say 'vectorize') the same asget_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.
There was a problem hiding this comment.
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 theshape
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
vsProductArray
, 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 aboutStatic
...
Same as above, you shouldn't use ShapeSpecification
.
- What's up with
hat
vsget_vector
...- is
vee
(where textbooks say 'vectorize') the same asget_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
andProductArray
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 alternativeProductArray
data representation". TheProductArray
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.
There was a problem hiding this comment.
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 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 hat
s 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).
There was a problem hiding this comment.
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.
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:
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) |
The usual way we have here is: SE(d) (the manifold type For different representations, use subtypes of
Similar as for points/vectors the default has a metric in mind, otherwise use new
exp/log mean the Riemannian https://juliamanifolds.github.io/Manifolds.jl/latest/interface.html#The-exponential-and-the-logarithmic-map,-and-geodesics All these functions (exp/log/group_exp/group_log/retract/...) have as their first argument always the manifold, hence the 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). :) |
Thanks!
I just added a piece of pseudo code to remember and illustrate, one mistake I made was the difference between After the discussion we had today, the interesting thing about
|
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 |
is For the last question with yes, it's
then
which is what you have, too, see
(for sure works the same with static arrays, I was just lazy). |
Thanks for all your patience and help while I'm learning more about manifolds and the Manifolds.jl implimentation. |
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 :) |
|
||
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)`). |
There was a problem hiding this comment.
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 <[email protected]>
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 I also still need to figure out how Please let me know what you think. |
The question is: How is it defined? Having seen it – does not give (me) the context. For |
Sorry I could have been clearer, it is defined in the code comment:
I'm just making a statement of my pitfalls coming from engineering.
Yes, this is how I now understand it. |
HI, perhaps I can ask a related question -- I'm trying to understand how to use neither
i.e. |
You are again a little imprecise here. So if you have 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 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} |
@kellertuer , I think we are close to breaking through on this. Here is a minimum example showing my confusion on 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)
I would expect this to be point All I can think of is we are using the wrong |
Let me phrase that carefully: Sure Keep in mind that in the first (R2 component) 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 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.
Watt is a “user point” here? Which coordinate frame do you have in mind? |
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 = 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? |
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. |
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.
Yes, I'm expecting translation from
The reason I expect 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 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 |
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. |
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 |
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! |
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. |
...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. |
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. |
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}: |
On |
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. |
I'm personally wary how
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 |
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. |
Thanks again for all the help, I'm slowly getting there with the basics of Manifolds.jl.
I'm not completely following the definition in: Manifolds.jl/src/groups/group.jl Lines 861 to 866 in c5dfdd7
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 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 |
No, I haven't really thought about Lie brackets for any of the groups, since I haven't had a use for them. |
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 .
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.
No problem, they look easy enough to implement 🙂 . Except for special euclidean which I haven't figured out yet. |
Hm, it looks like |
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.
The trick here is that for matrix lie groups, the Lie bracket is |
Thanks, I'm really learning a lot.
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.
Can you perhaps please elaborate on this part (perhaps with SE(n) as an example):
The reason I'm looking into this is the second case in the article I mentioned in #366 (comment): 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? 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. |
Yes, that's right, I over-simplified it a bit.
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.
Thanks, that's quite cool 👍 . |
A small update: now that #381 is done, |
Hi Folks, I added this Manifolds 101 page in the Caesar.jl Docs: Which points to this and sibling tutorial. Thought I'd link it here so long... |
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? |
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? |
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. |
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. |
What should we do about this PR? You said you started a replacement? We could still for example close this one? However, we do have a groups tutorial now, so maybe that is also enough? |
I'd prefer to keep it open for now as a reminder to finish the replacement 😉 |
Ok. |
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