예제 #1
0
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)
예제 #2
0
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
예제 #4
0
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
예제 #5
0
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)