Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 7699db4

Browse files
committed
Merge branch 'u/gh-ehaka/free_nilpotent_lie_algebras-26076' of git://trac.sagemath.org/sage into public/lie_algebras/free_nilpotent-26076
2 parents 120c02a + b70bebd commit 7699db4

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

src/sage/algebras/lie_algebras/lie_algebra.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,17 @@ class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators):
319319
sage: L.<X,Y,Z> = LieAlgebra(QQ, {('X','Y'): {'Z': 1}}, category=C); L
320320
Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field
321321
322+
Free nilpotent Lie algebras are the truncated versions of the free Lie
323+
algebras. That is, the only relations other than anticommutativity and the
324+
Jacobi identity among the Lie brackets are that brackets of length higher
325+
than the nilpotency step vanish. They can be created by using the
326+
``step`` keyword::
327+
328+
sage: L = LieAlgebra(ZZ, 2, step=3); L
329+
Free Nilpotent Lie algebra on 5 generators (X_1, X_2, X_12, X_112, X_122) over Integer Ring
330+
sage: L.step()
331+
3
332+
322333
REFERENCES:
323334
324335
- [deG2000]_ Willem A. de Graaf. *Lie Algebras: Theory and Algorithms*.
@@ -422,6 +433,13 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None,
422433
# Otherwise it must be either a free or abelian Lie algebra
423434

424435
if arg1 in ZZ:
436+
step = kwds.get("step", None)
437+
if step:
438+
# Parse input as a free nilpotent Lie algebra
439+
from sage.algebras.lie_algebras.nilpotent_lie_algebra import FreeNilpotentLieAlgebra
440+
del kwds["step"]
441+
return FreeNilpotentLieAlgebra(R, arg1, step, names=names, **kwds)
442+
425443
if isinstance(arg0, str):
426444
names = arg0
427445
if names is None:

src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,268 @@ def _repr_(self):
153153
Nilpotent Lie algebra on 4 generators (X, Y, Z, W) over Rational Field
154154
"""
155155
return "Nilpotent %s" % (super(NilpotentLieAlgebra_dense, self)._repr_())
156+
157+
class FreeNilpotentLieAlgebra(NilpotentLieAlgebra_dense):
158+
r"""
159+
Returns the free nilpotent Lie algebra of step ``s`` with ``r`` generators.
160+
161+
The free nilpotent Lie algebra `L` of step `s` with `r` generators is the
162+
quotient of the free Lie algebra on `r` generators by the `s+1`th term of
163+
the lower central series. That is, the only relations in the Lie algebra `L`
164+
are anticommutativity, the Jacobi identity, and the vanishing of all
165+
brackets of length more than `s`.
166+
167+
INPUT:
168+
169+
- ``R`` -- the base ring
170+
- ``r`` -- an integer; the number of generators
171+
- ``s`` -- an integer; the nilpotency step of the algebra
172+
- ``names`` -- (optional) a string or a list of strings used to name the
173+
basis elements; If ``names`` is a string, then names for the basis will be
174+
autogenerated as determined by the ``naming`` parameter.
175+
- ``naming`` -- (optional) a string; the naming scheme to use for the basis.
176+
Valid values are:
177+
'index' : The basis elements are ``names_w``, where `w` are Lyndon
178+
words indexing the basis.
179+
This naming scheme is not supported if `r > 10`, since it
180+
leads to ambiguous names. When `r \leq 10`, this is the
181+
default naming scheme.
182+
'linear' : the basis is indexed ``names_1``,...,``names_n`` in the
183+
ordering of the Lyndon basis. When `r > 10`, this is the
184+
default naming scheme.
185+
186+
EXAMPLES:
187+
188+
We compute the free step 4 Lie algebra on 2 generators and verify the only
189+
non-trivial relation [[1,[1,2]],2] = [1,[[1,2],2]]::
190+
191+
sage: L = LieAlgebra(QQ, 2, step=4)
192+
sage: L.basis().list()
193+
[X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222]
194+
sage: X_1, X_2 = L.basis().list()[:2]
195+
sage: L[[X_1, [X_1, X_2]], X_2]
196+
X_1122
197+
sage: L[[X_1, [X_1, X_2]], X_2] == L[X_1, [[X_1, X_2], X_2]]
198+
True
199+
200+
The linear naming scheme on the same Lie algebra::
201+
202+
sage: K = LieAlgebra(QQ, 2, step=4, names='Y', naming='linear')
203+
sage: K.basis().list()
204+
[Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8]
205+
sage: K.inject_variables()
206+
Defining Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8
207+
sage: Y_2.bracket(Y_3)
208+
-Y_5
209+
sage: Y_5.bracket(Y_1)
210+
-Y_7
211+
sage: Y_3.bracket(Y_4)
212+
0
213+
214+
A fully custom naming scheme on the Heisenberg algebra::
215+
216+
sage: L = LieAlgebra(ZZ, 2, step=2, names = ('X', 'Y', 'Z'))
217+
sage: a, b, c = L.basis()
218+
sage: L.basis().list()
219+
[X, Y, Z]
220+
sage: a.bracket(b)
221+
Z
222+
223+
An equivalent way to define custom names for the basis elements and
224+
bind them as local variables simultaneously::
225+
226+
sage: L.<X,Y,Z> = LieAlgebra(ZZ, 2, step=2)
227+
sage: L.basis().list()
228+
[X, Y, Z]
229+
sage: X.bracket(Y)
230+
Z
231+
232+
A free nilpotent Lie algebra is a stratified nilpotent Lie algebra::
233+
234+
sage: L = LieAlgebra(QQ, 3, step=3)
235+
sage: L.category()
236+
Category of finite dimensional stratified lie algebras with basis over Rational Field
237+
sage: L in LieAlgebras(QQ).Nilpotent()
238+
True
239+
240+
Being graded means that each basis element has a degree::
241+
242+
sage: L in LieAlgebras(QQ).Graded()
243+
True
244+
sage: L.homogeneous_component_basis(1).list()
245+
[X_1, X_2, X_3]
246+
sage: L.homogeneous_component_basis(2).list()
247+
[X_12, X_13, X_23]
248+
sage: L.homogeneous_component_basis(3).list()
249+
[X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233]
250+
251+
TESTS:
252+
253+
Verify all bracket relations in the free nilpotent Lie algebra of step 5
254+
with 2 generators::
255+
256+
sage: L = LieAlgebra(QQ, 2, step=5)
257+
sage: L.inject_variables()
258+
Defining X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222,
259+
X_11112, X_11122, X_11212, X_11222, X_12122, X_12222
260+
sage: [X_1.bracket(Xk) for Xk in L.basis()]
261+
[0, X_12, X_112, X_1112, X_1122,
262+
X_11112, X_11122, X_11222, 0, 0, 0, 0, 0, 0]
263+
sage: [X_2.bracket(Xk) for Xk in L.basis()]
264+
[-X_12, 0, -X_122, -X_1122, -X_1222,
265+
-X_11122 + X_11212, -X_11222 - X_12122, -X_12222, 0, 0, 0, 0, 0, 0]
266+
sage: [X_12.bracket(Xk) for Xk in L.basis()]
267+
[-X_112, X_122, 0, -X_11212, X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0]
268+
sage: [X_112.bracket(Xk) for Xk in L.basis()]
269+
[-X_1112, X_1122, X_11212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
270+
sage: [X_122.bracket(Xk) for Xk in L.basis()]
271+
[-X_1122, X_1222, -X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
272+
sage: [X_1112.bracket(Xk) for Xk in L.basis()]
273+
[-X_11112, X_11122 - X_11212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
274+
sage: [X_1122.bracket(Xk) for Xk in L.basis()]
275+
[-X_11122, X_11222 + X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
276+
sage: [X_1222.bracket(Xk) for Xk in L.basis()]
277+
[-X_11222, X_12222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
278+
sage: [X_11112.bracket(Xk) for Xk in L.basis()]
279+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
280+
sage: [X_11122.bracket(Xk) for Xk in L.basis()]
281+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
282+
sage: [X_11212.bracket(Xk) for Xk in L.basis()]
283+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
284+
sage: [X_11222.bracket(Xk) for Xk in L.basis()]
285+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
286+
sage: [X_12122.bracket(Xk) for Xk in L.basis()]
287+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
288+
sage: [X_12222.bracket(Xk) for Xk in L.basis()]
289+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
290+
291+
The dimensions of the smallest free nilpotent Lie algebras on 2 or 3
292+
generators::
293+
294+
sage: l = [LieAlgebra(QQ, 2, step=k) for k in range(1, 7)]
295+
sage: [L.dimension() for L in l]
296+
[2, 3, 5, 8, 14, 23]
297+
sage: l = [LieAlgebra(QQ, 3, step=k) for k in range(1, 4)]
298+
sage: [L.dimension() for L in l]
299+
[3, 6, 14]
300+
301+
Test suite for a free nilpotent Lie algebra::
302+
303+
sage: L = LieAlgebra(QQ, 4, step=3)
304+
sage: TestSuite(L).run()
305+
"""
306+
307+
def __init__(self, R, r, s, names=None, naming=None, category=None, **kwds):
308+
r"""
309+
Initialize ``self``
310+
311+
EXAMPLES::
312+
313+
sage: LieAlgebra(ZZ, 2, step=2)
314+
Free Nilpotent Lie algebra on 3 generators (X_1, X_2, X_12) over Integer Ring
315+
"""
316+
from sage.rings.integer_ring import ZZ
317+
318+
if r not in ZZ or r <= 0:
319+
raise ValueError("number of generators %s is not "
320+
"a positive integer" % r)
321+
if s not in ZZ or s <= 0:
322+
raise ValueError("step %s is not a positive integer" % s)
323+
324+
# extract an index set from the Lyndon words of the corresponding
325+
# free Lie algebra, and store the corresponding elements in a dict
326+
from sage.algebras.lie_algebras.lie_algebra import LieAlgebra
327+
328+
free_gen_names = ['F%d' % k for k in range(r)]
329+
L = LieAlgebra(R, free_gen_names).Lyndon()
330+
from collections import OrderedDict
331+
basis_dict = OrderedDict()
332+
for d in range(1, s + 1):
333+
for X in L.graded_basis(d):
334+
# convert brackets of form [X_1, [X_1, X_2]] to words (1,1,2)
335+
w = tuple(free_gen_names.index(s) + 1
336+
for s in X.leading_support().to_word())
337+
basis_dict[w] = X
338+
339+
# define names for basis elements
340+
if not names:
341+
names = 'X'
342+
if isinstance(names, str):
343+
names = tuple(names)
344+
if len(names) == 1 and len(basis_dict)>1:
345+
if not naming:
346+
if r > 10:
347+
naming = 'linear'
348+
else:
349+
naming = 'index'
350+
if naming == 'linear':
351+
names = ['%s_%d' % (names[0], k + 1)
352+
for k in range(len(basis_dict))]
353+
elif naming == 'index':
354+
if r > 10:
355+
raise ValueError("'index' naming scheme not supported for "
356+
"over 10 generators")
357+
names = ['%s_%s' % (names[0], "".join(str(s) for s in w))
358+
for w in basis_dict]
359+
else:
360+
raise ValueError("unknown naming scheme %s" % naming)
361+
362+
# extract structural coefficients from the free Lie algebra
363+
s_coeff = {}
364+
for k,X_ind in enumerate(basis_dict):
365+
X = basis_dict[X_ind]
366+
degX = len(X_ind)
367+
for Y_ind in basis_dict.keys()[k+1:]:
368+
# brackets are only computed when deg(X) + deg(Y) <= s
369+
degY = len(Y_ind)
370+
if degX + degY > s:
371+
continue
372+
373+
Y = basis_dict[Y_ind]
374+
Z = L[X, Y]
375+
if not Z.is_zero():
376+
s_coeff[(X_ind, Y_ind)] = {W: Z[basis_dict[W].leading_support()]
377+
for W in basis_dict}
378+
379+
from sage.structure.indexed_generators import standardize_names_index_set
380+
names, index_set = standardize_names_index_set(names, basis_dict.keys())
381+
s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(
382+
s_coeff, index_set)
383+
384+
category = LieAlgebras(R).FiniteDimensional().WithBasis() \
385+
.Graded().Stratified().or_subcategory(category)
386+
NilpotentLieAlgebra_dense.__init__(self, R, s_coeff, names,
387+
index_set, s,
388+
category=category, **kwds)
389+
390+
def _repr_generator(self, w):
391+
r"""
392+
Returns the string representation of the basis element
393+
indexed by the word ``w`` in ``self``.
394+
395+
EXAMPLES::
396+
397+
sage: L = LieAlgebra(QQ, 2, step=4)
398+
sage: L._repr_generator((1, 1, 2, 2))
399+
'X_1122'
400+
sage: L = LieAlgebra(QQ, 2, step=4, naming='linear')
401+
sage: L._repr_generator((1, 1, 2, 2))
402+
'X_7'
403+
sage: L.<X,Y,Z> = LieAlgebra(QQ, 2, step=2)
404+
sage: L._repr_generator((1, 2))
405+
'Z'
406+
"""
407+
i = self.indices().index(w)
408+
return self.variable_names()[i]
409+
410+
def _repr_(self):
411+
"""
412+
Return a string representation of ``self``.
413+
414+
EXAMPLES::
415+
416+
sage: L = LieAlgebra(QQ, 2, step=3)
417+
sage: L
418+
Free Nilpotent Lie algebra on 5 generators (X_1, X_2, X_12, X_112, X_122) over Rational Field
419+
"""
420+
return "Free %s" % (super(FreeNilpotentLieAlgebra, self)._repr_())

0 commit comments

Comments
 (0)