@@ -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