class JordanAlgebraSymmetricBilinear(JordanAlgebra): r""" A Jordan algebra given by a symmetric bilinear form `m`. """ def __init__(self, R, form, names=None): """ Initialize ``self``. TESTS:: sage: m = matrix([[-2,3],[3,4]]) sage: J = JordanAlgebra(m) sage: TestSuite(J).run() """ self._form = form self._M = FreeModule(R, form.ncols()) cat = MagmaticAlgebras( R).Commutative().Unital().FiniteDimensional().WithBasis() self._no_generic_basering_coercion = True # Remove once 16492 is fixed Parent.__init__(self, base=R, names=names, category=cat) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: m = matrix([[-2,3],[3,4]]) sage: JordanAlgebra(m) Jordan algebra over Integer Ring given by the symmetric bilinear form: [-2 3] [ 3 4] """ return "Jordan algebra over {} given by the symmetric bilinear" \ " form:\n{}".format(self.base_ring(), self._form) def _element_constructor_(self, *args): """ Construct an element of ``self`` from ``s``. Here ``s`` can be a pair of an element of `R` and an element of `M`, or an element of `R`, or an element of `M`, or an element of a(nother) Jordan algebra given by a symmetric bilinear form. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J(2) 2 + (0, 0) sage: J((-4, (2, 5))) -4 + (2, 5) sage: J((-4, (ZZ^2)((2, 5)))) -4 + (2, 5) sage: J(2, (-2, 3)) 2 + (-2, 3) sage: J(2, (ZZ^2)((-2, 3))) 2 + (-2, 3) sage: J(-1, 1, 0) -1 + (1, 0) sage: J((ZZ^2)((1, 3))) 0 + (1, 3) sage: m = matrix([[2]]) sage: J = JordanAlgebra(m) sage: J(2) 2 + (0) sage: J((-4, (2,))) -4 + (2) sage: J(2, (-2,)) 2 + (-2) sage: J(-1, 1) -1 + (1) sage: J((ZZ^1)((3,))) 0 + (3) sage: m = Matrix(QQ, []) sage: J = JordanAlgebra(m) sage: J(2) 2 + () sage: J((-4, ())) -4 + () sage: J(2, ()) 2 + () sage: J(-1) -1 + () sage: J((ZZ^0)(())) 0 + () """ R = self.base_ring() if len(args) == 1: s = args[0] if isinstance(s, JordanAlgebraSymmetricBilinear.Element): if s.parent() is self: return s return self.element_class(self, R(s._s), self._M(s._v)) if isinstance(s, (list, tuple)): if len(s) != 2: raise ValueError("must be length 2") return self.element_class(self, R(s[0]), self._M(s[1])) if s in self._M: return self.element_class(self, R.zero(), self._M(s)) return self.element_class(self, R(s), self._M.zero()) if len(args) == 2 and (isinstance(args[1], (list, tuple)) or args[1] in self._M): return self.element_class(self, R(args[0]), self._M(args[1])) if len(args) == self._form.ncols() + 1: return self.element_class(self, R(args[0]), self._M(args[1:])) raise ValueError("unable to construct an element from the given data") @cached_method def basis(self): """ Return a basis of ``self``. The basis returned begins with the unity of `R` and continues with the standard basis of `M`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.basis() Family (1 + (0, 0), 0 + (1, 0), 0 + (0, 1)) """ R = self.base_ring() ret = (self.element_class(self, R.one(), self._M.zero()), ) ret += tuple( self.element_class(self, R.zero(), x) for x in self._M.basis()) return Family(ret) algebra_generators = basis def gens(self): """ Return the generators of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.basis() Family (1 + (0, 0), 0 + (1, 0), 0 + (0, 1)) """ return tuple(self.algebra_generators()) @cached_method def zero(self): """ Return the element 0. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.zero() 0 + (0, 0) """ return self.element_class(self, self.base_ring().zero(), self._M.zero()) @cached_method def one(self): """ Return the element 1 if it exists. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.one() 1 + (0, 0) """ return self.element_class(self, self.base_ring().one(), self._M.zero()) class Element(AlgebraElement): """ An element of a Jordan algebra defined by a symmetric bilinear form. """ def __init__(self, parent, s, v): """ Initialize ``self``. TESTS:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: TestSuite(a + 2*b - c).run() """ self._s = s self._v = v AlgebraElement.__init__(self, parent) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a + 2*b - c 1 + (2, -1) """ return "{} + {}".format(self._s, self._v) def _latex_(self): r""" Return a latex representation of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: latex(a + 2*b - c) 1 + \left(2,\,-1\right) """ from sage.misc.latex import latex return "{} + {}".format(latex(self._s), latex(self._v)) def __bool__(self): """ Return if ``self`` is non-zero. TESTS:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: bool(1) True sage: bool(b) True sage: bool(a + 2*b - c) True """ return bool(self._s) or bool(self._v) __nonzero__ = __bool__ def __eq__(self, other): """ Check equality. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x == J((4, (-1, 3))) True sage: a == x False sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 4*a - b + 3*c == x False """ if not isinstance(other, JordanAlgebraSymmetricBilinear.Element): return False if other.parent() != self.parent(): return False return self._s == other._s and self._v == other._v def __ne__(self, other): """ Check inequality. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x != J((4, (-1, 3))) False sage: a != x True sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 4*a - b + 3*c != x True """ return not self == other def _add_(self, other): """ Add ``self`` and ``other``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a + b 1 + (1, 0) sage: b + c 0 + (1, 1) """ return self.__class__(self.parent(), self._s + other._s, self._v + other._v) def _neg_(self): """ Negate ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: -(a + b - 2*c) -1 + (-1, 2) """ return self.__class__(self.parent(), -self._s, -self._v) def _sub_(self, other): """ Subtract ``other`` from ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a - b 1 + (-1, 0) sage: b - c 0 + (1, -1) """ return self.__class__(self.parent(), self._s - other._s, self._v - other._v) def _mul_(self, other): """ Multiply ``self`` and ``other``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (4*a - b + 3*c)*(2*a + 2*b - c) 12 + (6, 2) sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (4*a - b + 3*c)*(2*a + 2*b - c) 21 + (6, 2) """ P = self.parent() return self.__class__( P, self._s * other._s + (self._v * P._form * other._v.column())[0], other._s * self._v + self._s * other._v) def _lmul_(self, other): """ Multiply ``self`` by the scalar ``other`` on the left. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (a + b - c) * 2 2 + (2, -2) """ return self.__class__(self.parent(), self._s * other, self._v * other) def _rmul_(self, other): """ Multiply ``self`` with the scalar ``other`` by the right action. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 2 * (a + b - c) 2 + (2, -2) """ return self.__class__(self.parent(), other * self._s, other * self._v) def monomial_coefficients(self, copy=True): """ Return a dictionary whose keys are indices of basis elements in the support of ``self`` and whose values are the corresponding coefficients. INPUT: - ``copy`` -- ignored EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: elt = a + 2*b - c sage: elt.monomial_coefficients() {0: 1, 1: 2, 2: -1} """ d = {0: self._s} for i, c in enumerate(self._v): d[i + 1] = c return d def trace(self): r""" Return the trace of ``self``. The trace of an element `\alpha + x \in M^*` is given by `t(\alpha + x) = 2 \alpha`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x.trace() 8 """ return 2 * self._s def norm(self): r""" Return the norm of ``self``. The norm of an element `\alpha + x \in M^*` is given by `n(\alpha + x) = \alpha^2 - (x, x)`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c; x 4 + (-1, 3) sage: x.norm() 13 """ return self._s * self._s - (self._v * self.parent()._form * self._v.column())[0] def bar(self): r""" Return the result of the bar involution of ``self``. The bar involution `\bar{\cdot}` is the `R`-linear endomorphism of `M^*` defined by `\bar{1} = 1` and `\bar{x} = -x` for `x \in M`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x.bar() 4 + (1, -3) We check that it is an algebra morphism:: sage: y = 2*a + 2*b - c sage: x.bar() * y.bar() == (x*y).bar() True """ return self.__class__(self.parent(), self._s, -self._v)
class JordanAlgebraSymmetricBilinear(JordanAlgebra): r""" A Jordan algebra given by a symmetric bilinear form `m`. """ def __init__(self, R, form, names=None): """ Initialize ``self``. TESTS:: sage: m = matrix([[-2,3],[3,4]]) sage: J = JordanAlgebra(m) sage: TestSuite(J).run() """ self._form = form self._M = FreeModule(R, form.ncols()) cat = MagmaticAlgebras(R).Commutative().Unital().FiniteDimensional().WithBasis() self._no_generic_basering_coercion = True # Remove once 16492 is fixed Parent.__init__(self, base=R, names=names, category=cat) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: m = matrix([[-2,3],[3,4]]) sage: JordanAlgebra(m) Jordan algebra over Integer Ring given by the symmetric bilinear form: [-2 3] [ 3 4] """ return "Jordan algebra over {} given by the symmetric bilinear" \ " form:\n{}".format(self.base_ring(), self._form) def _element_constructor_(self, *args): """ Construct an element of ``self`` from ``s``. Here ``s`` can be a pair of an element of `R` and an element of `M`, or an element of `R`, or an element of `M`, or an element of a(nother) Jordan algebra given by a symmetric bilinear form. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J(2) 2 + (0, 0) sage: J((-4, (2, 5))) -4 + (2, 5) sage: J((-4, (ZZ^2)((2, 5)))) -4 + (2, 5) sage: J(2, (-2, 3)) 2 + (-2, 3) sage: J(2, (ZZ^2)((-2, 3))) 2 + (-2, 3) sage: J(-1, 1, 0) -1 + (1, 0) sage: J((ZZ^2)((1, 3))) 0 + (1, 3) sage: m = matrix([[2]]) sage: J = JordanAlgebra(m) sage: J(2) 2 + (0) sage: J((-4, (2,))) -4 + (2) sage: J(2, (-2,)) 2 + (-2) sage: J(-1, 1) -1 + (1) sage: J((ZZ^1)((3,))) 0 + (3) sage: m = Matrix(QQ, []) sage: J = JordanAlgebra(m) sage: J(2) 2 + () sage: J((-4, ())) -4 + () sage: J(2, ()) 2 + () sage: J(-1) -1 + () sage: J((ZZ^0)(())) 0 + () """ R = self.base_ring() if len(args) == 1: s = args[0] if isinstance(s, JordanAlgebraSymmetricBilinear.Element): if s.parent() is self: return s return self.element_class(self, R(s._s), self._M(s._v)) if isinstance(s, (list, tuple)): if len(s) != 2: raise ValueError("must be length 2") return self.element_class(self, R(s[0]), self._M(s[1])) if s in self._M: return self.element_class(self, R.zero(), self._M(s)) return self.element_class(self, R(s), self._M.zero()) if len(args) == 2 and (isinstance(args[1], (list, tuple)) or args[1] in self._M): return self.element_class(self, R(args[0]), self._M(args[1])) if len(args) == self._form.ncols() + 1: return self.element_class(self, R(args[0]), self._M(args[1:])) raise ValueError("unable to construct an element from the given data") @cached_method def basis(self): """ Return a basis of ``self``. The basis returned begins with the unity of `R` and continues with the standard basis of `M`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.basis() Family (1 + (0, 0), 0 + (1, 0), 0 + (0, 1)) """ R = self.base_ring() ret = (self.element_class(self, R.one(), self._M.zero()),) ret += tuple(self.element_class(self, R.zero(), x) for x in self._M.basis()) return Family(ret) algebra_generators = basis def gens(self): """ Return the generators of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.basis() Family (1 + (0, 0), 0 + (1, 0), 0 + (0, 1)) """ return tuple(self.algebra_generators()) @cached_method def zero(self): """ Return the element 0. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.zero() 0 + (0, 0) """ return self.element_class(self, self.base_ring().zero(), self._M.zero()) @cached_method def one(self): """ Return the element 1 if it exists. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.one() 1 + (0, 0) """ return self.element_class(self, self.base_ring().one(), self._M.zero()) class Element(AlgebraElement): """ An element of a Jordan algebra defined by a symmetric bilinear form. """ def __init__(self, parent, s, v): """ Initialize ``self``. TESTS:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: TestSuite(a + 2*b - c).run() """ self._s = s self._v = v AlgebraElement.__init__(self, parent) def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a + 2*b - c 1 + (2, -1) """ return "{} + {}".format(self._s, self._v) def _latex_(self): r""" Return a latex representation of ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: latex(a + 2*b - c) 1 + \left(2,\,-1\right) """ from sage.misc.latex import latex return "{} + {}".format(latex(self._s), latex(self._v)) def __bool__(self): """ Return if ``self`` is non-zero. TESTS:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: bool(1) True sage: bool(b) True sage: bool(a + 2*b - c) True """ return bool(self._s) or bool(self._v) __nonzero__ = __bool__ def __eq__(self, other): """ Check equality. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x == J((4, (-1, 3))) True sage: a == x False sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 4*a - b + 3*c == x False """ if not isinstance(other, JordanAlgebraSymmetricBilinear.Element): return False if other.parent() != self.parent(): return False return self._s == other._s and self._v == other._v def __ne__(self, other): """ Check inequality. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x != J((4, (-1, 3))) False sage: a != x True sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 4*a - b + 3*c != x True """ return not self == other def _add_(self, other): """ Add ``self`` and ``other``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a + b 1 + (1, 0) sage: b + c 0 + (1, 1) """ return self.__class__(self.parent(), self._s + other._s, self._v + other._v) def _neg_(self): """ Negate ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: -(a + b - 2*c) -1 + (-1, 2) """ return self.__class__(self.parent(), -self._s, -self._v) def _sub_(self, other): """ Subtract ``other`` from ``self``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: a - b 1 + (-1, 0) sage: b - c 0 + (1, -1) """ return self.__class__(self.parent(), self._s - other._s, self._v - other._v) def _mul_(self, other): """ Multiply ``self`` and ``other``. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (4*a - b + 3*c)*(2*a + 2*b - c) 12 + (6, 2) sage: m = matrix([[-2,3],[3,4]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (4*a - b + 3*c)*(2*a + 2*b - c) 21 + (6, 2) """ P = self.parent() return self.__class__(P, self._s * other._s + (self._v * P._form * other._v.column())[0], other._s * self._v + self._s * other._v) def _lmul_(self, other): """ Multiply ``self`` by the scalar ``other`` on the left. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: (a + b - c) * 2 2 + (2, -2) """ return self.__class__(self.parent(), self._s * other, self._v * other) def _rmul_(self, other): """ Multiply ``self`` with the scalar ``other`` by the right action. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: 2 * (a + b - c) 2 + (2, -2) """ return self.__class__(self.parent(), other * self._s, other * self._v) def monomial_coefficients(self, copy=True): """ Return a dictionary whose keys are indices of basis elements in the support of ``self`` and whose values are the corresponding coefficients. INPUT: - ``copy`` -- ignored EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: elt = a + 2*b - c sage: elt.monomial_coefficients() {0: 1, 1: 2, 2: -1} """ d = {0: self._s} for i,c in enumerate(self._v): d[i+1] = c return d def trace(self): r""" Return the trace of ``self``. The trace of an element `\alpha + x \in M^*` is given by `t(\alpha + x) = 2 \alpha`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x.trace() 8 """ return 2 * self._s def norm(self): r""" Return the norm of ``self``. The norm of an element `\alpha + x \in M^*` is given by `n(\alpha + x) = \alpha^2 - (x, x)`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c; x 4 + (-1, 3) sage: x.norm() 13 """ return self._s * self._s - (self._v * self.parent()._form * self._v.column())[0] def bar(self): r""" Return the result of the bar involution of ``self``. The bar involution `\bar{\cdot}` is the `R`-linear endomorphism of `M^*` defined by `\bar{1} = 1` and `\bar{x} = -x` for `x \in M`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J.<a,b,c> = JordanAlgebra(m) sage: x = 4*a - b + 3*c sage: x.bar() 4 + (1, -3) We check that it is an algebra morphism:: sage: y = 2*a + 2*b - c sage: x.bar() * y.bar() == (x*y).bar() True """ return self.__class__(self.parent(), self._s, -self._v)
class LieAlgebraWithStructureCoefficients(FinitelyGeneratedLieAlgebra, IndexedGenerators): r""" A Lie algebra with a set of specified structure coefficients. The structure coefficients are specified as a dictionary `d` whose keys are pairs of basis indices, and whose values are dictionaries which in turn are indexed by basis indices. The value of `d` at a pair `(u, v)` of basis indices is the dictionary whose `w`-th entry (for `w` a basis index) is the coefficient of `b_w` in the Lie bracket `[b_u, b_v]` (where `b_x` means the basis element with index `x`). INPUT: - ``R`` -- a ring, to be used as the base ring - ``s_coeff`` -- a dictionary, indexed by pairs of basis indices (see below), and whose values are dictionaries which are indexed by (single) basis indices and whose values are elements of `R` - ``names`` -- list or tuple of strings - ``index_set`` -- (default: ``names``) list or tuple of hashable and comparable elements OUTPUT: A Lie algebra over ``R`` which (as an `R`-module) is free with a basis indexed by the elements of ``index_set``. The `i`-th basis element is displayed using the name ``names[i]``. If we let `b_i` denote this `i`-th basis element, then the Lie bracket is given by the requirement that the `b_k`-coefficient of `[b_i, b_j]` is ``s_coeff[(i, j)][k]`` if ``s_coeff[(i, j)]`` exists, otherwise ``-s_coeff[(j, i)][k]`` if ``s_coeff[(j, i)]`` exists, otherwise `0`. EXAMPLES: We create the Lie algebra of `\QQ^3` under the Lie bracket defined by `\times` (cross-product):: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}) sage: (x,y,z) = L.gens() sage: L.bracket(x, y) z sage: L.bracket(y, x) -z TESTS: We can input structure coefficients that fail the Jacobi identity, but the test suite will call us out on it:: sage: Fake = LieAlgebra(QQ, 'x,y,z', {('x','y'):{'z':3}, ('y','z'):{'z':1}, ('z','x'):{'y':1}}) sage: TestSuite(Fake).run() Failure in _test_jacobi_identity: ... Old tests !!!!!placeholder for now!!!!!:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) sage: L.basis() Finite family {'y': y, 'x': x} """ @staticmethod def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, **kwds): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: L1 = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) sage: L2 = LieAlgebra(QQ, 'x,y', {('y','x'): {'x':-1}}) sage: L1 is L2 True Check that we convert names to the indexing set:: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}, index_set=list(range(3))) sage: (x,y,z) = L.gens() sage: L[x,y] L[2] """ names, index_set = standardize_names_index_set(names, index_set) # Make sure the structure coefficients are given by the index set if names is not None and names != tuple(index_set): d = {x: index_set[i] for i,x in enumerate(names)} get_pairs = lambda X: X.items() if isinstance(X, dict) else X try: s_coeff = {(d[k[0]], d[k[1]]): [(d[x], y) for x,y in get_pairs(s_coeff[k])] for k in s_coeff} except KeyError: # At this point we assume they are given by the index set pass s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(s_coeff, index_set) if s_coeff.cardinality() == 0: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set, **kwds) if (names is None and len(index_set) <= 1) or len(names) <= 1: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set, **kwds) return super(LieAlgebraWithStructureCoefficients, cls).__classcall__( cls, R, s_coeff, names, index_set, **kwds) @staticmethod def _standardize_s_coeff(s_coeff, index_set): """ Helper function to standardize ``s_coeff`` into the appropriate form (dictionary indexed by pairs, whose values are dictionaries). Strips items with coefficients of 0 and duplicate entries. This does not check the Jacobi relation (nor antisymmetry if the cardinality is infinite). EXAMPLES:: sage: from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients sage: d = {('y','x'): {'x':-1}} sage: LieAlgebraWithStructureCoefficients._standardize_s_coeff(d, ('x', 'y')) Finite family {('x', 'y'): (('x', 1),)} """ # Try to handle infinite basis (once/if supported) #if isinstance(s_coeff, AbstractFamily) and s_coeff.cardinality() == infinity: # return s_coeff index_to_pos = {k: i for i,k in enumerate(index_set)} sc = {} # Make sure the first gen is smaller than the second in each key for k in s_coeff.keys(): v = s_coeff[k] if isinstance(v, dict): v = v.items() if index_to_pos[k[0]] > index_to_pos[k[1]]: key = (k[1], k[0]) vals = tuple((g, -val) for g, val in v if val != 0) else: if not index_to_pos[k[0]] < index_to_pos[k[1]]: if k[0] == k[1]: if not all(val == 0 for g, val in v): raise ValueError("elements {} are equal but their bracket is not set to 0".format(k)) continue key = tuple(k) vals = tuple((g, val) for g, val in v if val != 0) if key in sc.keys() and sorted(sc[key]) != sorted(vals): raise ValueError("two distinct values given for one and the same bracket") if vals: sc[key] = vals return Family(sc) def __init__(self, R, s_coeff, names, index_set, category=None, prefix=None, bracket=None, latex_bracket=None, string_quotes=None, **kwds): """ Initialize ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) sage: TestSuite(L).run() """ default = (names != tuple(index_set)) if prefix is None: if default: prefix = 'L' else: prefix = '' if bracket is None: bracket = default if latex_bracket is None: latex_bracket = default if string_quotes is None: string_quotes = default #self._pos_to_index = dict(enumerate(index_set)) self._index_to_pos = {k: i for i,k in enumerate(index_set)} if "sorting_key" not in kwds: kwds["sorting_key"] = self._index_to_pos.__getitem__ cat = LieAlgebras(R).WithBasis().FiniteDimensional().or_subcategory(category) FinitelyGeneratedLieAlgebra.__init__(self, R, names, index_set, cat) IndexedGenerators.__init__(self, self._indices, prefix=prefix, bracket=bracket, latex_bracket=latex_bracket, string_quotes=string_quotes, **kwds) self._M = FreeModule(R, len(index_set)) # Transform the values in the structure coefficients to elements def to_vector(tuples): vec = [R.zero()]*len(index_set) for k,c in tuples: vec[self._index_to_pos[k]] = c vec = self._M(vec) vec.set_immutable() return vec self._s_coeff = {(self._index_to_pos[k[0]], self._index_to_pos[k[1]]): to_vector(s_coeff[k]) for k in s_coeff.keys()} # For compatibility with CombinatorialFreeModuleElement _repr_term = IndexedGenerators._repr_generator _latex_term = IndexedGenerators._latex_generator def structure_coefficients(self, include_zeros=False): """ Return the dictionary of structure coefficients of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'x':1}}) sage: L.structure_coefficients() Finite family {('x', 'y'): x} sage: S = L.structure_coefficients(True); S Finite family {('x', 'y'): x, ('x', 'z'): 0, ('y', 'z'): 0} sage: S['x','z'].parent() is L True TESTS: Check that :trac:`23373` is fixed:: sage: L = lie_algebras.sl(QQ, 2) sage: sorted(L.structure_coefficients(True), key=str) [-2*E[-alpha[1]], -2*E[alpha[1]], h1] """ if not include_zeros: pos_to_index = dict(enumerate(self._indices)) return Family({(pos_to_index[k[0]], pos_to_index[k[1]]): self.element_class(self, self._s_coeff[k]) for k in self._s_coeff}) ret = {} zero = self._M.zero() for i,x in enumerate(self._indices): for j, y in enumerate(self._indices[i+1:]): if (i, j+i+1) in self._s_coeff: elt = self._s_coeff[i, j+i+1] elif (j+i+1, i) in self._s_coeff: elt = -self._s_coeff[j+i+1, i] else: elt = zero ret[x,y] = self.element_class(self, elt) # +i+1 for offset return Family(ret) def dimension(self): """ Return the dimension of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) sage: L.dimension() 2 """ return self.basis().cardinality() def module(self, sparse=True): """ Return ``self`` as a free module. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'):{'z':1}}) sage: L.module() Sparse vector space of dimension 3 over Rational Field """ return FreeModule(self.base_ring(), self.dimension(), sparse=sparse) @cached_method def zero(self): """ Return the element `0` in ``self``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.zero() 0 """ return self.element_class(self, self._M.zero()) def monomial(self, k): """ Return the monomial indexed by ``k``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.monomial('x') x """ return self.element_class(self, self._M.basis()[self._index_to_pos[k]]) def term(self, k, c=None): """ Return the term indexed by ``i`` with coefficient ``c``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.term('x', 4) 4*x """ if c is None: c = self.base_ring().one() else: c = self.base_ring()(c) return self.element_class(self, c * self._M.basis()[self._index_to_pos[k]]) def from_vector(self, v): """ Return an element of ``self`` from the vector ``v``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.from_vector([1, 2, -2]) x + 2*y - 2*z """ return self.element_class(self, self._M(v)) def some_elements(self): """ Return some elements of ``self``. EXAMPLES:: sage: L = lie_algebras.three_dimensional(QQ, 4, 1, -1, 2) sage: L.some_elements() [X, Y, Z, X + Y + Z] """ return list(self.basis()) + [self.sum(self.basis())] class Element(StructureCoefficientsElement): def _sorted_items_for_printing(self): """ Return a list of pairs ``(k, c)`` used in printing. .. WARNING:: The internal representation order is fixed, whereas this depends on ``"sorting_key"`` print option as it is used only for printing. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: elt = x + y/2 - z; elt x + 1/2*y - z sage: elt._sorted_items_for_printing() [('x', 1), ('y', 1/2), ('z', -1)] sage: key = {'x': 2, 'y': 1, 'z': 0} sage: L.print_options(sorting_key=key.__getitem__) sage: elt._sorted_items_for_printing() [('z', -1), ('y', 1/2), ('x', 1)] sage: elt -z + 1/2*y + x """ print_options = self.parent().print_options() pos_to_index = dict(enumerate(self.parent()._indices)) v = [(pos_to_index[k], c) for k, c in iteritems(self.value)] try: v.sort(key=lambda monomial_coeff: print_options['sorting_key'](monomial_coeff[0]), reverse=print_options['sorting_reverse']) except Exception: # Sorting the output is a plus, but if we can't, no big deal pass return v
class LieAlgebraWithStructureCoefficients(FinitelyGeneratedLieAlgebra, IndexedGenerators): r""" A Lie algebra with a set of specified structure coefficients. The structure coefficients are specified as a dictionary `d` whose keys are pairs of basis indices, and whose values are dictionaries which in turn are indexed by basis indices. The value of `d` at a pair `(u, v)` of basis indices is the dictionary whose `w`-th entry (for `w` a basis index) is the coefficient of `b_w` in the Lie bracket `[b_u, b_v]` (where `b_x` means the basis element with index `x`). INPUT: - ``R`` -- a ring, to be used as the base ring - ``s_coeff`` -- a dictionary, indexed by pairs of basis indices (see below), and whose values are dictionaries which are indexed by (single) basis indices and whose values are elements of `R` - ``names`` -- list or tuple of strings - ``index_set`` -- (default: ``names``) list or tuple of hashable and comparable elements OUTPUT: A Lie algebra over ``R`` which (as an `R`-module) is free with a basis indexed by the elements of ``index_set``. The `i`-th basis element is displayed using the name ``names[i]``. If we let `b_i` denote this `i`-th basis element, then the Lie bracket is given by the requirement that the `b_k`-coefficient of `[b_i, b_j]` is ``s_coeff[(i, j)][k]`` if ``s_coeff[(i, j)]`` exists, otherwise ``-s_coeff[(j, i)][k]`` if ``s_coeff[(j, i)]`` exists, otherwise `0`. EXAMPLES: We create the Lie algebra of `\QQ^3` under the Lie bracket defined by `\times` (cross-product):: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}) sage: (x,y,z) = L.gens() sage: L.bracket(x, y) z sage: L.bracket(y, x) -z TESTS: We can input structure coefficients that fail the Jacobi identity, but the test suite will call us out on it:: sage: Fake = LieAlgebra(QQ, 'x,y,z', {('x','y'):{'z':3}, ('y','z'):{'z':1}, ('z','x'):{'y':1}}) sage: TestSuite(Fake).run() Failure in _test_jacobi_identity: ... Old tests !!!!!placeholder for now!!!!!:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) sage: L.basis() Finite family {'x': x, 'y': y} """ @staticmethod def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, **kwds): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: L1 = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) sage: L2 = LieAlgebra(QQ, 'x,y', {('y','x'): {'x':-1}}) sage: L1 is L2 True Check that we convert names to the indexing set:: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}, index_set=list(range(3))) sage: (x,y,z) = L.gens() sage: L[x,y] L[2] """ names, index_set = standardize_names_index_set(names, index_set) # Make sure the structure coefficients are given by the index set if names is not None and names != tuple(index_set): d = {x: index_set[i] for i, x in enumerate(names)} get_pairs = lambda X: X.items() if isinstance(X, dict) else X try: s_coeff = {(d[k[0]], d[k[1]]): [(d[x], y) for x, y in get_pairs(s_coeff[k])] for k in s_coeff} except KeyError: # At this point we assume they are given by the index set pass s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff( s_coeff, index_set) if s_coeff.cardinality() == 0: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set, **kwds) if (names is None and len(index_set) <= 1) or len(names) <= 1: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set, **kwds) return super(LieAlgebraWithStructureCoefficients, cls).__classcall__(cls, R, s_coeff, names, index_set, **kwds) @staticmethod def _standardize_s_coeff(s_coeff, index_set): """ Helper function to standardize ``s_coeff`` into the appropriate form (dictionary indexed by pairs, whose values are dictionaries). Strips items with coefficients of 0 and duplicate entries. This does not check the Jacobi relation (nor antisymmetry if the cardinality is infinite). EXAMPLES:: sage: from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients sage: d = {('y','x'): {'x':-1}} sage: LieAlgebraWithStructureCoefficients._standardize_s_coeff(d, ('x', 'y')) Finite family {('x', 'y'): (('x', 1),)} """ # Try to handle infinite basis (once/if supported) #if isinstance(s_coeff, AbstractFamily) and s_coeff.cardinality() == infinity: # return s_coeff index_to_pos = {k: i for i, k in enumerate(index_set)} sc = {} # Make sure the first gen is smaller than the second in each key for k in s_coeff.keys(): v = s_coeff[k] if isinstance(v, dict): v = v.items() if index_to_pos[k[0]] > index_to_pos[k[1]]: key = (k[1], k[0]) vals = tuple((g, -val) for g, val in v if val != 0) else: if not index_to_pos[k[0]] < index_to_pos[k[1]]: if k[0] == k[1]: if not all(val == 0 for g, val in v): raise ValueError( "elements {} are equal but their bracket is not set to 0" .format(k)) continue key = tuple(k) vals = tuple((g, val) for g, val in v if val != 0) if key in sc.keys() and sorted(sc[key]) != sorted(vals): raise ValueError( "two distinct values given for one and the same bracket") if vals: sc[key] = vals return Family(sc) def __init__(self, R, s_coeff, names, index_set, category=None, prefix=None, bracket=None, latex_bracket=None, string_quotes=None, **kwds): """ Initialize ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) sage: TestSuite(L).run() """ default = (names != tuple(index_set)) if prefix is None: if default: prefix = 'L' else: prefix = '' if bracket is None: bracket = default if latex_bracket is None: latex_bracket = default if string_quotes is None: string_quotes = default #self._pos_to_index = dict(enumerate(index_set)) self._index_to_pos = {k: i for i, k in enumerate(index_set)} if "sorting_key" not in kwds: kwds["sorting_key"] = self._index_to_pos.__getitem__ cat = LieAlgebras(R).WithBasis().FiniteDimensional().or_subcategory( category) FinitelyGeneratedLieAlgebra.__init__(self, R, names, index_set, cat) IndexedGenerators.__init__(self, self._indices, prefix=prefix, bracket=bracket, latex_bracket=latex_bracket, string_quotes=string_quotes, **kwds) self._M = FreeModule(R, len(index_set)) # Transform the values in the structure coefficients to elements def to_vector(tuples): vec = [R.zero()] * len(index_set) for k, c in tuples: vec[self._index_to_pos[k]] = c vec = self._M(vec) vec.set_immutable() return vec self._s_coeff = {(self._index_to_pos[k[0]], self._index_to_pos[k[1]]): to_vector(s_coeff[k]) for k in s_coeff.keys()} # For compatibility with CombinatorialFreeModuleElement _repr_term = IndexedGenerators._repr_generator _latex_term = IndexedGenerators._latex_generator def structure_coefficients(self, include_zeros=False): """ Return the dictionary of structure coefficients of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'x':1}}) sage: L.structure_coefficients() Finite family {('x', 'y'): x} sage: S = L.structure_coefficients(True); S Finite family {('x', 'y'): x, ('x', 'z'): 0, ('y', 'z'): 0} sage: S['x','z'].parent() is L True TESTS: Check that :trac:`23373` is fixed:: sage: L = lie_algebras.sl(QQ, 2) sage: sorted(L.structure_coefficients(True), key=str) [-2*E[-alpha[1]], -2*E[alpha[1]], h1] """ if not include_zeros: pos_to_index = dict(enumerate(self._indices)) return Family({(pos_to_index[k[0]], pos_to_index[k[1]]): self.element_class(self, self._s_coeff[k]) for k in self._s_coeff}) ret = {} zero = self._M.zero() for i, x in enumerate(self._indices): for j, y in enumerate(self._indices[i + 1:]): if (i, j + i + 1) in self._s_coeff: elt = self._s_coeff[i, j + i + 1] elif (j + i + 1, i) in self._s_coeff: elt = -self._s_coeff[j + i + 1, i] else: elt = zero ret[x, y] = self.element_class(self, elt) # +i+1 for offset return Family(ret) def dimension(self): """ Return the dimension of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) sage: L.dimension() 2 """ return self.basis().cardinality() def module(self, sparse=True): """ Return ``self`` as a free module. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'):{'z':1}}) sage: L.module() Sparse vector space of dimension 3 over Rational Field """ return FreeModule(self.base_ring(), self.dimension(), sparse=sparse) @cached_method def zero(self): """ Return the element `0` in ``self``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.zero() 0 """ return self.element_class(self, self._M.zero()) def monomial(self, k): """ Return the monomial indexed by ``k``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.monomial('x') x """ return self.element_class(self, self._M.basis()[self._index_to_pos[k]]) def term(self, k, c=None): """ Return the term indexed by ``i`` with coefficient ``c``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.term('x', 4) 4*x """ if c is None: c = self.base_ring().one() else: c = self.base_ring()(c) return self.element_class(self, c * self._M.basis()[self._index_to_pos[k]]) def from_vector(self, v): """ Return an element of ``self`` from the vector ``v``. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: L.from_vector([1, 2, -2]) x + 2*y - 2*z """ return self.element_class(self, self._M(v)) def some_elements(self): """ Return some elements of ``self``. EXAMPLES:: sage: L = lie_algebras.three_dimensional(QQ, 4, 1, -1, 2) sage: L.some_elements() [X, Y, Z, X + Y + Z] """ return list(self.basis()) + [self.sum(self.basis())] def change_ring(self, R): r""" Return a Lie algebra with identical structure coefficients over ``R``. INPUT: - ``R`` -- a ring EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(ZZ, {('x','y'): {'z':1}}) sage: L.structure_coefficients() Finite family {('x', 'y'): z} sage: LQQ = L.change_ring(QQ) sage: LQQ.structure_coefficients() Finite family {('x', 'y'): z} sage: LSR = LQQ.change_ring(SR) sage: LSR.structure_coefficients() Finite family {('x', 'y'): z} """ return LieAlgebraWithStructureCoefficients( R, self.structure_coefficients(), names=self.variable_names(), index_set=self.indices()) class Element(StructureCoefficientsElement): def _sorted_items_for_printing(self): """ Return a list of pairs ``(k, c)`` used in printing. .. WARNING:: The internal representation order is fixed, whereas this depends on ``"sorting_key"`` print option as it is used only for printing. EXAMPLES:: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'): {'z':1}}) sage: elt = x + y/2 - z; elt x + 1/2*y - z sage: elt._sorted_items_for_printing() [('x', 1), ('y', 1/2), ('z', -1)] sage: key = {'x': 2, 'y': 1, 'z': 0} sage: L.print_options(sorting_key=key.__getitem__) sage: elt._sorted_items_for_printing() [('z', -1), ('y', 1/2), ('x', 1)] sage: elt -z + 1/2*y + x """ print_options = self.parent().print_options() pos_to_index = dict(enumerate(self.parent()._indices)) v = [(pos_to_index[k], c) for k, c in self.value.items()] try: v.sort(key=lambda monomial_coeff: print_options['sorting_key'] (monomial_coeff[0]), reverse=print_options['sorting_reverse']) except Exception: # Sorting the output is a plus, but if we can't, no big deal pass return v
def stratification(L): r""" Return a stratification of the Lie algebra if one exists. INPUT: - ``L`` -- a Lie algebra OUTPUT: A grading of the Lie algebra `\mathfrak{g}` over the integers such that the layer `\mathfrak{g}_1` generates the full Lie algebra. EXAMPLES:: A stratification for a free nilpotent Lie algebra is the one based on the length of the defining bracket:: sage: from lie_gradings.gradings.grading import stratification sage: from lie_gradings.gradings.utilities import in_new_basis sage: L = LieAlgebra(QQ, 3, step=3) sage: strat = stratification(L) sage: strat Grading over Additive abelian group isomorphic to Z of Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field with nonzero layers (1) : (X_1, X_2, X_3) (2) : (X_12, X_13, X_23) (3) : (X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) The main use case is when the original basis of the stratifiable Lie algebra is not adapted to a stratification. Consider the following quotient Lie algebra:: sage: X_1, X_2, X_3 = L.basis().list()[:3] sage: Q = L.quotient(L[X_2, X_3]) sage: Q Lie algebra quotient L/I of dimension 10 over Rational Field where L: Free Nilpotent Lie algebra on 14 generators (X_1, X_2, X_3, X_12, X_13, X_23, X_112, X_113, X_122, X_123, X_132, X_133, X_223, X_233) over Rational Field I: Ideal (X_23) We switch to a basis which does not define a stratification:: sage: Y_1 = Q(X_1) sage: Y_2 = Q(X_2) + Q[X_1, X_2] sage: Y_3 = Q(X_3) sage: basis = [Y_1, Y_2, Y_3] + Q.basis().list()[3:] sage: Y_labels = ["Y_%d"%(k+1) for k in range(len(basis))] sage: K = in_new_basis(Q, basis,Y_labels) sage: K.inject_variables() Defining Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9, Y_10 sage: K[Y_2, Y_3] Y_9 sage: K[[Y_1, Y_3], Y_2] Y_9 We may reconstruct a stratification in the new basis without any knowledge of the original stratification:: sage: stratification(K) Grading over Additive abelian group isomorphic to Z of Nilpotent Lie algebra on 10 generators (Y_1, Y_2, Y_3, Y_4, Y_5, Y_6, Y_7, Y_8, Y_9, Y_10) over Rational Field with nonzero layers (1) : (Y_1, Y_2 - Y_4, Y_3) (2) : (Y_4, Y_5) (3) : (Y_10, Y_6, Y_7, Y_8, Y_9) sage: K[Y_1, Y_2 - Y_4] Y_4 sage: K[Y_1, Y_3] Y_5 sage: K[Y_2 - Y_4, Y_3] 0 A non-stratifiable Lie algebra raises an error:: sage: L = LieAlgebra(QQ, {('X_1','X_3'): {'X_4': 1}, ....: ('X_1','X_4'): {'X_5': 1}, ....: ('X_2','X_3'): {'X_5': 1}}, ....: names='X_1,X_2,X_3,X_4,X_5') sage: stratification(L) Traceback (most recent call last): ... ValueError: Lie algebra on 5 generators (X_1, X_2, X_3, X_4, X_5) over Rational Field is not a stratifiable Lie algebra """ lcs = L.lower_central_series(submodule=True) quots = [V.quotient(W) for V, W in zip(lcs, lcs[1:])] # find a basis adapted to the filtration by the lower central series adapted_basis = [] weights = [] for k, q in enumerate(quots): weights += [k + 1] * q.dimension() for v in q.basis(): b = q.lift(v) adapted_basis.append(b) # define a submodule to compute structural # coefficients in the filtration adapted basis try: m = L.module() except AttributeError: m = FreeModule(L.base_ring(), L.dimension()) sm = m.submodule_with_basis(adapted_basis) # form the linear system Ax=b of constraints from the Leibniz rule paramspace = [(k, h) for k in range(L.dimension()) for h in range(L.dimension()) if weights[h] > weights[k]] Arows = [] bvec = [] zerovec = m.zero() for i in range(L.dimension()): Y_i = adapted_basis[i] w_i = weights[i] for j in range(i + 1, L.dimension()): Y_j = adapted_basis[j] w_j = weights[j] Y_ij = L.bracket(Y_i, Y_j) c_ij = sm.coordinate_vector(Y_ij.to_vector()) bcomp = L.zero() for k in range(L.dimension()): w_k = weights[k] Y_k = adapted_basis[k] bcomp += (w_k - w_i - w_j) * c_ij[k] * Y_k bv = bcomp.to_vector() Acomp = {} for k, h in paramspace: w_k = weights[k] Y_h = adapted_basis[h] if k == i: Acomp[(k, h)] = L.bracket(Y_h, Y_j).to_vector() elif k == j: Acomp[(k, h)] = L.bracket(Y_i, Y_h).to_vector() elif w_k >= w_i + w_j: Acomp[(k, h)] = -c_ij[k] * Y_h for r in range(L.dimension()): Arows.append( [Acomp.get((k, h), zerovec)[r] for k, h in paramspace]) bvec.append(bv[r]) A = matrix(L.base_ring(), Arows) b = vector(L.base_ring(), bvec) # solve the linear system Ax=b if possible try: coeffs_flat = A.solve_right(b) except ValueError: raise ValueError("%s is not a stratifiable Lie algebra" % L) coeffs = {(k, h): ckh for (k, h), ckh in zip(paramspace, coeffs_flat)} # define the matrix of the derivation determined by the solution # in the adapted basis cols = [] for k in range(L.dimension()): w_k = weights[k] Y_k = adapted_basis[k] hspace = [h for (l, h) in paramspace if l == k] Yk_im = w_k * Y_k + sum( (coeffs[(k, h)] * adapted_basis[h] for h in hspace), L.zero()) cols.append(sm.coordinate_vector(Yk_im.to_vector())) der = matrix(L.base_ring(), cols).transpose() # the layers of the stratification are V_k = ker(der - kI) layers = {} for k in range(len(quots)): degree = k + 1 B = der - degree * matrix.identity(L.dimension()) adapted_kernel = B.right_kernel() # convert back to the original basis Vk_basis = [sm.from_vector(X) for X in adapted_kernel.basis()] layers[(degree, )] = [ L.from_vector(v) for v in m.submodule(Vk_basis).basis() ] return grading(L, layers, magma=AdditiveAbelianGroup([0]), projections=True)