Skip to content

Commit

Permalink
gh-37693: Implement the hypercenter and upper central series for fini…
Browse files Browse the repository at this point in the history
…te dimensional Lie algebras

    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes #12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes #12345". -->

We implement the upper central series and hypercenter of a finite
dimensional Lie algebra.

As part of the testing, we found improvements could be made to the
generic example (needed so the example would work with these tests): We
moved the `reduce()` method up to the category. Implemented methods
expected for the subalgebras. Better input verification. Elements are
now hashable. We had to work around the fact that `lift()` is serving
multiple roles as to the UEA and to the ambient space for a Lie
subalgebra. This functionality needs to be divided (I take full
responsibility for this bad design) by renaming `lift` to something for
the UEA map, but this is a significant change that should be done on a
followup PR.

We also fixed some bugs and improved efficiency with changing vectors
into Lie algebra elements.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - #12345: short description why this is a dependency -->
<!-- - #34567: ... -->
    
URL: #37693
Reported by: Travis Scrimshaw
Reviewer(s): Matthias Köppe, Travis Scrimshaw
  • Loading branch information
Release Manager committed May 1, 2024
2 parents f0a2850 + 4fa759a commit e4e3325
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 82 deletions.
27 changes: 22 additions & 5 deletions src/sage/algebras/lie_algebras/quotient.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,10 @@ def __classcall_private__(cls, I, ambient=None, names=None,
index_set = [i for i in sorted_indices if i not in I_supp]

if names is None:
amb_names = dict(zip(sorted_indices, ambient.variable_names()))
names = [amb_names[i] for i in index_set]
if ambient._names is not None:
# ambient has assigned variable names, so use those
amb_names = dict(zip(sorted_indices, ambient.variable_names()))
names = [amb_names[i] for i in index_set]
elif isinstance(names, str):
if len(index_set) == 1:
names = [names]
Expand Down Expand Up @@ -252,15 +254,15 @@ def __init__(self, I, L, names, index_set, category=None):
B = L.basis()
sm = L.module().submodule_with_basis([I.reduce(B[i]).to_vector()
for i in index_set])
SB = sm.basis()
SB = [L.from_vector(b) for b in sm.basis()]

# compute and normalize structural coefficients for the quotient
s_coeff = {}
for i, ind_i in enumerate(index_set):
for j in range(i + 1, len(index_set)):
ind_j = index_set[j]

brkt = I.reduce(L.bracket(SB[i], SB[j]))
brkt = I.reduce(SB[i].bracket(SB[j]))
brktvec = sm.coordinate_vector(brkt.to_vector())
s_coeff[(ind_i, ind_j)] = dict(zip(index_set, brktvec))
s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(
Expand Down Expand Up @@ -291,11 +293,26 @@ def _repr_(self):
L: General linear Lie algebra of rank 2 over Rational Field
I: Ideal ([0 0]
[0 1])
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: a,b,c = L.gens()
sage: I = L.ideal([a + 2*b, b + 3*c])
sage: Q = L.quotient(I)
sage: Q
Lie algebra quotient L/I of dimension 1 over Rational Field where
L: An example of a finite dimensional Lie algebra with basis:
the 3-dimensional abelian Lie algebra over Rational Field
I: Ideal ((1, 0, -6), (0, 1, 3))
"""
try:
ideal_repr = self._I._repr_short()
except AttributeError:
ideal_repr = repr(tuple(self._I.gens()))

return ("Lie algebra quotient L/I of dimension %s"
" over %s where\nL: %s\nI: Ideal %s" % (
self.dimension(), self.base_ring(),
self.ambient(), self._I._repr_short()))
self.ambient(), ideal_repr))

def _repr_generator(self, i):
r"""
Expand Down
65 changes: 0 additions & 65 deletions src/sage/algebras/lie_algebras/subalgebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,71 +888,6 @@ def is_ideal(self, A):
return True
return super().is_ideal(A)

def reduce(self, X):
r"""
Reduce an element of the ambient Lie algebra modulo the
ideal ``self``.
INPUT:
- ``X`` -- an element of the ambient Lie algebra
OUTPUT:
An element `Y` of the ambient Lie algebra that is contained in a fixed
complementary submodule `V` to ``self`` such that `X = Y` mod ``self``.
When the base ring of ``self`` is a field, the complementary submodule
`V` is spanned by the elements of the basis that are not the leading
supports of the basis of ``self``.
EXAMPLES:
An example reduction in a 6 dimensional Lie algebra::
sage: sc = {('a','b'): {'d': 1}, ('a','c'): {'e': 1},
....: ('b','c'): {'f': 1}}
sage: L.<a,b,c,d,e,f> = LieAlgebra(QQ, sc)
sage: I = L.ideal(c)
sage: I.reduce(a + b + c + d + e + f)
a + b + d
The reduction of an element is zero if and only if the
element belongs to the subalgebra::
sage: I.reduce(c + e)
0
sage: c + e in I
True
Over non-fields, the complementary submodule may not be spanned by
a subset of the basis of the ambient Lie algebra::
sage: L.<X,Y,Z> = LieAlgebra(ZZ, {('X','Y'): {'Z': 3}})
sage: I = L.ideal(Y)
sage: I.basis()
Family (Y, 3*Z)
sage: I.reduce(3*Z)
0
sage: I.reduce(Y + 14*Z)
2*Z
"""
R = self.base_ring()
for Y in self.basis():
Y = self.lift(Y)
k, c = Y.leading_item(key=self._order)

if R.is_field():
X = X - X[k] / c * Y
else:
try:
q, _ = X[k].quo_rem(c)
X = X - q * Y
except AttributeError:
pass

return X

class Element(LieSubalgebraElementWrapper):
def adjoint_matrix(self, sparse=False):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ def __init__(self, R, n=None, M=None, ambient=None):
self._ambient = ambient
Parent.__init__(self, base=R, category=cat)

from sage.categories.lie_algebras import LiftMorphism
self._lift_uea = LiftMorphism(self, self._construct_UEA())
self._lift_uea.register_as_coercion()

def _repr_(self):
"""
EXAMPLES::
Expand Down Expand Up @@ -135,6 +139,57 @@ def _element_constructor_(self, x):
x = x.value
return self.element_class(self, self._M(x))

def lift(self, x):
r"""
Return the lift of ``self``.
EXAMPLES::
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: a, b, c = L.gens()
sage: L.lift(a)
b0
sage: L.lift(b).parent() is L.universal_enveloping_algebra()
True
sage: I = L.ideal([a + 2*b, b + 3*c])
sage: I.lift(I.basis()[0])
(1, 0, -6)
"""
# FIXME: This method can likely be simplified or removed once we
# disentangle the UEA lift from the generic lift
A = self._ambient
if A is self:
return self._lift_uea(x)
return A.element_class(A, A._M(x.value))

def universal_enveloping_algebra(self):
r"""
Return the universal enveloping algebra of ``self``.
EXAMPLES::
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: L.universal_enveloping_algebra()
Noncommutative Multivariate Polynomial Ring in b0, b1, b2
over Rational Field, nc-relations: {}
"""
# FIXME: This method can likely be removed once we
# disentangle the UEA lift from the generic lift
return self._lift_uea.codomain()

def _order(self, x):
r"""
Return a key for sorting for the index ``x``.
TESTS::
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: L._order(2)
2
"""
return x

@cached_method
def zero(self):
"""
Expand Down Expand Up @@ -194,6 +249,8 @@ def subalgebra(self, gens):
[ 1 0 -1/2]
[ 0 1 1]
"""
if isinstance(gens, AbelianLieAlgebra):
gens = [self(g) for g in gens.gens()]
N = self._M.subspace([g.value for g in gens])
return AbelianLieAlgebra(self.base_ring(), M=N, ambient=self._ambient)

Expand Down Expand Up @@ -252,7 +309,7 @@ def gens(self) -> tuple:
sage: L.gens()
((1, 0, 0), (0, 1, 0), (0, 0, 1))
"""
return tuple(self._M.basis())
return tuple([self.element_class(self, b) for b in self._M.basis()])

def module(self):
"""
Expand Down Expand Up @@ -302,7 +359,36 @@ def from_vector(self, v, order=None):
"""
return self.element_class(self, self._M(v))

def leading_monomials(self):
r"""
Return the set of leading monomials of the basis of ``self``.
EXAMPLES::
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: a, b, c = L.lie_algebra_generators()
sage: I = L.ideal([2*a + b, b + c])
sage: I.leading_monomials()
((1, 0, 0), (0, 1, 0))
"""
# for free modules, the leading monomial is actually the trailing monomial
return tuple([self._ambient._M(b.value).trailing_monomial()
for b in self.basis()])

class Element(BaseExample.Element):
def __init__(self, parent, value):
"""
Initialize ``self``.
EXAMPLES::
sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
sage: a, b, c = L.lie_algebra_generators()
sage: TestSuite(a).run()
"""
value.set_immutable()
super().__init__(parent, value)

def __iter__(self):
"""
Iterate over ``self`` by returning pairs ``(i, c)`` where ``i``
Expand Down
14 changes: 14 additions & 0 deletions src/sage/categories/examples/lie_algebras.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ def __ne__(self, rhs):
"""
return not self.__eq__(rhs)

def __hash__(self):
r"""
Return the hash of ``self``.
EXAMPLES::
sage: # needs sage.combinat sage.groups
sage: L = LieAlgebras(QQ).example()
sage: x, y = L.lie_algebra_generators()
sage: hash(x) == hash(x.value)
True
"""
return hash(self.value)

def __bool__(self) -> bool:
"""
Check non-zero.
Expand Down

0 comments on commit e4e3325

Please sign in to comment.