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

Commit

Permalink
Implementation of the construction of free nilpotent Lie algebras
Browse files Browse the repository at this point in the history
  • Loading branch information
ehaka committed Aug 17, 2018
1 parent 19c27eb commit f58cddd
Showing 1 changed file with 298 additions and 14 deletions.
312 changes: 298 additions & 14 deletions src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,26 +146,270 @@ def is_nilpotent(self):
"""
return True

def NilpotentLieAlgebra(R, arg, step=None, names=None,
free=False, category=None, **kwds):

class FreeNilpotentLieAlgebra(NilpotentLieAlgebra_dense):
r"""
Returns the free nilpotent Lie algebra of step ``s`` with ``r`` generators.
The free nilpotent Lie algebra `L` of step `s` with `r` generators is the
quotient of the free Lie algebra on `r` generators by the `s+1`th term of
the lower central series. That is, the only relations in the Lie algebra `L`
are anticommutativity, the Jacobi identity, and the vanishing of all
brackets of length more than `s`.
INPUT:
- ``R`` -- the base ring
- ``r`` -- an integer; the number of generators
- ``s`` -- an integer; the nilpotency step of the algebra
- ``naming`` -- a string (default: ``None``); the naming scheme to use for
the basis. Valid values are:
'index' : The basis elements are `X_w`, where `w` are Lyndon words.
This naming scheme is not supported if `r > 10`, since it
leads to ambiguous names. When `r \leq 10`, this is the
default naming scheme.
'linear' : the basis is indexed `X_1`,...,`X_n` in the ordering of the
Lyndon basis. When `r > 10`, this is the
default naming scheme.
EXAMPLES:
We compute the free step 4 Lie algebra on 2 generators and verify the only
non-trivial relation [[1,[1,2]],2] = [1,[[1,2],2]]::
sage: from sage.algebras.lie_algebras.nilpotent_lie_algebra import FreeNilpotentLieAlgebra
sage: L = FreeNilpotentLieAlgebra(QQ, 2, 4)
sage: L.basis().list()
[X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222]
sage: X_1, X_2 = L.basis().list()[:2]
sage: L[[X_1, [X_1, X_2]], X_2]
X_1122
sage: L[[X_1, [X_1, X_2]], X_2] == L[X_1, [[X_1, X_2], X_2]]
True
The linear naming scheme on the same Lie algebra::
sage: K = FreeNilpotentLieAlgebra(QQ, 2, 4, naming='linear')
sage: K.basis().list()
[X_1, X_2, X_3, X_4, X_5, X_6, X_7, X_8]
sage: K.inject_variables()
Defining X_1, X_2, X_3, X_4, X_5, X_6, X_7, X_8
sage: X_2.bracket(X_3)
-X_5
sage: X_5.bracket(X_1)
-X_7
sage: X_3.bracket(X_4)
0
A custom naming scheme on the Heisenberg algebra::
sage: L = FreeNilpotentLieAlgebra(ZZ, 2, 2, names = ('X', 'Y', 'Z'))
sage: a, b, c = L.basis()
sage: L.basis().list()
[X, Y, Z]
sage: a.bracket(b)
Z
An equivalent way to define custom names for the basis elements and
bind them as local variables simultaneously::
sage: L.<X,Y,Z> = FreeNilpotentLieAlgebra(ZZ, 2, 2)
sage: L.basis().list()
[X, Y, Z]
sage: X.bracket(Y)
Z
A free nilpotent Lie algebra is a stratified nilpotent Lie algebra::
sage: L = FreeNilpotentLieAlgebra(QQ, 3, 3)
sage: L.category()
Category of stratified finite dimensional lie algebras with basis over Rational Field
sage: L in LieAlgebras(QQ).Nilpotent()
True
Being graded means that each basis element has a degree::
sage: L in LieAlgebras.FiniteDimensional.WithBasis.Graded
True
sage: L.homogeneous_component_basis(1).list()
[X_1, X_2, X_3]
sage: L.homogeneous_component_basis(2).list()
[X_12, X_13, X_23]
sage: L.homogeneous_component_basis(3).list()
[X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233]
TESTS:
Verify all bracket relations in the free nilpotent Lie algebra step 5
with 2 generators::
sage: L = FreeNilpotentLieAlgebra(QQ, 2, 5)
sage: L.inject_variables()
Defining X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222,
X_11112, X_11122, X_11212, X_11222, X_12122, X_12222
sage: [X_1.bracket(Xk) for Xk in L.basis()]
[0, X_12, X_112, X_1112, X_1122,
X_11112, X_11122, X_11222, 0, 0, 0, 0, 0, 0]
sage: [X_2.bracket(Xk) for Xk in L.basis()]
[-X_12, 0, -X_122, -X_1122, -X_1222,
-X_11122 + X_11212, -X_11222 - X_12122, -X_12222, 0, 0, 0, 0, 0, 0]
sage: [X_12.bracket(Xk) for Xk in L.basis()]
[-X_112, X_122, 0, -X_11212, X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_112.bracket(Xk) for Xk in L.basis()]
[-X_1112, X_1122, X_11212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_122.bracket(Xk) for Xk in L.basis()]
[-X_1122, X_1222, -X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_1112.bracket(Xk) for Xk in L.basis()]
[-X_11112, X_11122 - X_11212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_1122.bracket(Xk) for Xk in L.basis()]
[-X_11122, X_11222 + X_12122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_1222.bracket(Xk) for Xk in L.basis()]
[-X_11222, X_12222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_11112.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_11122.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_11212.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_11222.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_12122.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: [X_12222.bracket(Xk) for Xk in L.basis()]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
The dimensions of the smallest free nilpotent Lie algebras on 2 or 3
generators::
sage: l = [FreeNilpotentLieAlgebra(QQ, 2, k) for k in range(1, 7)]
sage: [L.dimension() for L in l]
[2, 3, 5, 8, 14, 23]
sage: l = [FreeNilpotentLieAlgebra(QQ, 3, k) for k in range(1, 4)]
sage: [L.dimension() for L in l]
[3, 6, 14]
Test suite for a free nilpotent Lie algebra::
sage: L = FreeNilpotentLieAlgebra(QQ, 4, 3)
sage: TestSuite(L).run()
"""

def __init__(self, R, r, s, naming=None, names=None, category=None, **kwds):
r"""
Initialize ``self``
EXAMPLES::
sage: from sage.algebras.lie_algebras.nilpotent_lie_algebra import FreeNilpotentLieAlgebra
sage: FreeNilpotentLieAlgebra(ZZ, 2, 2, naming='linear')
Nilpotent Lie algebra on 3 generators (X_1, X_2, X_3) over Integer Ring
"""
from sage.rings.integer_ring import ZZ

if r not in ZZ or r <= 0:
raise ValueError("number of generators %s is not "
"a positive integer" % r)
if s not in ZZ or s <= 0:
raise ValueError("step %s is not a positive integer" % s)

# extract an index set from the Lyndon words of the corresponding
# free Lie algebra, and store the corresponding elements in a dict
from sage.algebras.lie_algebras.lie_algebra import LieAlgebra

free_gen_names = ['F%d' % k for k in range(r)]
L = LieAlgebra(R, free_gen_names).Lyndon()
from collections import OrderedDict
basis_dict = OrderedDict()
for d in range(1, s + 1):
for X in L.graded_basis(d):
# convert brackets of form [X_1, [X_1, X_2]] to words (1,1,2)
w = tuple(free_gen_names.index(s) + 1
for s in X.leading_support().to_word())
basis_dict[w] = X

# define names for basis elements
if names == None:
if not naming:
if r > 10:
naming = 'linear'
else:
naming = 'index'
if naming == 'linear':
names = ['X_%d' % (k + 1) for k in range(len(basis_dict))]
elif naming == 'index':
if r > 10:
raise ValueError("'index' naming scheme not supported for "
"over 10 generators")
names = ['X_%s' % "".join(str(s) for s in w)
for w in basis_dict]
else:
raise ValueError("unknown naming scheme %s" % naming)

# extract structural coefficients from the free Lie algebra
s_coeff = {}
for k,X_ind in enumerate(basis_dict):
X = basis_dict[X_ind]
degX = len(X_ind)
for Y_ind in basis_dict.keys()[k+1:]:
# brackets are only computed when deg(X) + deg(Y) <= s
degY = len(Y_ind)
if degX + degY > s:
continue

Y = basis_dict[Y_ind]
Z = L[X, Y]
if not Z.is_zero():
s_coeff[(X_ind, Y_ind)] = {W: Z[basis_dict[W].leading_support()]
for W in basis_dict}

from sage.structure.indexed_generators import standardize_names_index_set
names, index_set = standardize_names_index_set(names, basis_dict.keys())
s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(
s_coeff, index_set)

category = LieAlgebras(R).FiniteDimensional().WithBasis() \
.Stratified().or_subcategory(category)
NilpotentLieAlgebra_dense.__init__(self, R, s_coeff, names,
index_set, s,
category, **kwds)

def _repr_generator(self, w):
r"""
Returns the string representation of the basis element
indexed by the word ``w`` in ``self``.
EXAMPLES::
sage: from sage.algebras.lie_algebras.nilpotent_lie_algebra import FreeNilpotentLieAlgebra
sage: L = FreeNilpotentLieAlgebra(QQ, 2, 4)
sage: L._repr_generator((1, 1, 2, 2))
'X_1122'
sage: L = FreeNilpotentLieAlgebra(QQ, 2, 4, naming='linear')
sage: L._repr_generator((1, 1, 2, 2))
'X_7'
sage: L.<X,Y,Z> = FreeNilpotentLieAlgebra(QQ, 2, 2)
sage: L._repr_generator((1, 2))
'Z'
"""
i = self.indices().index(w)
return self.variable_names()[i]

def NilpotentLieAlgebra(R, arg, step=None, names=None, category=None, **kwds):
r"""
Constructs a nilpotent Lie algebra.
INPUT:
- ``R`` -- the base ring
- ``arg`` -- If the argument ``free`` is ``False``, then ``arg`` should be
a dictionary of structural coefficients.
If ``free`` is ``True``, then ``arg`` should be the number of generators
of the free nilpotent Lie algebra.
- ``arg`` -- a dictionary of structural coefficients, or an integer.
An integer is interpreted as the number of generators of a free nilpotent
Lie algebra.
- ``step`` -- (default:``None``) an integer; the nilpotency step of the
Lie algebra. If ``None``, the nilpotency step is undefined and will need
to be computed later. The ``step`` must be specified if ``free`` is
``True``.
Lie algebra. If ``None``, the nilpotency step is undefined and will be
computed later. The ``step`` must be specified if ``arg`` is an integer.
- ``names`` -- (default:``None``) a list of strings to use as names of basis
elements. If ``None`` and ``free`` is ``False``, the names will be
extracted from the structural coefficients. If ``None`` and ``free`` is
``True``, the names will be autogenerated.
elements. If ``None``, the names will be autogenerated from the structural
coefficients or the basis of the free nilpotent Lie algebra.
- ``category`` -- (default:``None``) a subcategory of
:class:`~sage.categories.lie_algebras.LieAlgebras.FiniteDimensional.WithBasis.Nilpotent`
Expand All @@ -190,9 +434,49 @@ def NilpotentLieAlgebra(R, arg, step=None, names=None,
W
sage: E[X, [X, [X, Y + Z]]]
0
Free nilpotent Lie algebras are created by passing an integer instead
of the structural coefficients. In this case the nilpotency step must
be specified::
sage: NilpotentLieAlgebra(QQ, 2)
Traceback (most recent call last):
...
ValueError: step None is not a positive integer
For example the Heisenberg algebra is also the free nilpotent Lie
algebra of step 2 on 2 generators::
sage: L = NilpotentLieAlgebra(QQ, 2, 2); L
Nilpotent Lie algebra on 3 generators (X_1, X_2, X_12) over Rational Field
When the names of elements are unspecified, the default naming scheme is
to label the variables `X_w`, where `w` is the Lyndon word related to
the construction. Passing the parameter ``naming='linear'`` changes
the default naming scheme::
sage: L = NilpotentLieAlgebra(QQ, 2, 2, naming='linear'); L
Nilpotent Lie algebra on 3 generators (X_1, X_2, X_3) over Rational Field
A completely custom naming is also possible::
sage: L.<X,Y,Z> = NilpotentLieAlgebra(QQ, 2, 2); L
Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field
A higher dimensional example over a different base ring: the free
nilpotent Lie algebra of step 5 on 2 generators over a polynomial ring::
sage: NilpotentLieAlgebra(QQ['x'], 2, 5)
Nilpotent Lie algebra on 14 generators (X_1, X_2, X_12, X_112,
X_122, X_1112, X_1122, X_1222, X_11112, X_11122, X_11212, X_11222,
X_12122, X_12222) over Univariate Polynomial Ring in x over Rational Field
..SEEALSO: :class:`FreeNilpotentLieAlgebra`
"""
if free:
raise NotImplementedError("free nilpotent Lie algebras not yet implemented")
from sage.rings.integer_ring import ZZ
if arg in ZZ:
return FreeNilpotentLieAlgebra(R, arg, step, names=names,
category=category, **kwds)

return NilpotentLieAlgebra_dense(R, arg, names, step=step,
category=category, **kwds)

0 comments on commit f58cddd

Please sign in to comment.