示例#1
0
 def matrix(self):
     V = FreeModule(ZZ, len(self._start))
     columns = [copy(v) for v in V.basis()]
     for p, winner, side in self:
         winner_letter = p._labels[winner][side]
         loser_letter = p._labels[1 - winner][side]
         columns[loser_letter] += columns[winner_letter]
     m = MatrixSpace(ZZ, len(p))(columns).transpose()
     perm_on_list_inplace(self._relabelling,
                          m,
                          swap=sage.matrix.matrix0.Matrix.swap_columns)
     return m
    def matrix(self):
        r"""
        Return the Rauzy matrix of this path.

        TESTS::

            sage: from surface_dynamics import iet
            sage: p = iet.Permutation('a b', 'b a')
            sage: iet.FlipSequence(p, '').matrix()
            [1 0]
            [0 1]
        """
        V = FreeModule(ZZ, len(self._start))
        columns = [copy(v) for v in V.basis()]
        for p, winner, side in self:
            winner_letter = p._labels[winner][side]
            loser_letter = p._labels[1-winner][side]
            columns[loser_letter] += columns[winner_letter]
        m = MatrixSpace(ZZ, len(self._start))(columns).transpose()
        perm_on_list_inplace(self._relabelling, m, swap=sage.matrix.matrix0.Matrix.swap_columns)
        return m
示例#3
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)
示例#4
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 DegreeGrading( Grading_abstract ) :
    r"""
    This class implements a monomial grading for a polynomial ring
    `R[x_1, .., x_n]`.
    """
  
    def __init__( self, degrees ) :
        r"""
        INPUT:
            - ``degrees`` -- A list or tuple of `n` positive integers.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading((1,2))
            sage: g = DegreeGrading([5,2,3])
        """
        
        self.__degrees = tuple(degrees)
        self.__module = FreeModule(ZZ, len(degrees))
        self.__module_basis = self.__module.basis()
        
    def ngens(self) :
        r"""
        The number of generators of the polynomial ring.
        
        OUTPUT:
            An integer.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading((1,2)).ngens()
            2
            sage: DegreeGrading([5,2,3]).ngens()
            3
        """
        return len(self.__degrees)
     
    def gen(self, i) :
        r"""
        The number of generators of the polynomial ring.
        
        OUTPUT:
            An integer.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading([5,2,3]).gen(0)
            5
            sage: DegreeGrading([5,2,3]).gen(3)
            Traceback (most recent call last):
            ...
            ValueError: Generator 3 does not exist.
        """
        if i < len(self.__degrees) :
            return self.__degrees[i]
        
        raise ValueError("Generator %s does not exist." % (i,))
    
    def gens(self) :
        r"""
        The gradings of the generators of the polynomial ring.
        
        OUTPUT:
            A tuple of integers.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading((1,2)).gens()
            (1, 2)
            sage: DegreeGrading([5,2,3]).gens()
            (5, 2, 3)
        """
        return self.__degrees
                
    def index(self, x) :
        r"""
        The grading value of `x` with respect to this grading. 

        INPUT:
            - `x` -- A tuple of length `n`.

        OUTPUT:
            An integer.
                
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.index((2,4,5))
            33
            sage: g.index((2,3))
            Traceback (most recent call last):
            ...
            ValueError: Tuple must have length 3.
        """
        ## TODO: We shouldn't need this.
        #if len(x) == 0 : return infinity
        
        if len(x) != len(self.__degrees) :
            raise ValueError( "Tuple must have length %s." % (len(self.__degrees),))
        
        return sum( map(mul, x, self.__degrees) )
        
    def basis(self, index, vars = None) :
        r"""
        All monomials that are have given grading index involving only the
        given variables.
        
        INPUT:
            - ``index`` -- A grading index.
            - ``vars``  -- A list of integers from `0` to `n - 1` or ``None``
                           (default: ``None``); If ``None`` all variables
                           will be considered.
        
        OUTPUT:
            A list of elements in `\Z^n`.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.basis(2)
            [(0, 1, 0)]
            sage: g.basis(10, vars = [0])
            [(2, 0, 0)]
            sage: g.basis(20, vars = [1,2])
            [(0, 10, 0), (0, 7, 2), (0, 4, 4), (0, 1, 6)]
            sage: g.basis(-1)
            []
            sage: g.basis(0)
            [(0, 0, 0)]
        """
        if index < 0 :
            return []
        elif index == 0 :
            return [self.__module.zero_vector()]
        
        if vars == None :
            vars = [m for (m,d) in enumerate(self.__degrees) if d <= index]
        if len(vars) == 0 :
            return []
            
        res = [ self.__module_basis[vars[0]] + m
                for m in self.basis(index - self.__degrees[vars[0]], vars) ]
        res += self.basis(index, vars[1:])
        
        return res

    def subgrading(self, gens) :
        r"""
        The grading of same type for the ring with only the variables given by
        ``gens``.
        
        INPUT:
            - ``gens`` - A list of integers.
        
        OUTPUT:
            An instance of :class:~`.DegreeGrading`.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.subgrading([2])
            Degree grading (3,)
            sage: g.subgrading([])
            Degree grading ()
        """
        return DegreeGrading([self.__degrees[i] for i in gens])
        
    def __contains__(self, x) :
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: 5 in g
            True
            sage: "t" in g
            False
        """
        return isinstance(x, (int, Integer)) 
    
    def __cmp__(self, other) :
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g == DegreeGrading([5,2,3])
            True
            sage: g == DegreeGrading((2,4))
            False
        """
        c = cmp(type(self), type(other))
        
        if c == 0 :
            c = cmp(self.__degrees, other.__degrees)

        return c
    
    def __hash__(self) :
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: hash( DegreeGrading([5,2,3]) )
            -1302562269         # 32-bit
            7573306103633312291 # 64-bit
        """
        return hash(self.__degrees)
    
    def _repr_(self) :
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading([5,2,3])
            Degree grading (5, 2, 3)
        """
        return "Degree grading %s" % str(self.__degrees)
    
    def _latex_(self) :
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: latex( DegreeGrading([5,2,3]) )
            \text{Degree grading } \left(5, 2, 3\right)
        """
        return r"\text{Degree grading }" + latex(self.__degrees)
示例#6
0
def herm_modform_space(D, HermWeight, B_cF=10, parallelization=None, reduction_method_flags=-1):
	"""
	This calculates the vectorspace of Fourier expansions to
	Hermitian modular forms of weight `HermWeight` over \Gamma,
	where \Gamma = \Sp_2(\curlO) and \curlO is the maximal order
	of \QQ(\sqrt{D}).

	Each Fourier coefficient vector is indexed up to a precision
	\curlF which is given by `B_cF` such that for every
	[a,b,c] \in \curlF \subset \Lambda, we have 0 \le a,c \le B_cF.

	The function `herm_modform_indexset()` returns reduced matrices
	of that precision index set \curlF.
	"""

	if HermWeight % 3 != 0:
		raise TypeError, "the modulform is trivial/zero if HermWeight is not divisible by 3"

	# Transform these into native Python objects. We don't want to have
	# any Sage objects (e.g. Integer) here so that the cache index stays
	# unique.
	D = int(D)
	HermWeight = int(HermWeight)
	B_cF = int(B_cF)
	reduction_method_flags = int(reduction_method_flags)

	calc = C.Calc()
	calc.init(D = D, HermWeight = HermWeight, B_cF=B_cF)
	calc.calcReducedCurlF()
	reducedCurlFSize = calc.matrixColumnCount

	# Calculate the dimension of Hermitian modular form space.
	dim = herm_modform_space_dim(D=D, HermWeight=HermWeight)

	cacheIdx = (D, HermWeight, B_cF)
	if reduction_method_flags != -1:
		cacheIdx += (reduction_method_flags,)
	try:
		herm_modform_fe_expannsion, calc, curlS_denoms, pending_tasks = hermModformSpaceCache[cacheIdx]
		if not isinstance(calc, C.Calc): raise TypeError
		print "Resuming from %s" % hermModformSpaceCache._filename_for_key(cacheIdx)
	except (TypeError, ValueError, KeyError, EOFError): # old format or not cached or cache incomplete
		herm_modform_fe_expannsion = FreeModule(QQ, reducedCurlFSize)
		curlS_denoms = set() # the denominators of the visited matrices S
		pending_tasks = ()

	current_dimension = herm_modform_fe_expannsion.dimension()

	verbose("current dimension: %i, wanted: %i" % (herm_modform_fe_expannsion.dimension(), dim))
	if dim == 0:
		print "dim == 0 -> exit"
		return

	def task_iter_func():
		# Iterate S \in Mat_2^T(\curlO), S > 0.
		while True:
			# Get the next S.
			# If calc.curlS is not empty, this is because we have recovered from a resume.
			if len(calc.curlS) == 0:
				S = calc.getNextS()
			else:
				assert len(calc.curlS) == 1
				S = calc.curlS[0]

			l = S.det()
			l = toInt(l)
			curlS_denoms.add(l)

			verbose("trying S={0}, det={1}".format(S, l))

			if reduction_method_flags & Method_Elliptic_reduction:
				yield CalcTask(
					func=modform_restriction_info, calc=calc, kwargs={"S":S, "l":l})

			if reduction_method_flags & Method_EllipticCusp_reduction:
				precLimit = calcPrecisionDimension(B_cF=B_cF, S=S)
				yield CalcTask(
					func=modform_cusp_info, calc=calc, kwargs={"S":S, "l":l, "precLimit": precLimit})

			calc.curlS_clearMatrices() # In the C++ internal curlS, clear previous matrices.

	task_iter = task_iter_func()
	if parallelization:
		parallelization.task_iter = task_iter

	if parallelization and pending_tasks:
		for func, name in pending_tasks:
			parallelization.exec_task(func=func, name=name)

	step_counter = 0
	while True:
		if parallelization:
			new_task_count = 0

			spaces = []
			for task, exc, newspace in parallelization.get_all_ready_results():
				if exc: raise exc

				if newspace is None:
					verbose("no data from %r" % task)
					continue
				if newspace.dimension() == reducedCurlFSize:
					verbose("no information gain from %r" % task)
					continue

				spacecomment = task
				assert newspace.dimension() >= dim, "%r, %r" % (task, newspace)
				if newspace.dimension() < herm_modform_fe_expannsion.dimension():
					# Swap newspace with herm_modform_fe_expannsion.
					herm_modform_fe_expannsion, newspace = newspace, herm_modform_fe_expannsion
					current_dimension = herm_modform_fe_expannsion.dimension()
					if current_dimension == dim:
						if not isinstance(task, IntersectSpacesTask):
							verbose("warning: we expected IntersectSpacesTask for final dim but got: %r" % task)
					verbose("new dimension: %i, wanted: %i" % (current_dimension, dim))
					if current_dimension <= 20:
						pprint(herm_modform_fe_expannsion.basis())
					spacecomment = "<old base space>"

				spaces += [(spacecomment, newspace)]

			if spaces:
				parallelization.exec_task(IntersectSpacesTask(herm_modform_fe_expannsion, spaces))
				new_task_count += 1

			new_task_count += parallelization.maybe_queue_tasks()
			time.sleep(0.1)

		else: # no parallelization
			new_task_count = 1
			if pending_tasks: # from some resuming
				task,_ = pending_tasks[0]
				pending_tasks = pending_tasks[1:]
			else:
				task = next(task_iter)
			newspace = task()

			if newspace is None:
				verbose("no data from %r" % task)
			if newspace is not None and newspace.dimension() == reducedCurlFSize:
				verbose("no information gain from %r" % task)
				newspace = None

			if newspace is not None:
				new_task_count += 1
				spacecomment = task
				herm_modform_fe_expannsion_new = IntersectSpacesTask(
					herm_modform_fe_expannsion, [(spacecomment, newspace)])()
				if herm_modform_fe_expannsion_new is not None:
					herm_modform_fe_expannsion = herm_modform_fe_expannsion_new
				current_dimension = herm_modform_fe_expannsion.dimension()
				verbose("new dimension: %i, wanted: %i" % (current_dimension, dim))

		if new_task_count > 0:
			step_counter += 1

			if step_counter % 10 == 0:
				verbose("save state after %i steps to %s" % (step_counter, os.path.basename(hermModformSpaceCache._filename_for_key(cacheIdx))))
				if parallelization:
					pending_tasks = parallelization.get_pending_tasks()
				hermModformSpaceCache[cacheIdx] = (herm_modform_fe_expannsion, calc, curlS_denoms, pending_tasks)

		if current_dimension == dim:
			verbose("finished!")
			break

	# Test for some other S with other not-yet-seen denominator.
	check_herm_modform_space(
		calc, herm_modform_space=herm_modform_fe_expannsion,
		used_curlS_denoms=curlS_denoms
		)

	return herm_modform_fe_expannsion
示例#7
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
示例#8
0
class DegreeGrading(Grading_abstract):
    r"""
    This class implements a monomial grading for a polynomial ring
    `R[x_1, .., x_n]`.
    """
    def __init__(self, degrees):
        r"""
        INPUT:
            - ``degrees`` -- A list or tuple of `n` positive integers.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading((1,2))
            sage: g = DegreeGrading([5,2,3])
        """

        self.__degrees = tuple(degrees)
        self.__module = FreeModule(ZZ, len(degrees))
        self.__module_basis = self.__module.basis()

    def ngens(self):
        r"""
        The number of generators of the polynomial ring.
        
        OUTPUT:
            An integer.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading((1,2)).ngens()
            2
            sage: DegreeGrading([5,2,3]).ngens()
            3
        """
        return len(self.__degrees)

    def gen(self, i):
        r"""
        The number of generators of the polynomial ring.
        
        OUTPUT:
            An integer.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading([5,2,3]).gen(0)
            5
            sage: DegreeGrading([5,2,3]).gen(3)
            Traceback (most recent call last):
            ...
            ValueError: Generator 3 does not exist.
        """
        if i < len(self.__degrees):
            return self.__degrees[i]

        raise ValueError("Generator %s does not exist." % (i, ))

    def gens(self):
        r"""
        The gradings of the generators of the polynomial ring.
        
        OUTPUT:
            A tuple of integers.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading((1,2)).gens()
            (1, 2)
            sage: DegreeGrading([5,2,3]).gens()
            (5, 2, 3)
        """
        return self.__degrees

    def index(self, x):
        r"""
        The grading value of `x` with respect to this grading. 

        INPUT:
            - `x` -- A tuple of length `n`.

        OUTPUT:
            An integer.
                
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.index((2,4,5))
            33
            sage: g.index((2,3))
            Traceback (most recent call last):
            ...
            ValueError: Tuple must have length 3.
        """
        ## TODO: We shouldn't need this.
        #if len(x) == 0 : return infinity

        if len(x) != len(self.__degrees):
            raise ValueError("Tuple must have length %s." %
                             (len(self.__degrees), ))

        return sum(map(mul, x, self.__degrees))

    def basis(self, index, vars=None):
        r"""
        All monomials that are have given grading index involving only the
        given variables.
        
        INPUT:
            - ``index`` -- A grading index.
            - ``vars``  -- A list of integers from `0` to `n - 1` or ``None``
                           (default: ``None``); If ``None`` all variables
                           will be considered.
        
        OUTPUT:
            A list of elements in `\Z^n`.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.basis(2)
            [(0, 1, 0)]
            sage: g.basis(10, vars = [0])
            [(2, 0, 0)]
            sage: g.basis(20, vars = [1,2])
            [(0, 10, 0), (0, 7, 2), (0, 4, 4), (0, 1, 6)]
            sage: g.basis(-1)
            []
            sage: g.basis(0)
            [(0, 0, 0)]
        """
        if index < 0:
            return []
        elif index == 0:
            return [self.__module.zero_vector()]

        if vars == None:
            vars = [m for (m, d) in enumerate(self.__degrees) if d <= index]
        if len(vars) == 0:
            return []

        res = [
            self.__module_basis[vars[0]] + m
            for m in self.basis(index - self.__degrees[vars[0]], vars)
        ]
        res += self.basis(index, vars[1:])

        return res

    def subgrading(self, gens):
        r"""
        The grading of same type for the ring with only the variables given by
        ``gens``.
        
        INPUT:
            - ``gens`` - A list of integers.
        
        OUTPUT:
            An instance of :class:~`.DegreeGrading`.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g.subgrading([2])
            Degree grading (3,)
            sage: g.subgrading([])
            Degree grading ()
        """
        return DegreeGrading([self.__degrees[i] for i in gens])

    def __contains__(self, x):
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: 5 in g
            True
            sage: "t" in g
            False
        """
        return isinstance(x, (int, Integer))

    def __cmp__(self, other):
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: g = DegreeGrading([5,2,3])
            sage: g == DegreeGrading([5,2,3])
            True
            sage: g == DegreeGrading((2,4))
            False
        """
        c = cmp(type(self), type(other))

        if c == 0:
            c = cmp(self.__degrees, other.__degrees)

        return c

    def __hash__(self):
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: hash( DegreeGrading([5,2,3]) )
            -1302562269         # 32-bit
            7573306103633312291 # 64-bit
        """
        return hash(self.__degrees)

    def _repr_(self):
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: DegreeGrading([5,2,3])
            Degree grading (5, 2, 3)
        """
        return "Degree grading %s" % str(self.__degrees)

    def _latex_(self):
        r"""
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.gradedexpansions.gradedexpansion_grading import *
            sage: latex( DegreeGrading([5,2,3]) )
            \text{Degree grading } \left(5, 2, 3\right)
        """
        return r"\text{Degree grading }" + latex(self.__degrees)
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
示例#10
0
def maximal_grading(L):
    r"""
    Return a maximal grading of a Lie algebra defined over an
    algebraically closed field.

    A maximal grading of a Lie algebra `\mathfrak{g}` is the finest
    possible grading of `\mathfrak{g}` over torsion free abelian groups.
    If `\mathfrak{g} = \bigoplus_{n\in \mathbb{Z}^k} \mathfrak{g}_n`
    is a maximal grading, then there exists no other grading
    `\mathfrak{g} = \bigoplus_{a\in A} \mathfrak{g}_a` over a torsion
    free abelian group `A` such that every `\mathfrak{g}_a` is contained
    in some `\mathfrak{g}_n`.

    EXAMPLES:

    A maximal grading of an abelian Lie algebra puts each basis element
    into an independent layer::

        sage: import sys, pathlib
        sage: sys.path.append(str(pathlib.Path().absolute()))
        sage: from lie_gradings.gradings.grading import maximal_grading
        sage: L = LieAlgebra(QQbar, 4, abelian=True)
        sage: maximal_grading(L)
        Grading over Additive abelian group isomorphic to Z + Z + Z + Z
        of Abelian Lie algebra on 4 generators (L[0], L[1], L[2], L[3])
        over Algebraic Field with nonzero layers
          (1, 0, 0, 0) : (L[3],)
          (0, 1, 0, 0) : (L[2],)
          (0, 0, 1, 0) : (L[1],)
          (0, 0, 0, 1) : (L[0],)

    A maximal grading of a free nilpotent Lie algebra decomposes the Lie
    algebra based on how many times each generator appears in the
    defining Lie bracket::

        sage: L = LieAlgebra(QQbar, 3, step=3)
        sage: maximal_grading(L)
        Grading over Additive abelian group isomorphic to Z + Z + 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 Algebraic Field with nonzero layers
          (1, 0, 0) : (X_1,)
          (0, 1, 0) : (X_2,)
          (1, 1, 0) : (X_12,)
          (1, 2, 0) : (X_122,)
          (2, 1, 0) : (X_112,)
          (0, 0, 1) : (X_3,)
          (0, 1, 1) : (X_23,)
          (0, 1, 2) : (X_233,)
          (0, 2, 1) : (X_223,)
          (1, 0, 1) : (X_13,)
          (1, 0, 2) : (X_133,)
          (1, 1, 1) : (X_123, X_132)
          (2, 0, 1) : (X_113,)
    """

    # define utilities to convert from matrices to vectors and back
    R = L.base_ring()
    n = L.dimension()
    MS = MatrixSpace(R, n, n)

    def matrix_to_vec(A):
        return vector(R, sum((list(Ar) for Ar in A.rows()), []))

    db = L.derivations_basis()

    def derivation_lincomb(vec):
        return sum((vk * dk for vk, dk in zip(vec, db)), MS.zero())

    # iteratively construct larger and larger tori of derivations
    t = []
    while True:
        # compute the centralizer of the torus in the derivation algebra
        ker = FreeModule(R, len(db))
        for der in t:
            # form the matrix of ad(der) with rows
            # the images of basis derivations
            A = matrix([matrix_to_vec(der * X - X * der) for X in db])
            ker = ker.intersection(A.left_kernel())
        cb = [derivation_lincomb(v) for v in ker.basis()]

        # check the basis of the centralizer for semisimple parts outside of t
        gl = FreeModule(R, n * n)
        t_submodule = gl.submodule([matrix_to_vec(der) for der in t])
        for A in cb:
            As, An = jordan_decomposition(A)
            if matrix_to_vec(As) not in t_submodule:
                # extend the torus by As
                t.append(As)
                break
        else:
            # no new elements found, so the torus is maximal
            break

    # compute the eigenspace intersections to get the concrete grading
    common_eigenspaces = [([], FreeModule(R, n))]
    for A in t:
        new_eigenspaces = []
        eig = A.right_eigenspaces()
        for ev, V in common_eigenspaces:
            for ew, W in eig:
                VW = V.intersection(W)
                if VW.dimension() > 0:
                    new_eigenspaces.append((ev + [ew], VW))
        common_eigenspaces = new_eigenspaces

    if not t:
        # zero dimensional maximal torus
        # the only grading is the trivial grading
        magma = AdditiveAbelianGroup([])
        layers = {magma.zero(): L.basis().list()}
        return grading(L, layers, magma=magma)

    # define a grading with layers indexed by tuples of eigenvalues
    cm = get_coercion_model()
    all_eigenvalues = sum((ev for ev, V in common_eigenspaces), [])
    k = len(common_eigenspaces[0][0])
    evR = cm.common_parent(*all_eigenvalues)
    layers = {
        tuple(ev): [L.from_vector(v) for v in V.basis()]
        for ev, V in common_eigenspaces
    }
    magma = evR.cartesian_product(*[evR] * (k - 1))
    gr = grading(L, layers, magma=magma)

    # convert to a grading over Z^k
    return gr.universal_realization()
示例#11
0
class Lattice_class (SageObject):
    """
    A class representing a lattice $L$.

    NOTE
        We build this class as a sort of wrapper
        around \code{FreeModule(ZZ,n)}.
        This is reflecting the fact that
        a lattice is not a free module, but a pair
        consisting of a free module and a scalar product.
        In addition, we avoid in this way to add new
        items to the Sage category system, which we
        better leave to the specialists.

    EXAMPLES
        sage: L = Lattice_class( [2,1,2]); L
        The ZZ-lattice (ZZ^2, x^tGy), where G = 
        [2 1]
        [1 2]
        sage: L.is_even()
        True
        sage: L.gram_matrix()
        [2 1]
        [1 2]
        sage: L.dual_vectors()
        {(2/3, -1/3), (0, 0), (4/3, -2/3)}
        sage: L.representatives(2)
        {}
        sage: L.representatives(1)
        {(2/3, -1/3), (4/3, -2/3)}
        sage: L.values()          
        {0: {(0, 0)}, 1: {(2/3, -1/3), (4/3, -2/3)}}
        sage: L.det()                 
        3

        sage: g = lambda n: [1] if 1 == n else [1] + (n-1)*[0] + g(n-1) 
        sage: Z8 = Lattice_class( g(8))
        sage: a,A8 = Z8.ev()
        sage: M8 = A8.fqm()
        sage: M8.jordan_decomposition().genus_symbol()
        '2^2'
    """
    
    def __init__( self, q):
        """
        We initialize by a list of integers $[a_1,...,a_N]$. The
        lattice self is then $L = (L,\beta) = (G^{-1}\ZZ^n/\ZZ^n, G[x])$,
        where $G$ is the symmetric matrix
        $G=[a_1,...,a_n;*,a_{n+1},....;..;* ... * a_N]$,
        i.e.~the $a_j$ denote the elements above the diagonal of $G$.
        """
        self.__form = q
        # We compute the rank
        N = len(q)
        assert is_square( 1+8*N)
        n = Integer( (-1+isqrt(1+8*N))/2)
        self.__rank = n
        # We set up the Gram matrix
        self.__G = matrix( IntegerRing(), n, n)
        i = j = 0
        for a in q:
            self.__G[i,j] = self.__G[j,i] = Integer(a)
            if j < n-1:
                j += 1
            else:
                i += 1
                j = i
        # We compute the level
        Gi = self.__G**-1
        self.__Gi = Gi
        a = lcm( map( lambda x: x.denominator(), Gi.list()))
        I = Gi.diagonal()
        b =  lcm( map( lambda x: (x/2).denominator(), I))
        self.__level = lcm( a, b)
        # We define the undelying module and the ambient space
        self.__module = FreeModule( IntegerRing(), n)
        self.__space = self.__module.ambient_vector_space()
        # We compute a shadow vector
        self.__shadow_vector = self.__space([(a%2)/2 for a in self.__G.diagonal()])*Gi
        # We define a basis
        M = Matrix( IntegerRing(), n, n, 1)
        self.__basis = self.__module.basis()
        # We prepare a cache
        self.__dual_vectors = None
        self.__values = None
        self.__chi = {}


    def _latex_( self):
        return 'The ZZ-lattice $(ZZ^{%d}, x\'Gy)$, where $G = %s$' % (self.__rank, latex(self.__G))

    
    def _repr_( self):
        return 'The ZZ-lattice (ZZ^%d, x^tGy), where G = \n%r' % (self.__rank, self.__G)


    def rank( self):
        return self.__rank


    def level( self):
        """
        Return the smallest integer $l$ such that $lG[x]/2 \in \Z$
        for all $x$ in $L^*$.
        """
        return self.__level


    def basis( self):
        return self.__basis


    def module( self):
        """
        Return the underlying module.
        """
        return self.__module


    def hom( self, im_gens, codomain=None, check=True):
        # return self.module().hom( im_gens, codomain = codomain, check = check)
        if not codomain:
            raise NotImplementedError()
        A = matrix( im_gens)
        if codomain and True == check:
            assert self.gram_matrix() == A*codomain.gram_matrix()*A.transpose()
        return Embedding( self, matrix( im_gens), codomain) 


    def space( self):
        """
        Return the ambient vector space.
        """
        return self.__space


    def is_positive( self):
        pass


    def is_even( self):
        I = self.gram_matrix().diagonal()
        for a in I:
            if is_odd(a):
                return False
        return True


    def is_maximal( self):
        pass


    def shadow_level( self):
        """
        Return the level of $L_ev$, where $L_ev$ is the kernel
        of the map $x\mapsto e(G[x]/2)$.

        REMARK
            Lemma: If $L$ is odd, if $s$ is a shadow vector, and if $N$ denotes the
            denominator of $G[s]/2$, then the level of $L_ev$
            equals the lcm of the order of $s$, $N$ and the level of $L$.

            (For the proof:  the requested level is the smallest integer $l$
            such that $lG[x]/2$ is integral for all $x$ in $L^*$ and $x$ in $s+L^*$
            since $L_ev^* = L^* \cup s+L^*$. This $l$ is the smallest integer
            such that $lG[s]/2$, $lG[x]/2$ and $ls^tGx$ are integral for all $x$ in $L^*$.)
        """
        if self.is_even():
            return self.level()
        s = self.a_shadow_vector()
        N = self.beta(s).denominator()
        h = lcm( [x.denominator() for x in s])
        return lcm( [h, N, self.level()])


    def gram_matrix( self):
        return self.__G


    def beta(self, x, y = None):
        if None == y:
            return (x*self.gram_matrix()*x)/2
        else:
            return x*self.gram_matrix()*y


    def det( self):
        return self.gram_matrix().det()


    def dual_vectors( self):
        """
        Return a set of representatives
        for $L^#/L$.
        """
        D,U,V = self.gram_matrix().smith_form()
        # hence D = U * self.gram_matrix() * V
        if None == self.__dual_vectors:
            W = V*D**-1
            S = self.space()
            self.__dual_vectors = [ W*S(v) for v in mrange( D.diagonal())]
        return self.__dual_vectors


    def is_in_dual( self, y):
        """
        Checks whether $y$ is in the dual of self.
        """
        return self._is_int_vec( self.gram_matrix()*y)


    def a_shadow_vector( self):
        """
        Return a vector in the shadow $L^\bullet$ of $L$.

        REMARK
            As shadow vector one can take the diagnal of the Gram
            matrix reduced modulo 2 and multiplied by $1/2G^{-1}$.

            Note that this vector $s$ is arbitrary (in particular,
            it does not satisfy in general $2s \in L$).
        """
        return self.__shadow_vector


    def shadow_vectors( self):
        """
        Return a list of representatives for the vectors in $L^\bullet/L$.
        """
        if self.is_even():
            return self.dual_vectors() 
        R = self.dual_vectors()
        s = self.a_shadow_vector()
        return [s+r for r in R]


    def shadow_vectors_of_order_2( self):
        """
        Return the list of representatives for those
        vectors $x$ in $L^\bullet/L$ such that
        $2x=0$.
        """
        R =  self.shadow_vectors()
        return [r for r in R if self._is_int_vec( 2*r)]
        

    def is_in_shadow( self, r):
        """
        Checks whether $r$ is a shadow vector.
        """
        c = self.a_shadow_vector()
        return self.is_in_dual( r - c)


    def o_invariant( self):
        """
        Return $0$ if $L$ is even, otherwise
        the parity of $G[2*s]$, where $s$ is a shadow vector such that
        $2s$ is in $L$ (so that $(-1)^parity = e(G[2*s]/2)$.
        
        REMARK
            The o_invariant equals the parity of $n_2$, where
            $n_2$ is as in Lemma 3.1 of [Joli-I].
        """
        V = self.shadow_vectors_of_order_2()
        s = V[0]
        return Integer(4*(s*self.gram_matrix()*s))%2
 

    def values( self):
        """
        Return a dictionary
        N \mapsto set of representatives r in $L^\bullet/L$ which satisfy
        $\beta(r) \equiv n/l \bmod \ZZ$, where $l$ is the shadow_level
        (i.e. the level of $L_ev$).

        REMARK
            $\beta(r)+ \Z$ does only depend on $r+L$ if $r$ is a shadow vector,
            otherwise this is not necessarily true. Hence we return only
            shadow vectors here.
        """
        if None == self.__values:
            self.__values = {}
            G = self.gram_matrix()
            l = self.shadow_level()
            R =  self.dual_vectors()
            if not self.is_even():
                s = self.a_shadow_vector()
                R = [s+r for r in R]
            for r in R:
                N = Integer( self.beta(r)*l)
                N = N%l
                if not self.__values.has_key(N):
                    self.__values[N] = [r]
                else:
                    self.__values[N] += [r]
        return self.__values


    def representatives( self, N):
        """
        Return the subset of representatives $r$
        in $L^\bullet/L$ which satisfy $\beta(r) \equiv N/level \bmod \ZZ$.
        """
        v = self.values()
        return v.get(N%self.shadow_level(), [])
        

    def chi( self, t):
        """
        Return the value of the Gauss sum
        $\sum_{x\in L^\bullet/L} e(tG[x]/2)/\sqrt D$,
        where $D = |L^\bullet/L|$.
        """
        t = Integer(t)%self.shadow_level()
        if self.__chi.has_key(t):
            return self.__chi[t]
        l = self.shadow_level()
        z = QQbar.zeta(l)
        w = QQbar(self.det().sqrt())
        V = self.values()
        self.__chi[t] = sum(len(V[a])*z**(t*a) for a in V)/w
        return self.__chi[t]


    def ev( self):
        """
        Return a pair $alpha, L_{ev}$, where $L_{ev}$
        is isomorphic to the kernel of $L\rightarrow \{\pm 1\}$, 
        $x\mapsto e(\beta(x))$,
        and where $alpha$ is an embedding of $L_{ev}$ into $L$
        whose image is this kernel.

        REMARK
            We have to find the kernel of the map
            $x\mapsto G[x] \equiv \sum_{j\in S}x_2 \bmod 2$.
            Here $S$ is the set of indices $i$ such that
            the $i$-diagonal element of $G$ is odd.
            A basis is given by
            $e_i$ ($i not\in S$) and $e_i + e_j$ ($i \in S$, $i\not=j$)
            and $2e_j$, where $j$ is any fixed index in $S$.
        """
        if self.is_even():
            return self.hom( self.basis(), self.module()), self
        D = self.gram_matrix().diagonal()
        S = [ i for i in range( len(D)) if is_odd(D[i])]
        j = min(S)
        e = self.basis()
        n = len(e)
        form = lambda i: e[i] if i not in S else 2*e[j] if i == j else e[i]+e[j] 
        a = [ form(i) for i in range( n)]
        # A = matrix( a).transpose()
        # return A, Lattice_class( [ self.beta( a[i],a[j]) for i in range(n) for j in range(i,n)])
        Lev = Lattice_class( [ self.beta( a[i],a[j]) for i in range(n) for j in range(i,n)])
        alpha = Lev.hom( a, self.module()) 
        return alpha, Lev
 

    def twist( self, a):
        """
        Return the lattice self rescaled by the integer $a$.
        """
        e = self.basis()
        n = len(e)
        return Lattice_class( [ a*self.beta( e[i],e[j]) for i in range(n) for j in range(i,n)])
        

    def fqm( self):
        """
        Return a pair $(f,M)$, where $M$ is the discriminant module
        of $L_ev$ up to isomorphism and $f:L\rightarrow M$ the
        canonical map.

        TODO
            Currently returns only M.
        """
        if self.is_even():
            return FiniteQuadraticModule( self.gram_matrix())
        else:
            a,Lev = self.ev()
            return FiniteQuadraticModule( Lev.gram_matrix())


    def ZN_embeddings( self):
        """
        Return a list of all embeddings of $L$ into $\ZZ^N$ modulo
        the action of $O(\ZZ^N)$.
        """
        def cs_range( f, subset = None):
            """
            For a symmetric semi-positive integral matrix $f$,
            return a list of all integral $n$-vectors $v$ such that
            $x^tfx - (v*x)^2 >= 0$ for all $x$.
            """
            n = f.dimensions()[0]
            b = vector( s.isqrt() for s in f.diagonal())
            zv = vector([0]*n)
            box = [b - vector(t) for t in mrange( (2*b + vector([1]*n)).list()) if b - vector(t) > zv]
            if subset:
                box = [v for v in box if v in subset]
            rge = [v for v in box if min( (f - matrix(v).transpose()*matrix(v)).eigenvalues()) >=0]
            return rge

        def embs( f, subset = None):
            """
            For a semipositive matrix $f$ return a list of all integral
            matrices $M$ such that $f=M^t * M$ modulo the left action
            of $\lim O(\ZZ^N)$ on the set of these matrices.
            """
            res = []
            csr = cs_range( f, subset)
            for v in csr:
                fv = f - matrix(v).transpose()*matrix(v)
                if 0 == fv:
                    res.append( matrix(v))
                else:
                    tmp = embs( fv, csr[ csr.index( v):])
                    for e in tmp:
                        res.append( e.insert_row(0, v))
            return res 

        l_embs = embs( self.gram_matrix())
        import lattice_index
        return map( lambda a: self.hom( a.columns(), lattice_index.LatticeIndex( 'Z^'+ str(a.nrows()))), l_embs)


    def vectors_in_shell ( self, bound, max = 10000):
        """
        Return the list of all $x\not=0$ in self
        such that $\beta(x) <= bound$.
        """
        M = self.module()
        return map( lambda x: M(x), self._vectors_in_shell ( self.gram_matrix(), 2*bound, max))


    def dual_vectors_in_shell ( self, bound, max = 10000):
        """
        Return the list of all $x\not=0$ in the dual of self
        such that $\beta(x) <= bound$.
        """
        l = self.level()
        F = l*self.__Gi
        S = self.space()
        return map( lambda x: S(self.__Gi*x), self._vectors_in_shell ( F, 2*l*bound, max))


    def shadow_vectors_in_shell (self, bound, max = 10000):
        """
        Return the list of all $x\not=0$ in the bullet of self
        such that $\beta(x) <= bound$.
        """
        if self.is_even():
            return self.dual_vectors_in_shell ( bound, max)
        a, Lev = L.ev()
        l = Lev.dual_vectors_in_shell ( bound, max)
        A = a.matrix()
        return filter( lambda x: self.is_in_shadow(x) ,[y*A for y in l])


    @staticmethod
    def _vectors_in_shell( F, bound, max = 1000):
        """
        For an (integral) positive symmetric matrix $F$
        return a list of vectors such that $x^tFx <= bound$.
        The method returns only nonzero vectors
        and for each pair $v$ and $-v$
        only one.

        NOTE
            A wrapper for the pari method qfminim().
        """
        # assert bound >=1, 'Pari bug: bound should be >= 2'
        if bound < 1: return []
        p = F._pari_()
        po = p.qfminim( bound, max, flag = 0).sage()
        if max == po[0]:
            raise ArithmeticError( 'Increase max')
        ves = [vector([0]*F.nrows())]
        for x in po[2].columns():
            ves += [x,-x]
        # return po[2].columns()
        return ves

    
    @staticmethod
    def _is_int_vec( v):
        for c in v:
            if c.denominator() > 1:
                return False
        return True