-
Notifications
You must be signed in to change notification settings - Fork 56
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
dehann
wants to merge
2
commits into
JuliaManifolds:master
Choose a base branch
from
dehann:21Q2/docs/howtose2
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
# [How to do Rigid Transforms](@id how_to_do_rigid_transforms) | ||
|
||
This tutorial follows the previous tutorial [How to work with Rotations](@ref how_to_work_with_rotations). The examples aim to demonstrate how familiar rigid body transformations in 2D can be achieved with the JuliaManifolds packages. The previous tutorial showed how to use the `SpecialOrthogonal(2)` manifold by itself. This tutorial shows how the `ProductManifold` of `Translation` with `Rotations` is combined into a predefined [`Manifolds.SpecialEuclidean`](@ref). | ||
|
||
The rigid transforms examples discussed here are in the same spirit as packages such as [CoordinateTransformations.jl](https://github.com/JuliaGeometry/CoordinateTransformations.jl). Manifolds.jl instead builds coordinate transformations from the ground up using fundamental abstractions about generalized manifolds. The `SpecialEuclidean(2)` group manifold used in this tutorial is one specific manifold, which is the product of two underlying manifolds. | ||
|
||
!!! note | ||
While many of the operations listed below at first seem excessive for basic rigid transform operations, this is not the case. Once the data formats and representations are setup, the actual `compose` and `apply` operations are super fast and syntactically easy. Significant efforts have gone into defining a general framework for manifold operations that can go beyond operations such as Riemannian geometry. | ||
|
||
## Rigid Transforms Setup | ||
|
||
Let's load load some packages first | ||
```julia | ||
using Manifolds | ||
using LinearAlgebra | ||
using StaticArrays | ||
``` | ||
|
||
Consider a combination of translation with a rotation of some avatar or axis-dyad on the xy-plane, where each location (i.e. the combo or position and rotation) which will be represented as some point `p` on a group `G` manifold `M`. This is indicated by: | ||
```julia | ||
# xy dimension in this case is 2 | ||
n = 2 | ||
# the group manifold also known as SE(2) | ||
G = SpecialEuclidean(n) | ||
M = base_manifold(G) | ||
# can separately take the Translation and Rotations manifolds within M | ||
M_T = M[1] | ||
M_R = M[2] | ||
``` | ||
|
||
Next, define the default basis to use later and also a starting reference rotation and translation | ||
```julia | ||
B = DefaultOrthogonalBasis() | ||
# choose the identity reference translation and rotation | ||
Tr0, R0 = SA[0.0; 0], SA[1.0 0; 0 1] | ||
``` | ||
|
||
To structure the tutorial, let's "walk" the perimeter of a rectangle, passing through various points on the manifold. To do so, we need to understand how coordinates, vectors in the Lie algebra, and group elements are used. | ||
|
||
## From Coordinates, SE(2) | ||
|
||
The coordinates are easily readible / sharable numbers that we humans frequently stack together as a vector. In this case, the three degrees of freedom representing a transformation, namely `[Δx; Δy; Δθ]`. To reduce the notational load, this example will drop the `Δ` and simply state `[x;y;θ]` as the transform coordinates (i.e. a point on the `SpecialEuclidean(2)` manifold, but not yet in the group data representation format). | ||
|
||
First, define the individual manifold tangent vectors (translation, rotation) of the identity element coordinates | ||
```julia | ||
t0, w0 = [0.0; 0], get_vector(M_R, R0, 0, B) | ||
``` | ||
|
||
!!! note | ||
The `hat` notation could also be used in this case: | ||
```julia | ||
_t0, _w0 = [0.0; 0], hat(M_R, R0, 0) | ||
@assert isapprox(t0, _t0); @assert isapprox(w0, _w0) | ||
``` | ||
|
||
Next, define delta elements that each represent a certain "rigid body transform" so that the rectangular path can be completed. Starting from the "origin" (i.e. reference point) above, we define relative segments between points using the coordinates and we will `compose` these segments together in an upcoming section below: | ||
```julia | ||
# walk forward on side x and turn left | ||
t1a, w1a = [1.0; 0], get_vector(M_R, R0, 0 , B) | ||
t1b, w1b = [1.0; 0], get_vector(M_R, R0, π/2, B) | ||
|
||
# walk positive y and turn left again | ||
t2, w2 = [1.0; 0], get_vector(M_R, R0, π/2, B) | ||
|
||
# walk negative x and turn left | ||
t3, w3 = [2.0; 0], get_vector(M_R, R0, π/2, B) | ||
|
||
# walk negative y back onto origin and turn left | ||
t4, w4 = [1.0; 0], get_vector(M_R, R0, π/2, B) | ||
``` | ||
|
||
Notice that each of these coordinates are **not** defined against the origin, but are fully relative translations and rotations. | ||
|
||
Before we can compose these separate translation and rotation pairs, we need to leverage some pairing data structure by which Manifolds.jl can identify a Product manifold operation is needed. There are currently two ways to do so. The `compose` hereafter can work with either the `ProductRepr` or `ProductArray` representations. | ||
|
||
### ProductRepr for SpecialEuclidean(2) | ||
|
||
Let's first define the identity group element on the manifold directly: | ||
```julia | ||
p0 = ProductRepr(Tr0, R0) | ||
``` | ||
|
||
!!! note | ||
Using the language or reference frames, this is the identity reference to which other points will be defined. For example, consider measuring various points relative to a common "reference frame", then `p0` might represent that reference point in-and-about the other points on the same `SpecialEuclidean(2)` manifold. | ||
|
||
Above, we defined individual tranlate and rotate vectors such as `t2,w2`. The next step towards using `compose` for rigid body transformations is to properly pair (i.e. data structuring) the Lie algebra elements and then map to the equivalent Lie group element on the group manifold `G` or `M`. | ||
|
||
The trivial case is a zero vector from the point `p0` | ||
```julia | ||
# make Lie Algebra element | ||
x0 = ProductRepr(t0, w0) | ||
|
||
p0_ = exp(G, p0, x0) # or, exp(M, p0, x0) | ||
``` | ||
|
||
The exponential maps the Lie algebra to the associated Lie group, but in this trivial zero-vector case should be exactly the same point on the manifold | ||
```julia | ||
@assert isapprox(p0_.parts[1], p0.parts[1]); @assert isapprox(p0_.parts[2], p0.parts[2]) | ||
``` | ||
|
||
All the other relative vector segments are mapped onto the manifold, and carefully not relative to the point `p0` in each case -- we will soon show why | ||
```julia | ||
# calculate the exponential mapping from point p0 | ||
x1a = ProductRepr(t1a, w1a) # Lie algebra element | ||
p1a = exp(G, p0, x1a) # Lie group element | ||
|
||
x1b = ProductRepr(t1b, w1b) | ||
p1b = exp(G, p0, x1b) | ||
|
||
x2 = ProductRepr(t2, w2) | ||
p2 = exp(G, p0, x2) | ||
|
||
x3 = ProductRepr(t3, w3) | ||
p3 = exp(G, p0, x3) | ||
|
||
x4 = ProductRepr(t4, w4) | ||
p4 = exp(G, p0, x4) | ||
``` | ||
|
||
If you want to continue using `ProductRepr` then skip ahead to [Compose Rigid Transforms, 2D](@ref compose_rigid_transforms_2d). | ||
|
||
### 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. | ||
|
||
!!! note | ||
`ProductArray` might seem a bit more laborious than `ProductRepr` at first, but the increased structure allows for faster implementation details within the Manifolds.jl ecosystem. | ||
|
||
The underlying data is represented according to a shape specification | ||
```julia | ||
reshaper = Manifolds.StaticReshaper() | ||
shape_G = Manifolds.ShapeSpecification(reshaper, M) | ||
shape_se = Manifolds.ShapeSpecification(reshaper, M.manifolds...) | ||
``` | ||
|
||
And the associated Lie group elements (still `SpecialEuclidean(2)`) are mapped from the vector elements above | ||
```julia | ||
p0 = Manifolds.prod_point(shape_se, (t0,exp(M_R, R0, w0))... ) | ||
p1a = Manifolds.prod_point(shape_se, (t1a,exp(M_R, R0, w1a))... ) | ||
p1b = Manifolds.prod_point(shape_se, (t1b,exp(M_R, R0, w1b))... ) | ||
p2 = Manifolds.prod_point(shape_se, (t2,exp(M_R, R0, w2))... ) | ||
p3 = Manifolds.prod_point(shape_se, (t3,exp(M_R, R0, w3))... ) | ||
p4 = Manifolds.prod_point(shape_se, (t4,exp(M_R, R0, w4))... ) | ||
``` | ||
|
||
Just as with the `ProductRepr` approach, each of the points, e.g. `p2`, represent a relative rigid transform -- note again that the exponential map was done relative to the choosen identity `R0`. The identity point for translations `M_T` are trivial, i.e. `[0;0]`, and therefore not elaborated as is done with the Rotations under `M_R`. | ||
|
||
## [Compose Rigid Transforms, 2D](@id compose_rigid_transforms_2d) | ||
|
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. You can achieve a similar result using |
||
|
||
```julia | ||
## Consecutively compose the "rigid transforms" (i.e. points) together around a rectangle | ||
|
||
p_01a = compose(G, p0, p1a) | ||
p_01b = compose(G, p_01a, p1b) | ||
p_012 = compose(G, p_01b, p2) | ||
p_0123 = compose(G, p_012, p3) | ||
p_01234 = compose(G, p_0123, p4) | ||
``` | ||
|
||
Staring from the choosen origin `p0` and looking in the direction along the choosen x-axis (i.e. `θ = 0`), walk forwards 1.0 units towards a new point `p_01a`. This relative transform from `p0` on the manifold to the new point `p_01a` on the manifold is captured in the group element `p1a` (also `SpecialEuclidean(2)`). | ||
|
||
Next, walk from `p_01a` to `p_01b` according to the relative transformation captured by the group element `p1b` which is again forwards 1.0 units, but also followed by a positive rotation of `π/2` radians. Taking the default basis `B` as an xy-tangent plane, we understand a positive rotation according to the right-hand-rule as going from the local x-axis towards the y-axis. | ||
|
||
!!! note | ||
As a sneak peak, we expect this new point `p_01b` in human readible coordinates to be at `[2;0;π/2]` relative to the choosen reference point `p0`. We will show and confirm this computation below. | ||
|
||
It is worth reiterating that the relative group actions `p1a` and `p1b` are Lie group elements that were constructed from local Lie algebra vectors using `p0` in each case. Here, during the `compose` operation those "relative transforms" are chained together (i.e. composed) into new (totally separate) points `p_01a, p_01b` on the `SpecialEuclidean(2)` manifold. Our cartoon interpretation of walking around a rectangle mentally easiest when operating relative to the starting point `p0`. The full use of Manifolds.jl goes well beyond this "rigid transform" construct. | ||
|
||
To complete the walk-about, we compose `p2` onto the previous to get a new point `p_012` which is now the opposite corner of the rectangle. Two more consecutive "transforms" `p3, p4` complete the rectangle through points `p_0123` and `p_02134`, respectively, and each time turning `π/2` radians. We therefore expect point `p_01234` to exactly the same as the reference starting point `p0`. | ||
|
||
## To Coordinates, SE(2) | ||
|
||
Thus far, we converted user inputs from a convenient coordinate represenation into two computational representations (either `ProductRepr` or `ProductArray`). The next step is to be able to convert back from the manifold data representations back to coordinates. In this example, we first take the logmap of a group element in `SpecialEuclidean(2)` to get the associated Lie algebra element, and then extract the coordinates from that vector, and lets do so for the point `p_01` as elluded to above | ||
```julia | ||
x_01b_ = get_coordinates(G, p0, log(G, p0, p_01b), B) | ||
|
||
# check the coordinates are as expected | ||
@assert isapprox( x_01b_[1:2], [2;0], atol = 1e-10 ) | ||
@assert isapprox( abs(x_01b_[3]), π/2 , atol = 1e-10 ) | ||
``` | ||
|
||
Notice that both the `log` and `get_coordinates` operations are performed relative to our choosen reference point `p0`. These operations therefore are extracting the tangent space vector (and its coordinates) relative to the origin. If we were to extract the vector coordinates of `p_01b` relative to `p_01a`, we would expect to get the same numerical coordinate values used to construction the relative transform `p1b`. | ||
|
||
Similarly, we check that the opposite corner of the walked rectangle is as we expect | ||
```julia | ||
x_012_ = get_coordinates(G, p0, log(G, p0, p_012), B) | ||
@assert isapprox( x_012_[1:2], [2;1], atol = 1e-10) | ||
@assert isapprox( abs(x_012_[3]), π, atol = 1e-10 ) | ||
``` | ||
|
||
And also the past point on the path `p_01234` lands back on the reference point `p0` as expected | ||
```julia | ||
x_01234_ = get_coordinates(G, p0, log(G, p0, p_01234), B) | ||
@assert isapprox( x_01234_, [0;0;0], atol = 1e-10 ) | ||
``` | ||
|
||
This concludes the tutorial with emphasis on functions: | ||
- `get_vector` / `hat`, | ||
- `exp` vs. `log`, | ||
- `get_coordinates` / `vee`, and | ||
- `compose`. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 theshape
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:
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...ShapeSpecification
, and should I worry aboutStatic
...hat
vsget_vector
...vee
(where textbooks say 'vectorize') the same asget_vector
...get_coordinates
take either group or vector (i.e. algebra) elements, and should I always write that myself...shape
, and how much does Julia's own ::Array get used for dispatch...exp
, does the code know whether you are giving it coordinates or a algebra element...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
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.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're welcome 🙂 . I'm happy that you find Manifolds.jl useful.
I'm really not satisfied with the design
ProductArray
, and I'm almost sure that we can makeProductRepr
cover all possible use cases for product manifolds, so I'd suggest just reporting problems with that approach and not usingProductArray
at all.Calling
base_manifold
should rarely be necessary. Feel free to report any cases where calling an operation onbase_manifold
works but directly on a group it does not.I think you should just stop using
ProductArray
. It was an idea I had when I doubtedProductRepr
could work with broadcasting and power manifolds, but nowProductRepr
does work there.Same as above, you shouldn't use
ShapeSpecification
.vee
andhat
were supposed to be aliases to certain variants ofget_coordinates
andget_vector
to make people familiar with them more comfortable but it doesn't seem to work this way... Personally, I never usehat
andvee
.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 thatget_coordinates
originates in differential geometry, not Lie theory, and as such it needs to know which point the vector is tangent at.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, sometimesSVector
), but the thing you get there is the vector of coordinates, whileget_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 calledget_tangent_vector
).I think we almost never dispatch on Julia's
Array
, though in many cases we dispatch onAbstractArray
.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.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.
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.
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 throughProductArray
.ProductRepr
is definitely easier, and with the recent changes I'm certain it can also always be as fast, if not faster, thanProductArray
.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
andhat
(cf. https://en.wikipedia.org/wiki/3D_rotation_group#Lie_algebra) and it seems that there is one confusion with your questionFirst 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$\omega$ and returns an alement from the Lie algebra. So with the identification of every tangent space
hat
function takes a set of coordinateshat
reconstructs a vector from given coefficients in a basis, sohat
=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 isvee
=get_coordinates
for the special case of using theVeeOrthogonalBasis
(this is also the basis forhat
sget_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
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.
Ah, that helps thanks!
Thanks, kellertuer also mentioned that. Hopefully 355 and 366 help newcomers through that faster than I was able to.
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!
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.
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
andretraction
(as we spoke through last week).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.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.
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.