예제 #1
0
def guard_bits(dop, maj, pt, ordrec, nterms):
    r"""
    Helper for choosing a working precision.

    This is done under the assumption that the first terms of the coefficient
    sequence are computed in interval arithmetic, and then, starting from some
    cutoff index, we switch to something like floating-point arithmetic with an
    rounding error bound computed on the side. This function returns a suggested
    cutoff index and a corresponding number of guard bits to add to the
    precision of the output.

    The computation done by this function is heuristic, but the output does not
    affect the correctness of the final result (only its sharpness and/or the
    computation time).

    The algorithm is based on what we can expect to happen at an ordinary point
    and may or may not work in the regular singular case.
    """

    new_cost = cur_cost = sys.maxsize
    new_bits = cur_bits = None
    new_n0 = cur_n0 = orddeq = dop.order()
    refine = False

    cst = abs(bounds.IC(maj.dop.leading_coefficient()[0])) # ???

    while True:

        # Roughly speaking, the computation of a new coefficient of the series
        # *multiplies* the diameter by the order of the recurrence (minus two).
        # Thus, it is not unreasonable that the loss of precision is of the
        # order of log2(ordrec^nterms). This observation is far from explaining
        # everything, though; in particular, it completely ignores the size of
        # the coefficients. Anyhow, this formula seems to work reasonaly well in
        # practice. It is perhaps a bit pessimistic for simple equations.
        guard_bits_intervals = new_n0*max(1, ZZ(ordrec - 2).nbits())

        # est_rnd_err = rough estimate of global round-off error
        # ≈ (local error for a single term) × (propagation factor)
        # ≈ (ordrec × working prec epsilon) × (value of majorant series)
        rnd_maj = maj(new_n0)
        rnd_maj >>= new_n0
        est_lg_rnd_fac = (cst*rnd_maj.bound(pt.rad, rows=orddeq)).log(2)
        est_lg_rnd_err = 2*bounds.IR(ordrec + 1).log(2)
        if not est_lg_rnd_fac < bounds.IR.zero():
            est_lg_rnd_err += est_lg_rnd_fac
        if est_lg_rnd_fac.is_finite():
            guard_bits_squashed = int(est_lg_rnd_err.ceil().upper()) + 2
        else:
            guard_bits_squashed = sys.maxsize

        # We expect the effective working precision to decrease linearly in the
        # first phase due to interval blow-up, and then stabilize around (target
        # prec + guard_bits_squashed).
        new_cost = (new_n0//2)*guard_bits_intervals + nterms*guard_bits_squashed
        new_bits = guard_bits_intervals + guard_bits_squashed

        logger.debug(
                "n0 = %s, terms = %s, guard bits = %s+%s = %s, cost = %s",
                new_n0, nterms, guard_bits_intervals, guard_bits_squashed,
                new_bits, new_cost)

        if cur_cost <= new_cost < sys.maxsize:
            return cur_n0, cur_bits

        if (refine and maj.can_refine() and
             guard_bits_squashed > guard_bits_intervals + 50):
            maj.refine()
        else:
            new_n0, cur_n0 = new_n0*2, new_n0
            cur_cost = new_cost
            cur_bits = new_bits
        refine = not refine

        if new_n0 > nterms:
            return nterms, guard_bits_intervals
예제 #2
0
    def to_sage(self):
        """
        EXAMPLES::

            sage: macaulay2(ZZ).to_sage()      # optional - macaulay2
            Integer Ring
            sage: macaulay2(QQ).to_sage()      # optional - macaulay2
            Rational Field

            sage: macaulay2(2).to_sage()       # optional - macaulay2
            2
            sage: macaulay2(1/2).to_sage()     # optional - macaulay2
            1/2
            sage: macaulay2(2/1).to_sage()     # optional - macaulay2
            2
            sage: _.parent()                   # optional - macaulay2
            Rational Field
            sage: macaulay2([1,2,3]).to_sage() # optional - macaulay2
            [1, 2, 3]

            sage: m = matrix([[1,2],[3,4]])
            sage: macaulay2(m).to_sage()       # optional - macaulay2
            [1 2]
            [3 4]

            sage: macaulay2(QQ['x,y']).to_sage()    # optional - macaulay2
            Multivariate Polynomial Ring in x, y over Rational Field
            sage: macaulay2(QQ['x']).to_sage()      # optional - macaulay2
            Univariate Polynomial Ring in x over Rational Field
            sage: macaulay2(GF(7)['x,y']).to_sage() # optional - macaulay2
            Multivariate Polynomial Ring in x, y over Finite Field of size 7

            sage: macaulay2(GF(7)).to_sage()       # optional - macaulay2
            Finite Field of size 7
            sage: macaulay2(GF(49, 'a')).to_sage() # optional - macaulay2
            Finite Field in a of size 7^2

            sage: R.<x,y> = QQ[]
            sage: macaulay2(x^2+y^2+1).to_sage()   # optional - macaulay2
            x^2 + y^2 + 1

            sage: R = macaulay2("QQ[x,y]")         # optional - macaulay2
            sage: I = macaulay2("ideal (x,y)")     # optional - macaulay2
            sage: I.to_sage()                      # optional - macaulay2
            Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field

            sage: X = R/I       # optional - macaulay2
            sage: X.to_sage()   # optional - macaulay2
            Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x, y)

            sage: R = macaulay2("QQ^2")  # optional - macaulay2
            sage: R.to_sage()            # optional - macaulay2
            Vector space of dimension 2 over Rational Field

            sage: m = macaulay2('"hello"')  # optional - macaulay2
            sage: m.to_sage()               # optional - macaulay2
            'hello'

        """
        repr_str = str(self)
        cls_str = str(self.cls())
        cls_cls_str = str(self.cls().cls())

        if repr_str == "ZZ":
            from sage.rings.all import ZZ
            return ZZ
        elif repr_str == "QQ":
            from sage.rings.all import QQ
            return QQ

        if cls_cls_str == "Type":
            if cls_str == "List":
                return [entry.to_sage() for entry in self]
            elif cls_str == "Matrix":
                from sage.matrix.all import matrix
                base_ring = self.ring().to_sage()
                entries = self.entries().to_sage()
                return matrix(base_ring, entries)
            elif cls_str == "Ideal":
                parent = self.ring().to_sage()
                gens = self.gens().entries().flatten().to_sage()
                return parent.ideal(*gens)
            elif cls_str == "QuotientRing":
                #Handle the ZZ/n case
                if "ZZ" in repr_str and "--" in repr_str:
                    from sage.rings.all import ZZ, GF
                    external_string = self.external_string()
                    zz, n = external_string.split("/")

                    #Note that n must be prime since it is
                    #coming from Macaulay 2
                    return GF(ZZ(n))

                ambient = self.ambient().to_sage()
                ideal = self.ideal().to_sage()
                return ambient.quotient(ideal)
            elif cls_str == "PolynomialRing":
                from sage.rings.all import PolynomialRing
                from sage.rings.polynomial.term_order import inv_macaulay2_name_mapping

                #Get the base ring
                base_ring = self.coefficientRing().to_sage()

                #Get a string list of generators
                gens = str(self.gens())[1:-1]

                # Check that we are dealing with default degrees, i.e. 1's.
                if self.degrees().any("x -> x != {1}").to_sage():
                    raise ValueError(
                        "cannot convert Macaulay2 polynomial ring with non-default degrees to Sage"
                    )
                #Handle the term order
                external_string = self.external_string()
                order = None
                if "MonomialOrder" not in external_string:
                    order = "degrevlex"
                else:
                    for order_name in inv_macaulay2_name_mapping:
                        if order_name in external_string:
                            order = inv_macaulay2_name_mapping[order_name]
                if len(gens) > 1 and order is None:
                    raise ValueError(
                        "cannot convert Macaulay2's term order to a Sage term order"
                    )

                return PolynomialRing(base_ring, order=order, names=gens)
            elif cls_str == "GaloisField":
                from sage.rings.all import ZZ, GF
                gf, n = repr_str.split(" ")
                n = ZZ(n)
                if n.is_prime():
                    return GF(n)
                else:
                    gen = str(self.gens())[1:-1]
                    return GF(n, gen)
            elif cls_str == "Boolean":
                if repr_str == "true":
                    return True
                elif repr_str == "false":
                    return False
            elif cls_str == "String":
                return str(repr_str)
            elif cls_str == "Module":
                from sage.modules.all import FreeModule
                if self.isFreeModule().to_sage():
                    ring = self.ring().to_sage()
                    rank = self.rank().to_sage()
                    return FreeModule(ring, rank)
        else:
            #Handle the integers and rationals separately
            if cls_str == "ZZ":
                from sage.rings.all import ZZ
                return ZZ(repr_str)
            elif cls_str == "QQ":
                from sage.rings.all import QQ
                repr_str = self.external_string()
                if "/" not in repr_str:
                    repr_str = repr_str + "/1"
                return QQ(repr_str)

            m2_parent = self.cls()
            parent = m2_parent.to_sage()

            if cls_cls_str == "PolynomialRing":
                from sage.misc.sage_eval import sage_eval
                gens_dict = parent.gens_dict()
                return sage_eval(self.external_string(), gens_dict)

        from sage.misc.sage_eval import sage_eval
        try:
            return sage_eval(repr_str)
        except Exception:
            raise NotImplementedError("cannot convert %s to a Sage object" %
                                      repr_str)
예제 #3
0
def make_conductor(ecnfdata, hfield):
    N, c, d = [ZZ(c) for c in ecnfdata['conductor_ideal'][1:-1].split(',')]
    return hfield.K().ideal([N // d, c + d * hfield.K().gen()])
예제 #4
0
    def __init__(self, a, b=None, parent=None, check=True):
        r"""
        Create the cusp a/b in `\mathbb{P}^1(\QQ)`, where if b=0
        this is the cusp at infinity.

        When present, b must either be Infinity or coercible to an
        Integer.

        EXAMPLES::

            sage: Cusp(2,3)
            2/3
            sage: Cusp(3,6)
            1/2
            sage: Cusp(1,0)
            Infinity
            sage: Cusp(infinity)
            Infinity
            sage: Cusp(5)
            5
            sage: Cusp(1/2)
            1/2
            sage: Cusp(1.5)
            3/2
            sage: Cusp(int(7))
            7
            sage: Cusp(1, 2, check=False)
            1/2
            sage: Cusp('sage', 2.5, check=False)          # don't do this!
            sage/2.50000000000000

        ::

            sage: I**2
            -1
            sage: Cusp(I)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert I to a cusp

        ::

            sage: a = Cusp(2,3)
            sage: loads(a.dumps()) == a
            True

        ::

            sage: Cusp(1/3,0)
            Infinity
            sage: Cusp((1,0))
            Infinity

        TESTS::

            sage: Cusp("1/3", 5)
            1/15
            sage: Cusp(Cusp(3/5), 7)
            3/35
            sage: Cusp(5/3, 0)
            Infinity
            sage: Cusp(3,oo)
            0
            sage: Cusp((7,3), 5)
            7/15
            sage: Cusp(int(5), 7)
            5/7

        ::

            sage: Cusp(0,0)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert (0, 0) to a cusp

        ::

            sage: Cusp(oo,oo)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert (+Infinity, +Infinity) to a cusp

        ::

            sage: Cusp(Cusp(oo),oo)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert (Infinity, +Infinity) to a cusp
        """
        if parent is None:
            parent = Cusps
        Element.__init__(self, parent)

        if not check:
            self.__a = a
            self.__b = b
            return

        if b is None:
            if isinstance(a, Integer):
                self.__a = a
                self.__b = ZZ.one()
            elif isinstance(a, Rational):
                self.__a = a.numer()
                self.__b = a.denom()
            elif is_InfinityElement(a):
                self.__a = ZZ.one()
                self.__b = ZZ.zero()
            elif isinstance(a, Cusp):
                self.__a = a.__a
                self.__b = a.__b
            elif isinstance(a, integer_types):
                self.__a = ZZ(a)
                self.__b = ZZ.one()
            elif isinstance(a, (tuple, list)):
                if len(a) != 2:
                    raise TypeError("unable to convert %r to a cusp" % a)
                if ZZ(a[1]) == 0:
                    self.__a = ZZ.one()
                    self.__b = ZZ.zero()
                    return
                try:
                    r = QQ((a[0], a[1]))
                    self.__a = r.numer()
                    self.__b = r.denom()
                except (ValueError, TypeError):
                    raise TypeError("unable to convert %r to a cusp" % a)
            else:
                try:
                    r = QQ(a)
                    self.__a = r.numer()
                    self.__b = r.denom()
                except (ValueError, TypeError):
                    raise TypeError("unable to convert %r to a cusp" % a)
            return

        if is_InfinityElement(b):
            if is_InfinityElement(a) or (isinstance(a, Cusp)
                                         and a.is_infinity()):
                raise TypeError("unable to convert (%r, %r) to a cusp" %
                                (a, b))
            self.__a = ZZ.zero()
            self.__b = ZZ.one()
            return
        elif not b:
            if not a:
                raise TypeError("unable to convert (%r, %r) to a cusp" %
                                (a, b))
            self.__a = ZZ.one()
            self.__b = ZZ.zero()
            return

        if isinstance(a, Integer) or isinstance(a, Rational):
            r = a / ZZ(b)
        elif is_InfinityElement(a):
            self.__a = ZZ.one()
            self.__b = ZZ.zero()
            return
        elif isinstance(a, Cusp):
            if a.__b:
                r = a.__a / (a.__b * b)
            else:
                self.__a = ZZ.one()
                self.__b = ZZ.zero()
                return
        elif isinstance(a, integer_types):
            r = ZZ(a) / b
        elif isinstance(a, (tuple, list)):
            if len(a) != 2:
                raise TypeError("unable to convert (%r, %r) to a cusp" %
                                (a, b))
            r = ZZ(a[0]) / (ZZ(a[1]) * b)
        else:
            try:
                r = QQ(a) / b
            except (ValueError, TypeError):
                raise TypeError("unable to convert (%r, %r) to a cusp" %
                                (a, b))

        self.__a = r.numer()
        self.__b = r.denom()
예제 #5
0
파일: q_analogues.py 프로젝트: yjjcc/sage
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`.

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- the values `n` and `k` defined above

    - ``q`` -- (default: ``None``) the variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`

    - ``algorithm`` -- (default: ``'auto'``) the algorithm to use and can be
      one of the following:

      - ``'auto'`` -- automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- use the naive algorithm
      - ``'cyclotomic'`` -- use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(10, 4, QQbar(I))
        2

    Note that the symbolic computation works (see :trac:`14982`)::

        sage: q_binomial(10, 4, I)
        2

    Check that the algorithm does not matter::

        sage: q_binomial(6, 3, algorithm='naive') == q_binomial(6, 3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    Check that it works with Python integers::

        sage: r = q_binomial(3r, 2r, 1r); r
        3
        sage: type(r)
        <type 'int'>

    Check that arbitrary polynomials work::

        sage: R.<x> = ZZ[]
        sage: q_binomial(2, 1, x^2 - 1, algorithm="naive")
        x^2
        sage: q_binomial(2, 1, x^2 - 1, algorithm="cyclotomic")
        x^2

    Check that the parent is always the parent of ``q``::

        sage: R.<q> = CyclotomicField(3)
        sage: for algo in ["naive", "cyclotomic"]:
        ....:     for n in range(4):
        ....:         for k in range(4):
        ....:             a = q_binomial(n, k, q, algorithm=algo)
        ....:             assert a.parent() is R

    ::

        sage: q_binomial(2, 1, x^2 - 1, algorithm="quantum")
        Traceback (most recent call last):
        ...
        ValueError: unknown algorithm 'quantum'

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, *Factors of the Gaussian
       coefficients*, Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frédéric Chapoton, David Joyner and William Stein
    """
    # sanity checks
    n = ZZ(n)
    k = ZZ(k)
    if n < 0:
        raise ValueError('n must be nonnegative')

    k = min(n - k, k)  # Pick the smallest k

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)

    # We support non-Sage Elements too, where parent(q) is really
    # type(q). The calls R(0) and R(1) should work in all cases to
    # generate the correct 0 and 1 elements.
    R = parent(q)
    zero = R(0)
    one = R(1)

    if k <= 0:
        return one if k == 0 else zero

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        if n <= 70 or k <= n // 4:
            algorithm = 'naive'
        elif is_polynomial:
            algorithm = 'cyclotomic'
        else:
            from sage.symbolic.ring import SR
            if R is SR:
                algorithm = 'cyclotomic'
            else:
                algorithm = 'naive'

    # the algorithms
    while algorithm == 'naive':
        denom = prod(one - q**i for i in range(1, k + 1))
        if not denom:  # q is a root of unity, use the cyclotomic algorithm
            algorithm = 'cyclotomic'
            break
        else:
            num = prod(one - q**i for i in range(n - k + 1, n + 1))
            try:
                try:
                    return num // denom
                except TypeError:
                    return num / denom
            except (TypeError, ZeroDivisionError):
                # use substitution instead
                return q_binomial(n, k)(q)
    if algorithm == 'cyclotomic':
        from sage.rings.polynomial.cyclotomic import cyclotomic_value
        return prod(
            cyclotomic_value(d, q) for d in range(2, n + 1)
            if (n // d) != (k // d) + ((n - k) // d))
    else:
        raise ValueError("unknown algorithm {!r}".format(algorithm))
예제 #6
0
    def contains_coeff_ring(self):
        r"""
        Return whether ``self`` contains its coefficient ring.

        EXAMPLES::

            sage: from sage.modular.modform_hecketriangle.space import ModularForms
            sage: MF = ModularForms(k=0, ep=1, n=8)
            sage: subspace = MF.subspace([1])
            sage: subspace.contains_coeff_ring()
            True
            sage: subspace = MF.subspace([])
            sage: subspace.contains_coeff_ring()
            False
            sage: MF = ModularForms(k=0, ep=-1, n=8)
            sage: subspace = MF.subspace([])
            sage: subspace.contains_coeff_ring()
            False
        """

        return (super(SubSpaceForms, self).contains_coeff_ring() and self.dimension()==ZZ(1))
예제 #7
0
def AdditiveAbelianGroup(invs, remember_generators=True):
    r"""
    Construct a finitely-generated additive abelian group.

    INPUT:

    - ``invs`` (list of integers): the invariants.
      These should all be greater than or equal to zero.

    - ``remember_generators`` (boolean): whether or not to fix a set of
      generators (corresponding to the given invariants, which need not be in
      Smith form).

    OUTPUT:

    The abelian group `\bigoplus_i \ZZ / n_i \ZZ`, where `n_i` are the invariants.

    EXAMPLES::

        sage: AdditiveAbelianGroup([0, 2, 4])
        Additive abelian group isomorphic to Z + Z/2 + Z/4

    An example of the ``remember_generators`` switch::

        sage: G = AdditiveAbelianGroup([0, 2, 3]); G
        Additive abelian group isomorphic to Z + Z/2 + Z/3
        sage: G.gens()
        ((1, 0, 0), (0, 1, 0), (0, 0, 1))

        sage: H = AdditiveAbelianGroup([0, 2, 3], remember_generators = False); H
        Additive abelian group isomorphic to Z/6 + Z
        sage: H.gens()
        ((0, 1, 2), (1, 0, 0))

    There are several ways to create elements of an additive abelian group.
    Realize that there are two sets of generators:  the "obvious" ones composed
    of zeros and ones, one for each invariant given to construct the group, the
    other being a set of minimal generators.  Which set is the default varies
    with the use of the ``remember_generators`` switch.

    First with "obvious" generators.  Note that a raw list will use the
    minimal generators and a vector (a module element) will use the generators
    that pair up naturally with the invariants.  We create the same element
    repeatedly. ::

        sage: H=AdditiveAbelianGroup([3,2,0], remember_generators=True)
        sage: H.gens()
        ((1, 0, 0), (0, 1, 0), (0, 0, 1))
        sage: [H.0, H.1, H.2]
        [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
        sage: p=H.0+H.1+6*H.2; p
        (1, 1, 6)

        sage: H.smith_form_gens()
        ((2, 1, 0), (0, 0, 1))
        sage: q=H.linear_combination_of_smith_form_gens([5,6]); q
        (1, 1, 6)
        sage: p==q
        True

        sage: r=H(vector([1,1,6])); r
        (1, 1, 6)
        sage: p==r
        True

        sage: s=H(p)
        sage: p==s
        True

    Again, but now where the generators are the minimal set.  Coercing a
    list or a vector works as before, but the default generators are different. ::

        sage: G=AdditiveAbelianGroup([3,2,0], remember_generators=False)
        sage: G.gens()
        ((2, 1, 0), (0, 0, 1))
        sage: [G.0, G.1]
        [(2, 1, 0), (0, 0, 1)]
        sage: p=5*G.0+6*G.1; p
        (1, 1, 6)

        sage: H.smith_form_gens()
        ((2, 1, 0), (0, 0, 1))
        sage: q=G.linear_combination_of_smith_form_gens([5,6]); q
        (1, 1, 6)
        sage: p==q
        True

        sage: r=G(vector([1,1,6])); r
        (1, 1, 6)
        sage: p==r
        True

        sage: s=H(p)
        sage: p==s
        True
    """
    invs = [ZZ(x) for x in invs]
    if not all(x >= 0 for x in invs):
        raise ValueError("Invariants must be nonnegative")
    A, B = cover_and_relations_from_invariants(invs)
    if remember_generators:
        G = AdditiveAbelianGroup_fixed_gens(A, B, A.gens())
    else:
        G = AdditiveAbelianGroup_class(A, B)
    return G
예제 #8
0
    def dvalue(self):
        r"""
        Return a symbolic expression (or an exact value in case n=3, 4, 6)
        for the transfinite diameter (or capacity) of ``self``.
        I.e. the first nontrivial Fourier coefficient of the Hauptmodul
        for the Hecke triangle group in case it is normalized to ``J_inv(i)=1``.

        EXAMPLES::

            sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup
            sage: HeckeTriangleGroup(3).dvalue()
            1/1728
            sage: HeckeTriangleGroup(4).dvalue()
            1/256
            sage: HeckeTriangleGroup(5).dvalue()
            e^(2*euler_gamma - 4*pi/(sqrt(5) + 1) + psi(17/20) + psi(13/20))
            sage: HeckeTriangleGroup(6).dvalue()
            1/108
            sage: HeckeTriangleGroup(10).dvalue()
            e^(2*euler_gamma - 4*pi/sqrt(2*sqrt(5) + 10) + psi(4/5) + psi(7/10))
            sage: HeckeTriangleGroup(infinity).dvalue()
            1/64
        """

        n = self._n
        if (n == 3):
            return ZZ(1) / ZZ(2**6 * 3**3)
        elif (n == 4):
            return ZZ(1) / ZZ(2**8)
        elif (n == 6):
            return ZZ(1) / ZZ(2**2 * 3**3)
        elif (n == infinity):
            return ZZ(1) / ZZ(2**6)
        else:
            return exp(-ZZ(2) * psi1(ZZ(1)) + psi1(ZZ(1) - self.alpha()) +
                       psi1(ZZ(1) - self.beta()) - pi * sec(pi / self._n))
예제 #9
0
    def get_FD(self, z):
        r"""
        Return a tuple (A,w) which determines how to map ``z``
        to the usual (strict) fundamental domain of ``self``.

        INPUT:

        - ``z`` -- a complex number or an element of AlgebraicField().

        OUTPUT:

        A tuple ``(A, w)``.

        - ``A`` -- a matrix in ``self`` such that ``A.acton(w)==z``
          (if ``z`` is exact at least).

        - ``w`` -- a complex number or an element of AlgebraicField()
          (depending on the type ``z``) which lies inside the (strict)
          fundamental domain of ``self`` (``self.in_FD(w)==True``) and
          which is equivalent to ``z`` (by the above property).

        EXAMPLES::

            sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup
            sage: G = HeckeTriangleGroup(8)
            sage: z = AlgebraicField()(1+i/2)
            sage: (A, w) = G.get_FD(z)
            sage: A
            [-lam    1]
            [  -1    0]
            sage: A.acton(w) == z
            True

            sage: from sage.modular.modform_hecketriangle.space import ModularForms
            sage: z = (134.12 + 0.22*i).n()
            sage: (A, w) = G.get_FD(z)
            sage: A
            [-73*lam^3 + 74*lam       73*lam^2 - 1]
            [        -lam^2 + 1                lam]
            sage: w
            0.769070776942... + 0.779859114103...*I
            sage: z
            134.120000000... + 0.220000000000...*I
            sage: A.acton(w)
            134.1200000... + 0.2200000000...*I
        """

        ID = self.I()
        T = self.T()
        S = self.S()
        TI = self.T(-1)

        A = ID
        w = z
        while (abs(w) < ZZ(1) or abs(w.real()) > self.lam() / ZZ(2)):
            if (abs(w) < ZZ(1)):
                w = self.S().acton(w)
                A = S * A
            while (w.real() >= self.lam() / ZZ(2)):
                w = TI.acton(w)
                A = TI * A
            while (w.real() < -self.lam() / ZZ(2)):
                w = T.acton(w)
                A = T * A
        if (w.real() == self.lam() / ZZ(2)):
            w = TI.acton(w)
            A = TI * A
        if (abs(w) == ZZ(1) and w.real() > ZZ(0)):
            w = S.acton(w)
            A = S * A

        AI = A.inverse()

        return (AI, A.acton(z))
예제 #10
0
    def contradicts(self, soln):
        """
        Returns ``True`` if this assumption is violated by the given
        variable assignment(s).

        INPUT:

        - ``soln`` - Either a dictionary with variables as keys or a symbolic
            relation with a variable on the left hand side.
        
        EXAMPLES::
        
            sage: from sage.symbolic.assumptions import GenericDeclaration
            sage: GenericDeclaration(x, 'integer').contradicts(x==4)
            False
            sage: GenericDeclaration(x, 'integer').contradicts(x==4.0)
            False
            sage: GenericDeclaration(x, 'integer').contradicts(x==4.5)
            True
            sage: GenericDeclaration(x, 'integer').contradicts(x==sqrt(17))
            True
            sage: GenericDeclaration(x, 'noninteger').contradicts(x==sqrt(17))
            False
            sage: GenericDeclaration(x, 'noninteger').contradicts(x==17)
            True
            sage: GenericDeclaration(x, 'even').contradicts(x==3)
            True
            sage: GenericDeclaration(x, 'complex').contradicts(x==3)
            False
            sage: GenericDeclaration(x, 'imaginary').contradicts(x==3)
            True
            sage: GenericDeclaration(x, 'imaginary').contradicts(x==I)
            False

            sage: var('y,z')
            (y, z)
            sage: GenericDeclaration(x, 'imaginary').contradicts(x==y+z)
            False

            sage: GenericDeclaration(x, 'rational').contradicts(y==pi)
            False
            sage: GenericDeclaration(x, 'rational').contradicts(x==pi)
            True
            sage: GenericDeclaration(x, 'irrational').contradicts(x!=pi)
            False
            sage: GenericDeclaration(x, 'rational').contradicts({x: pi, y: pi})
            True
            sage: GenericDeclaration(x, 'rational').contradicts({z: pi, y: pi})
            False
       """
        if isinstance(soln, dict):
            value = soln.get(self._var)
            if value is None:
                return False
        elif soln.lhs() == self._var:
            value = soln.rhs()
        else:
            return False
        try:
            CC(value)
        except:
            return False
        if self._assumption == 'integer':
            return value not in ZZ
        elif self._assumption == 'noninteger':
            return value in ZZ
        elif self._assumption == 'even':
            return value not in ZZ or ZZ(value) % 2 != 0
        elif self._assumption == 'odd':
            return value not in ZZ or ZZ(value) % 2 != 1
        elif self._assumption == 'rational':
            return value not in QQ
        elif self._assumption == 'irrational':
            return value in QQ
        elif self._assumption == 'real':
            return value not in RR
        elif self._assumption == 'imaginary':
            return value not in CC or CC(value).real() != 0
        elif self._assumption == 'complex':
            return value not in CC
예제 #11
0
def BinaryQF_reduced_representatives(D, primitive_only=False):
    r"""
    Returns a list of inequivalent reduced representatives for the
    equivalence classes of positive definite binary forms of
    discriminant D.

    INPUT:

    - `D` -- (integer) A negative discriminant.

    - ``primitive_only`` -- (bool, default False) flag controlling whether only
      primitive forms are included.

    OUTPUT:

    (list) A lexicographically-ordered list of inequivalent reduced
    representatives for the equivalence classes of positive definite binary
    forms of discriminant `D`.  If ``primitive_only`` is ``True`` then
    imprimitive forms (which only exist when `D` is not fundamental) are
    omitted; otherwise they are included.

    EXAMPLES::

        sage: BinaryQF_reduced_representatives(-4)
        [x^2 + y^2]

        sage: BinaryQF_reduced_representatives(-163)
        [x^2 + x*y + 41*y^2]

        sage: BinaryQF_reduced_representatives(-12)
        [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]

        sage: BinaryQF_reduced_representatives(-16)
        [x^2 + 4*y^2, 2*x^2 + 2*y^2]

        sage: BinaryQF_reduced_representatives(-63)
        [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2]

    The number of inequivalent reduced binary forms with a fixed negative
    fundamental discriminant D is the class number of the quadratic field
    `Q(\sqrt{D})`::

        sage: len(BinaryQF_reduced_representatives(-13*4))
        2
        sage: QuadraticField(-13*4, 'a').class_number()
        2
        sage: p=next_prime(2^20); p
        1048583
        sage: len(BinaryQF_reduced_representatives(-p))
        689
        sage: QuadraticField(-p, 'a').class_number()
        689

        sage: BinaryQF_reduced_representatives(-23*9)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        3*x^2 + 3*x*y + 18*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        6*x^2 - 3*x*y + 9*y^2,
        6*x^2 + 3*x*y + 9*y^2,
        8*x^2 + 7*x*y + 8*y^2]
        sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        8*x^2 + 7*x*y + 8*y^2]

    TESTS::

        sage: BinaryQF_reduced_representatives(5)
        Traceback (most recent call last):
        ...
        ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4
    """
    D = ZZ(D)
    if not ( D < 0 and (D % 4 in [0,1])):
        raise ValueError("discriminant must be negative and congruent to 0 or 1 modulo 4")

    # For a fundamental discriminant all forms are primitive so we need not check:
    if primitive_only:
        primitive_only = not is_fundamental_discriminant(D)

    form_list = []

    from sage.arith.srange import xsrange

    # Only iterate over positive a and over b of the same
    # parity as D such that 4a^2 + D <= b^2 <= a^2
    for a in xsrange(1,1+((-D)//3).isqrt()):
        a4 = 4*a
        s = D + a*a4
        w = 1+(s-1).isqrt() if s > 0 else 0
        if w%2 != D%2: w += 1
        for b in xsrange(w,a+1,2):
            t = b*b-D
            if t % a4 == 0:
                c = t // a4
                if (not primitive_only) or gcd([a,b,c])==1:
                    if b>0 and a>b and c>a:
                        form_list.append(BinaryQF([a,-b,c]))
                    form_list.append(BinaryQF([a,b,c]))

    form_list.sort()
    return form_list
예제 #12
0
 def h(c):
     den = c.denominator()
     num = den * c
     l = list(num)
     l.append(den)
     return max(ZZ(a).nbits() for a in l)
예제 #13
0
파일: axiom.py 프로젝트: sajedel/testsage
    def _sage_(self):
        """
        Convert self to a Sage object.

        EXAMPLES::

            sage: a = axiom(1/2); a #optional - axiom
              1
              -
              2
            sage: a.sage()          #optional - axiom
            1/2
            sage: _.parent()        #optional - axiom
            Rational Field

            sage: gp(axiom(1/2))    #optional - axiom
            1/2

            sage: fricas(1/2).sage() #optional - fricas
            1/2

        DoubleFloat's in Axiom are converted to be in RDF in Sage.

        ::

            sage: axiom(2.0).as_type('DoubleFloat').sage()  #optional - axiom
            2.0
            sage: _.parent() #optional - axiom
            Real Double Field

            sage: axiom(2.1234)._sage_() #optional - axiom
            2.12340000000000
            sage: _.parent()             #optional - axiom
            Real Field with 53 bits of precision
            sage: a = RealField(100)(pi)
            sage: axiom(a)._sage_()      #optional - axiom
            3.1415926535897932384626433833
            sage: _.parent()             #optional - axiom
            Real Field with 100 bits of precision
            sage: axiom(a)._sage_() == a #optional - axiom
            True
            sage: axiom(2.0)._sage_() #optional - axiom
            2.00000000000000
            sage: _.parent() #optional  - axiom
            Real Field with 53 bits of precision

        We can also convert Axiom's polynomials to Sage polynomials.
            sage: a = axiom(x^2 + 1)   #optional - axiom
            sage: a.type()             #optional - axiom
            Polynomial Integer
            sage: a.sage()             #optional - axiom
            x^2 + 1
            sage: _.parent()           #optional - axiom
            Univariate Polynomial Ring in x over Integer Ring
            sage: axiom('x^2 + y^2 + 1/2').sage()    #optional - axiom
            y^2 + x^2 + 1/2
            sage: _.parent()                         #optional - axiom
            Multivariate Polynomial Ring in y, x over Rational Field

        """
        P = self._check_valid()
        type = str(self.type())

        if type in ["Type", "Domain"]:
            return self._sage_domain()

        if type == "Float":
            from sage.rings.all import RealField, ZZ
            prec = max(self.mantissa().length()._sage_(), 53)
            R = RealField(prec)
            x,e,b = self.unparsed_input_form().lstrip('float(').rstrip(')').split(',')
            return R(ZZ(x)*ZZ(b)**ZZ(e))
        elif type == "DoubleFloat":
            from sage.rings.all import RDF
            return RDF(repr(self))
        elif type.startswith('Polynomial'):
            from sage.rings.all import PolynomialRing
            base_ring = P(type.lstrip('Polynomial '))._sage_domain()
            vars = str(self.variables())[1:-1]
            R = PolynomialRing(base_ring, vars)
            return R(self.unparsed_input_form())

        #If all else fails, try using the unparsed input form
        try:
            import sage.misc.sage_eval
            return sage.misc.sage_eval.sage_eval(self.unparsed_input_form())
        except StandardError:
            raise NotImplementedError
예제 #14
0
def interval_series_sum_wrapper(dop, inis, pt, tgt_error, bwrec, stop,
                                fail_fast, effort, stride, ctx=dctx):

    real = pt.is_real_or_symbolic() and all(ini.is_real(dop) for ini in inis)
    if pt.is_numeric and cyPartialSum() is not PartialSum:
        ivs = ComplexBallField
    elif real:
        ivs = RealBallField
    else:
        ivs = ComplexBallField
    input_accuracy = max(0, min(chain([pt.accuracy()],
                                      (ini.accuracy() for ini in inis))))
    logger.log(logging.INFO - 1, "target error = %s", tgt_error)
    if stride is None:
        stride = min(max(50, 2*bwrec.order), max(2, input_accuracy))

    ordinary = dop.leading_coefficient()[0] != 0
    bit_prec0 = utilities.prec_from_eps(tgt_error.eps)
    old_bit_prec = 8 + bit_prec0*(1 + ZZ(bwrec.order - 2).nbits())
    if ctx.squash_intervals and ordinary:
        nterms, lg_mag = dop.est_terms(pt, bit_prec0)
        nterms = (bwrec.order*dop.order() + nterms)*1.2 # let's be pragmatic
        nterms = ZZ((nterms//stride + 1)*stride)
        bit_prec0 += ZZ(dop._naive_height()).nbits() + lg_mag + nterms.nbits()
        n0_squash, g = guard_bits(dop, stop.maj, pt, bwrec.order, nterms)
        # adding twice the computed number of guard bits seems to work better
        # in practice, but I don't really understand why
        bit_prec = bit_prec0 + 2*g
        logger.info("initial working precision = %s + %s = %s (naive = %s), "
                    "squashing intervals for n >= %s",
                    bit_prec0, 2*g, bit_prec, old_bit_prec, n0_squash)
        if fail_fast and bit_prec > 4*bit_prec0 and effort <= 1:
            raise accuracy.PrecisionError
    else:
        bit_prec = old_bit_prec
        n0_squash = sys.maxsize
        logger.info("initial working precision = %s bits", bit_prec)
    max_prec = bit_prec + 2*input_accuracy

    err=None
    for attempt in count(1):
        Intervals = ivs(bit_prec)
        ini_are_accurate = 2*input_accuracy > bit_prec
        # Strictly decrease eps every time to avoid situations where doit
        # would be happy with the result and stop at the same point despite
        # the higher bit_prec. Since attempt starts at 1, we have a bit of
        # room for round-off errors.
        stop.reset(tgt_error.eps >> (4*attempt),
                   stop.fast_fail and ini_are_accurate)

        if _use_inexact_recurrence(bwrec, bit_prec):
            bwrec1 = bwrec.change_base(Intervals)
        else:
            bwrec1 = bwrec

        try:
            sols = series_sum_regular(Intervals, dop, bwrec1, inis, pt, stop,
                                      stride, n0_squash, real)
        except accuracy.PrecisionError:
            if attempt > effort:
                raise
        else:
            logger.debug("bit_prec = %s, err = %s (tgt = %s)", bit_prec,
                        max(sol.total_error for sol in sols), tgt_error)
            if all(tgt_error.reached(
                            sol.total_error,
                            abs(sol.value[0]) if pt.is_numeric else None)
                    for sol in sols):
                return sols

        # if interval squashing didn't give accurate result, switch back to the
        # classical method
        n0_squash = sys.maxsize

        bit_prec *= 2
        if attempt <= effort and bit_prec < max_prec:
            logger.info("lost too much precision, restarting with %d bits",
                        bit_prec)
            continue
        if fail_fast:
            raise accuracy.PrecisionError
        else:
            logger.info("lost too much precision, giving up")
            return sols
예제 #15
0
파일: coxeter_group.py 프로젝트: yjjcc/sage
    def __init__(self, coxeter_matrix, base_ring, index_set):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])
            sage: TestSuite(W).run() # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar)
            sage: TestSuite(W).run() # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]])
            sage: TestSuite(W).run(max_runs=30) # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]])
            sage: TestSuite(W).run(max_runs=30) # long time

        We check that :trac:`16630` is fixed::

            sage: CoxeterGroup(['D',4], base_ring=QQ).category()
            Category of finite irreducible coxeter groups
            sage: CoxeterGroup(['H',4], base_ring=QQbar).category()
            Category of finite irreducible coxeter groups
            sage: F = CoxeterGroups().Finite()
            sage: all(CoxeterGroup([letter,i]) in F
            ....:     for i in range(2,5) for letter in ['A','B','D'])
            True
            sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9))
            True
            sage: CoxeterGroup(['F',4]).category()
            Category of finite irreducible coxeter groups
            sage: CoxeterGroup(['G',2]).category()
            Category of finite irreducible coxeter groups
            sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5))
            True
            sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5))
            True
        """
        self._matrix = coxeter_matrix
        n = coxeter_matrix.rank()
        # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`.
        MS = MatrixSpace(base_ring, n, sparse=True)
        one = MS.one()
        # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty
        E = UniversalCyclotomicField().gen
        if base_ring is UniversalCyclotomicField():

            def val(x):
                if x == -1:
                    return 2
                else:
                    return E(2 * x) + ~E(2 * x)
        elif is_QuadraticField(base_ring):

            def val(x):
                if x == -1:
                    return 2
                else:
                    return base_ring(
                        (E(2 * x) + ~E(2 * x)).to_cyclotomic_field())
        else:
            from sage.functions.trig import cos
            from sage.symbolic.constants import pi

            def val(x):
                if x == -1:
                    return 2
                else:
                    return base_ring(2 * cos(pi / x))

        gens = [
            one + MS([
                SparseEntry(i, j,
                            val(coxeter_matrix[index_set[i], index_set[j]]))
                for j in range(n)
            ]) for i in range(n)
        ]
        # Make the generators dense matrices for consistency and speed
        gens = [g.dense_matrix() for g in gens]
        category = CoxeterGroups()
        # Now we shall see if the group is finite, and, if so, refine
        # the category to ``category.Finite()``. Otherwise the group is
        # infinite and we refine the category to ``category.Infinite()``.
        if self._matrix.is_finite():
            category = category.Finite()
        else:
            category = category.Infinite()
        if all(self._matrix._matrix[i, j] == 2 for i in range(n)
               for j in range(i)):
            category = category.Commutative()
        if self._matrix.is_irreducible():
            category = category.Irreducible()
        self._index_set_inverse = {
            i: ii
            for ii, i in enumerate(self._matrix.index_set())
        }
        FinitelyGeneratedMatrixGroup_generic.__init__(self,
                                                      ZZ(n),
                                                      base_ring,
                                                      gens,
                                                      category=category)
예제 #16
0
    def _conjugacy_representatives(self, max_block_length=ZZ(0), D=None):
        r"""
        Store conjugacy representatives up to block length
        ``max_block_length`` (a non-negative integer, default: 0)
        in the internal dictionary. Previously calculated data is reused.
        This is a helper function for e.g. :meth:`class_number`.

        The set of all (hyperbolic) conjugacy types of block length
        ``t`` is stored in ``self._conj_block[t]``.
        The set of all primitive representatives (so far) with
        discriminant ``D`` is stored in ``self._conj_prim[D]``.
        The set of all non-primitive representatives (so far) with
        discriminant ``D`` is stored in ``self._conj_nonprim[D]``.

        The case of non-positive discriminants is done manually.

        INPUT:

        - ``max_block_length`` -- A non-negative integer (default: ``0``),
                                  the maximal block length.

        - ``D``                -- An element/discriminant of the base ring or
                                  more generally an upper bound for the
                                  involved discriminants. If ``D != None``
                                  then an upper bound for ``max_block_length``
                                  is deduced from ``D`` (default: ``None``).

        EXAMPLES::

            sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup
            sage: G = HeckeTriangleGroup(n=5)
            sage: G.element_repr_method("conj")
            sage: G._conjugacy_representatives(2)

            sage: list(G._conj_block[2])
            [((4, 1), (3, 1)), ((2, 2),), ((3, 2),), ((3, 1), (1, 1)), ((4, 1), (1, 1)), ((4, 1), (2, 1)), ((3, 1), (2, 1)), ((2, 1), (1, 1))]

            sage: [key for key in sorted(G._conj_prim)]
            [-4, lam - 3, 0, 4*lam, 7*lam + 6, 9*lam + 5, 15*lam + 6, 33*lam + 21]
            sage: for key in sorted(G._conj_prim):
            ....:     print(G._conj_prim[key])
            [[S], [S]]
            [[U], [U]]
            [[V(4)]]
            [[V(3)], [V(2)]]
            [[V(1)*V(4)]]
            [[V(3)*V(4)], [V(1)*V(2)]]
            [[V(1)*V(3)], [V(2)*V(4)]]
            [[V(2)*V(3)]]
            sage: [key for key in sorted(G._conj_nonprim)]
            [-lam - 2, lam - 3, 32*lam + 16]

            sage: for key in sorted(G._conj_nonprim):
            ....:     print(G._conj_nonprim[key])
            [[U^(-2)], [U^2], [U^(-2)], [U^2]]
            [[U^(-1)], [U^(-1)]]
            [[V(2)^2], [V(3)^2]]

            sage: G.element_repr_method("default")
        """

        from sage.combinat.partition import OrderedPartitions
        from sage.combinat.combinat import tuples
        from sage.arith.all import divisors

        if not D is None:
            max_block_length = max(coerce_AA(0),
                                   coerce_AA((D + 4) /
                                             (self.lam()**2))).sqrt().floor()
        else:
            try:
                max_block_length = ZZ(max_block_length)
                if max_block_length < 0:
                    raise TypeError
            except TypeError:
                raise ValueError(
                    "max_block_length must be a non-negative integer!")

        if not hasattr(self, "_max_block_length"):
            self._max_block_length = ZZ(0)
            self._conj_block = {}
            self._conj_nonprim = {}
            self._conj_prim = {}

            # It is not clear how to define the class number for D=0:
            # Conjugacy classes are V(n-1)^(+-k) for arbitrary k
            # and the trivial class (what about self_conj_block[0]?).
            #
            # One way is to define it using the fixed points and in
            # that case V(n-1) would be a good representative.
            # The non-primitive case is unclear however...
            #
            # We set it here to ensure that 0 is enlisted as a discriminant...
            #
            self._conj_prim[ZZ(0)] = []
            self._conj_prim[ZZ(0)].append(self.V(self.n() - 1))

            self._elliptic_conj_reps()

        if max_block_length <= self._max_block_length:
            return

        def is_cycle(seq):
            length = len(seq)
            for n in divisors(length):
                if n < length and is_cycle_of_length(seq, n):
                    return True
            return False

        def is_cycle_of_length(seq, n):
            for i in range(n, len(seq)):
                if seq[i] != seq[i % n]:
                    return False
            return True

        j_list = range(1, self.n())

        for t in range(self._max_block_length + 1, max_block_length + 1):
            t_ZZ = ZZ(t)
            if t_ZZ not in self._conj_block:
                self._conj_block[t_ZZ] = set()

            partitions = OrderedPartitions(t).list()
            for par in partitions:
                len_par = len(par)
                exp_list = tuples(j_list, len_par)
                for ex in exp_list:
                    keep = True
                    if len_par > 1:
                        for k in range(-1, len_par - 1):
                            if ex[k] == ex[k + 1]:
                                keep = False
                                break
                    # We don't want powers of V(1)
                    elif ex[0] == 1:
                        keep = False
                    # But: Do we maybe want powers of V(n-1)??
                    # For now we exclude the parabolic cases...
                    elif ex[0] == self.n() - 1:
                        keep = False

                    if keep:
                        conj_type = cyclic_representative(
                            tuple((ZZ(ex[k]), ZZ(par[k]))
                                  for k in range(len_par)))
                        self._conj_block[t_ZZ].add(conj_type)

            for el in self._conj_block[t_ZZ]:
                group_el = prod(
                    [self.V(el[k][0])**el[k][1] for k in range(len(el))])

                #if el != group_el.conjugacy_type():
                #    raise AssertionError("This shouldn't happen!")

                D = group_el.discriminant()
                if coerce_AA(D) < 0:
                    raise AssertionError("This shouldn't happen!")
                if coerce_AA(D) == 0:
                    raise AssertionError("This shouldn't happen!")
                    #continue

                # The primitive cases
                #if group_el.is_primitive():
                if not ((len(el) == 1 and el[0][1] > 1) or is_cycle(el)):
                    if D not in self._conj_prim:
                        self._conj_prim[D] = []
                    self._conj_prim[D].append(group_el)
                # The remaining cases
                else:
                    if D not in self._conj_nonprim:
                        self._conj_nonprim[D] = []
                    self._conj_nonprim[D].append(group_el)

        self._max_block_length = max_block_length
예제 #17
0
    def normal_cone(self):
        r"""
        Return the (closure of the) normal cone of the triangulation.

        Recall that a regular triangulation is one that equals the
        "crease lines" of a convex piecewise-linear function. This
        support function is not unique, for example, you can scale it
        by a positive constant. The set of all piecewise-linear
        functions with fixed creases forms an open cone. This cone can
        be interpreted as the cone of normal vectors at a point of the
        secondary polytope, which is why we call it normal cone. See
        [GKZ]_ Section 7.1 for details.

        OUTPUT:

        The closure of the normal cone. The `i`-th entry equals the
        value of the piecewise-linear function at the `i`-th point of
        the configuration.

        For an irregular triangulation, the normal cone is empty. In
        this case, a single point (the origin) is returned.

        EXAMPLES::

            sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal')
            sage: triangulation
            (<0,1,3>, <0,2,3>)
            sage: N = triangulation.normal_cone();  N
            4-d cone in 4-d lattice
            sage: N.rays()
            (-1,  0,  0,  0),
            ( 1,  0,  1,  0),
            (-1,  0, -1,  0),
            ( 1,  0,  0, -1),
            (-1,  0,  0,  1),
            ( 1,  1,  0,  0),
            (-1, -1,  0,  0)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring
            sage: N.dual().rays()
            (-1, 1, 1, -1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring

        TESTS::

            sage: polytopes.n_simplex(2).triangulate().normal_cone()
            3-d cone in 3-d lattice
            sage: _.dual().is_trivial()
            True
        """
        if not self.point_configuration().base_ring().is_subring(QQ):
            raise NotImplementedError(
                'Only base rings ZZ and QQ are supported')
        from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron
        from sage.matrix.constructor import matrix
        from sage.misc.misc import uniq
        from sage.rings.arith import lcm
        pc = self.point_configuration()
        cs = Constraint_System()
        for facet in self.interior_facets():
            s0, s1 = self._boundary_simplex_dictionary()[facet]
            p = set(s0).difference(facet).pop()
            q = set(s1).difference(facet).pop()
            origin = pc.point(p).reduced_affine_vector()
            base_indices = [i for i in s0 if i != p]
            base = matrix([
                pc.point(i).reduced_affine_vector() - origin
                for i in base_indices
            ])
            sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin)
            relation = [0] * pc.n_points()
            relation[p] = sum(sol) - 1
            relation[q] = 1
            for i, base_i in enumerate(base_indices):
                relation[base_i] = -sol[i]
            rel_denom = lcm([QQ(r).denominator() for r in relation])
            relation = [ZZ(r * rel_denom) for r in relation]
            ex = Linear_Expression(relation, 0)
            cs.insert(ex >= 0)
        from sage.modules.free_module import FreeModule
        ambient = FreeModule(ZZ, self.point_configuration().n_points())
        if cs.empty():
            cone = C_Polyhedron(ambient.dimension(), 'universe')
        else:
            cone = C_Polyhedron(cs)
        from sage.geometry.cone import _Cone_from_PPL
        return _Cone_from_PPL(cone, lattice=ambient)
예제 #18
0
    def sturm_bound(self, weight=2):
        r"""
        Returns the Sturm bound for modular forms of the given weight and level
        this subgroup.

        INPUT:

        -  ``weight`` - an integer `\geq 2` (default: 2)

        EXAMPLES::

            sage: Gamma0(11).sturm_bound(2)
            2
            sage: Gamma0(389).sturm_bound(2)
            65
            sage: Gamma0(1).sturm_bound(12)
            1
            sage: Gamma0(100).sturm_bound(2)
            30
            sage: Gamma0(1).sturm_bound(36)
            3
            sage: Gamma0(11).sturm_bound()
            2
            sage: Gamma0(13).sturm_bound()
            3
            sage: Gamma0(16).sturm_bound()
            4
            sage: GammaH(16,[13]).sturm_bound()
            8
            sage: GammaH(16,[15]).sturm_bound()
            16
            sage: Gamma1(16).sturm_bound()
            32
            sage: Gamma1(13).sturm_bound()
            28
            sage: Gamma1(13).sturm_bound(5)
            70

        FURTHER DETAILS: This function returns a positive integer
        `n` such that the Hecke operators
        `T_1,\ldots, T_n` acting on *cusp forms* generate the
        Hecke algebra as a `\ZZ`-module when the character
        is trivial or quadratic. Otherwise, `T_1,\ldots,T_n`
        generate the Hecke algebra at least as a
        `\ZZ[\varepsilon]`-module, where
        `\ZZ[\varepsilon]` is the ring generated by the
        values of the Dirichlet character `\varepsilon`.
        Alternatively, this is a bound such that if two cusp forms
        associated to this space of modular symbols are congruent modulo
        `(\lambda, q^n)`, then they are congruent modulo
        `\lambda`.

        REFERENCES:

        - See the Agashe-Stein appendix to Lario and Schoof,
          *Some computations with Hecke rings and deformation rings*,
          Experimental Math., 11 (2002), no. 2, 303-311.

        - This result originated in the paper Sturm,
          *On the congruence of modular forms*,
          Springer LNM 1240, 275-280, 1987.

        REMARK: Kevin Buzzard pointed out to me (William Stein) in Fall
        2002 that the above bound is fine for `\Gamma_1(N)` with
        character, as one sees by taking a power of `f`. More
        precisely, if `f \cong 0 \pmod{p}` for first
        `s` coefficients, then `f^r \cong 0 \pmod{p}` for
        first `sr` coefficients. Since the weight of `f^r`
        is `r\cdot k(f)`, it follows that if
        `s \geq b`, where `b` is the Sturm bound for
        `\Gamma_0(N)` at weight `k(f)`, then `f^r`
        has valuation large enough to be forced to be `0` at
        `r*k(f)` by Sturm bound (which is valid if we choose
        `r` correctly). Thus `f \cong 0 \pmod{p}`.
        Conclusion: For `\Gamma_1(N)` with fixed character, the
        Sturm bound is *exactly* the same as for `\Gamma_0(N)`.

        A key point is that we are finding
        `\ZZ[\varepsilon]` generators for the Hecke algebra
        here, not `\ZZ`-generators. So if one wants
        generators for the Hecke algebra over `\ZZ`, this
        bound must be suitably modified (and I'm not sure what the
        modification is).

        AUTHORS:

        - William Stein
        """
        return ZZ((self.index() * weight / ZZ(12)).ceil())
예제 #19
0
def validate_mwrank_input(s):
    r"""
    Returns a string suitable for mwrank input, or raises an error.

    INPUT:

    - `s` -- one of the following:

        - a list or tuple of 5 integers [a1,a2,a3,a4,a6] or (a1,a2,a3,a4,a6)
        - a string of the form '[a1,a2,a3,a4,a6]' or 'a1 a2 a3 a4 a6' where a1, a2, a3, a4, a6 are integers

    OUTPUT:

    For valid input, a string of the form '[a1,a2,a3,a4,a6]'.  For invalid input a ValueError is raised.

    EXAMPLES:

    A list or tuple of 5 integers::

        sage: from sage.interfaces.mwrank import validate_mwrank_input
        sage: validate_mwrank_input([1,2,3,4,5])
        '[1, 2, 3, 4, 5]'
        sage: validate_mwrank_input((-1,2,-3,4,-55))
        '[-1, 2, -3, 4, -55]'
        sage: validate_mwrank_input([1,2,3,4])
        Traceback (most recent call last):
        ...
        ValueError: [1, 2, 3, 4] is not valid input to mwrank (should have 5 entries)
        sage: validate_mwrank_input([1,2,3,4,i])
        Traceback (most recent call last):
        ...
        ValueError: [1, 2, 3, 4, I] is not valid input to mwrank (entries should be integers)


    A string of the form '[a1,a2,a3,a4,a6]' with any whitespace and integers ai::

        sage: validate_mwrank_input('0 -1 1 -7 6')
        '[0,-1,1,-7,6]'
        sage: validate_mwrank_input("[0,-1,1,0,0]\n")
        '[0,-1,1,0,0]'
        sage: validate_mwrank_input('0\t -1\t 1\t 0\t 0\n')
        '[0,-1,1,0,0]'
        sage: validate_mwrank_input('0 -1 1 -7 ')
        Traceback (most recent call last):
        ...
        ValueError: 0 -1 1 -7  is not valid input to mwrank

    """
    if isinstance(s,(list,tuple)):
        from sage.rings.all import ZZ
        if len(s)!=5:
            raise ValueError("%s is not valid input to mwrank (should have 5 entries)" % s)
        try:
            ai = [ZZ(a) for a in s]
            return str(ai)
        except (TypeError,ValueError):
            raise ValueError("%s is not valid input to mwrank (entries should be integers)" % s)

    if isinstance(s,str):
        if AINVS_PLAIN_RE.match(s):
            ai = s.split()
            return "["+",".join(ai)+"]"
        ss = s.replace(' ','').replace('\n','').replace('\t','')
        if AINVS_LIST_RE.match(ss):
            return ss
    raise ValueError("%s is not valid input to mwrank" % s)
예제 #20
0
    def _find_generators(self, maxweight, start_gens, start_weight):
        r"""
        For internal use. This function is called by :meth:`generators` and
        :meth:`gen_forms`: it returns a list of triples `(k, f, F)` where `F`
        is a modular form of weight `k` and `f` is its `q`-expansion coerced
        into the base ring of self.

        INPUT:

        - maxweight: maximum weight to try
        - start_weight: minimum weight to try
        - start_gens: a sequence of tuples of the form `(k, f, F)`, where `F` is a
          modular form of weight `k` and `f` is its `q`-expansion coerced into
          ``self.base_ring()`. Either (but not both) of `f` and `F` may be
          None.

        OUTPUT:

        a list of tuples, formatted as with ``start_gens``.

        EXAMPLES::

            sage: R = ModularFormsRing(Gamma1(4))
            sage: R._find_generators(8, (), 2)
            [(2, 1 + 24*q^2 + 24*q^4 + 96*q^6 + 24*q^8 + O(q^9), 1 + 24*q^2 + 24*q^4 + O(q^6)), (2, q + 4*q^3 + 6*q^5 + 8*q^7 + O(q^9), q + 4*q^3 + 6*q^5 + O(q^6)), (3, 1 + 12*q^2 + 64*q^3 + 60*q^4 + 160*q^6 + 384*q^7 + 252*q^8 + O(q^9), 1 + 12*q^2 + 64*q^3 + 60*q^4 + O(q^6)), (3, q + 4*q^2 + 8*q^3 + 16*q^4 + 26*q^5 + 32*q^6 + 48*q^7 + 64*q^8 + O(q^9), q + 4*q^2 + 8*q^3 + 16*q^4 + 26*q^5 + O(q^6))]
        """
        default_params = (start_gens == () and start_weight == 2)

        if default_params and self.__cached_maxweight != -1:
            verbose("Already know generators up to weight %s -- using those" %
                    self.__cached_maxweight)

            if self.__cached_maxweight >= maxweight:
                return [(k, f, F) for k, f, F in self.__cached_gens
                        if k <= maxweight]

            start_gens = self.__cached_gens
            start_weight = self.__cached_maxweight + 1

        if self.group().is_even():
            increment = 2
        else:
            increment = 1

        working_prec = self.modular_forms_of_weight(maxweight).sturm_bound()

        # parse the list of start gens
        G = []
        for x in start_gens:
            k, f, F = x
            if F is None and f.prec() < working_prec:
                raise ValueError("Need start gens to precision at least %s" %
                                 working_prec)
            elif f is None or f.prec() < working_prec:
                f = F.qexp(working_prec).change_ring(self.base_ring())
            G.append((k, f, F))

        k = start_weight
        if increment == 2 and (k % 2) == 1: k += 1

        while k <= maxweight:

            if self.modular_forms_of_weight(k).dimension() == 0:
                k += increment
                continue

            verbose('Looking at k = %s' % k)
            M = self.modular_forms_of_weight(k)

            # 1. Multiply together all forms in G that give an element
            #    of M.
            if G != []:
                F = _span_of_forms_in_weight(G, k, M.sturm_bound(), None,
                                             False)
            else:
                F = (self.base_ring()**M.sturm_bound()).zero_submodule()

            # 2. If the dimension of the span of the result is equal
            #    to the dimension of M, increment k.
            if F.rank() == M.dimension():
                if self.base_ring().is_field() or F.index_in_saturation() == 1:
                    # TODO: Do something clever if the submodule's of the right
                    # rank but not saturated -- avoid triggering needless
                    # modular symbol computations.
                    verbose('Nothing new in weight %s' % k)
                    k += increment
                    continue

            # 3. If the dimension is less, compute a basis for G, and
            #    try adding basis elements of M into G.

            verbose(
                "Known generators span a subspace of dimension %s of space of dimension %s"
                % (F.dimension(), M.dimension()))
            if self.base_ring() == ZZ:
                verbose("saturation index is %s" % F.index_in_saturation())

            t = verbose("Computing more modular forms at weight %s" % k)
            kprec = M.sturm_bound()
            if self.base_ring() == QQ:
                B = M.q_echelon_basis(working_prec)
            else:
                B = M.q_integral_basis(working_prec)
            t = verbose("done computing forms", t)
            V = F.ambient_module().submodule_with_basis(
                [f.padded_list(kprec) for f in B])
            Q = V / F
            for q in Q.gens():
                try:
                    qc = V.coordinates(Q.lift(q))
                except AttributeError:
                    # work around a silly free module bug
                    qc = V.coordinates(q.lift())
                qcZZ = [ZZ(_) for _ in qc]  # lift to ZZ so we can define F
                f = sum([B[i] * qcZZ[i] for i in range(len(B))])
                F = M(f)
                G.append((k, f.change_ring(self.base_ring()), F))

            verbose('added %s new generators' % Q.ngens(), t)
            k += increment

        if default_params:
            self.__cached_maxweight = maxweight
            self.__cached_gens = G

        return G
예제 #21
0
    def is_gamma0_equiv(self, other, N, transformation=None):
        r"""
        Return whether self and other are equivalent modulo the action of
        `\Gamma_0(N)` via linear fractional transformations.

        INPUT:


        -  ``other`` - Cusp

        -  ``N`` - an integer (specifies the group
           Gamma_0(N))

        -  ``transformation`` - None (default) or either the string 'matrix' or 'corner'. If 'matrix',
           it also returns a matrix in Gamma_0(N) that sends self to other. The matrix is chosen such that the lower left entry is as small as possible in absolute value. If 'corner' (or True for backwards compatibility), it returns only the upper left entry of such a matrix.


        OUTPUT:


        -  a boolean - True if self and other are equivalent

        -  a matrix or an integer- returned only if transformation is 'matrix' or 'corner', respectively.


        EXAMPLES::

            sage: x = Cusp(2,3)
            sage: y = Cusp(4,5)
            sage: x.is_gamma0_equiv(y, 2)
            True
            sage: _, ga = x.is_gamma0_equiv(y, 2, 'matrix'); ga
            [-1  2]
            [-2  3]
            sage: x.is_gamma0_equiv(y, 3)
            False
            sage: x.is_gamma0_equiv(y, 3, 'matrix')
            (False, None)
            sage: Cusp(1/2).is_gamma0_equiv(1/3,11,'corner')
            (True, 19)

            sage: Cusp(1,0)
            Infinity
            sage: z = Cusp(1,0)
            sage: x.is_gamma0_equiv(z, 3, 'matrix')
            (
                  [-1  1]
            True, [-3  2]
            )


        ALGORITHM: See Proposition 2.2.3 of Cremona's book 'Algorithms for
        Modular Elliptic Curves', or Prop 2.27 of Stein's Ph.D. thesis.
        """
        if transformation not in [False, True, "matrix", None, "corner"]:
            raise ValueError(
                "Value %s of the optional argument transformation is not valid."
            )

        if not isinstance(other, Cusp):
            other = Cusp(other)
        N = ZZ(N)
        u1 = self.__a
        v1 = self.__b
        u2 = other.__a
        v2 = other.__b

        zero = ZZ.zero()
        one = ZZ.one()

        if transformation == "matrix":
            from sage.matrix.constructor import matrix

        if v1 == v2 and u1 == u2:
            if not transformation:
                return True
            elif transformation == "matrix":
                return True, matrix(ZZ, [[1, 0], [0, 1]])
            else:
                return True, one

        # a necessary, but not sufficient condition unless N is square-free
        if v1.gcd(N) != v2.gcd(N):
            if not transformation:
                return False
            else:
                return False, None

        if (u1, v1) != (zero, one):
            if v1 in [zero, one]:
                s1 = one
            else:
                s1 = u1.inverse_mod(v1)
        else:
            s1 = 0
        if (u2, v2) != (zero, one):
            if v2 in [zero, one]:
                s2 = one
            else:
                s2 = u2.inverse_mod(v2)
        else:
            s2 = zero
        g = (v1 * v2).gcd(N)
        a = s1 * v2 - s2 * v1
        if a % g != 0:
            if not transformation:
                return False
            else:
                return False, None

        if not transformation:
            return True

        # Now we know the cusps are equivalent.  Use the proof of Prop 2.2.3
        # of Cremona to find a matrix in Gamma_0(N) relating them.
        if v1 == 0:  # the first is oo
            if v2 == 0:  # both are oo
                if transformation == "matrix":
                    return (True, matrix(ZZ, [[1, 0], [0, 1]]))
                else:
                    return (True, one)
            else:
                dum, s2, r2 = u2.xgcd(-v2)
                assert dum.is_one()
                if transformation == "matrix":
                    return (True, matrix(ZZ, [[u2, r2], [v2, s2]]))
                else:
                    return (True, u2)

        elif v2 == 0:  # the second is oo
            dum, s1, r1 = u1.xgcd(-v1)
            assert dum.is_one()
            if transformation == "matrix":
                return (True, matrix(ZZ, [[s1, -r1], [-v1, u1]]))
            else:
                return (True, s1)

        dum, s2, r2 = u2.xgcd(-v2)
        assert dum.is_one()
        dum, s1, r1 = u1.xgcd(-v1)
        assert dum.is_one()
        a = s1 * v2 - s2 * v1
        assert (a % g).is_zero()
        # solve x*v1*v2 + a = 0 (mod N).
        d, x0, y0 = (v1 * v2).xgcd(N)  # x0*v1*v2 + y0*N = d = g.
        # so x0*v1*v2 - g = 0 (mod N)
        x = -x0 * ZZ(a / g)
        # now  x*v1*v2 + a = 0 (mod N)

        # the rest is all added in trac #10926
        s1p = s1 + x * v1
        M = N // g

        if transformation == "matrix":
            C = s1p * v2 - s2 * v1
            if C % (M * v1 * v2) == 0:
                k = -C // (M * v1 * v2)
            else:
                k = -(C / (M * v1 * v2)).round()

            s1pp = s1p + k * M * v1
            # C += k*M*v1*v2  # is now the smallest in absolute value
            C = s1pp * v2 - s2 * v1
            A = u2 * s1pp - r2 * v1

            r1pp = r1 + (x + k * M) * u1
            B = r2 * u1 - r1pp * u2
            D = s2 * u1 - r1pp * v2

            ga = matrix(ZZ, [[A, B], [C, D]])
            assert ga.det() == 1
            assert C % N == 0
            assert (A * u1 + B * v1) / (C * u1 + D * v1) == u2 / v2
            return (True, ga)

        else:
            # mainly for backwards compatibility and
            # for how it is used in modular symbols
            A = (u2 * s1p - r2 * v1)
            if u2 != 0 and v1 != 0:
                A = A % (u2 * v1 * M)
            return (True, A)
예제 #22
0
    def cuspidal_ideal_generators(self, maxweight=8, prec=None):
        r"""
        Calculate generators for the ideal of cuspidal forms in this ring, as a
        module over the whole ring.

        EXAMPLES::

            sage: ModularFormsRing(Gamma0(3)).cuspidal_ideal_generators(maxweight=12)
            [(6, q - 6*q^2 + 9*q^3 + 4*q^4 + O(q^5), q - 6*q^2 + 9*q^3 + 4*q^4 + 6*q^5 + O(q^6))]
            sage: [k for k,f,F in ModularFormsRing(13, base_ring=ZZ).cuspidal_ideal_generators(maxweight=14)]
            [4, 4, 4, 6, 6, 12]
        """
        working_prec = self.modular_forms_of_weight(maxweight).sturm_bound()

        if self.__cached_cusp_maxweight > -1:
            k = self.__cached_cusp_maxweight + 1
            verbose(
                "Already calculated cusp gens up to weight %s -- using those" %
                (k - 1))

            # we may need to increase the precision of the cached cusp
            # generators
            G = []
            for j, f, F in self.__cached_cusp_gens:
                if f.prec() >= working_prec:
                    f = F.qexp(working_prec).change_ring(self.base_ring())
                G.append((j, f, F))
        else:
            k = 2
            G = []

        while k <= maxweight:
            t = verbose("Looking for cusp generators in weight %s" % k)

            kprec = self.modular_forms_of_weight(k).sturm_bound()

            flist = []

            for (j, f, F) in G:
                for g in self.q_expansion_basis(k - j, prec=kprec):
                    flist.append(g * f)
            A = self.base_ring()**kprec
            W = A.span([A(f.padded_list(kprec)) for f in flist])

            S = self.modular_forms_of_weight(k).cuspidal_submodule()
            if (W.rank() == S.dimension() and
                (self.base_ring().is_field() or W.index_in_saturation() == 1)):
                verbose("Nothing new in weight %s" % k, t)
                k += 1
                continue

            t = verbose(
                "Known cusp generators span a submodule of dimension %s of space of dimension %s"
                % (W.rank(), S.dimension()), t)

            B = S.q_integral_basis(prec=working_prec)
            V = A.span([
                A(f.change_ring(self.base_ring()).padded_list(kprec))
                for f in B
            ])
            Q = V / W

            for q in Q.gens():
                try:
                    qc = V.coordinates(Q.lift(q))
                except AttributeError:
                    # work around a silly free module bug
                    qc = V.coordinates(q.lift())
                qcZZ = [ZZ(_) for _ in qc]  # lift to ZZ so we can define F
                f = sum([B[i] * qcZZ[i] for i in range(len(B))])
                F = S(f)
                G.append((k, f.change_ring(self.base_ring()), F))

            verbose('added %s new generators' % Q.ngens(), t)
            k += 1

        self.__cached_cusp_maxweight = maxweight
        self.__cached_cusp_gens = G

        if prec is None:
            return G
        elif prec <= working_prec:
            return [(k, f.truncate_powerseries(prec), F) for k, f, F in G]
        else:
            # user wants increased precision, so we may as well cache that
            Gnew = [(k, F.qexp(prec).change_ring(self.base_ring()), F)
                    for k, f, F in G]
            self.__cached_cusp_gens = Gnew
            return Gnew
예제 #23
0
    def is_gamma_h_equiv(self, other, G):
        r"""
        Return a pair (b, t), where b is True or False as self and other
        are equivalent under the action of G, and t is 1 or -1, as
        described below.

        Two cusps `u1/v1` and `u2/v2` are equivalent modulo
        Gamma_H(N) if and only if `v1 =  h*v2 (\mathrm{mod} N)` and
        `u1 =  h^{(-1)}*u2 (\mathrm{mod} gcd(v1,N))` or
        `v1 = -h*v2 (mod N)` and
        `u1 = -h^{(-1)}*u2 (\mathrm{mod} gcd(v1,N))` for some
        `h \in H`. Then t is 1 or -1 as c and c' fall into the
        first or second case, respectively.

        INPUT:


        -  ``other`` - Cusp

        -  ``G`` - a congruence subgroup Gamma_H(N)


        OUTPUT:


        -  ``bool`` - True if self and other are equivalent

        -  ``int`` - -1, 0, 1; extra info


        EXAMPLES::

            sage: x = Cusp(2,3)
            sage: y = Cusp(4,5)
            sage: x.is_gamma_h_equiv(y,GammaH(13,[2]))
            (True, 1)
            sage: x.is_gamma_h_equiv(y,GammaH(13,[5]))
            (False, 0)
            sage: x.is_gamma_h_equiv(y,GammaH(5,[]))
            (False, 0)
            sage: x.is_gamma_h_equiv(y,GammaH(23,[4]))
            (True, -1)

        Enumerating the cusps for a space of modular symbols uses this
        function.

        ::

            sage: G = GammaH(25,[6]) ; M = G.modular_symbols() ; M
            Modular Symbols space of dimension 11 for Congruence Subgroup Gamma_H(25) with H generated by [6] of weight 2 with sign 0 and over Rational Field
            sage: M.cusps()
            [37/75, 1/2, 31/125, 1/4, -2/5, 2/5, -1/5, 1/10, -3/10, 1/15, 7/15, 9/20]
            sage: len(M.cusps())
            12

        This is always one more than the associated space of weight 2 Eisenstein
        series.

        ::

            sage: G.dimension_eis(2)
            11
            sage: M.cuspidal_subspace()
            Modular Symbols subspace of dimension 0 of Modular Symbols space of dimension 11 for Congruence Subgroup Gamma_H(25) with H generated by [6] of weight 2 with sign 0 and over Rational Field
            sage: G.dimension_cusp_forms(2)
            0
        """
        from sage.modular.arithgroup.all import is_GammaH
        if not isinstance(other, Cusp):
            other = Cusp(other)
        if not is_GammaH(G):
            raise TypeError("G must be a group GammaH(N).")

        H = G._list_of_elements_in_H()
        N = ZZ(G.level())
        u1 = self.__a
        v1 = self.__b
        u2 = other.__a
        v2 = other.__b
        g = v1.gcd(N)

        for h in H:
            v_tmp = (h * v1) % N
            u_tmp = (h * u2) % N
            if (v_tmp - v2) % N == 0 and (u_tmp - u1) % g == 0:
                return True, 1
            if (v_tmp + v2) % N == 0 and (u_tmp + u1) % g == 0:
                return True, -1
        return False, 0
예제 #24
0
    def _group_gens(self):
        r"""
        Return a set of generators of the group `S(K_0) / S(K_u)` (which is
        either `{\rm SL}_2(\ZZ / p^u \ZZ)` if the conductor is even, and a
        quotient of an Iwahori subgroup if the conductor is odd).

        EXAMPLES::

            sage: from sage.modular.local_comp.type_space import example_type_space
            sage: example_type_space()._group_gens()
            [[1, 1, 0, 1], [0, -1, 1, 0]]
            sage: example_type_space(3)._group_gens()
            [[1, 1, 0, 1], [1, 0, 3, 1], [2, 0, 0, 5]]
        """
        if (self.conductor() % 2) == 0:
            return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(0), ZZ(-1), ZZ(1), ZZ(0)] ]
        else:
            p = self.prime()
            if p == 2:
                return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)] ]
            else:
                a = Zmod(p**(self.u() + 1))(ZZ(Zmod(p).unit_gens()[0]))
                return [ [ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)],
                         [ZZ(a), 0, 0, ZZ(~a)] ]
    def __init__(self, group, base_ring=QQ):
        r"""
        The ring of modular forms (of weights 0 or at least 2) for a congruence
        subgroup of `{\rm SL}_2(\ZZ)`, with coefficients in a specified base ring.

        INPUT:

        - ``group`` -- a congruence subgroup of `{\rm SL}_2(\ZZ)`, or a
          positive integer `N` (interpreted as `\Gamma_0(N)`)

        - ``base_ring`` (ring, default: `\QQ`) -- a base ring, which should be
          `\QQ`, `\ZZ`, or the integers mod `p` for some prime `p`.

        EXAMPLES::

            sage: ModularFormsRing(Gamma1(13))
            Ring of modular forms for Congruence Subgroup Gamma1(13) with coefficients in Rational Field
            sage: m = ModularFormsRing(4); m
            Ring of modular forms for Congruence Subgroup Gamma0(4) with coefficients in Rational Field
            sage: m.modular_forms_of_weight(2)
            Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(4) of weight 2 over Rational Field
            sage: m.modular_forms_of_weight(10)
            Modular Forms space of dimension 6 for Congruence Subgroup Gamma0(4) of weight 10 over Rational Field
            sage: m == loads(dumps(m))
            True
            sage: m.generators()
            [(2, 1 + 24*q^2 + 24*q^4 + 96*q^6 + 24*q^8 + O(q^10)),
            (2, q + 4*q^3 + 6*q^5 + 8*q^7 + 13*q^9 + O(q^10))]
            sage: m.q_expansion_basis(2,10)
            [1 + 24*q^2 + 24*q^4 + 96*q^6 + 24*q^8 + O(q^10),
             q + 4*q^3 + 6*q^5 + 8*q^7 + 13*q^9 + O(q^10)]
            sage: m.q_expansion_basis(3,10)
            []
            sage: m.q_expansion_basis(10,10)
            [1 + 10560*q^6 + 3960*q^8 + O(q^10),
             q - 8056*q^7 - 30855*q^9 + O(q^10),
             q^2 - 796*q^6 - 8192*q^8 + O(q^10),
             q^3 + 66*q^7 + 832*q^9 + O(q^10),
             q^4 + 40*q^6 + 528*q^8 + O(q^10),
             q^5 + 20*q^7 + 190*q^9 + O(q^10)]

        TESTS:

        Check that :trac:`15037` is fixed::

            sage: ModularFormsRing(3.4)
            Traceback (most recent call last):
            ...
            ValueError: Group (=3.40000000000000) should be a congruence subgroup
            sage: ModularFormsRing(Gamma0(2), base_ring=PolynomialRing(ZZ,x))
            Traceback (most recent call last):
            ...
            ValueError: Base ring (=Univariate Polynomial Ring in x over Integer Ring) should be QQ, ZZ or a finite prime field
        """
        if isinstance(group, (int, long, Integer)):
            group = Gamma0(group)
        elif not is_CongruenceSubgroup(group):
            raise ValueError("Group (=%s) should be a congruence subgroup" %
                             group)

        if base_ring != ZZ and not base_ring.is_prime_field():
            raise ValueError(
                "Base ring (=%s) should be QQ, ZZ or a finite prime field" %
                base_ring)

        self.__group = group
        self.__base_ring = base_ring
        self.__cached_maxweight = ZZ(-1)
        self.__cached_gens = []
        self.__cached_cusp_maxweight = ZZ(-1)
        self.__cached_cusp_gens = []
예제 #26
0
    def rho(self, g):
        r"""
        Calculate the action of the group element `g` on the type space.

        EXAMPLES::

            sage: from sage.modular.local_comp.type_space import example_type_space
            sage: T = example_type_space(2)
            sage: m = T.rho([2,0,0,1]); m
            [ 1 -2  1  0]
            [ 1 -1  0  1]
            [ 1  0 -1  1]
            [ 0  1 -2  1]
            sage: v = T.eigensymbol_subspace().basis()[0]
            sage: m * v == v
            True

        We test that it is a left action::

            sage: T = example_type_space(0)
            sage: a = [0,5,4,3]; b = [0,2,3,5]; ab = [1,4,2,2]
            sage: T.rho(ab) == T.rho(a) * T.rho(b)
            True

        An odd level example::

            sage: from sage.modular.local_comp.type_space import TypeSpace
            sage: T = TypeSpace(Newform('54a'), 3)
            sage: a = [0,1,3,0]; b = [2,1,0,1]; ab = [0,1,6,3]
            sage: T.rho(ab) == T.rho(a) * T.rho(b)
            True
        """
        if not self.is_minimal():
            raise NotImplementedError( "Group action on non-minimal type space not implemented" )

        if self.u() == 0:
           # silly special case: rep is principal series or special, so SL2
           # action on type space is trivial
           raise ValueError( "Representation is not supercuspidal" )

        p = self.prime()
        f = p**self.u()
        g = [ZZ(_) for _ in g]
        d = (g[0]*g[3] - g[2]*g[1])

        # g is in S(K_0) (easy case)
        if d % f == 1:
            return self._rho_s(g)

        # g is in K_0, but not in S(K_0)

        if d % p != 0:
            try:
                a = self._a
            except AttributeError:
                self._discover_torus_action()
                a = self._a
            i = 0
            while (d * a**i) % f != 1:
                i += 1
                if i > f: raise ArithmeticError
            return self._rho_s([a**i*g[0], g[1], a**i*g[2], g[3]]) * self._amat**(-i)

        # funny business

        if (self.conductor() % 2 == 0):
            if all([x.valuation(p) > 0 for x in g]):
                eps = self.form().character()(crt(1, p, f, self.tame_level()))
                return ~eps * self.rho([x // p for x in g])
            else:
                raise ArithmeticError( "g(={0}) not in K".format(g) )

        else:
            m = matrix(ZZ, 2, g)
            s = m.det().valuation(p)
            mm = (matrix(QQ, 2, [0, -1, p, 0])**(-s) * m).change_ring(ZZ)
            return self._unif_ramified()**s * self.rho(mm.list())
예제 #27
0
def hecke_stable_subspace(chi, aux_prime=ZZ(2)):
    r"""
    Compute a q-expansion basis for S_1(chi). 

    Results are returned as q-expansions to a certain fixed (and fairly high)
    precision. If more precision is required this can be obtained with
    :func:`modular_ratio_to_prec`.

    EXAMPLES::

        sage: from sage.modular.modform.weight1 import hecke_stable_subspace
        sage: hecke_stable_subspace(DirichletGroup(59, QQ).0)
        [q - q^3 + q^4 - q^5 - q^7 - q^12 + q^15 + q^16 + 2*q^17 - q^19 - q^20 + q^21 + q^27 - q^28 - q^29 + q^35 + O(q^40)]
    """
    from sage.modular.modform.constructor import EisensteinForms
    if chi(-1) == 1: return []
    N = chi.modulus()
    chi = chi.minimize_base_ring()
    K = chi.base_ring()

    # Auxiliary prime for Hecke stability method
    l = aux_prime
    while l.divides(N):
        l = l.next_prime()
    verbose("Auxilliary prime: %s" % l, level=1)

    # Compute working precision
    R = l * Gamma0(N).sturm_bound(l + 2)

    t = verbose("Computing modular ratio space", level=1)
    mrs = modular_ratio_space(chi)

    t = verbose("Computing modular ratios to precision %s" % R, level=1)
    qexps = [modular_ratio_to_prec(chi, f, R) for f in mrs]
    verbose("Done", t=t, level=1)

    # We want to compute the largest subspace of I stable under T_l. To do
    # this, we compute I intersect T_l(I) modulo q^(R/l), and take its preimage
    # under T_l, which is then well-defined modulo q^R.

    from sage.modular.modform.hecke_operator_on_qexp import hecke_operator_on_qexp

    t = verbose("Computing Hecke-stable subspace", level=1)
    A = PowerSeriesRing(K, 'q')
    r = R // l
    V = K**R
    W = K**r
    Tl_images = [hecke_operator_on_qexp(f, l, 1, chi) for f in qexps]
    qvecs = [V(x.padded_list(R)) for x in qexps]
    qvecs_trunc = [W(x.padded_list(r)) for x in qexps]
    Tvecs = [W(x.padded_list(r)) for x in Tl_images]

    I = V.submodule(qvecs)
    Iimage = W.span(qvecs_trunc)
    TlI = W.span(Tvecs)
    Jimage = Iimage.intersection(TlI)
    J = I.Hom(W)(Tvecs).inverse_image(Jimage)

    verbose("Hecke-stable subspace is %s-dimensional" % J.dimension(),
            t=t,
            level=1)

    if J.rank() == 0: return []

    # The theory does not guarantee that J is exactly S_1(chi), just that it is
    # intermediate between S_1(chi) and M_1(chi). In every example I know of,
    # it is equal to S_1(chi), but just for honesty, we check this anyway.
    t = verbose("Checking cuspidality", level=1)
    JEis = V.span(
        V(x.padded_list(R))
        for x in EisensteinForms(chi, 1).q_echelon_basis(prec=R))
    D = JEis.intersection(J)
    if D.dimension() != 0:
        raise ArithmeticError("Got non-cuspidal form!")
    verbose("Done", t=t, level=1)
    qexps = Sequence(A(x.list()).add_bigoh(R) for x in J.gens())
    return qexps
예제 #28
0
def CyclicSievingPolynomial(L, cyc_act=None, order=None, get_order=False):
    """
    Returns the unique polynomial p of degree smaller than order such that the triple
    ( L, cyc_act, p ) exhibits the CSP. If ``cyc_act`` is None, ``L`` is expected to contain the orbit lengths.

    INPUT:

    - L -- if cyc_act is None: list of orbit sizes, otherwise list of objects

    - cyc_act -- (default:None) function taking an element of L and returning an element of L (must define a bijection on L)

    - order -- (default:None) if set to an integer, this cyclic order of cyc_act is used (must be an integer multiple of the order of cyc_act)
                               otherwise, the order of cyc_action is used

    - get_order -- (default:False) if True, a tuple [p,n] is returned where p as above, and n is the order

    EXAMPLES::

        sage: from sage.combinat.cyclic_sieving_phenomenon import CyclicSievingPolynomial
        sage: S42 = [ Set(S) for S in subsets([1,2,3,4]) if len(S) == 2 ]; S42
        [{1, 2}, {1, 3}, {2, 3}, {1, 4}, {2, 4}, {3, 4}]
        sage: cyc_act = lambda S: Set( i.mod(4)+1 for i in S)
        sage: cyc_act([1,3])
        {2, 4}
        sage: cyc_act([1,4])
        {1, 2}
        sage: CyclicSievingPolynomial( S42, cyc_act )
        q^3 + 2*q^2 + q + 2
        sage: CyclicSievingPolynomial( S42, cyc_act, get_order=True )
        [q^3 + 2*q^2 + q + 2, 4]
        sage: CyclicSievingPolynomial( S42, cyc_act, order=8 )
        q^6 + 2*q^4 + q^2 + 2
        sage: CyclicSievingPolynomial([4,2])
        q^3 + 2*q^2 + q + 2

    TESTS:

    We check that :trac:`13997` is handled::

        sage: CyclicSievingPolynomial( S42, cyc_act, order=8, get_order=True )
        [q^6 + 2*q^4 + q^2 + 2, 8]
    """
    if cyc_act:
        orbits = orbit_decomposition(L, cyc_act)
    else:
        orbits = [list(range(k)) for k in L]

    R = QQ['q']
    q = R.gen()
    p = R(0)

    orbit_sizes = {}
    for orbit in orbits:
        l = len(orbit)
        if l in orbit_sizes:
            orbit_sizes[l] += 1
        else:
            orbit_sizes[l] = 1

    n = lcm(list(orbit_sizes))

    if order:
        if order.mod(n) != 0:
            raise ValueError(
                "The given order is not valid as it is not a multiple of the order of the given cyclic action"
            )
    else:
        order = n

    for i in range(n):
        if i == 0:
            j = sum(orbit_sizes.values())
        else:
            j = sum(orbit_sizes[l] for l in orbit_sizes
                    if ZZ(i).mod(n / l) == 0)
        p += j * q**i

    p = p(q**ZZ(order / n))

    if get_order:
        return [p, order]
    else:
        return p
예제 #29
0
    def chebyshev_polynomial(self, n, kind='first', monic=False):
        """
        Generates an endomorphism of this affine line by a Chebyshev polynomial.

        Chebyshev polynomials are a sequence of recursively defined orthogonal
        polynomials. Chebyshev of the first kind are defined as `T_0(x) = 1`,
        `T_1(x) = x`, and `T_{n+1}(x) = 2xT_n(x) - T_{n-1}(x)`. Chebyshev of
        the second kind are defined as `U_0(x) = 1`,
        `U_1(x) = 2x`, and `U_{n+1}(x) = 2xU_n(x) - U_{n-1}(x)`.

        INPUT:

        - ``n`` -- a non-negative integer.

        - ``kind`` -- ``first`` or ``second`` specifying which kind of chebyshev the user would like
          to generate. Defaults to ``first``.

        - ``monic`` -- ``True`` or ``False`` specifying if the polynomial defining the system
          should be monic or not. Defaults to ``False``.

        OUTPUT: :class:`DynamicalSystem_affine`

        EXAMPLES::

            sage: A.<x> = AffineSpace(QQ, 1)
            sage: A.chebyshev_polynomial(5, 'first')
            Dynamical System of Affine Space of dimension 1 over Rational Field
            Defn: Defined on coordinates by sending (x) to
            (16*x^5 - 20*x^3 + 5*x)

        ::

            sage: A.<x> = AffineSpace(QQ, 1)
            sage: A.chebyshev_polynomial(3, 'second')
            Dynamical System of Affine Space of dimension 1 over Rational Field
            Defn: Defined on coordinates by sending (x) to
            (8*x^3 - 4*x)

        ::

            sage: A.<x> = AffineSpace(QQ, 1)
            sage: A.chebyshev_polynomial(3, 2)
            Traceback (most recent call last):
            ...
            ValueError: keyword 'kind' must have a value of either 'first' or 'second'

        ::

            sage: A.<x> = AffineSpace(QQ, 1)
            sage: A.chebyshev_polynomial(-4, 'second')
            Traceback (most recent call last):
            ...
            ValueError: first parameter 'n' must be a non-negative integer

        ::

            sage: A = AffineSpace(QQ, 2, 'x')
            sage: A.chebyshev_polynomial(2)
            Traceback (most recent call last):
            ...
            TypeError: affine space must be of dimension 1

        ::

            sage: A.<x> = AffineSpace(QQ, 1)
            sage: A.chebyshev_polynomial(7, monic=True)
            Dynamical System of Affine Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x) to
                    (x^7 - 7*x^5 + 14*x^3 - 7*x)

        ::

            sage: F.<t> = FunctionField(QQ)
            sage: A.<x> = AffineSpace(F,1)
            sage: A.chebyshev_polynomial(4, monic=True)
            Dynamical System of Affine Space of dimension 1 over Rational function field in t over Rational Field
              Defn: Defined on coordinates by sending (x) to
                    (x^4 + (-4)*x^2 + 2)
        """
        if self.dimension_relative() != 1:
            raise TypeError("affine space must be of dimension 1")
        n = ZZ(n)
        if (n < 0):
            raise ValueError(
                "first parameter 'n' must be a non-negative integer")
        from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine
        if kind == 'first':
            if monic and self.base().characteristic() != 2:
                f = DynamicalSystem_affine([chebyshev_T(n, self.gen(0))],
                                           domain=self)
                f = f.homogenize(1)
                f = f.conjugate(matrix([[1 / ZZ(2), 0], [0, 1]]))
                f = f.dehomogenize(1)
                return f
            return DynamicalSystem_affine([chebyshev_T(n, self.gen(0))],
                                          domain=self)
        elif kind == 'second':
            if monic and self.base().characteristic() != 2:
                f = DynamicalSystem_affine([chebyshev_T(n, self.gen(0))],
                                           domain=self)
                f = f.homogenize(1)
                f = f.conjugate(matrix([[1 / ZZ(2), 0], [0, 1]]))
                f = f.dehomogenize(1)
                return f
            return DynamicalSystem_affine([chebyshev_U(n, self.gen(0))],
                                          domain=self)
        else:
            raise ValueError(
                "keyword 'kind' must have a value of either 'first' or 'second'"
            )
예제 #30
0
def _siegel_modular_forms_generators(parent, prec=None, degree=0):
    r"""
    Compute the four Igusa generators of the ring of Siegel modular forms 
    of degree 2 and level 1 and even weight (this happens if weights='even').  
    If weights = 'all' you get the Siegel modular forms of degree 2, level 1 
    and even and odd weight.

    EXAMPLES::

        sage: A, B, C, D = SiegelModularFormsAlgebra().gens()
        sage: C[(2, 0, 9)]
        -390420
        sage: D[(4, 2, 5)]
        17689760
        sage: A2, B2, C2, D2 = SiegelModularFormsAlgebra().gens(prec=50)
        sage: A[(1, 0, 1)] == A2[(1, 0, 1)]
        True
        sage: A3, B3, C3, D3 = SiegelModularFormsAlgebra().gens(prec=500)
        sage: B2[(2, 1, 3)] == B3[(2, 1, 3)]
        True

    TESTS::

        sage: from sage.modular.siegel.siegel_modular_forms_algebra import _siegel_modular_forms_generators
        sage: S = SiegelModularFormsAlgebra()
        sage: S.gens() == _siegel_modular_forms_generators(S)
        True
    """
    group = parent.group()
    weights = parent.weights()
    if prec is None:
        prec = parent.default_prec()
    if group == 'Sp(4,Z)' and 0 == degree:
        from sage.modular.all import ModularForms
        E4 = ModularForms(1, 4).gen(0)
        M6 = ModularForms(1, 6)
        E6 = ModularForms(1, 6).gen(0)
        M8 = ModularForms(1, 8)
        M10 = ModularForms(1, 10)
        Delta = ModularForms(1, 12).cuspidal_subspace().gen(0)
        M14 = ModularForms(1, 14)
        A = SiegelModularForm(60 * E4, M6(0), prec=prec, name='Igusa_4')
        B = SiegelModularForm(-84 * E6, M8(0), prec=prec, name='Igusa_6')
        C = SiegelModularForm(M10(0), -Delta, prec=prec, name='Igusa_10')
        D = SiegelModularForm(Delta, M14(0), prec=prec, name='Igusa_12')
        # TODO: the base_ring of A, B, ... should be ZZ
        # Here a hack for now:
        a = [A, B, C, D]
        b = []
        from sage.rings.all import ZZ
        for F in a:
            c = F.coeffs()
            for f in c.keys():
                c[f] = ZZ(c[f])
            F = parent.element_class(parent=parent,
                                     weight=F.weight(),
                                     coeffs=c,
                                     prec=prec,
                                     name=F.name())
            b.append(F)
        if weights == 'even': return b
        if weights == 'all':
            from .fastmult import chi35
            coeffs35 = chi35(prec, b[0], b[1], b[2], b[3])
            from sage.groups.all import KleinFourGroup
            G = KleinFourGroup()
            from sage.algebras.all import GroupAlgebra
            R = GroupAlgebra(G)
            det = R(G.gen(0))
            E = parent.element_class(parent=parent,
                                     weight=35,
                                     coeffs=coeffs35,
                                     prec=prec,
                                     name='Delta_35')
            E = E * det
            b.append(E)
            return b
        raise ValueError(
            "weights = '{0}': should be 'all' or 'even'".format(weights))
    if group == 'Sp(4,Z)' and weights == 'even' and 2 == degree:
        b = _siegel_modular_forms_generators(parent=parent)
        c = []
        for F in b:
            i = b.index(F)
            for G in b[(i + 1):]:
                c.append(F.satoh_bracket(G))
        return c
    raise NotImplementedError("Not yet implemented")