#26344 closed enhancement (fixed)
Nilpotent Lie groups
Reported by:  ghehaka  Owned by:  

Priority:  major  Milestone:  sage8.5 
Component:  group theory  Keywords:  Lie groups, Lie algebras, manifolds, nilpotent 
Cc:  tscrim, egourgoulhon  Merged in:  
Authors:  Eero Hakavuori  Reviewers:  Eric Gourgoulhon, Travis Scrimshaw 
Report Upstream:  N/A  Work issues:  
Branch:  b2ef2d9 (Commits, GitHub, GitLab)  Commit:  
Dependencies:  #26080  Stopgaps: 
Description
Implementation of nilpotent Lie groups as manifolds with a distinguished global coordinate system (exponential coordinates).
Planned features:
 representation of elements in exponential coordinates of the first or second kind
 multiplication of symbolic points (using the manifold point framework)
 the frames of leftinvariant and rightinvariant vector fields
 the exponential map
exp:\mathfrak{g}\to G
and the logarithm  the adjoint map
Ad:G\times \mathfrak{g}\to\mathfrak{g}
Change History (41)
comment:1 Changed 4 years ago by
 Branch set to u/ghehaka/nilpotent_lie_groups26344
 Commit set to 63006ea2885a50569e2b95b24b83db8a7998dbc8
comment:2 Changed 4 years ago by
 Dependencies set to #26080
comment:3 Changed 4 years ago by
 Cc egourgoulhon added
Eric, I am ccing you in case you have any comments.
The symbolic ring is usually a bit of a slow beast. it definitely feels wrong to have to have a second copy of the Lie algebra over SR
. You probably could just use the structure coeffs info.
Anyways, I will think more about this.
comment:4 Changed 4 years ago by
Good to see that Lie groups are arriving in Sage!
I'll have a look tomorrow, focusing on the SR
issue.
comment:5 Changed 4 years ago by
 Commit changed from 63006ea2885a50569e2b95b24b83db8a7998dbc8 to 8e4dfb1820eb636dd5c5b84b3de90bab50445a12
Branch pushed to git repo; I updated commit sha1. New commits:
8e4dfb1  trac #26344: invariant vector fields

comment:6 followup: ↓ 7 Changed 4 years ago by
I am not sure I agree with caching the *_invariant_frame
. I think this is already done by the atlas for the manifold and there is probably not much benefit to having this cached at the top level as not a lot of computation is done in the methods themselves.
Another comment is that structure_coefficients
should exist anytime the Lie algebra has a basis because of the category. I believe the variable_names()
and indices()
should not fail either. So I don't see a meaningful exception being thrown.
Now that I have enough time, I see why you are using the symbolic ring to create the group law. You are considering things as generic parameters and seeing what the result is. In principle, creating the LieAlgebraWithStructureCoefficients
should be relatively quick. It might be better to implement a change_ring()
method for the class instead of the helper function (which could have more general use). Unfortunately I don't see a way out of creating that (at least, one that is efficient), but it should not take a lot (well, a disproportionate amount) of memory.
comment:7 in reply to: ↑ 6 Changed 4 years ago by
Replying to tscrim:
I am not sure I agree with caching the
*_invariant_frame
. I think this is already done by the atlas for the manifold and there is probably not much benefit to having this cached at the top level as not a lot of computation is done in the methods themselves.
Right, I did not realize there is manifold level caching. I will remove it from here.
Another comment is that
structure_coefficients
should exist anytime the Lie algebra has a basis because of the category.
This is a good point, and I should in fact change indices()
to basis().keys()
as in the category code of structure_coefficients
, so it will work regardless within the category.
It might be better to implement a
change_ring()
method for the class instead of the helper function (which could have more general use).
Ideally construction of a Lie group would not rely on the Lie algebra having a change_ring()
method, but instead only rely on things required/defined in the finite dimensional (nilpotent) Lie algebras with basis category.
However it would make sense to by default use change_ring
if it exists, and to implement this at least for LieAlgebraWithStructureCoefficients
. Then only in the case when change_ring
is not defined, the code could fall back to creation of a new Lie algebra using structure_coefficients()
.
Thanks for the comments!
comment:8 Changed 4 years ago by
 Commit changed from 8e4dfb1820eb636dd5c5b84b3de90bab50445a12 to 61963e06463f59ef392fec1d5e488870788d25c6
Branch pushed to git repo; I updated commit sha1. New commits:
61963e0  trac #26344: added change_ring to LieAlgebraWithStructureCoefficients and modified symbolic Lie algebra creation

comment:9 followup: ↓ 10 Changed 4 years ago by
I gave a first glance: this looks very nice!
A very minor remark: in the example shown in line 151 of lie_group.py
(as well as in other similar examples), a shortcut for
sage: list(X[0].components(exp1_frame))
is
sage: X[0][exp1_frame,:]
Maybe you chose the longer version as being more explicit?
Also you can display the components of the vector field X[0]
by
sage: X[0].display(exp1_frame) X_0 = d/dx_0  1/2*x_1 d/dx_2
Since exp1_frame
is the default vector frame on G
, this can be shorten to simply
X[0].display()
.
comment:10 in reply to: ↑ 9 ; followup: ↓ 11 Changed 4 years ago by
Replying to egourgoulhon:
I gave a first glance: this looks very nice!
A very minor remark: in the example shown in line 151 of
lie_group.py
(as well as in other similar examples), a shortcut forsage: list(X[0].components(exp1_frame))is
sage: X[0][exp1_frame,:]Maybe you chose the longer version as being more explicit? Also you can display the components of the vector field
X[0]
bysage: X[0].display(exp1_frame) X_0 = d/dx_0  1/2*x_1 d/dx_2
This choice was just a consequence of lack of knowledge on my part. Thanks for the advice, this will clean up the doc quite a bit!
Since
exp1_frame
is the default vector frame onG
, this can be shorten to simplyX[0].display()
.
At this point I don't know what should be the default frame. The best sounding options are either the coordinate frame of the default system of coordinates, or the leftinvariant frame.
comment:11 in reply to: ↑ 10 Changed 4 years ago by
Replying to ghehaka:
Since
exp1_frame
is the default vector frame onG
, this can be shorten to simplyX[0].display()
.At this point I don't know what should be the default frame. The best sounding options are either the coordinate frame of the default system of coordinates, or the leftinvariant frame.
In this case, it is probably better to keep the explicit form, i.e. X[0].display(exp1_frame)
.
comment:12 followup: ↓ 13 Changed 4 years ago by
In the documentation, maybe you could use the method at()
of vector fields to illustrate that the members of G.left_invariant_frame()
do obey the definition of a left invariant vector field, for instance:
sage: L = lie_algebras.Heisenberg(QQ, 1) sage: G = NilpotentLieGroup(L, 'G'); G Lie group of Heisenberg algebra of rank 1 over Rational Field sage: p,q,z = L.basis()
Let us first introduce a specific element k
and generic element g
in G
:
sage: k = G.exp(p)*G.exp(q); k exp(p1 + q1 + 1/2*z) sage: g = G(G.chart_exp1()[:]); g # a generic element of G exp(x_0*p1 + x_1*q1 + x_2*z)
We introduce next the left translation by k
:
sage: L_k = G.diff_map(G, coord_functions=(k*g).coordinates()); L_k Differentiable map from the Lie group of Heisenberg algebra of rank 1 over Rational Field to itself sage: L_k.display() G > G (x_0, x_1, x_2) > (x_0 + 1, x_1 + 1, 1/2*x_0 + 1/2*x_1 + x_2 + 1/2)
Then we check that X[0]
obeys the definition of a left invariant vector field as follows:
sage: X = G.left_invariant_frame(); X Vector frame (G, (X_0,X_1,X_2)) sage: L_k.differential(g)(X[0].at(g)) == X[0].at(L_k(g)) True
Of course, we have as well
sage: L_k.differential(g)(X[1].at(g)) == X[1].at(L_k(g)) True sage: L_k.differential(g)(X[2].at(g)) == X[2].at(L_k(g)) True
Of course, a full check would require that k
is a generic element of G
; this is possible, by defining k
from symbolic coordinates.
comment:13 in reply to: ↑ 12 Changed 4 years ago by
 Branch changed from u/ghehaka/nilpotent_lie_groups26344 to public/groups/nilpotent_lie_groups26344
 Commit changed from 61963e06463f59ef392fec1d5e488870788d25c6 to 8a022e85abb13436b8dfc18528f45621309aa4f3
I moved the code over to a public branch so you can feel free to add improvements in if you so wish. I'm also still happy to hear comments and corrections and do the implementation myself as well.
Replying to egourgoulhon:
In the documentation, maybe you could use the method
at()
of vector fields to illustrate that the members ofG.left_invariant_frame()
do obey the definition of a left invariant vector field
Thanks for the suggestion, this is indeed a very good demonstration of basic Lie group functionality. I also ended up adding the creation of left/right translations as a prebuilt method as these maps should see heavy use in practice.
I have still to add in the methods for the logarithm and adjoint maps, after that I would for the moment be satisfied with the basic Lie group functionality. Or at least I can't think of what it is I am missing.
New commits:
8a022e8  trac #26344: added methods to create left and right translations and improved docs

comment:14 Changed 4 years ago by
There is also no real penalty for having more tickets (at least, I will continue to be reviewing tickets for at least the next year :)
). So it doesn't hurt to do things in smaller bites too.
comment:15 Changed 4 years ago by
 Commit changed from 8a022e85abb13436b8dfc18528f45621309aa4f3 to c16d1f1bac71cbf7f6c21f24cd6f9284a43cebe2
Branch pushed to git repo; I updated commit sha1. New commits:
c16d1f1  trac #26344: conjugation, adjoint and logarithm

comment:16 Changed 4 years ago by
 Status changed from new to needs_review
The missing parts of the initial set of features I had in mind are now in. It didn't seem right to add the adjoint without adding the conjugation map as well so that is now there as well.
At this point I'll move the ticket over to the bughunting/improvement phase.
comment:17 Changed 4 years ago by
I took the liberty of adding a new section "Lie groups" at the manifold metaticket #18528 and put your ticket there. I hope you don't mind.
comment:18 Changed 4 years ago by
A question came to my mind: at the moment
sage: L = G.lie_algebra(); L Heisenberg algebra of rank 1 over Rational Field sage: T1G = G.tangent_space(G.one()); T1G Tangent space at exp(0)
are two different beasts:
sage: L.category() Category of finite dimensional nilpotent lie algebras with basis over Rational Field sage: T1G.category() Category of finite dimensional vector spaces over Symbolic Ring sage: L.basis() Finite family {'p1': p1, 'q1': q1, 'z': z} sage: T1G.bases() [Basis (d/dx_0,d/dx_1,d/dx_2) on the Tangent space at exp(0)]
also as Python objects: L
inherits from sage.algebras.lie_algebras.lie_algebra.LieAlgebraWithGenerators
, while T1G
inherits from sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule
.
Would it be worth to implement the canonical vector space isomorphism between the two? Would this be useful? A natural way to do it would be via Sage coercions, i.e. implement at least a coercion from L
to T1G
(the reverse may not work for vectors with symbolic components). If you think this is worth, this could anyway be differed to another ticket...
comment:19 Changed 4 years ago by
Replying to egourgoulhon:
I took the liberty of adding a new section "Lie groups" at the manifold metaticket #18528 and put your ticket there. I hope you don't mind.
This is perfectly fine by me, organization is good.
Replying to egourgoulhon:
Would it be worth to implement the canonical vector space isomorphism between the two? Would this be useful? A natural way to do it would be via Sage coercions, i.e. implement at least a coercion from
L
toT1G
(the reverse may not work for vectors with symbolic components). If you think this is worth, this could anyway be postponed to another ticket...
Yes this makes sense, and at least the simple coercion should be quite immediate to define, since the basis of the Lie algebra and the basis coming from the exponential coordinate frame are already in direct correspondence as lists.
It might actually also be nice to give T1G
the full Lie algebra structure and upgrade the coercion to one of Lie algebras. Since vector fields already have a Lie bracket operation defined, this should be doable through computation with the leftinvariant frame. It would also allow computing symbolic Lie brackets.
All of this combined would add up to quite a bit though. I think leaving it to a new ticket would be a good idea.
comment:20 Changed 4 years ago by
While having a coercion would be good, as Eric points out, the mismatch of base rings would force the direction of the coercion L > T1G
. However, because coercions have to preserve structure, T1G
would have to carry a Lie algebra structure.
Now, this does not have to be a coercion. You could implement the conversions (and have a helper method, such as to_construction_Lie_algebra
or some better name) between the two objects. You probably could do this for the tangent space at a generic element since that just corresponds to conjugating by the element.
Also as previously stated, that would be good for a followup ticket.
For this ticket, the only other things I might want to see are a lie_group
method on the Lie algebra (a generic one would raise a NotImplementedError
or be an @abstract_method(optional=True)
) and an exp
method on the Lie algebra element that does self.parent().lie_group().exp(self)
.
comment:21 Changed 4 years ago by
 Commit changed from c16d1f1bac71cbf7f6c21f24cd6f9284a43cebe2 to 7607b8c2a0358502c3796410640db3763ee82967
comment:22 Changed 4 years ago by
 Commit changed from 7607b8c2a0358502c3796410640db3763ee82967 to d3c77ac7b0569691b39e0c4e4f0b3ec4aefbaad4
Branch pushed to git repo; I updated commit sha1. New commits:
d3c77ac  trac #26344: added name parameter to Lie algebra element method exp

comment:23 followup: ↓ 25 Changed 4 years ago by
I currently set the Lie algebra > Lie group interface to default to naming the group 'G'. This allows both L.lie_group()
and X.exp()
, since requiring the name parameter for exp
did not seem convenient for use.
Should different(ly named) Lie groups of the same Lie algebra have a predefined canonical coercion between each other? Or should these be considered separate objects? I'm not sure about the intended use of the _name
parameter when the underlying manifold is the same. In particular, it is clear that
sage: NilpotentLieGroup(L, 'G') is NilpotentLieGroup(L, 'H') False
is desired behavior. Would
sage: NilpotentLieGroup(L, 'G') == NilpotentLieGroup(L, 'H') False
also be desired behavior?
comment:24 followup: ↓ 26 Changed 4 years ago by
Another question I have is where should the Lie group functionality go in the source and reference?
Currently the source is under sage.groups.lie_group.py
and the doc is not linked anywhere since I didn't know where to put it.
comment:25 in reply to: ↑ 23 Changed 4 years ago by
Replying to ghehaka:
I currently set the Lie algebra > Lie group interface to default to naming the group 'G'. This allows both
L.lie_group()
andX.exp()
, since requiring the name parameter forexp
did not seem convenient for use.
I would favor passing the Lie group itself and not the string representing its name as the argument of the exp
defined in the element methods of LieAlgebras
, i.e.
something like
def exp(self, lie_group=None): if not lie_group: return self.parent().lie_group().exp(self) return lie_group.exp(self)
This seems more robust to me, especially at this level (element method of all Lie algebras).
Should different(ly named) Lie groups of the same Lie algebra have a predefined canonical coercion between each other? Or should these be considered separate objects? I'm not sure about the intended use of the
_name
parameter when the underlying manifold is the same. In particular, it is clear thatsage: NilpotentLieGroup(L, 'G') is NilpotentLieGroup(L, 'H') Falseis desired behavior.
Yes.
Would
sage: NilpotentLieGroup(L, 'G') == NilpotentLieGroup(L, 'H') Falsealso be desired behavior?
I would say yes as well (this would be in the same vein as what we have for manifolds), but I am not 100% sure this is the best option.
comment:26 in reply to: ↑ 24 Changed 4 years ago by
Replying to ghehaka:
Another question I have is where should the Lie group functionality go in the source and reference? Currently the source is under
sage.groups.lie_group.py
and the doc is not linked anywhere since I didn't know where to put it.
For the reference manual, there are naturally two options:
 a new subsection "Lie groups" of http://doc.sagemath.org/html/en/reference/groups/
 a new subsection "Lie groups" of http://doc.sagemath.org/html/en/reference/manifolds/
since Lie groups lie at the intersection of both topics. Depending on which one you choose, I would rename the source file lie_group.py
to nilpotent_lie_group.py
and place it in the new subdirectory src/sage/groups/lie_groups
or src/sage/manifolds/differentiable/lie_groups
.
comment:27 Changed 4 years ago by
I would probably put it in with the groups as I think that is there more prominent feature. Moreover, our canonical Lie groups are usually thought of as (matrix) groups, GL, O, U, etc.
comment:28 Changed 4 years ago by
 Commit changed from d3c77ac7b0569691b39e0c4e4f0b3ec4aefbaad4 to 383a19aea9a8f5d3541557a0f5b698e9338d3300
Branch pushed to git repo; I updated commit sha1. New commits:
383a19a  trac #26344: lie_gps subfolder and doc fixes

comment:29 Changed 4 years ago by
I moved the file to sage/groups/lie_gps/nilpotent_lie_group.py
. All the other subfolders in groups were *_gps
so I copied that folder convention.
I added the subsection of Lie groups to the group reference, but it is slightly disturbing to have a separate "Lie groups" subsection when there is the matrix groups heading containing GL and others. On the other hand, the abstractmanifold Lie group doesn't fit under the "Matrix and affine groups" heading, and extending it to something like "Matrix, affine, and Lie groups" seemed even worse. Hopefully this a problem that will solve itself once more Lie group functionality eventually gets added in so that the "Lie groups" heading feels more fleshed out.
comment:30 Changed 4 years ago by
I would argue that this is okay since the other matrix groups do not know they are Lie groups when working over topological fields and also work for a larger class of base fields (e.g., finite fields).
comment:31 Changed 4 years ago by
 Commit changed from 383a19aea9a8f5d3541557a0f5b698e9338d3300 to 790338ea26862e2800068d3a3d7fe35db5e9cfbc
Branch pushed to git repo; I updated commit sha1. New commits:
790338e  trac #26344: doc tweaks

comment:32 Changed 4 years ago by
I changed the docs of NilpotentLieGroup
to suggest using the L.lie_group('G')
syntax over NilpotentLieGroup(L, 'G')
. Having now played around with these for a bit, the former seemed to be the more convenient entry point.
Although now the NilpotentLieGroup
entry point is not really even relevant as it does not provide any extra functionality. Possibly it could/should be removed from the global namespace? The only benefit I see to keeping it at this moment is being able to use NilpotentLieGroup?
to see some usage examples.
comment:33 Changed 4 years ago by
 Commit changed from 790338ea26862e2800068d3a3d7fe35db5e9cfbc to e0d909f408fa28a35a351144fff2fee44dd2aa91
Branch pushed to git repo; I updated commit sha1. New commits:
e0d909f  New (sub)catalog of groups.lie, some small code improvements and doc tweaks.

comment:34 followup: ↓ 37 Changed 4 years ago by
 Reviewers set to Eric Gourgoulhon and Travis Scrimshaw
It probably should be removed, but it should be a new subcatalog groups.lie
. So I did this.
I removed the Group.__init__
as that was not doing anything useful.
I cached chart_exp2
and just called that instead of _Exp2
.
Other misc doc tweaks.
If my changes are good, then I think we can set a positive review unless Eric has some more comments.
comment:35 Changed 4 years ago by
 Commit changed from e0d909f408fa28a35a351144fff2fee44dd2aa91 to b2ef2d9896ca92c139c12781b7d92d2b56397bec
Branch pushed to git repo; I updated commit sha1. New commits:
b2ef2d9  trac #26344: avoid creation of exp2 chart in _repr_ + typo fix

comment:36 Changed 4 years ago by
Ah, I didn't know about the groups catalog, this is a very good solution, thanks! I fixed a typo in the catalog and removed the now empty file lie_gps/all.py
I modified the _repr_
method of Lie group elements to avoid unnecessarily calling chart_exp2
in case the default chart is _Exp1
, since the computation is quite expensive. Although the current form is not ideal either, since now if the default coordinate chart is something other than chart_exp2
or _Exp1
, then it will still unnecessarily compute the transitions to and from exp2 coordinates. Not sure how to handle this without the _Exp2=None
default.
Other than that, the changes look good to me.
comment:37 in reply to: ↑ 34 Changed 4 years ago by
Replying to tscrim:
If my changes are good, then I think we can set a positive review unless Eric has some more comments.
Everything looks good to me, so I agree with the positive review. Thanks for the nice work!
comment:38 Changed 4 years ago by
 Status changed from needs_review to positive_review
comment:39 Changed 4 years ago by
 Branch changed from public/groups/nilpotent_lie_groups26344 to b2ef2d9896ca92c139c12781b7d92d2b56397bec
 Resolution set to fixed
 Status changed from positive_review to closed
comment:40 Changed 4 years ago by
 Commit b2ef2d9896ca92c139c12781b7d92d2b56397bec deleted
 Reviewers changed from Eric Gourgoulhon and Travis Scrimshaw to Eric Gourgoulhon, Travis Scrimshaw
comment:41 Changed 4 years ago by
 Milestone changed from sage8.4 to sage8.5
This should be retargeted for 8.5.
A partial first version is now ready, containing the exponential map, exponential coordinates of the first and second kind and group multiplication. Vector fields and the adjoint map are still completely missing, but I thought it would be good to get a working copy up in case there are already some glaring flaws.
In particular, one thing that did not feel quite right was the current method of computing the symbolic group law. The only way I could think of was to extract the structural coefficients of the Lie algebra and to create a new Lie algebra over
SR
using the same structural coefficients. Two immediate issues of the current approach areSR
Another thing is how to handle the symbolic group law. Currently it is done by creating two dummyvariables, computing the group law as a vector expression in exponential coordinates (through the BCH formula) and then evaluating the group law by substitution of the dummyvariables.
This is still workinprogress, but I would be happy to hear comments or suggestions.
New commits:
trac #26080: initial implementation of the BCH formula
trac #26080: converted bch iterator to a generator function and added interface for nonnilpotent Lie algebras
trac #26080: efficiency improvements
trac #26080: replaced old stopiteration
trac #26344: minimal working example of nilpotent Lie groups as manifolds