Manifolds.jl icon indicating copy to clipboard operation
Manifolds.jl copied to clipboard

missing `affine_matrix(::Rotations{n}, p)`?

Open dehann opened this issue 3 years ago • 9 comments

Perhaps there already is a function to extract the rotation matrix from SpecialOrthogonal(n) or Rotations(n)? If not, would affine_matrix be the right API, since rotations can be implemented as a MultiplicationAction using matrices? Seems affine_matrix is currently only implemented for SpecialEuclidean(n).

M = SpecialOrthogonal(2)

R0 = [1.0 0; 0 1]
p1 = exp(M, R0, hat(M, R0, π/2 ))

R1  = affine_matrix(M, p1)                 # doesn't exist
R1_ = affine_matrix(base_manifold(M), p1)  # doesn't exist  (Rotations(2))

I'm not sure how Manifolds.jl will abstract the API for all these cases in general?


Rabbit Hole

For completeness (and likely a separate issue thread), if n=3 and UnitQuaternions (0 < theta/2 -- i.e. nearly exp(i+j+k)) were used as representation for Rotations(3), then the operations can be implemented in at least two ways (one being matrix multiplication). If n=2 then Sphere(1) is very nearly exp(im*theta) (connection to Circle()), is very nearly SpecialOrthogonal(2) (basically variations in coefficient mechanics and bases). Similarly, Octonians for n=4. Does AngleAxis fit in here too in terms of representations (Clifford Algebra / Plucker Coordinates); DualQuaternions; ...

I guess the fundamental question is whether a "manifold" represents "points" with properties like exp/log/distance/etc. regardless of underlying representation; OR if a "manifold" has duplicity such that for example SO(3) and UnitQuaternion are entirely different "groups" regardless of the fact that they describe exactly the same "thing" -- but are they the same "manifold"? (cc'ing @david-m-rosen, since we kinda spoke about this before but never came to an answer)

I wanted to mention in a previous comment, perhaps here is best. Should there be a LazyManifoldsUtils.jl package (name TBD), which wraps the complexity of switching between representations? I'd like to deprecate my old TransformUtils.jl package, but think many of the "Lazy" conversions and promotions are a downstream consequence of Manifolds.jl, and does not necessary belong inside Manifolds.jl? (cc'ing @Affie)

EDIT: maybe part of the answer is if you ask for something like (not sure about API yet):

dehann avatar May 10 '21 13:05 dehann

Rabbit Hole...

I was also looking at TransformUtils.jl and wondering the same thing about the different representations.

A Lazy implementation would be great since it seems we can avoid a lot of the complexities of Manifolds needed for it to be general.

Affie avatar May 10 '21 14:05 Affie

avoid a lot of the complexities of Manifolds needed for it to be general.

Do you mean easier to use for a wider audience, since Manifolds.jl design is meant to be as general as possible (in the math sense)?

dehann avatar May 10 '21 14:05 dehann

Perhaps there already is a function to extract the rotation matrix from SpecialOrthogonal(n) or Rotations(n)? If not, would affine_matrix be the right API, since rotations can be implemented as a MultiplicationAction using matrices? Seems affine_matrix is currently only implemented for SpecialEuclidean(n).

So, you'd like to have affine_matrix return the affine matrix with translation equal to 0 for rotations? That looks fine :slightly_smiling_face: . Currently points on SpecialOrthogonal are represented as rotation matrices anyway.

For completeness (and likely a separate issue thread), if n=3 and UnitQuaternions (0 < theta/2 -- i.e. nearly exp(i+j+k)) were used as representation for Rotations(3), then the operations can be implemented in at least two ways (one being matrix multiplication). If n=2 then Sphere(1) is very nearly exp(im*theta) (connection to Circle()), is very nearly SpecialOrthogonal(2) (basically variations in coefficient mechanics and bases). Similarly, Octonians for n=4. Does AngleAxis fit in here too in terms of representations (Clifford Algebra / Plucker Coordinates); DualQuaternions; ...

I guess the fundamental question is whether a "manifold" represents "points" with properties like exp/log/distance/etc. regardless of underlying representation; OR if a "manifold" has duplicity such that for example SO(3) and UnitQuaternion are entirely different "groups" regardless of the fact that they describe exactly the same "thing" -- but are they the same "manifold"? (cc'ing @david-m-rosen, since we kinda spoke about this before but never came to an answer)

Currently the best example for the relation between manifolds and point/tangent vector representation is the Hyperbolic manifold. We have three different representations that work with the same ::Hyperbolic object that represents the manifold and the representation is determined by the type of point arguments.

I wanted to mention in a previous comment, perhaps here is best. Should there be a LazyManifoldsUtils.jl package (name TBD), which wraps the complexity of switching between representations? I'd like to deprecate my old TransformUtils.jl package, but think many of the "Lazy" conversions and promotions are a downstream consequence of Manifolds.jl, and does not necessary belong inside Manifolds.jl? (cc'ing @Affie)

I'll have to think about it a bit but why should TransformUtils.jl be deprecated? I think conversions in Manifolds.jl could just use TransformUtils.jl.

EDIT: maybe part of the answer is if you ask for something like (not sure about API yet):

action_matrix seems to be a decent idea but the API needs to be different. The first argument needs to be of type <:AbstractGroupAction. We currently have RotationAction as an example for that. The point of this is that you could for example have an action of SO(2) on R^3 represented by an object of type that's a subtype of AbstractGroupAction that, for example, stores the rotation axis. Or something completely different, the point is that a group can act on a manifold in many different ways.

I hope this helps :slightly_smiling_face:

mateuszbaran avatar May 10 '21 14:05 mateuszbaran

@dehann, I would think all Manifold types and operations related to rigid transformations that are currently in TransformUtils.jl so an overly verbose name could be: LazyRigidTransformationManifolds.jl

Thanks, @mateuszbaran, I'll have a look at the Hyperbolic manifold.

Affie avatar May 10 '21 14:05 Affie

Thanks for this interesting topic and rabbit hole.

To add to the types, we currently do not necessarily have a strict bound on types for manifolds, usually you just implement e.g. exp(M,p,X) assuming a certain (default) array representation. The idea is that the first implementation defines a (suitable) default, so other types you then define a <:MPoint (to be renamed to ManifoldPoint soon). An example was mentioned by @mateuszbaran – we use that in Hyperbolic (with the default being the embedded Hyperboloid model, and two further ones)

For the Question in UnitQuarternions – That could be done using M = Sphere(3,ℍ), though please check it before, I am not 100% sure how tested that is. The same holds for SpecialOrthogonal(2), Circle and Sphere (compare for example Circle(ℂ) which is the same as Sphere(2), just that the first acts on complex numbers while the second acts on vectors from R2, both with unit norm).

Conversions would require ManifoldPoint types, I think, or we have to come up with a good idea to convert(M1,p) to convert p (an array representing a point in M2 say) to a representation of the same point on M1.

Concerning the affine matrix – Rotations(n) are currently exactly represented by rotation matrices, so there is no need for the affine_matrix function there – but one could to a ManifoldPoint type for different representations for sure, and then provide converts (again see Hyperbolic for comparison how this can be done). Keep in mind that most probably you also need a way to convert tangent vectors (or also Lie algebra elements) most probably

kellertuer avatar May 10 '21 15:05 kellertuer

Just a remark on this. Currently also SpecialOrthogonal (see https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.SpecialOrthogonal) is a manifold whose points are rotation matrices. But I am happy to discuss and help establishing other representations (see again Hyperbolic where we made that).

kellertuer avatar May 10 '21 17:05 kellertuer

Thanks all, that helps. I have a lot of code reading to do. I'll first finish the two tutorial examples in the docs and will come back to this with a proposal based on my learnings from Hyperbolic!

Acknowledge API starting with a ::Manifold.

I like the idea of returning a matrix as operator somehow related to MultiplicstionAction and/or based on another action abstraction (if it exists). That would be easier for user bespoke Manifolds to define their own "matrix operator action". Also a way to throw errors if matrix operands not available. I'm thinking future tensor operators etc. This is related to apply! or compose!, but perhaps a bridge for users to interact/learn more.

Okay, if i understand correctly: separate groups, algebras, representation types -- but could be the same "manifold". I still think implementing separate mechanics is important, maybe other benefits or reasons beyond just the numerical result. Sounds like the way to go is to implement different types for each representation but populate the conversions between possible ones so that a user can easily move between representations, even perhaps mix and match (ultimately when everything is implemented over several PRs). E.g Base.convert(::SO(3), q::UnitQuaternion)

I think it is probably best not to have affine_matrix for Rotations with a zero translation added, but just some user friendly way to get the SO(3) matrix (even if representation underneath is say UnitQuaternion). I'll have to think more based on comments, thanks!

PS, i previously got pushback on the name TransformUtils.jl being very nondescript outside of "rigid transformations". Can find a better name when there is something to do show-and-tell with.

dehann avatar May 12 '21 03:05 dehann

Thanks for all the time you invest.

Yes I think a convert(::SpecialOrthogonal{3}, q:UnitQuartenion) would convert the unit quaternion to the default representation (i.e. rotation matrices) on SO(3); that would surely be nice to have. Also, if the formula of any operation is nice, an distance(M,p::UnitQuarternion, q::UnitQuarternion= method would also be nice to have for sure.

What do you want to refer to with the pushback? Is it the analogue of the adjoint of the differential? Because the differential between manifolds is often called pullback, so then I would prefer pushforward. But I can check what you actually use it for :)

kellertuer avatar May 12 '21 07:05 kellertuer

Thanks all, that helps. I have a lot of code reading to do. I'll first finish the two tutorial examples in the docs and will come back to this with a proposal based on my learnings from Hyperbolic!

Thanks for your help, it's sometimes difficult for me to see what needs more detailed explanation so your perspective is very valuable :slightly_smiling_face: .

One think I'd add here is that wrappers for points is not our only option. For PowerManifold we have two different representations (and another one is planned) that are differentiated by the type parameter of the manifold, and in both representations points can be plain arrays. I wouldn't advise reading the code of PowerManifold though, I am aware that some interesting design decisions are not documented appropriately.

What do you want to refer to with the pushback? Is it the analogue of the adjoint of the differential? Because the differential between manifolds is often called pullback, so then I would prefer pushforward. But I can check what you actually use it for :)

I think this is the correct definition of pushback https://www.dictionary.com/browse/pushback :slightly_smiling_face: .

mateuszbaran avatar May 12 '21 08:05 mateuszbaran