def _check_muqt(mu, q, t, pi=None):
    """
    EXAMPLES::

        sage: from sage.combinat.sf.ns_macdonald import _check_muqt
        sage: P, q, t, n, R, x = _check_muqt([0,0,1],None,None)
        sage: P
        Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
        sage: q
        q
        sage: t
        t
        sage: n
        Nonattacking fillings of [0, 0, 1]
        sage: R
        Multivariate Polynomial Ring in x0, x1, x2 over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
        sage: x
        (x0, x1, x2)

    ::

        sage: q,t = var('q,t')
        sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,None)
        Traceback (most recent call last):
        ...
        ValueError: you must specify either both q and t or neither of them

    ::

        sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,2)
        Traceback (most recent call last):
        ...
        ValueError: the parents of q and t must be the same
    """
    if q is None and t is None:
        P = PolynomialRing(QQ, 'q,t').fraction_field()
        q, t = P.gens()
    elif q is not None and t is not None:
        if q.parent() != t.parent():
            raise ValueError("the parents of q and t must be the same")
        P = q.parent()
    else:
        raise ValueError(
            "you must specify either both q and t or neither of them")
    n = NonattackingFillings(mu, pi)
    R = PolynomialRing(P, len(n._shape), 'x')
    x = R.gens()
    return P, q, t, n, R, x
Beispiel #2
0
    def _sage_(self):
        """
        EXAMPLES:
            sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie
            sage: m.sage()  # optional - lie
            [ 1  0  3  3]
            [12  4 -4  7]
            [-1  9  8  0]
            [ 3 -5 -2  9]

        """
        t = self.type()
        if t == "grp":
            raise ValueError, "cannot convert Lie groups to native Sage objects"
        elif t == "mat":
            import sage.matrix.constructor

            return sage.matrix.constructor.matrix(eval(str(self).replace("\n", "").strip()))
        elif t == "pol":
            import sage.misc.misc
            from sage.rings.all import PolynomialRing, QQ

            # Figure out the number of variables
            s = str(self)
            open_bracket = s.find("[")
            close_bracket = s.find("]")
            nvars = len(s[open_bracket:close_bracket].split(","))

            # create the polynomial ring
            R = PolynomialRing(QQ, nvars, "x")
            x = R.gens()
            pol = R(0)

            # Split up the polynomials into terms
            terms = []
            for termgrp in s.split(" - "):
                # The first entry in termgrp has
                # a negative coefficient
                termgrp = "-" + termgrp.strip()
                terms += termgrp.split("+")
            # Make sure we don't accidentally add a negative
            # sign to the first monomial
            if s[0] != "-":
                terms[0] = terms[0][1:]

            # go through all the terms in s
            for term in terms:
                xpos = term.find("X")
                coef = eval(term[:xpos].strip())
                exps = eval(term[xpos + 1 :].strip())
                monomial = sage.misc.misc.prod(map(lambda i: x[i] ** exps[i], range(nvars)))
                pol += coef * monomial

            return pol
        elif t == "tex":
            return repr(self)
        elif t == "vid":
            return None
        else:
            return ExpectElement._sage_(self)
Beispiel #3
0
    def generator_relations(self, K):
        """
        An ideal `I` in a polynomial ring `R` over `K`, such that the
        associated ring is `R / I` surjects onto the ring of modular forms
        with coefficients in `K`.
        
        INPUT:
            - `K` -- A ring.
            
        OUTPUT:
            An ideal in a polynomial ring.
        
        TESTS::
            sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import *
            sage: t = ModularFormTestType_scalar()
            sage: t.generator_relations(QQ)
            Ideal (g1^2 - g2, g1^3 - g3, g1^4 - g4, g1^5 - g5) of Multivariate Polynomial Ring in g1, g2, g3, g4, g5 over Rational Field
        """
        if K.has_coerce_map_from(ZZ):
            R = PolynomialRing(K, self._generator_names(K))
            g1 = R.gen(0)
            return R.ideal([
                g1**i - g
                for (i, g) in list(enumerate([None] + list(R.gens())))[2:]
            ])

        raise NotImplementedError
Beispiel #4
0
def _check_muqt(mu, q, t, pi=None):
    """
    EXAMPLES::

        sage: from sage.combinat.sf.ns_macdonald import _check_muqt
        sage: P, q, t, n, R, x = _check_muqt([0,0,1],None,None)
        sage: P
        Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
        sage: q
        q
        sage: t
        t
        sage: n
        Nonattacking fillings of [0, 0, 1]
        sage: R
        Multivariate Polynomial Ring in x0, x1, x2 over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field
        sage: x
        (x0, x1, x2)

    ::

        sage: q,t = var('q,t')
        sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,None)
        Traceback (most recent call last):
        ...
        ValueError: you must specify either both q and t or neither of them

    ::

        sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,2)
        Traceback (most recent call last):
        ...
        ValueError: the parents of q and t must be the same
    """
    if q is None and t is None:
        P = PolynomialRing(QQ,'q,t').fraction_field()
        q,t = P.gens()
    elif q is not None and t is not None:
        if q.parent() != t.parent():
            raise ValueError("the parents of q and t must be the same")
        P = q.parent()
    else:
        raise ValueError("you must specify either both q and t or neither of them")
    n = NonattackingFillings(mu, pi)
    R = PolynomialRing(P, len(n._shape), 'x')
    x = R.gens()
    return P, q, t, n, R, x
Beispiel #5
0
    def _sage_(self):
        """
        EXAMPLES:
            sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie
            sage: m.sage()  # optional - lie
            [ 1  0  3  3]
            [12  4 -4  7]
            [-1  9  8  0]
            [ 3 -5 -2  9]

        """
        t = self.type()
        if t == 'grp':
            raise ValueError, "cannot convert Lie groups to native Sage objects"
        elif t == 'mat':
            import sage.matrix.constructor
            return  sage.matrix.constructor.matrix( eval( str(self).replace('\n','').strip())  )
        elif t == 'pol':
            import sage.misc.misc
            from sage.rings.all import PolynomialRing, QQ

            #Figure out the number of variables
            s = str(self)
            open_bracket = s.find('[')
            close_bracket = s.find(']')
            nvars = len(s[open_bracket:close_bracket].split(','))

            #create the polynomial ring
            R = PolynomialRing(QQ, nvars, 'x')
            x = R.gens()
            pol = R(0)

            #Split up the polynomials into terms
            terms = []
            for termgrp in s.split(' - '):
                #The first entry in termgrp has
                #a negative coefficient
                termgrp = "-"+termgrp.strip()
                terms += termgrp.split('+')
            #Make sure we don't accidentally add a negative
            #sign to the first monomial
            if s[0] != "-":
                terms[0] = terms[0][1:]

            #go through all the terms in s
            for term in terms:
                xpos = term.find('X')
                coef = eval(term[:xpos].strip())
                exps = eval(term[xpos+1:].strip())
                monomial = sage.misc.misc.prod(map(lambda i: x[i]**exps[i] , range(nvars)))
                pol += coef * monomial

            return pol
        elif t == 'tex':
            return repr(self)
        elif t == 'vid':
            return None
        else:
            return ExpectElement._sage_(self)
Beispiel #6
0
    def _sage_(self):
        """
        EXAMPLES::

            sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie
            sage: m.sage()  # optional - lie
            [ 1  0  3  3]
            [12  4 -4  7]
            [-1  9  8  0]
            [ 3 -5 -2  9]

        """
        t = self.type()
        if t == 'grp':
            raise ValueError("cannot convert Lie groups to native Sage objects")
        elif t == 'mat':
            import sage.matrix.constructor
            return  sage.matrix.constructor.matrix( eval( str(self).replace('\n','').strip())  )
        elif t == 'pol':
            from sage.rings.all import PolynomialRing, QQ

            #Figure out the number of variables
            s = str(self)
            open_bracket = s.find('[')
            close_bracket = s.find(']')
            nvars = len(s[open_bracket:close_bracket].split(','))

            #create the polynomial ring
            R = PolynomialRing(QQ, nvars, 'x')
            x = R.gens()
            pol = R(0)

            #Split up the polynomials into terms
            terms = []
            for termgrp in s.split(' - '):
                #The first entry in termgrp has
                #a negative coefficient
                termgrp = "-"+termgrp.strip()
                terms += termgrp.split('+')
            #Make sure we don't accidentally add a negative
            #sign to the first monomial
            if s[0] != "-":
                terms[0] = terms[0][1:]

            #go through all the terms in s
            for term in terms:
                xpos = term.find('X')
                coef = eval(term[:xpos].strip())
                exps = eval(term[xpos+1:].strip())
                monomial = prod([x[i]**exps[i] for i in range(nvars)])
                pol += coef * monomial

            return pol
        elif t == 'tex':
            return repr(self)
        elif t == 'vid':
            return None
        else:
            return ExpectElement._sage_(self)
Beispiel #7
0
def velu(kernel, domain=None):
    E = kernel[0].curve()
    Q = PolynomialRing(E.base_field(), ['x', 'y'])
    x, y = Q.gens()
    X = x
    Y = y
    R = []
    v = 0
    w = 0

    for P in kernel:

        g_x = 3 * P[0]**2 + E.a4()
        g_y = -2 * P[1]
        u_P = g_y**2
        if 2 * P == E(0, 1, 0):
            R.append(P)
            v_P = g_x
        else:
            if not -P in R:
                R.append(P)
                v_P = 2 * g_x
            else:
                continue

        v += v_P
        w += (u_P + P[0] * v_P)
        X += (v_P / (x - P[0]) + u_P / (x - P[0])**2)
        Y -= (2 * u_P * y / (x - P[0])**3 + v_P * (y - P[1]) / (x - P[0])**2 -
              g_x * g_y / (x - P[0])**2)

    a = E.a4() - 5 * v
    b = E.a6() - 7 * w
    if domain != None:
        try:

            a = domain.base_field()(a)
            b = domain.base_field()(b)
            codomain = EllipticCurve(domain.base_field(), [a, b])
            R = PolynomialRing(E.base_field(), ['x', 'y'])
            Rf = R.fraction_field()
            X = Rf(X)
            Y = Rf(Y)
            f = standard_form((X, Y), domain)
        except:

            codomain = EllipticCurve(E.base_field(), [a, b])
            f = standard_form((X, Y), E)
    else:
        codomain = EllipticCurve(E.base_field(), [a, b])
        f = standard_form((X, Y), E)
    return codomain, f
    def tiny_integrals_on_basis(self, P, Q):
        r"""
        Evaluate the integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}`
        by formally integrating a power series in a local parameter `t`.
        `P` and `Q` MUST be in the same residue disc for this result to make sense.

        INPUT:

        - P a point on self
        - Q a point on self (in the same residue disc as P)

        OUTPUT:

        The integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}`

        EXAMPLES::

            sage: K = pAdicField(17, 5)
            sage: E = EllipticCurve(K, [-31/3, -2501/108]) # 11a
            sage: P = E(K(14/3), K(11/2))
            sage: TP = E.teichmuller(P);
            sage: E.tiny_integrals_on_basis(P, TP)
            (17 + 14*17^2 + 17^3 + 8*17^4 + O(17^5), 16*17 + 5*17^2 + 8*17^3 + 14*17^4 + O(17^5))

        ::

            sage: K = pAdicField(11, 5)
            sage: x = polygen(K)
            sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16)
            sage: P = C.lift_x(11^(-2))
            sage: Q = C.lift_x(3*11^(-2))
            sage: C.tiny_integrals_on_basis(P,Q)
            (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8), 3*11 + 10*11^2 + 8*11^3 + 9*11^4 + 7*11^5 + O(11^6), 4*11^-1 + 2 + 6*11 + 6*11^2 + 7*11^3 + O(11^4), 11^-3 + 6*11^-2 + 2*11^-1 + 2 + O(11^2))


        Note that this fails if the points are not in the same residue disc::

            sage: S = C(0,1/4)
            sage: C.tiny_integrals_on_basis(P,S)
            Traceback (most recent call last):
            ...
            ValueError: (11^-2 + O(11^3) : 11^-5 + 8*11^-2 + O(11^0) : 1 + O(11^5)) and (0 : 3 + 8*11 + 2*11^2 + 8*11^3 + 2*11^4 + O(11^5) : 1 + O(11^5)) are not in the same residue disc

        """
        if P == Q:
            V = VectorSpace(self.base_ring(), 2 * self.genus())
            return V(0)
        R = PolynomialRing(self.base_ring(), ['x', 'y'])
        x, y = R.gens()
        return self.tiny_integrals([x**i for i in range(2 * self.genus())], P,
                                   Q)
Beispiel #9
0
def normalize_map(f, E):
    Q = PolynomialRing(E.base_field(), ['x', 'y'])
    x, y = Q.gens()
    u, v = f[0].numerator(), f[0].denominator()
    s, t = f[1].numerator(), f[1].denominator()
    k = u.numerator().coefficient({x: u.numerator().degree(x)})
    if k != 0:
        u /= k
        v /= k
    l = s.numerator().coefficient({x: s.numerator().degree(x), y: 1})
    if l != 0:
        s /= l
        t /= l
    return u / v, s / t
    def tiny_integrals_on_basis(self, P, Q):
        r"""
        Evaluate the integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}`
        by formally integrating a power series in a local parameter `t`.
        `P` and `Q` MUST be in the same residue disc for this result to make sense.

        INPUT:

        - P a point on self
        - Q a point on self (in the same residue disc as P)

        OUTPUT:

        The integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}`

        EXAMPLES::

            sage: K = pAdicField(17, 5)
            sage: E = EllipticCurve(K, [-31/3, -2501/108]) # 11a
            sage: P = E(K(14/3), K(11/2))
            sage: TP = E.teichmuller(P);
            sage: E.tiny_integrals_on_basis(P, TP)
            (17 + 14*17^2 + 17^3 + 8*17^4 + O(17^5), 16*17 + 5*17^2 + 8*17^3 + 14*17^4 + O(17^5))

        ::

            sage: K = pAdicField(11, 5)
            sage: x = polygen(K)
            sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16)
            sage: P = C.lift_x(11^(-2))
            sage: Q = C.lift_x(3*11^(-2))
            sage: C.tiny_integrals_on_basis(P,Q)
            (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8), 3*11 + 10*11^2 + 8*11^3 + 9*11^4 + 7*11^5 + O(11^6), 4*11^-1 + 2 + 6*11 + 6*11^2 + 7*11^3 + O(11^4), 11^-3 + 6*11^-2 + 2*11^-1 + 2 + O(11^2))


        Note that this fails if the points are not in the same residue disc::

            sage: S = C(0,1/4)
            sage: C.tiny_integrals_on_basis(P,S)
            Traceback (most recent call last):
            ...
            ValueError: (11^-2 + O(11^3) : 11^-5 + 8*11^-2 + O(11^0) : 1 + O(11^5)) and (0 : 3 + 8*11 + 2*11^2 + 8*11^3 + 2*11^4 + O(11^5) : 1 + O(11^5)) are not in the same residue disc

        """
        if P == Q:
            V = VectorSpace(self.base_ring(), 2*self.genus())
            return V(0)
        R = PolynomialRing(self.base_ring(), ['x', 'y'])
        x, y = R.gens()
        return self.tiny_integrals([x**i for i in range(2*self.genus())], P, Q)
Beispiel #11
0
def reduce_mod_curve(poly, E):
    Q = PolynomialRing(E.base_field(), ['x', 'y'])
    x, y = Q.gens()
    a, b = E.a4(), E.a6()
    equation = x ** 3 + a * x + b
    exponents = poly.exponents()  # list of (i,j) for each term x**i*y**j in poly
    for e_x, e_y in exponents:
        if e_y < 2:
            continue
        coef = poly.coefficient({x: e_x, y: e_y})
        e = e_y % 2
        poly -= coef * x ** (e_x) * y ** (e_y)
        poly += coef * x ** (e_x) * y ** e * equation ** ((e_y // 2))
    return poly
 def generator_relations(self, K) :
     """
     An ideal `I` in a polynomial ring `R` over `K`, such that the
     associated ring is `R / I` surjects onto the ring of modular forms
     with coefficients in `K`.
     
     INPUT:
         - `K` -- A ring.
         
     OUTPUT:
         An ideal in a polynomial ring.
     
     TESTS::
         sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import *
         sage: t = ModularFormTestType_scalar()
         sage: t.generator_relations(QQ)
         Ideal (g1^2 - g2, g1^3 - g3, g1^4 - g4, g1^5 - g5) of Multivariate Polynomial Ring in g1, g2, g3, g4, g5 over Rational Field
     """
     if K.has_coerce_map_from(ZZ) :
         R = PolynomialRing(K, self._generator_names(K))
         g1 = R.gen(0)
         return R.ideal([g1**i - g for (i,g) in list(enumerate([None] + list(R.gens())))[2:]])
         
     raise NotImplementedError
    def has_rational_point(self, point = False, algorithm = 'default',
        read_cache = True):
        r"""
        Returns True if and only if the conic ``self``
        has a point over its base field `F(t)`, which is a field of rational
        functions.

        If ``point`` is True, then returns a second output, which is
        a rational point if one exists.

        Points are cached whenever they are found. Cached information
        is used if and only if ``read_cache`` is True.
        
        The default algorithm does not (yet) work for all base fields `F`.
        In particular, sage is required to have:
        
        * an algorithm for finding the square root of elements in finite
          extensions of `F`;
        
        * a factorization and gcd algorithm for `F[t]`;
        
        * an algorithm for solving conics over `F`.
        
        ALGORITHM:
        
        The parameter ``algorithm`` specifies the algorithm
        to be used:

        * ``'default'`` -- use a native Sage implementation, based on the
          algorithm Conic in [HC2006]_.

        * ``'magma'`` (requires Magma to be installed) --
          delegates the task to the Magma computer algebra
          system.
        
        EXAMPLES:
        
        We can find points for function fields over (extensions of) `\QQ`
        and finite fields::
        
            sage: K.<t> = FractionField(PolynomialRing(QQ, 't'))
            sage: C = Conic(K, [t^2-2, 2*t^3, -2*t^3-13*t^2-2*t+18])
            sage: C.has_rational_point(point=True)
            (True, (-3 : (t + 1)/t : 1))
            sage: R.<t> = FiniteField(23)[]
            sage: C = Conic([2, t^2+1, t^2+5])
            sage: C.has_rational_point()
            True
            sage: C.has_rational_point(point=True)
            (True, (5*t : 8 : 1))
            sage: F.<i> = QuadraticField(-1)
            sage: R.<t> = F[]
            sage: C = Conic([1,i*t,-t^2+4])
            sage: C.has_rational_point(point = True)
            verbose 0 (3369: multi_polynomial_ideal.py, groebner_basis) Warning: falling back to very slow toy implementation.
            ...
            (True, (-t - 2*i : -2*i : 1))

        It works on non-diagonal conics as well::

            sage: K.<t> = QQ[]
            sage: C = Conic([4, -4, 8, 1, -4, t + 4])
            sage: C.has_rational_point(point=True)
            (True, (1/2 : 1 : 0))

        If no point exists output still depends on the argument ``point``::

            sage: K.<t> = QQ[]
            sage: C = Conic(K, [t^2, (t-1), -2*(t-1)])
            sage: C.has_rational_point()
            False
            sage: C.has_rational_point(point=True)
            (False, None)
        
        Due to limitations in Sage of algorithms we depend on, it is not
        yet possible to find points on conics over multivariate function fields
        (see the requirements above)::
        
            sage: F.<t1> = FractionField(QQ['t1'])
            sage: K.<t2> = FractionField(F['t2'])
            sage: a = K(1)
            sage: b = 2*t2^2+2*t1*t2-t1^2
            sage: c = -3*t2^4-4*t1*t2^3+8*t1^2*t2^2+16*t1^3-t2-48*t1^4
            sage: C = Conic([a,b,c])
            sage: C.has_rational_point()
            ...
            Traceback (most recent call last):
            ...
            NotImplementedError: is_square() not implemented for elements of
            Univariate Quotient Polynomial Ring in tbar over Fraction Field
            of Univariate Polynomial Ring in t1 over Rational Field with
            modulus tbar^2 + t1*tbar - 1/2*t1^2
        
        In some cases, the algorithm requires us to be
        able to solve conics over `F`. In particular, the following does not
        work::

            sage: P.<u> = QQ[]
            sage: E = P.fraction_field()
            sage: Q.<Y> = E[]
            sage: F.<v> = E.extension(Y^2 - u^3 - 1)
            sage: R.<t> = F[]
            sage: K = R.fraction_field()
            sage: C = Conic(K, [u, v, 1])
            sage: C.has_rational_point()
            ...
            Traceback (most recent call last):
            ...
            NotImplementedError: has_rational_point not implemented for conics
            over base field Univariate Quotient Polynomial Ring in v over
            Fraction Field of Univariate Polynomial Ring in u over Rational
            Field with modulus v^2 - u^3 - 1

        ``has_rational_point`` fails for some conics over function fields
        over finite fields, due to :trac:`20003`::

            sage: K.<t> = PolynomialRing(GF(7))
            sage: C = Conic([5*t^2+4, t^2+3*t+3, 6*t^2+3*t+2, 5*t^2+5, 4*t+3, 4*t^2+t+5])
            sage: C.has_rational_point()
            ...
            Traceback (most recent call last):
            ...
            TypeError: self (=Scheme morphism:
              From: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^2 + 4)*x^2 + ((6*t^3 + 3*t^2 + 5*t + 5)/(t + 3))*y^2 + ((6*t^6 + 3*t^5 + t^3 + 6*t^2 + 6*t + 2)/(t^4 + t^3 + 4*t^2 + 3*t + 1))*z^2
              To:   Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^2 + 4)*x^2 + (t^2 + 3*t + 3)*x*y + (5*t^2 + 5)*y^2 + (6*t^2 + 3*t + 2)*x*z + (4*t + 3)*y*z + (4*t^2 + t + 5)*z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (x + ((2*t + 5)/(t + 3))*y + ((3*t^4 + 2*t^3 + 5*t^2 + 5*t + 3)/(t^4 + t^3 + 4*t^2 + 3*t + 1))*z : y + ((6*t^3 + 6*t^2 + 3*t + 6)/(t^3 + 4*t^2 + 2*t + 2))*z : z)) domain must equal right (=Scheme morphism:
              From: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^3 + 6*t^2 + 3*t + 3)*x^2 + (t + 4)*y^2 + (6*t^7 + 2*t^5 + t^4 + 2*t^3 + 3*t^2 + 6*t + 6)*z^2
              To:   Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5/(t^3 + 4*t^2 + 2*t + 2))*x^2 + (1/(t^3 + 3*t^2 + 5*t + 1))*y^2 + ((6*t^6 + 3*t^5 + t^3 + 6*t^2 + 6*t + 2)/(t^9 + 5*t^8 + t^7 + 6*t^6 + 3*t^5 + 4*t^3 + t^2 + 5*t + 3))*z^2
              Defn: Defined on coordinates by sending (x : y : z) to
                    ((t^3 + 4*t^2 + 2*t + 2)*x : (t^2 + 5)*y : (t^5 + 4*t^4 + t^2 + 3*t + 3)*z)) codomain

            
        TESTS::

            sage: K.<t> = FractionField(PolynomialRing(QQ, 't'))
            sage: a = (2*t^2 - 3/2*t + 1)/(37/3*t^2 + t - 1/4)
            sage: b = (1/2*t^2 + 1/3)/(-73*t^2 - 2*t + 11/4)
            sage: c = (6934/3*t^6 + 8798/3*t^5 - 947/18*t^4 + 3949/9*t^3 + 20983/18*t^2 + 28/3*t - 131/3)/(-2701/3*t^4 - 293/3*t^3 + 301/6*t^2 + 13/4*t - 11/16)
            sage: C = Conic([a,b,c])
            sage: C.has_rational_point(point=True)
            (True, (4*t + 4 : 2*t + 2 : 1))

        A long time test::

            sage: K.<t> = FractionField(PolynomialRing(QQ, 't'))
            sage: a = (-1/3*t^6 - 14*t^5 - 1/4*t^4 + 7/2*t^2 - 1/2*t - 1)/(24/5*t^6 - t^5 - 1/4*t^4 + t^3 - 3*t^2 + 8/5*t + 5)
            sage: b = (-3*t^3 + 8*t + 1/2)/(-1/3*t^3 + 3/2*t^2 + 1/12*t + 1/2)
            sage: c = (1232009/225*t^25 - 1015925057/8100*t^24 + 1035477411553/1458000*t^23 + 7901338091/30375*t^22 - 1421379260447/729000*t^21 + 266121260843/972000*t^20 + 80808723191/486000*t^19 - 516656082523/972000*t^18 + 21521589529/40500*t^17 + 4654758997/21600*t^16 - 20064038625227/9720000*t^15 - 173054270347/324000*t^14 + 536200870559/540000*t^13 - 12710739349/50625*t^12 - 197968226971/135000*t^11 - 134122025657/810000*t^10 + 22685316301/120000*t^9 - 2230847689/21600*t^8 - 70624099679/270000*t^7 - 4298763061/270000*t^6 - 41239/216000*t^5 - 13523/36000*t^4 + 493/36000*t^3 + 83/2400*t^2 + 1/300*t + 1/200)/(-27378/125*t^17 + 504387/500*t^16 - 97911/2000*t^15 + 1023531/4000*t^14 + 1874841/8000*t^13 + 865381/12000*t^12 + 15287/375*t^11 + 6039821/6000*t^10 + 599437/1500*t^9 + 18659/250*t^8 + 1218059/6000*t^7 + 2025127/3000*t^6 + 1222759/6000*t^5 + 38573/200*t^4 + 8323/125*t^3 + 15453/125*t^2 + 17031/500*t + 441/10)
            sage: C = Conic([a,b,c])
            sage: C.has_rational_point(point = True) # long time (4 seconds)
            (True,
             ((-2/117*t^8 + 304/1053*t^7 + 40/117*t^6 - 1/27*t^5 - 110/351*t^4 - 2/195*t^3 + 11/351*t^2 + 1/117)/(t^4 + 2/39*t^3 + 4/117*t^2 + 2/39*t + 14/39) : -5/3*t^4 + 19*t^3 : 1))
        """
        from constructor import Conic
        
        if read_cache:
            if self._rational_point is not None:
                return (True, self._rational_point) if point else True
        
        if algorithm != 'default':
            return ProjectiveConic_field.has_rational_point(self, point,
                algorithm, read_cache)
        
        # Default algorithm
        if self.base_ring().characteristic() == 2:
            raise NotImplementedError("has_rational_point not implemented \
for function field of characteristic 2.")
        new_conic, transformation, inverse = self.diagonalization()
        coeff = new_conic.coefficients()
        if coeff[0] == 0:
            return (True, transformation([1,0,0])) if point else True
        elif coeff[3] == 0:
            return (True, transformation([0,1,0])) if point else True
        elif coeff[5] == 0:
            return (True, transformation([0,0,1])) if point else True
        
        # We save the coefficients of the reduced form in coeff
        # A zero of the reduced conic can be multiplied by multipliers
        # to get a zero of the old conic
        (coeff, multipliers) = new_conic._reduce_conic()
        new_conic = Conic(coeff)
        transformation = transformation \
            * new_conic.hom(diagonal_matrix(multipliers))
        if coeff[0].degree() % 2 == coeff[1].degree() % 2 and \
                coeff[1].degree() % 2 == coeff[2].degree() % 2:
            case = 0
        else:
            case = 1
        
        t, = self.base_ring().base().gens() # t in F[t]
        supp = []
        roots = [[], [], []]
        remove = None
        # loop through the coefficients and find a root of f_i (as in
        # [HC2006]) modulo each element in the coefficients' support
        for i in (0,1,2):
            supp.append(list(coeff[i].factor()))
            for p in supp[i]:
                if p[1] != 1:
                    raise ValueError("Expected factor of exponent 1.")
                # Convert to monic factor
                x = p[0]/list(p[0])[-1]
                N = p[0].base_ring().extension(x, 'tbar')
                R = PolynomialRing(N, 'u')
                u, = R.gens()
                # If p[0] has degree 1, sage might forget the "defining
                # polynomial" of N, so we define our own modulo operation
                if p[0].degree() == 1:
                    mod = t.parent().hom([-x[0]])
                else:
                    mod = N
                if i == 0:
                    x = -mod(coeff[2])/mod(coeff[1])
                elif i == 1:
                    x = -mod(coeff[0])/mod(coeff[2])
                else:
                    x = -mod(coeff[1])/mod(coeff[0])
                if x.is_square():
                    root = N(x.sqrt())
                else:
                    return (False, None) if point else False
                # if case == 0 and p[0] has degree 1, we switch to case
                # 1 and remove this factor out of the support. In [HC2006]
                # this is done later, in FindPoint.
                if case == 0 and p[0].degree() == 1:
                    case = 1
                    # remove later so the loop iterator stays in place.
                    remove = (i,p)
                else:
                    roots[i].append(root)
        if remove:
            supp[remove[0]].remove(remove[1])
        supp = [[p[0] for p in supp[i]] for i in (0,1,2)]

        if case == 0:
        # Find a solution of (5) in [HC2006]
            leading_conic = Conic(self.base_ring().base_ring(),
                        [coeff[0].leading_coefficient(),
                        coeff[1].leading_coefficient(),
                        coeff[2].leading_coefficient()])
            has_point = leading_conic.has_rational_point(True)
            if has_point[0]:
                if point:
                    pt = new_conic.find_point(supp, roots, case,
                        has_point[1])
                else:
                    pt = True
                return (True, transformation(pt)) if point else True
            else:
                return (False, None) if point else False
        # case == 1:
        if point:
            pt = new_conic.find_point(supp, roots, case)
        else:
            pt = True
        return (True, transformation(pt)) if point else True
Beispiel #14
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        ALGORITHM:

        Uses the PARI/GP function ``qfparam``.

        EXAMPLES ::

            sage: c = Conic([1,1,-1])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Rational Field
              To:   Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y) to
                    (2*x*y : x^2 - y^2 : x^2 + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              To:   Projective Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*x : -1/2*y + 1/2*z))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + 2*y^2 + z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError(
                    "The conic self (=%s) is not smooth, hence does not have a parametrization."
                    % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            Q = PolynomialRing(QQ, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            M = self.symmetric_matrix()
            M *= lcm([t.denominator() for t in M.list()])
            par1 = qfparam(M, point)
            B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)])
            # self is in the image of B and does not lie on a line,
            # hence B is invertible
            A = B.inverse()
            par2 = [sum([A[i, j] * gens[j] for j in range(3)]) for i in [1, 0]]
            par = ([Q(pol(x / y) * y**2) for pol in par1], par2)
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0], self), self.Hom(P1)(par[1], check=False)
Beispiel #15
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        EXAMPLES:

        An example over a finite field ::

            sage: c = Conic(GF(2), [1,1,1,1,1,0])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Finite Field of size 2
              To:   Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              Defn: Defined on coordinates by sending (x : y) to
                    (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              To:   Projective Space of dimension 1 over Finite Field of size 2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y : x))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + y^2 + 7*z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            B = self.base_ring()
            Q = PolynomialRing(B, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1'])
            [X, Y, T0, T1] = P.gens()
            c3 = [j for j in range(2,-1,-1) if point[j] != 0][0]
            c1 = [j for j in range(3) if j != c3][0]
            c2 = [j for j in range(3) if j != c3 and j != c1][0]
            L = [0,0,0]
            L[c1] = Y*T1*point[c1] + Y*T0
            L[c2] = Y*T1*point[c2] + X*T0
            L[c3] = Y*T1*point[c3]
            bezout = P(self.defining_polynomial()(L) / T0)
            t = [bezout([x,y,0,-1]),bezout([x,y,1,0])]
            par = (tuple([Q(p([x,y,t[0],t[1]])/y) for  p in L]),
                   tuple([gens[m]*point[c3]-gens[c3]*point[m]
                       for m in [c2,c1]]))
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
Beispiel #16
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of this space into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional).

        OUTPUT:

        Hom -- from this space to the appropriate subscheme of projective space.

        .. TODO::

            Cartesian products with more than two components.

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1, 2], CC, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).

            ::

            sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field
              To:   Closed subscheme of Projective Space of dimension 11 over
            Rational Field defined by:
              -u9*u10 + u8*u11,
              -u7*u10 + u6*u11,
              -u7*u8 + u6*u9,
              -u5*u10 + u4*u11,
              -u5*u8 + u4*u9,
              -u5*u6 + u4*u7,
              -u5*u9 + u3*u11,
              -u5*u8 + u3*u10,
              -u5*u8 + u2*u11,
              -u4*u8 + u2*u10,
              -u3*u8 + u2*u9,
              -u3*u6 + u2*u7,
              -u3*u4 + u2*u5,
              -u5*u7 + u1*u11,
              -u5*u6 + u1*u10,
              -u3*u7 + u1*u9,
              -u3*u6 + u1*u8,
              -u5*u6 + u0*u11,
              -u4*u6 + u0*u10,
              -u3*u6 + u0*u9,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u1*u4 + u0*u5,
              -u1*u2 + u0*u3
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to
                    (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6
            : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6).
        """
        N = self._dims
        M = prod([n + 1 for n in N]) - 1
        CR = self.coordinate_ring()

        vars = list(self.coordinate_ring().variable_names()) + [
            var + str(i) for i in range(M + 1)
        ]
        R = PolynomialRing(self.base_ring(),
                           self.ngens() + M + 1,
                           vars,
                           order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        index = self.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                R.gen(k + count) -
                prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J) - 1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n - 1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(), M,
                                 R.variable_names()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative() != M:
                raise ValueError(
                    "projective Space %s must be dimension %s") % (PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0] * k + list(S.gens()), S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        index = self.num_components() * [0]
        for count in range(M + 1):
            mapping.append(
                prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index) - 1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break  #only increment once
        phi = self.hom(mapping, Y)

        return phi
Beispiel #17
0
def _biquadratic_syzygy_quartic(quadratic1, quadratic2, variables=None):
    r"""
    Helper function for the Weierstrass form of a biquadratic in $`\mathbb{P}^3$

    The invariants and covariants of a quaternary biquadratic satisfy
    the relation
    :meth:`sage.rings.invariant_theory.TwoQuaternaryQuadratics.syzygy`,
    which is (modulo the two quadratic equations) of the form $J^2 =
    p_4(T, T')$ where

    * $J$, $T$, $T'$ are the covariants of the biquadratic.
    
    * $p_4$ is some quartic polynomial whose coefficients are
      invariants of the biquadratic.
  
    INPUT:

    See :func:`WeierstrassForm_P3`

    OUTPUT:

    A triple consisting of 

    - The quaternary biquadratic as an algebraic form
      :class:`~sage.rings.invariant_theory.TwoQuaternaryQuadratics`
      
    - The binary quartic $p_4$ as a
      :class:`~sage.rings.invariant_theory.BinaryQuartic`
      
    - The dictionary of variable substitutions from the variables of
      the quartic to the variables of the biquadratic.

    EXAMPLES::

        sage: from sage.schemes.toric.weierstrass_higher import _biquadratic_syzygy_quartic
        sage: R.<w,x,y,z> = QQ[]
        sage: _biquadratic_syzygy_quartic(w^2+x^2+y^2, z^2)
        (Joint quaternary quadratic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0) 
         and quaternary quadratic with coefficients (0, 0, 0, 1, 0, 0, 0, 0, 0, 0), 
         Binary quartic with coefficients (0, 0, 0, -1, 0), {aux...})
    """
    w, x, y, z = _check_polynomials_P3(quadratic1, quadratic2, variables)
    biquadratic = invariant_theory.quaternary_biquadratic(quadratic1, quadratic2, [w, x, y, z])
    
    # construct auxiliary polynomial ring to work with the rhs of the syzygy
    R = biquadratic.ring()
    n = R.ngens()
    R_aux = PolynomialRing(R.base_ring(), n+2, 'aux')
    to_aux = dict()
    from_aux = dict()
    for var, var_aux in zip(R.gens(), R_aux.gens()[0:n]):
        to_aux[var] = var_aux
        from_aux[var_aux] = var
    T, T_prime = R_aux.gens()[n:]
    from_aux[T] = biquadratic.T_covariant()
    from_aux[T_prime] = biquadratic.T_prime_covariant()

    # Syzygy is J^2 = syz_rhs + (terms that vanish on the biquadratic) with
    # J = biquadratic.J_covariant()
    syz_rhs = T**4 * biquadratic.Delta_invariant().subs(to_aux) \
        - T**3*T_prime * biquadratic.Theta_invariant().subs(to_aux) \
        + T**2*T_prime**2 * biquadratic.Phi_invariant().subs(to_aux) \
        - T*T_prime**3 * biquadratic.Theta_prime_invariant().subs(to_aux) \
        + T_prime**4 * biquadratic.Delta_prime_invariant().subs(to_aux)
    quartic = invariant_theory.binary_quartic(syz_rhs, [T, T_prime])
    return (biquadratic, quartic, from_aux)
Beispiel #18
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        ALGORITHM:

        Uses the PARI/GP function ``qfparam``.

        EXAMPLES ::

            sage: c = Conic([1,1,-1])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Rational Field
              To:   Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              Defn: Defined on coordinates by sending (x : y) to
                    (2*x*y : x^2 - y^2 : x^2 + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2
              To:   Projective Space of dimension 1 over Rational Field
              Defn: Defined on coordinates by sending (x : y : z) to
                    (1/2*x : -1/2*y + 1/2*z))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + 2*y^2 + z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self)
            if point is None:
                point = self.rational_point()
            point = Sequence(point)
            Q = PolynomialRing(QQ, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            M = self.symmetric_matrix()
            M *= lcm([ t.denominator() for t in M.list() ])
            par1 = qfparam(M, point)
            B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)])
            # self is in the image of B and does not lie on a line,
            # hence B is invertible
            A = B.inverse()
            par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]]
            par = ([Q(pol(x/y)*y**2) for pol in par1], par2)
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
Beispiel #19
0
def _load_euler_table(self, n, force=False, verbose=False):
    r"""
    Load the euler table for self over the degree n extension of
    $\mathbb{F}_q$ to disk. If self is the versal j-curve, the table
    is pulled from
    
    SAGE_ROOT/data/jcurve_euler_tables .
    
    Otherwise, the table is pulled from the `user` table

    SAGE_ROOT/data/local_euler_tables .

    This should eventually be implemented using MongoDB.

    It currently doesn't check if the key exist. If the key doesn't
    exist, a RuntimeError is raised by sage.database.db. This
    RuntimeError should be sufficient, so key checking may not be
    necessary.

    INPUT:

        - n -- the degree of the extension of F_q
        - force -- boolean that overwrites self's euler table with
                   one from database

    EXAMPLES::

        sage: import psage
        sage: K.<t> = psage.FunctionField(GF(11))
        sage: E = psage.ellff_EllipticCurve(K,[0,0,0,-27*t/(t-1728),54*t/(t-1728)])
        sage: E._euler_table(1)
        Traceback (most recent call last):
            ...
        RuntimeError: table is empty
        sage: E._load_euler_table(1)
        sage: E._euler_table(1)
        [0, 0, 4, -6, 3, 5, 1, -2, 4, -2, 3, 1]
            
    """
        
    import os
    SAGE_ROOT = os.environ['SAGE_ROOT']
        
    K = self.K
    R = self.R
    t = K.gens()[0]
    p = self.p
    d = self.d
    q = self.q
    R2 = PolynomialRing(GF(q), 's')
    s = R2.gens()[0]
    a1n = R2(0)
    a1d = R2(1)
    a2n = R2(0)
    a2d = R2(1)
    a3n = R2(0)
    a3d = R2(1)
    a4n = R2(self.a4.numerator().coeffs())
    a4d = R2(self.a4.denominator().coeffs())
    a6n = R2(self.a6.numerator().coeffs())
    a6d = R2(self.a6.denominator().coeffs())

    ainvs = [0, 0, 0, self.a4, self.a6]
    ainvs_pairs = ((a1n, a1d), (a2n, a2d), (a3n, a3d), (a4n, a4d), (a6n, a6d))

    # recognize if self is j-curve and use special repository
    if ainvs == [0,0,0,-27*t*(t-1728)**3,54*t*(t-1728)**5]:
        if verbose:
            print('j-curve recognized; saving euler table to database')
        if not os.path.exists(SAGE_ROOT + '/data/jcurve_euler_tables/jcurve_euler_tables'):
            print('Database does not exist; cannot load from it')
        else:
            euler_db = jCurveEulerTables()
            # check that keys exist?
            self._set_euler_table(n, euler_db[q][n], force)
            
    # work with user's repository of euler tables
    else:
        if not os.path.exists(SAGE_ROOT + '/data/local_euler_tables/local_euler_tables'):
            print('Database does not exist; cannot load from it')
        else:                
            local_euler_db = LocalEulerTables()
            # check that keys exist?
            self._set_euler_table(n, local_euler_db[ainvs_pairs][q][n], force)
def _load_euler_table(self, n, force=False, verbose=False):
    r"""
    Load the euler table for self over the degree n extension of
    $\mathbb{F}_q$ to disk. If self is the versal j-curve, the table
    is pulled from
    
    SAGE_ROOT/data/jcurve_euler_tables .
    
    Otherwise, the table is pulled from the `user` table

    SAGE_ROOT/data/local_euler_tables .

    This should eventually be implemented using MongoDB.

    It currently doesn't check if the key exist. If the key doesn't
    exist, a RuntimeError is raised by sage.database.db. This
    RuntimeError should be sufficient, so key checking may not be
    necessary.

    INPUT:

        - n -- the degree of the extension of F_q
        - force -- boolean that overwrites self's euler table with
                   one from database

    EXAMPLES::

        sage: import psage
        sage: K.<t> = psage.FunctionField(GF(11))
        sage: E = psage.ellff_EllipticCurve(K,[0,0,0,-27*t/(t-1728),54*t/(t-1728)])
        sage: E._euler_table(1)
        Traceback (most recent call last):
            ...
        RuntimeError: table is empty
        sage: E._load_euler_table(1)
        sage: E._euler_table(1)
        [0, 0, 4, -6, 3, 5, 1, -2, 4, -2, 3, 1]
            
    """
        
    import os
    SAGE_ROOT = os.environ['SAGE_ROOT']
        
    K = self.K
    R = self.R
    t = K.gens()[0]
    p = self.p
    d = self.d
    q = self.q
    R2 = PolynomialRing(GF(q), 's')
    s = R2.gens()[0]
    a1n = R2(0)
    a1d = R2(1)
    a2n = R2(0)
    a2d = R2(1)
    a3n = R2(0)
    a3d = R2(1)
    a4n = R2(self.a4.numerator().coeffs())
    a4d = R2(self.a4.denominator().coeffs())
    a6n = R2(self.a6.numerator().coeffs())
    a6d = R2(self.a6.denominator().coeffs())

    ainvs = [0, 0, 0, self.a4, self.a6]
    ainvs_pairs = ((a1n, a1d), (a2n, a2d), (a3n, a3d), (a4n, a4d), (a6n, a6d))

    # recognize if self is j-curve and use special repository
    if ainvs == [0,0,0,-27*t*(t-1728)**3,54*t*(t-1728)**5]:
        if verbose:
            print 'j-curve recognized; saving euler table to database'
        if not os.path.exists(SAGE_ROOT + '/data/jcurve_euler_tables/jcurve_euler_tables'):
            print 'Database does not exist; cannot load from it'
        else:
            euler_db = jCurveEulerTables()
            # check that keys exist?
            self._set_euler_table(n, euler_db[q][n], force)
            
    # work with user's repository of euler tables
    else:
        if not os.path.exists(SAGE_ROOT + '/data/local_euler_tables/local_euler_tables'):
            print 'Database does not exist; cannot load from it'
        else:                
            local_euler_db = LocalEulerTables()
            # check that keys exist?
            self._set_euler_table(n, local_euler_db[ainvs_pairs][q][n], force)
Beispiel #21
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of this space into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional).

        OUTPUT:

        Hom -- from this space to the appropriate subscheme of projective space.

        .. TODO::

            Cartesian products with more than two components.

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1, 2], CC, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).

            ::

            sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field
              To:   Closed subscheme of Projective Space of dimension 11 over
            Rational Field defined by:
              -u9*u10 + u8*u11,
              -u7*u10 + u6*u11,
              -u7*u8 + u6*u9,
              -u5*u10 + u4*u11,
              -u5*u8 + u4*u9,
              -u5*u6 + u4*u7,
              -u5*u9 + u3*u11,
              -u5*u8 + u3*u10,
              -u5*u8 + u2*u11,
              -u4*u8 + u2*u10,
              -u3*u8 + u2*u9,
              -u3*u6 + u2*u7,
              -u3*u4 + u2*u5,
              -u5*u7 + u1*u11,
              -u5*u6 + u1*u10,
              -u3*u7 + u1*u9,
              -u3*u6 + u1*u8,
              -u5*u6 + u0*u11,
              -u4*u6 + u0*u10,
              -u3*u6 + u0*u9,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u1*u4 + u0*u5,
              -u1*u2 + u0*u3
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to
                    (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6
            : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6).
        """
        N = self._dims
        M = prod([n+1 for n in N]) - 1
        CR = self.coordinate_ring()

        vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)]
        R = PolynomialRing(self.base_ring(), self.ngens()+M+1, vars, order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        index = self.num_components()*[0]
        for count in range(M + 1):
            mapping.append(R.gen(k+count)-prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index)-1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break #only increment once

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J)-1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n-1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(), M, R.variable_names()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative() != M:
                raise ValueError("projective Space %s must be dimension %s")%(PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0]*k + list(S.gens()), S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        index = self.num_components()*[0]
        for count in range(M + 1):
            mapping.append(prod([CR(self[i].gen(index[i])) for i in range(len(index))]))
            for i in range(len(index)-1, -1, -1):
                if index[i] == N[i]:
                    index[i] = 0
                else:
                    index[i] += 1
                    break #only increment once
        phi = self.hom(mapping, Y)

        return phi
def is_geom_trivial_when_field(C, bad_primes, B=200):
    r"""
    Determine if the geometric endomorphism ring is trivial assuming the
    geometric endomorphism algebra is a field.

    This is Algorithm 4.15 in [Lom2019]_.

    INPUT:

    - ``C`` -- the hyperelliptic curve.

    - ``bad_primes`` -- the list of odd primes of bad reduction.

    - ``B`` -- (default: 200) the bound which appears in the statement of
      the algorithm from [Lom2019]_

    OUTPUT:

    Boolean indicating whether or not the geometric endomorphism
    algebra is the field of rational numbers.

    WARNING:

    There is a very small chance that this algorithm returns ``False`` when in
    fact it is ``True``. In this case, as explained in the discussion
    immediately preceding Algorithm 4.15 of [Lom2019]_, this can be established
    by increasing the optional `B` parameter. Mathematically, this algorithm
    gives the correct answer only in the limit as `B \to \infty`, although in
    practice `B = 200` was sufficient to correctly verify every single entry
    in the LMFDB. However, strictly speaking, a ``False`` returned by this
    function is not provably ``False``.

    EXAMPLES:

    This is LMFDB curve 461.a.461.2::

        sage: from sage.schemes.hyperelliptic_curves.jacobian_endomorphism_utils import is_geom_trivial_when_field
        sage: R.<x> = QQ[]
        sage: f = 4*x^5 - 4*x^4 - 156*x^3 + 40*x^2 + 1088*x - 1223
        sage: C = HyperellipticCurve(f)
        sage: is_geom_trivial_when_field(C,[461])
        True

    This is LMFDB curve 4489.a.4489.1::

        sage: f = x^6 + 4*x^5 + 2*x^4 + 2*x^3 + x^2 - 2*x + 1
        sage: C = HyperellipticCurve(f)
        sage: is_geom_trivial_when_field(C,[67])
        False
    """

    running_gcd = 0
    R = PolynomialRing(ZZ,2,"xv")
    x,v = R.gens()
    T = PolynomialRing(QQ,'v')
    g = v - x**4

    for p in prime_range(3,B):
        if p not in bad_primes:
            Cp = C.change_ring(FiniteField(p))
            fp = Cp.frobenius_polynomial()
            if satisfies_coefficient_condition(fp, p):
                # This defines the polynomial f_v**[4] from the paper
                fp4 = T(R(fp).resultant(g))
                if fp4.is_irreducible():
                    running_gcd = gcd(running_gcd, NumberField(fp,'a').discriminant())
                    if running_gcd <= 24:
                        return True
    return False
Beispiel #23
0
def Conic(base_field, F=None, names=None, unique=True):
    r"""
    Return the plane projective conic curve defined by ``F``
    over ``base_field``.
    
    The input form ``Conic(F, names=None)`` is also accepted,
    in which case the fraction field of the base ring of ``F``
    is used as base field.

    INPUT:
    
    - ``base_field`` -- The base field of the conic.
    
    - ``names`` -- a list, tuple, or comma separated string
      of three variable names specifying the names
      of the coordinate functions of the ambient
      space `\Bold{P}^3`. If not specified or read
      off from ``F``, then this defaults to ``'x,y,z'``.

    - ``F`` -- a polynomial, list, matrix, ternary quadratic form,
      or list or tuple of 5 points in the plane.
                   
                   If ``F`` is a polynomial or quadratic form,
                   then the output is the curve in the projective plane
                   defined by ``F = 0``.

                   If ``F`` is a polynomial, then it must be a polynomial
                   of degree at most 2 in 2 variables, or a homogeneous
                   polynomial in of degree 2 in 3 variables.
                   
                   If ``F`` is a matrix, then the output is the zero locus
                   of `(x,y,z) F (x,y,z)^t`.
    
                   If ``F`` is a list of coefficients, then it has
                   length 3 or 6 and gives the coefficients of
                   the monomials `x^2, y^2, z^2` or all 6 monomials
                   `x^2, xy, xz, y^2, yz, z^2` in lexicographic order.

                   If ``F`` is a list of 5 points in the plane, then the output
                   is a conic through those points.
      
    - ``unique`` -- Used only if ``F`` is a list of points in the plane.
      If the conic through the points is not unique, then
      raise ``ValueError`` if and only if ``unique`` is True
                    
    OUTPUT:
    
    A plane projective conic curve defined by ``F`` over a field.
    
    EXAMPLES:
    
    Conic curves given by polynomials ::

        sage: X,Y,Z = QQ['X,Y,Z'].gens()
        sage: Conic(X^2 - X*Y + Y^2 - Z^2)
        Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2
        sage: x,y = GF(7)['x,y'].gens()
        sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W')
        Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2

    Conic curves given by matrices ::

        sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z')
        Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2

        sage: x,y,z = GF(11)['x,y,z'].gens()
        sage: C = Conic(x^2+y^2-2*z^2); C
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2
        sage: Conic(C.symmetric_matrix(), 'x,y,z')
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2

    Conics given by coefficients ::
    
        sage: Conic(QQ, [1,2,3])
        Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2
        sage: Conic(GF(7), [1,2,3,4,5,6], 'X')
        Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2
    
    The conic through a set of points ::

        sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C
        Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2
        sage: C.rational_point()
        (10 : 2 : 1)
        sage: C.point([3,4])
        (3 : 4 : 1)

        sage: a=AffineSpace(GF(13),2)
        sage: Conic([a([x,x^2]) for x in range(5)])
        Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z
    """
    if not (is_IntegralDomain(base_field) or base_field == None):
        if names is None:
            names = F
        F = base_field
        base_field = None
    if isinstance(F, (list,tuple)):
        if len(F) == 1:
            return Conic(base_field, F[0], names)
        if names == None:
            names = 'x,y,z'
        if len(F) == 5:
            L=[]
            for f in F:
                if isinstance(f, SchemeMorphism_point_affine):
                    C = Sequence(f, universe = base_field)
                    if len(C) != 2:
                        raise TypeError, "points in F (=%s) must be planar"%F
                    C.append(1)
                elif isinstance(f, SchemeMorphism_point_projective_field):
                    C = Sequence(f, universe = base_field)
                elif isinstance(f, (list, tuple)):
                    C = Sequence(f, universe = base_field)
                    if len(C) == 2:
                        C.append(1)
                else:
                    raise TypeError, "F (=%s) must be a sequence of planar " \
                                      "points" % F
                if len(C) != 3:
                    raise TypeError, "points in F (=%s) must be planar" % F
                P = C.universe()
                if not is_IntegralDomain(P):
                    raise TypeError, "coordinates of points in F (=%s) must " \
                                     "be in an integral domain" % F
                L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2,
                                   C[1]*C[2], C[2]**2], P.fraction_field()))
            M=Matrix(L)
            if unique and M.rank() != 5:
                raise ValueError, "points in F (=%s) do not define a unique " \
                                   "conic" % F
            con = Conic(base_field, Sequence(M.right_kernel().gen()), names)
            con.point(F[0])
            return con
        F = Sequence(F, universe = base_field)
        base_field = F.universe().fraction_field()
        temp_ring = PolynomialRing(base_field, 3, names)
        (x,y,z) = temp_ring.gens()
        if len(F) == 3:
            return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2)
        if len(F) == 6:
            return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \
                         F[4]*y*z + F[5]*z**2)
        raise TypeError, "F (=%s) must be a sequence of 3 or 6" \
                         "coefficients" % F
    if is_QuadraticForm(F):
        F = F.matrix()
    if is_Matrix(F) and F.is_square() and F.ncols() == 3:
        if names == None:
            names = 'x,y,z'
        temp_ring = PolynomialRing(F.base_ring(), 3, names)
        F = vector(temp_ring.gens()) * F * vector(temp_ring.gens())

    if not is_MPolynomial(F):
        raise TypeError, "F (=%s) must be a three-variable polynomial or " \
                         "a sequence of points or coefficients" % F

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2" % F

    if base_field == None:
        base_field = F.base_ring()
    if not is_IntegralDomain(base_field):
        raise ValueError, "Base field (=%s) must be a field" % base_field
    base_field = base_field.fraction_field()
    if names == None:
        names = F.parent().variable_names()
    pol_ring = PolynomialRing(base_field, 3, names)

    if F.parent().ngens() == 2:
        (x,y,z) = pol_ring.gens()
        F = pol_ring(F(x/z,y/z)*z**2)    

    if F == 0:
        raise ValueError, "F must be nonzero over base field %s" % base_field

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2 over base field %s" % \
                          (F, base_field)

    if F.parent().ngens() == 3:
        P2 = ProjectiveSpace(2, base_field, names)
        if is_PrimeFiniteField(base_field):
            return ProjectiveConic_prime_finite_field(P2, F)
        if is_FiniteField(base_field):
            return ProjectiveConic_finite_field(P2, F)
        if is_RationalField(base_field):
            return ProjectiveConic_rational_field(P2, F)
        if is_NumberField(base_field):
            return ProjectiveConic_number_field(P2, F)
        return ProjectiveConic_field(P2, F)

    raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
Beispiel #24
0
    def parametrization(self, point=None, morphism=True):
        r"""
        Return a parametrization `f` of ``self`` together with the
        inverse of `f`.

        If ``point`` is specified, then that point is used
        for the parametrization. Otherwise, use ``self.rational_point()``
        to find a point.

        If ``morphism`` is True, then `f` is returned in the form
        of a Scheme morphism. Otherwise, it is a tuple of polynomials
        that gives the parametrization.

        EXAMPLES:

        An example over a finite field ::

            sage: c = Conic(GF(2), [1,1,1,1,1,0])
            sage: c.parametrization()
            (Scheme morphism:
              From: Projective Space of dimension 1 over Finite Field of size 2
              To:   Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              Defn: Defined on coordinates by sending (x : y) to
                    (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2),
             Scheme morphism:
              From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y
            + y^2 + x*z + y*z
              To:   Projective Space of dimension 1 over Finite Field of size 2
              Defn: Defined on coordinates by sending (x : y : z) to
                    (y : x))

        An example with ``morphism = False`` ::

            sage: R.<x,y,z> = QQ[]
            sage: C = Curve(7*x^2 + 2*y*z + z^2)
            sage: (p, i) = C.parametrization(morphism = False); (p, i)
            ([-2*x*y, 7*x^2 + y^2, -2*y^2], [-1/2*x, -1/2*z])
            sage: C.defining_polynomial()(p)
            0
            sage: i[0](p) / i[1](p)
            x/y

        A ``ValueError`` is raised if ``self`` has no rational point ::

            sage: C = Conic(x^2 + y^2 + 7*z^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field!

        A ``ValueError`` is raised if ``self`` is not smooth ::

            sage: C = Conic(x^2 + y^2)
            sage: C.parametrization()
            Traceback (most recent call last):
            ...
            ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization.
        """
        if (not self._parametrization is None) and not point:
            par = self._parametrization
        else:
            if not self.is_smooth():
                raise ValueError, "The conic self (=%s) is not smooth, hence does not have a parametrization." % self
            if point == None:
                point = self.rational_point()
            point = Sequence(point)
            B = self.base_ring()
            Q = PolynomialRing(B, 'x,y')
            [x, y] = Q.gens()
            gens = self.ambient_space().gens()
            P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1'])
            [X, Y, T0, T1] = P.gens()
            c3 = [j for j in range(2,-1,-1) if point[j] != 0][0]
            c1 = [j for j in range(3) if j != c3][0]
            c2 = [j for j in range(3) if j != c3 and j != c1][0]
            L = [0,0,0]
            L[c1] = Y*T1*point[c1] + Y*T0
            L[c2] = Y*T1*point[c2] + X*T0
            L[c3] = Y*T1*point[c3]
            bezout = P(self.defining_polynomial()(L) / T0)
            t = [bezout([x,y,0,-1]),bezout([x,y,1,0])]
            par = (tuple([Q(p([x,y,t[0],t[1]])/y) for  p in L]),
                   tuple([gens[m]*point[c3]-gens[c3]*point[m]
                       for m in [c2,c1]]))
            if self._parametrization is None:
                self._parametrization = par
        if not morphism:
            return par
        P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y')
        return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
Beispiel #25
0
 t, = self.base_ring().base().gens()  # t in F[t]
 supp = []
 roots = [[], [], []]
 remove = None
 # loop through the coefficients and find a root of f_i (as in
 # [HC2006]) modulo each element in the coefficients' support
 for i in (0, 1, 2):
     supp.append(list(coeff[i].factor()))
     for p in supp[i]:
         if p[1] != 1:
             raise ValueError("Expected factor of exponent 1.")
         # Convert to monic factor
         x = p[0] / list(p[0])[-1]
         N = p[0].base_ring().extension(x, 'tbar')
         R = PolynomialRing(N, 'u')
         u, = R.gens()
         # If p[0] has degree 1, sage might forget the "defining
         # polynomial" of N, so we define our own modulo operation
         if p[0].degree() == 1:
             mod = t.parent().hom([-x[0]])
         else:
             mod = N
         if i == 0:
             x = -mod(coeff[2]) / mod(coeff[1])
         elif i == 1:
             x = -mod(coeff[0]) / mod(coeff[2])
         else:
             x = -mod(coeff[1]) / mod(coeff[0])
         if x.is_square():
             root = N(x.sqrt())
         else:
Beispiel #26
0
def rational_type(f, n=ZZ(3), base_ring=ZZ):
    r"""
    Return the basic analytic properties that can be determined
    directly from the specified rational function ``f``
    which is interpreted as a representation of an
    element of a FormsRing for the Hecke Triangle group
    with parameter ``n`` and the specified ``base_ring``.

    In particular the following degree of the generators is assumed:

    `deg(1) := (0, 1)`
    `deg(x) := (4/(n-2), 1)`
    `deg(y) := (2n/(n-2), -1)`
    `deg(z) := (2, -1)`

    The meaning of homogeneous elements changes accordingly.

    INPUT:

    - ``f``               - A rational function in ``x,y,z,d`` over ``base_ring``.
    - ``n``               - An integer greater or equal to ``3`` corresponding
                           to the ``HeckeTriangleGroup`` with that parameter
                           (default: ``3``).
    - ``base_ring```      - The base ring of the corresponding forms ring, resp.
                            polynomial ring (default: ``ZZ``).

    OUTPUT:
    
    A tuple ``(elem, h**o, k, ep, analytic_type)`` describing the basic
    analytic properties of ``f`` (with the interpretation indicated above).
    
    - ``elem``            - ``True`` if ``f`` has a homogeneous denominator.
    - ``h**o``            - ``True`` if ``f`` also has a homogeneous numerator.
    - ``k``               - ``None`` if ``f`` is not homogeneneous, otherwise
                            the weight of ``f`` (which is the first component
                            of its degree).
    - ``ep``              - ``None`` if ``f`` is not homogeneous, otherwise
                            the multiplier of ``f`` (which is the second component
                            of its degree)
    - ``analytic_type``   - The ``AnalyticType`` of ``f``.

    For the zero function the degree ``(0, 1)`` is choosen.

    This function is (heavily) used to determine the type of elements
    and to check if the element really is contained in its parent.


    EXAMPLES::

        sage: (x,y,z,d) = var("x,y,z,d")

        sage: rational_type(0, n=4)
        (True, True, 0, 1, zero)

        sage: rational_type(1, n=12)
        (True, True, 0, 1, modular)

        sage: rational_type(x^3 - y^2)
        (True, True, 12, 1, cuspidal)

        sage: rational_type(x * z, n=7)
        (True, True, 14/5, -1, quasi modular)

        sage: rational_type(1/(x^3 - y^2) + z/d)
        (True, False, None, None, quasi weakly holomorphic modular)

        sage: rational_type(x^3/(x^3 - y^2))
        (True, True, 0, 1, weakly holomorphic modular)

        sage: rational_type(1/(x + z))
        (False, False, None, None, None)

        sage: rational_type(1/x + 1/z)
        (True, False, None, None, quasi meromorphic modular)

        sage: rational_type(d/x, n=10)
        (True, True, -1/2, 1, meromorphic modular)

        sage: rational_type(1.1 * z * (x^8-y^2), n=8, base_ring=CC)
        (True, True, 22/3, -1, quasi cuspidal)
    """

    from analytic_type import AnalyticType
    AT = AnalyticType()

    # Determine whether f is zero
    if (f == 0):
        #       elem, h**o, k,     ep,    analytic_type
        return (True, True, QQ(0), ZZ(1), AT([]))
 
    analytic_type = AT(["quasi", "mero"])

    R          = PolynomialRing(base_ring,'x,y,z,d')
    F          = FractionField(R)
    (x,y,z,d)  = R.gens()
    R2         = PolynomialRing(PolynomialRing(base_ring, 'd'), 'x,y,z')
    dhom       = R.hom( R2.gens() + (R2.base().gen(),), R2)

    f          = F(f)
    n          = ZZ(n)

    num        = R(f.numerator())
    denom      = R(f.denominator())
    hom_num    = R(   num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) )
    hom_denom  = R( denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) )
    ep_num     = set([ZZ(1) - 2*(( sum([g.exponents()[0][m] for m in [1,2]]) )%2) for g in   dhom(num).monomials()])
    ep_denom   = set([ZZ(1) - 2*(( sum([g.exponents()[0][m] for m in [1,2]]) )%2) for g in dhom(denom).monomials()])

    # Determine whether the denominator of f is homogeneous
    if (len(ep_denom) == 1 and dhom(hom_denom).is_homogeneous()):
        elem = True
    else:
        #       elem,  h**o,  k,    ep,   analytic_type
        return (False, False, None, None, None)


    # Determine whether f is homogeneous
    if (len(ep_num) == 1 and dhom(hom_num).is_homogeneous()):
        h**o   = True
        weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) / (n-2)
        ep     = ep_num.pop() / ep_denom.pop()
    # TODO: decompose f (resp. its degrees) into homogeneous parts
    else:
        h**o   = False
        weight = None
        ep     = None

    # Note that we intentially leave out the d-factor!
    finf_pol = x**n-y**2

    # Determine whether f is modular
    if not ( (num.degree(z) > 0) or (denom.degree(z) > 0) ):
        analytic_type = analytic_type.reduce_to("mero")

    # Determine whether f is holomorphic
    if (dhom(denom).is_constant()):
        analytic_type = analytic_type.reduce_to(["quasi", "holo"])
        # Determine whether f is cuspidal in the sense that finf divides it...
        # Bug in singular: finf_pol.dividess(1.0) fails over RR
        if (not dhom(num).is_constant()) and finf_pol.divides(num):
            analytic_type = analytic_type.reduce_to(["quasi", "cusp"])
    else:
        # -> because of a bug with singular in case some cases
        try:
            while (finf_pol.divides(denom)):
                # a simple "denom /= finf_pol" is strangely not enough for non-exact rings
                denom = denom.quo_rem(finf_pol)[0]
                denom = R(denom)
        except TypeError:
            pass

        # Determine whether f is weakly holomorphic in the sense that at most powers of finf occur in denom
        if (dhom(denom).is_constant()):
            analytic_type = analytic_type.reduce_to(["quasi", "weak"])

    return (elem, h**o, weight, ep, analytic_type)
def get_is_geom_field(f, C, bad_primes, B=200):
    r"""
    Determine whether the geometric endomorphism algebra is a field.

    This is Algorithm 4.10 in [Lom2019]_. The computation done here
    may allow one to immediately conclude that the geometric endomorphism
    ring is trivial (i.e. the integer ring); this information is output
    in a second boolean to avoid unnecessary subsequent computation.

    An additional optimisation comes from Part (2) of Theorem 4.8 in
    [Lom2019]_, from which we can conclude that the endomorphism ring
    is geometrically trivial, and from Proposition 4.7 in loc. cit. from
    which we can rule out potential QM.

    INPUT:

    - ``f`` -- a polynomial defining the hyperelliptic curve.

    - ``C`` -- the hyperelliptic curve.

    - ``bad_primes`` -- the list of odd primes of bad reduction.

    - ``B`` -- (default: 200) the bound which appears in the statement of
      the algorithm from [Lom2019]_

    OUTPUT:

    Pair of booleans (bool1, bool2). `bool1` indicates if the
    geometric endomorphism algebra is a field; `bool2` indicates if the
    geometric endomorphism algebra is the field of rational numbers.

    WARNING:

    There is a very small chance that this algorithm return ``False`` when in
    fact it is ``True``. In this case, as explained in the discussion
    immediately preceding Algorithm 4.15 of [Lom2019]_, this can be established
    by increasing the optional `B` parameter. Mathematically, this algorithm
    gives the correct answer only in the limit as `B \to \infty`, although in
    practice `B = 200` was sufficient to correctly verify every single entry
    in the LMFDB. However, strictly speaking, a ``False`` returned by this
    function is not provably ``False``.

    EXAMPLES:

    This is LMFDB curve 940693.a.960693.1::

        sage: from sage.schemes.hyperelliptic_curves.jacobian_endomorphism_utils import get_is_geom_field
        sage: R.<x> = QQ[]
        sage: f = 4*x^6 - 12*x^5 + 20*x^3 - 8*x^2 - 4*x + 1
        sage: C = HyperellipticCurve(f)
        sage: get_is_geom_field(f,C,[13,269])
        (False, False)

    This is LMFDB curve 3125.a.3125.1::

        sage: f = 4*x^5 + 1
        sage: C = HyperellipticCurve(f)
        sage: get_is_geom_field(f,C,[5])
        (True, False)

    This is LMFDB curve 277.a.277.2::

        sage: f = 4*x^6 - 36*x^4 + 56*x^3 - 76*x^2 + 44*x - 23
        sage: C = HyperellipticCurve(f)
        sage: get_is_geom_field(f,C,[277])
        (True, True)
    """

    if C.has_odd_degree_model():
        C_odd = C.odd_degree_model()
        f_odd, h_odd = C_odd.hyperelliptic_polynomials()
        # if f was odd to begin with, then f_odd = f
        assert f_odd.degree() == 5

        if (4*f_odd + h_odd**2).degree() == 5:
            f_new = 4*f_odd + h_odd**2
            if f_new.is_irreducible():
                # i.e. the Jacobian is geometrically simple
                f_disc_odd_prime_exponents = [v for _,v in f_new.discriminant().prime_to_S_part([ZZ(2)]).factor()]
                if 1 in f_disc_odd_prime_exponents:
                    return (True, True)  # Theorem 4.8 (2)
                # At this point we are in the situation of Algorithm 4.10
                # Step 1, so either the geometric endomorphism algebra is a
                # field or it is a quaternion algebra. This latter case implies
                # that the Jacobian is the square of an elliptic curve modulo
                # a prime p where f is also irreducible (which exist by
                # Chebotarev density). This contradicts Prop 4.7, hence we can
                # conclude as follows.
                return (True, False)

    if f.is_irreducible():
        assert f.degree() == 6  # else we should already have exited by now
        G = f.galois_group()
        if G.order() in [360, 720]:
            return (True, True)  # Algorithm 4.10 Step 2

    R = PolynomialRing(ZZ,2,"xv")
    x,v = R.gens()
    T = PolynomialRing(QQ,'v')
    g = v - x**12

    for p in prime_range(3,B):
        if p not in bad_primes:
            fp = C.change_ring(FiniteField(p)).frobenius_polynomial()

            # This defines the polynomial f_v**[12] from the paper
            fp12 = T(R(fp).resultant(g))

            if fp12.is_irreducible():
                # i.e. the Jacobian is geometrically simple
                f_disc_odd_prime_exponents = [v for _,v in f.discriminant().prime_to_S_part([ZZ(2)]).factor()]
                if 1 in f_disc_odd_prime_exponents:
                    return (True, True)  # Theorem 4.8 (2)
                return (True, False) # Algorithm 4.10 Step 3 plus Prop 4.7 as above
    return (False, False)
Beispiel #28
0
def Conic(base_field, F=None, names=None, unique=True):
    r"""
    Return the plane projective conic curve defined by ``F``
    over ``base_field``.

    The input form ``Conic(F, names=None)`` is also accepted,
    in which case the fraction field of the base ring of ``F``
    is used as base field.

    INPUT:

    - ``base_field`` -- The base field of the conic.

    - ``names`` -- a list, tuple, or comma separated string
      of three variable names specifying the names
      of the coordinate functions of the ambient
      space `\Bold{P}^3`. If not specified or read
      off from ``F``, then this defaults to ``'x,y,z'``.

    - ``F`` -- a polynomial, list, matrix, ternary quadratic form,
      or list or tuple of 5 points in the plane.

                   If ``F`` is a polynomial or quadratic form,
                   then the output is the curve in the projective plane
                   defined by ``F = 0``.

                   If ``F`` is a polynomial, then it must be a polynomial
                   of degree at most 2 in 2 variables, or a homogeneous
                   polynomial in of degree 2 in 3 variables.

                   If ``F`` is a matrix, then the output is the zero locus
                   of `(x,y,z) F (x,y,z)^t`.

                   If ``F`` is a list of coefficients, then it has
                   length 3 or 6 and gives the coefficients of
                   the monomials `x^2, y^2, z^2` or all 6 monomials
                   `x^2, xy, xz, y^2, yz, z^2` in lexicographic order.

                   If ``F`` is a list of 5 points in the plane, then the output
                   is a conic through those points.

    - ``unique`` -- Used only if ``F`` is a list of points in the plane.
      If the conic through the points is not unique, then
      raise ``ValueError`` if and only if ``unique`` is True

    OUTPUT:

    A plane projective conic curve defined by ``F`` over a field.

    EXAMPLES:

    Conic curves given by polynomials ::

        sage: X,Y,Z = QQ['X,Y,Z'].gens()
        sage: Conic(X^2 - X*Y + Y^2 - Z^2)
        Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2
        sage: x,y = GF(7)['x,y'].gens()
        sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W')
        Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2

    Conic curves given by matrices ::

        sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z')
        Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2

        sage: x,y,z = GF(11)['x,y,z'].gens()
        sage: C = Conic(x^2+y^2-2*z^2); C
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2
        sage: Conic(C.symmetric_matrix(), 'x,y,z')
        Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2

    Conics given by coefficients ::

        sage: Conic(QQ, [1,2,3])
        Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2
        sage: Conic(GF(7), [1,2,3,4,5,6], 'X')
        Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2

    The conic through a set of points ::

        sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C
        Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2
        sage: C.rational_point()
        (10 : 2 : 1)
        sage: C.point([3,4])
        (3 : 4 : 1)

        sage: a=AffineSpace(GF(13),2)
        sage: Conic([a([x,x^2]) for x in range(5)])
        Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z
    """
    if not (is_IntegralDomain(base_field) or base_field == None):
        if names is None:
            names = F
        F = base_field
        base_field = None
    if isinstance(F, (list,tuple)):
        if len(F) == 1:
            return Conic(base_field, F[0], names)
        if names == None:
            names = 'x,y,z'
        if len(F) == 5:
            L=[]
            for f in F:
                if isinstance(f, SchemeMorphism_point_affine):
                    C = Sequence(f, universe = base_field)
                    if len(C) != 2:
                        raise TypeError, "points in F (=%s) must be planar"%F
                    C.append(1)
                elif isinstance(f, SchemeMorphism_point_projective_field):
                    C = Sequence(f, universe = base_field)
                elif isinstance(f, (list, tuple)):
                    C = Sequence(f, universe = base_field)
                    if len(C) == 2:
                        C.append(1)
                else:
                    raise TypeError, "F (=%s) must be a sequence of planar " \
                                      "points" % F
                if len(C) != 3:
                    raise TypeError, "points in F (=%s) must be planar" % F
                P = C.universe()
                if not is_IntegralDomain(P):
                    raise TypeError, "coordinates of points in F (=%s) must " \
                                     "be in an integral domain" % F
                L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2,
                                   C[1]*C[2], C[2]**2], P.fraction_field()))
            M=Matrix(L)
            if unique and M.rank() != 5:
                raise ValueError, "points in F (=%s) do not define a unique " \
                                   "conic" % F
            con = Conic(base_field, Sequence(M.right_kernel().gen()), names)
            con.point(F[0])
            return con
        F = Sequence(F, universe = base_field)
        base_field = F.universe().fraction_field()
        temp_ring = PolynomialRing(base_field, 3, names)
        (x,y,z) = temp_ring.gens()
        if len(F) == 3:
            return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2)
        if len(F) == 6:
            return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \
                         F[4]*y*z + F[5]*z**2)
        raise TypeError, "F (=%s) must be a sequence of 3 or 6" \
                         "coefficients" % F
    if is_QuadraticForm(F):
        F = F.matrix()
    if is_Matrix(F) and F.is_square() and F.ncols() == 3:
        if names == None:
            names = 'x,y,z'
        temp_ring = PolynomialRing(F.base_ring(), 3, names)
        F = vector(temp_ring.gens()) * F * vector(temp_ring.gens())

    if not is_MPolynomial(F):
        raise TypeError, "F (=%s) must be a three-variable polynomial or " \
                         "a sequence of points or coefficients" % F

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2" % F

    if base_field == None:
        base_field = F.base_ring()
    if not is_IntegralDomain(base_field):
        raise ValueError, "Base field (=%s) must be a field" % base_field
    base_field = base_field.fraction_field()
    if names == None:
        names = F.parent().variable_names()
    pol_ring = PolynomialRing(base_field, 3, names)

    if F.parent().ngens() == 2:
        (x,y,z) = pol_ring.gens()
        F = pol_ring(F(x/z,y/z)*z**2)

    if F == 0:
        raise ValueError, "F must be nonzero over base field %s" % base_field

    if F.total_degree() != 2:
        raise TypeError, "F (=%s) must have degree 2 over base field %s" % \
                          (F, base_field)

    if F.parent().ngens() == 3:
        P2 = ProjectiveSpace(2, base_field, names)
        if is_PrimeFiniteField(base_field):
            return ProjectiveConic_prime_finite_field(P2, F)
        if is_FiniteField(base_field):
            return ProjectiveConic_finite_field(P2, F)
        if is_RationalField(base_field):
            return ProjectiveConic_rational_field(P2, F)
        if is_NumberField(base_field):
            return ProjectiveConic_number_field(P2, F)
        return ProjectiveConic_field(P2, F)

    raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
Beispiel #29
0
class WeierstrassIsomorphism(EllipticCurveHom, baseWI):
    r"""
    Class representing a Weierstrass isomorphism between two elliptic curves.
    """
    def __init__(self, E=None, urst=None, F=None):
        r"""
        Constructor for WeierstrassIsomorphism class,

        INPUT:

        - ``E`` -- an EllipticCurve, or None (see below).

        - ``urst`` -- a 4-tuple `(u,r,s,t)`, or None (see below).

        - ``F`` -- an EllipticCurve, or None (see below).

        Given two Elliptic Curves ``E`` and ``F`` (represented by
        Weierstrass models as usual), and a transformation ``urst``
        from ``E`` to ``F``, construct an isomorphism from ``E`` to
        ``F``.  An exception is raised if ``urst(E)!=F``.  At most one
        of ``E``, ``F``, ``urst`` can be None.  If ``F==None`` then
        ``F`` is constructed as ``urst(E)``.  If ``E==None`` then
        ``E`` is constructed as ``urst^-1(F)``.  If ``urst==None``
        then an isomorphism from ``E`` to ``F`` is constructed if
        possible, and an exception is raised if they are not
        isomorphic.  Otherwise ``urst`` can be a tuple of length 4 or
        a object of type ``baseWI``.

        Users will not usually need to use this class directly, but instead use
        methods such as ``isomorphism`` of elliptic curves.

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
            sage: WeierstrassIsomorphism(EllipticCurve([0,1,2,3,4]),(-1,2,3,4))
            Elliptic-curve morphism:
            From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
            To:   Elliptic Curve defined by y^2 - 6*x*y - 10*y = x^3 - 2*x^2 - 11*x - 2 over Rational Field
            Via:  (u,r,s,t) = (-1, 2, 3, 4)
            sage: E = EllipticCurve([0,1,2,3,4])
            sage: F = EllipticCurve(E.cremona_label())
            sage: WeierstrassIsomorphism(E,None,F)
            Elliptic-curve morphism:
            From: Elliptic Curve defined by y^2 + 2*y = x^3 + x^2 + 3*x + 4 over Rational Field
            To:   Elliptic Curve defined by y^2  = x^3 + x^2 + 3*x + 5 over Rational Field
            Via:  (u,r,s,t) = (1, 0, 0, -1)
            sage: w = WeierstrassIsomorphism(None,(1,0,0,-1),F)
            sage: w._domain==E
            True
        """
        from .ell_generic import is_EllipticCurve

        if E is not None:
            if not is_EllipticCurve(E):
                raise ValueError(
                    "First argument must be an elliptic curve or None")
        if F is not None:
            if not is_EllipticCurve(F):
                raise ValueError(
                    "Third argument must be an elliptic curve or None")
        if urst is not None:
            if len(urst) != 4:
                raise ValueError("Second argument must be [u,r,s,t] or None")
        if len([par for par in [E, urst, F] if par is not None]) < 2:
            raise ValueError("At most 1 argument can be None")

        if F is None:  # easy case
            baseWI.__init__(self, *urst)
            F = EllipticCurve(baseWI.__call__(self, list(E.a_invariants())))

        elif E is None:  # easy case in reverse
            baseWI.__init__(self, *urst)
            inv_urst = baseWI.__invert__(self)
            E = EllipticCurve(baseWI.__call__(inv_urst,
                                              list(F.a_invariants())))

        elif urst is None:  # try to construct the morphism
            urst = isomorphisms(E, F, True)
            if urst is None:
                raise ValueError("Elliptic curves not isomorphic.")
            baseWI.__init__(self, *urst)

        else:  # none of the parameters is None:
            baseWI.__init__(self, *urst)
            if F != EllipticCurve(baseWI.__call__(self, list(
                    E.a_invariants()))):
                raise ValueError(
                    "second argument is not an isomorphism from first argument to third argument"
                )

        base_ring = get_coercion_model().common_parent(E.base_ring(),
                                                       F.base_ring(), *urst)
        self._mpoly_ring = PolynomialRing(base_ring, ['x', 'y'])
        self._poly_ring = PolynomialRing(base_ring, ['x'])

        self._domain = E
        self._codomain = F
        EllipticCurveHom.__init__(self, self._domain, self._codomain)

    def _richcmp_(self, other, op):
        r"""
        Standard comparison function for the WeierstrassIsomorphism class.

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
            sage: E = EllipticCurve('389a1')
            sage: F = E.change_weierstrass_model(1,2,3,4)
            sage: w1 = E.isomorphism_to(F)
            sage: w1 == w1
            True
            sage: w2 = F.automorphisms()[0] *w1
            sage: w1 == w2
            False

            sage: E = EllipticCurve_from_j(GF(7)(0))
            sage: F = E.change_weierstrass_model(2,3,4,5)
            sage: a = E.isomorphisms(F)
            sage: b = [w*a[0] for w in F.automorphisms()]
            sage: b.sort()
            sage: a == b
            True
            sage: c = [a[0]*w for w in E.automorphisms()]
            sage: c.sort()
            sage: a == c
            True
        """
        if isinstance(other, WeierstrassIsomorphism):
            lx = self._domain
            rx = other._domain
            if lx != rx:
                return richcmp_not_equal(lx, rx, op)

            lx = self._codomain
            rx = other._codomain
            if lx != rx:
                return richcmp_not_equal(lx, rx, op)

            return baseWI.__richcmp__(self, other, op)

        return EllipticCurveHom._richcmp_(self, other, op)

    def _eval(self, P):
        r"""
        Less strict evaluation method for internal use.

        In particular, this can be used to evaluate ``self`` at a
        point defined over an extension field.

        INPUT: a sequence of 3 coordinates defining a point on ``self``

        OUTPUT: the result of evaluating ``self'' at the given point

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
            sage: E = EllipticCurve([i,0]); E
            Elliptic Curve defined by y^2 = x^3 + I*x over Number Field in I with defining polynomial x^2 + 1 with I = 1*I
            sage: iso = WeierstrassIsomorphism(E, (i,1,2,3))
            sage: P = E.change_ring(QQbar).lift_x(QQbar.random_element())
            sage: Q = iso._eval(P)
            sage: Q.curve()
            Elliptic Curve defined by y^2 + (-4*I)*x*y + 6*I*y = x^3 + x^2 + (I-9)*x + (-I+8) over Algebraic Field
            sage: y = next(filter(bool, iter(QQbar.random_element, None)))  # sample until nonzero
            sage: iso._eval((0, y, 0)) == 0
            True
        """
        if self._domain.defining_polynomial()(*P):
            raise ValueError(f'{P} not on {self._domain}')

        Q = baseWI.__call__(self, P)
        k = Sequence(tuple(P) + tuple(Q)).universe()
        return self._codomain.base_extend(k).point(Q)

    def __call__(self, P):
        r"""
        Call function for WeierstrassIsomorphism class.

        INPUT:

        - ``P`` (Point) -- a point on the domain curve.

        OUTPUT:

        (Point) the transformed point on the codomain curve.

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
            sage: E = EllipticCurve('37a1')
            sage: w = WeierstrassIsomorphism(E,(2,3,4,5))
            sage: P = E(0,-1)
            sage: w(P)
            (-3/4 : 3/4 : 1)
            sage: w(P).curve() == E.change_weierstrass_model((2,3,4,5))
            True
        """
        if P[2] == 0:
            return self._codomain(0)
        return self._codomain.point(baseWI.__call__(self, tuple(P._coords)),
                                    check=False)

    def __invert__(self):
        r"""
        Return the inverse of this WeierstrassIsomorphism.

        EXAMPLES::

            sage: E = EllipticCurve('5077')
            sage: F = E.change_weierstrass_model([2,3,4,5]); F
            Elliptic Curve defined by y^2 + 4*x*y + 11/8*y = x^3 - 7/4*x^2 - 3/2*x - 9/32 over Rational Field
            sage: w = E.isomorphism_to(F)
            sage: P = E(-2,3,1)
            sage: w(P)
            (-5/4 : 9/4 : 1)
            sage: ~w
            Elliptic-curve morphism:
              From: Elliptic Curve defined by y^2 + 4*x*y + 11/8*y = x^3 - 7/4*x^2 - 3/2*x - 9/32 over Rational Field
              To:   Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field
              Via:  (u,r,s,t) = (1/2, -3/4, -2, 7/8)
            sage: Q = w(P); Q
            (-5/4 : 9/4 : 1)
            sage: (~w)(Q)
            (-2 : 3 : 1)
        """
        winv = baseWI.__invert__(self).tuple()
        return WeierstrassIsomorphism(self._codomain, winv, self._domain)

    @staticmethod
    def _composition_impl(left, right):
        r"""
        Return the composition of a ``WeierstrassIsomorphism``
        with another elliptic-curve morphism.

        Called by :meth:`EllipticCurveHom._composition_`.

        EXAMPLES::

            sage: E1 = EllipticCurve('5077')
            sage: E2 = E1.change_weierstrass_model([2,3,4,5])
            sage: w1 = E1.isomorphism_to(E2)
            sage: E3 = E2.change_weierstrass_model([6,7,8,9])
            sage: w2 = E2.isomorphism_to(E3)
            sage: P = E1(-2,3,1)
            sage: (w2*w1)(P) == w2(w1(P))
            True

        TESTS:

        We should return ``NotImplemented`` when passed a combination of
        elliptic-curve morphism types that we don't handle here::

            sage: E = EllipticCurve([1,0])
            sage: phi = E.isogeny(E(0,0))
            sage: w1._composition_impl(phi.dual(), phi)
            NotImplemented
        """
        if isinstance(left, WeierstrassIsomorphism) and isinstance(
                right, WeierstrassIsomorphism):
            if left._domain != right._codomain:
                raise ValueError(
                    "Domain of first argument must equal codomain of second")
            w = baseWI.__mul__(left, right)
            return WeierstrassIsomorphism(right._domain, w.tuple(),
                                          left._codomain)

        return NotImplemented

    def __repr__(self):
        r"""
        Return the string representation of this WeierstrassIsomorphism.

        OUTPUT:

        (string) The underlying morphism, together with an extra line
        showing the `(u,r,s,t)` parameters.

        EXAMPLES::

            sage: E1 = EllipticCurve('5077')
            sage: E2 = E1.change_weierstrass_model([2,3,4,5])
            sage: E1.isomorphism_to(E2)
            Elliptic-curve morphism:
            From: Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field
            To:   Elliptic Curve defined by y^2 + 4*x*y + 11/8*y = x^3 - 7/4*x^2 - 3/2*x - 9/32 over Rational Field
            Via:  (u,r,s,t) = (2, 3, 4, 5)
        """
        return EllipticCurveHom.__repr__(
            self) + "\n  Via:  (u,r,s,t) = " + baseWI.__repr__(self)

    # EllipticCurveHom methods

    def degree(self):
        """
        Return the degree as a rational map of this isomorphism.

        Isomorphisms always have degree `1` by definition.

        EXAMPLES::

            sage: E1 = EllipticCurve([1,2,3,4,5])
            sage: E2 = EllipticCurve_from_j(E1.j_invariant())
            sage: E1.isomorphism_to(E2).degree()
            1
        """
        return 1

    def rational_maps(self):
        """
        Return the pair of rational maps defining this isomorphism.

        EXAMPLES::

            sage: E1 = EllipticCurve([11,22,33,44,55])
            sage: E2 = EllipticCurve_from_j(E1.j_invariant())
            sage: iso = E1.isomorphism_to(E2); iso
            Elliptic-curve morphism:
              From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55 over Rational Field
              To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681 over Rational Field
              Via:  (u,r,s,t) = (1, -17, -5, 77)
            sage: iso.rational_maps()
            (x + 17, 5*x + y + 8)
            sage: f = E2.defining_polynomial()(*iso.rational_maps(), 1)
            sage: I = E1.defining_ideal()
            sage: x,y,z = I.ring().gens()
            sage: f in I + Ideal(z-1)
            True

        ::

            sage: E = EllipticCurve(GF(65537), [1,1,1,1,1])
            sage: w = E.isomorphism_to(E.short_weierstrass_model())
            sage: f,g = w.rational_maps()
            sage: P = E.random_point()
            sage: w(P).xy() == (f(P.xy()), g(P.xy()))
            True

        TESTS::

            sage: iso.rational_maps()[0].parent()
            Multivariate Polynomial Ring in x, y over Rational Field
            sage: iso.rational_maps()[1].parent()
            Multivariate Polynomial Ring in x, y over Rational Field
        """
        return tuple(baseWI.__call__(self, self._mpoly_ring.gens()))

    def x_rational_map(self):
        """
        Return the `x`-coordinate rational map of this isomorphism.

        EXAMPLES::

            sage: E1 = EllipticCurve([11,22,33,44,55])
            sage: E2 = EllipticCurve_from_j(E1.j_invariant())
            sage: iso = E1.isomorphism_to(E2); iso
            Elliptic-curve morphism:
              From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55 over Rational Field
              To:   Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681 over Rational Field
              Via:  (u,r,s,t) = (1, -17, -5, 77)
            sage: iso.x_rational_map()
            x + 17
            sage: iso.x_rational_map() == iso.rational_maps()[0]
            True

        TESTS::

            sage: iso.x_rational_map().parent()
            Univariate Polynomial Ring in x over Rational Field
        """
        x, = self._poly_ring.gens()
        return (x - self.r) / self.u**2

    def kernel_polynomial(self):
        """
        Return the kernel polynomial of this isomorphism.

        Isomorphisms have trivial kernel by definition, hence this
        method always returns `1`.

        EXAMPLES::

            sage: E1 = EllipticCurve([11,22,33,44,55])
            sage: E2 = EllipticCurve_from_j(E1.j_invariant())
            sage: iso = E1.isomorphism_to(E2)
            sage: iso.kernel_polynomial()
            1
            sage: psi = E1.isogeny(iso.kernel_polynomial(), codomain=E2); psi
            Isogeny of degree 1 from Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 684*x + 6681 over Rational Field
            sage: psi in {iso, -iso}
            True

        TESTS::

            sage: iso.kernel_polynomial().parent()
            Univariate Polynomial Ring in x over Rational Field
        """
        return self._poly_ring(1)

    def dual(self):
        """
        Return the dual isogeny of this isomorphism.

        For isomorphisms, the dual is just the inverse.

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
            sage: E = EllipticCurve(QuadraticField(-3), [0,1])
            sage: w = WeierstrassIsomorphism(E, (CyclotomicField(3).gen(),0,0,0))
            sage: (w.dual() * w).rational_maps()
            (x, y)

        ::

            sage: E1 = EllipticCurve([11,22,33,44,55])
            sage: E2 = E1.short_weierstrass_model()
            sage: iso = E1.isomorphism_to(E2)
            sage: iso.dual() == ~iso
            True
        """
        return ~self

    def __neg__(self):
        """
        Return the negative of this isomorphism, i.e., its composition
        with the negation map `[-1]`.

        EXAMPLES::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
            sage: E = EllipticCurve([11,22,33,44,55])
            sage: w = WeierstrassIsomorphism(E, (66,77,88,99))
            sage: -w
            Elliptic-curve morphism:
              From: Elliptic Curve defined by y^2 + 11*x*y + 33*y = x^3 + 22*x^2 + 44*x + 55 over Rational Field
              To:   Elliptic Curve defined by y^2 + 17/6*x*y + 49/13068*y = x^3 - 769/396*x^2 - 3397/862488*x + 44863/7513995456 over Rational Field
              Via:  (u,r,s,t) = (-66, 77, -99, -979)
            sage: -(-w) == w
            True

        ::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
            sage: E = EllipticCurve(QuadraticField(-3), [0,1])
            sage: w = WeierstrassIsomorphism(E, (CyclotomicField(3).gen(),0,0,0))
            sage: w.tuple()
            (zeta3, 0, 0, 0)
            sage: (-w).tuple()
            (-zeta3, 0, 0, 0)
            sage: (-w)^3 == -(w^3)
            True

        ::

            sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
            sage: E = EllipticCurve(QuadraticField(-1), [1,0])
            sage: t = WeierstrassIsomorphism(E, (i,0,0,0))
            sage: -t^2 == WeierstrassIsomorphism(E, (1,0,0,0))
            True
        """
        a1, _, a3, _, _ = self._domain.a_invariants()
        w = baseWI(-1, 0, -a1, -a3)
        urst = baseWI.__mul__(self, w).tuple()
        return WeierstrassIsomorphism(self._domain, urst, self._codomain)
Beispiel #30
0
def _biquadratic_syzygy_quartic(quadratic1, quadratic2, variables=None):
    r"""
    Helper function for the Weierstrass form of a biquadratic in $`\mathbb{P}^3$

    The invariants and covariants of a quaternary biquadratic satisfy
    the relation
    :meth:`sage.rings.invariant_theory.TwoQuaternaryQuadratics.syzygy`,
    which is (modulo the two quadratic equations) of the form $J^2 =
    p_4(T, T')$ where

    * $J$, $T$, $T'$ are the covariants of the biquadratic.

    * $p_4$ is some quartic polynomial whose coefficients are
      invariants of the biquadratic.

    INPUT:

    See :func:`WeierstrassForm_P3`

    OUTPUT:

    A triple consisting of

    - The quaternary biquadratic as an algebraic form
      :class:`~sage.rings.invariant_theory.TwoQuaternaryQuadratics`

    - The binary quartic $p_4$ as a
      :class:`~sage.rings.invariant_theory.BinaryQuartic`

    - The dictionary of variable substitutions from the variables of
      the quartic to the variables of the biquadratic.

    EXAMPLES::

        sage: from sage.schemes.toric.weierstrass_higher import _biquadratic_syzygy_quartic
        sage: R.<w,x,y,z> = QQ[]
        sage: _biquadratic_syzygy_quartic(w^2+x^2+y^2, z^2)
        (Joint quaternary quadratic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0)
         and quaternary quadratic with coefficients (0, 0, 0, 1, 0, 0, 0, 0, 0, 0),
         Binary quartic with coefficients (0, 0, 0, -1, 0), {aux...})
    """
    w, x, y, z = _check_polynomials_P3(quadratic1, quadratic2, variables)
    biquadratic = invariant_theory.quaternary_biquadratic(
        quadratic1, quadratic2, [w, x, y, z])

    # construct auxiliary polynomial ring to work with the rhs of the syzygy
    R = biquadratic.ring()
    n = R.ngens()
    R_aux = PolynomialRing(R.base_ring(), n + 2, 'aux')
    to_aux = dict()
    from_aux = dict()
    for var, var_aux in zip(R.gens(), R_aux.gens()[0:n]):
        to_aux[var] = var_aux
        from_aux[var_aux] = var
    T, T_prime = R_aux.gens()[n:]
    from_aux[T] = biquadratic.T_covariant()
    from_aux[T_prime] = biquadratic.T_prime_covariant()

    # Syzygy is J^2 = syz_rhs + (terms that vanish on the biquadratic) with
    # J = biquadratic.J_covariant()
    syz_rhs = T**4 * biquadratic.Delta_invariant().subs(to_aux) \
        - T**3*T_prime * biquadratic.Theta_invariant().subs(to_aux) \
        + T**2*T_prime**2 * biquadratic.Phi_invariant().subs(to_aux) \
        - T*T_prime**3 * biquadratic.Theta_prime_invariant().subs(to_aux) \
        + T_prime**4 * biquadratic.Delta_prime_invariant().subs(to_aux)
    quartic = invariant_theory.binary_quartic(syz_rhs, [T, T_prime])
    return (biquadratic, quartic, from_aux)
Beispiel #31
0
    def segre_embedding(self, PP=None, var='u'):
        r"""
        Return the Segre embedding of ``self`` into the appropriate
        projective space.

        INPUT:

        -  ``PP`` -- (default: ``None``) ambient image projective space;
            this is constructed if it is not given.

        - ``var`` -- string, variable name of the image projective space, default `u` (optional)

        OUTPUT:

        Hom -- from ``self`` to the appropriate subscheme of projective space

        .. TODO::

            Cartesian products with more than two components

        EXAMPLES::

            sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ,[2,2])
            sage: phi = X.segre_embedding(); phi
            Scheme morphism:
              From: Product of projective spaces P^2 x P^2 over Integer Ring
              To:   Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by:
              -u5*u7 + u4*u8,
              -u5*u6 + u3*u8,
              -u4*u6 + u3*u7,
              -u2*u7 + u1*u8,
              -u2*u4 + u1*u5,
              -u2*u6 + u0*u8,
              -u1*u6 + u0*u7,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to
                    (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5).

            ::

            sage: T = ProductProjectiveSpaces([1,2],CC,'z')
            sage: T.segre_embedding()
            Scheme morphism:
              From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision
              To:   Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by:
              -u2*u4 + u1*u5,
              -u2*u3 + u0*u5,
              -u1*u3 + u0*u4
              Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to
                    (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4).
        """
        N = self._dims
        if len(N) > 2:
            raise NotImplementedError("Cannot have more than two components.")
        M = (N[0]+1)*(N[1]+1)-1

        vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)]
        R = PolynomialRing(self.base_ring(),self.ngens()+M+1, vars, order='lex')

        #set-up the elimination for the segre embedding
        mapping = []
        k = self.ngens()
        for i in range(N[0]+1):
            for j in range(N[0]+1,N[0]+N[1]+2):
                mapping.append(R.gen(k)-R(self.gen(i)*self.gen(j)))
                k+=1

        #change the defining ideal of the subscheme into the variables
        I = R.ideal(list(self.defining_polynomials()) + mapping)
        J = I.groebner_basis()
        s = set(R.gens()[:self.ngens()])
        n = len(J)-1
        L = []
        while s.isdisjoint(J[n].variables()):
            L.append(J[n])
            n = n-1

        #create new subscheme
        if PP is None:
            PS = ProjectiveSpace(self.base_ring(),M,R.gens()[self.ngens():])
            Y = PS.subscheme(L)
        else:
            if PP.dimension_relative()!= M:
                raise ValueError("Projective Space %s must be dimension %s")%(PP, M)
            S = PP.coordinate_ring()
            psi = R.hom([0]*(N[0]+N[1]+2) + list(S.gens()),S)
            L = [psi(l) for l in L]
            Y = PP.subscheme(L)

        #create embedding for points
        mapping = []
        for i in range(N[0]+1):
            for j in range(N[0]+1,N[0]+N[1]+2):
                mapping.append(self.gen(i)*self.gen(j))
        phi = self.hom(mapping,Y)

        return phi
Beispiel #32
0
def rational_type(f, n=ZZ(3), base_ring=ZZ):
    r"""
    Return the basic analytic properties that can be determined
    directly from the specified rational function ``f``
    which is interpreted as a representation of an
    element of a FormsRing for the Hecke Triangle group
    with parameter ``n`` and the specified ``base_ring``.

    In particular the following degree of the generators is assumed:

    `deg(1) := (0, 1)`
    `deg(x) := (4/(n-2), 1)`
    `deg(y) := (2n/(n-2), -1)`
    `deg(z) := (2, -1)`

    The meaning of homogeneous elements changes accordingly.

    INPUT:

    - ``f``              -- A rational function in ``x,y,z,d`` over ``base_ring``.

    - ``n``              -- An integer greater or equal to `3` corresponding
                            to the ``HeckeTriangleGroup`` with that parameter
                            (default: `3`).

    - ``base_ring``      -- The base ring of the corresponding forms ring, resp.
                            polynomial ring (default: ``ZZ``).

    OUTPUT:

    A tuple ``(elem, h**o, k, ep, analytic_type)`` describing the basic
    analytic properties of `f` (with the interpretation indicated above).

    - ``elem``           -- ``True`` if `f` has a homogeneous denominator.

    - ``h**o``           -- ``True`` if `f` also has a homogeneous numerator.

    - ``k``              -- ``None`` if `f` is not homogeneous, otherwise
                            the weight of `f` (which is the first component
                            of its degree).

    - ``ep``             -- ``None`` if `f` is not homogeneous, otherwise
                            the multiplier of `f` (which is the second component
                            of its degree)

    - ``analytic_type``  -- The ``AnalyticType`` of `f`.

    For the zero function the degree `(0, 1)` is choosen.

    This function is (heavily) used to determine the type of elements
    and to check if the element really is contained in its parent.


    EXAMPLES::

        sage: from sage.modular.modform_hecketriangle.constructor import rational_type
        sage: (x,y,z,d) = var("x,y,z,d")

        sage: rational_type(0, n=4)
        (True, True, 0, 1, zero)

        sage: rational_type(1, n=12)
        (True, True, 0, 1, modular)

        sage: rational_type(x^3 - y^2)
        (True, True, 12, 1, cuspidal)

        sage: rational_type(x * z, n=7)
        (True, True, 14/5, -1, quasi modular)

        sage: rational_type(1/(x^3 - y^2) + z/d)
        (True, False, None, None, quasi weakly holomorphic modular)

        sage: rational_type(x^3/(x^3 - y^2))
        (True, True, 0, 1, weakly holomorphic modular)

        sage: rational_type(1/(x + z))
        (False, False, None, None, None)

        sage: rational_type(1/x + 1/z)
        (True, False, None, None, quasi meromorphic modular)

        sage: rational_type(d/x, n=10)
        (True, True, -1/2, 1, meromorphic modular)

        sage: rational_type(1.1 * z * (x^8-y^2), n=8, base_ring=CC)
        (True, True, 22/3, -1, quasi cuspidal)

        sage: rational_type(x-y^2, n=infinity)
        (True, True, 4, 1, modular)

        sage: rational_type(x*(x-y^2), n=infinity)
        (True, True, 8, 1, cuspidal)

        sage: rational_type(1/x, n=infinity)
        (True, True, -4, 1, weakly holomorphic modular)
    """

    from .analytic_type import AnalyticType
    AT = AnalyticType()

    # Determine whether f is zero
    if (f == 0):
        #       elem, h**o, k,     ep,    analytic_type
        return (True, True, QQ(0), ZZ(1), AT([]))

    analytic_type = AT(["quasi", "mero"])

    R = PolynomialRing(base_ring, 'x,y,z,d')
    F = FractionField(R)
    (x, y, z, d) = R.gens()
    R2 = PolynomialRing(PolynomialRing(base_ring, 'd'), 'x,y,z')
    dhom = R.hom(R2.gens() + (R2.base().gen(), ), R2)

    f = F(f)

    num = R(f.numerator())
    denom = R(f.denominator())
    ep_num = set([
        ZZ(1) - 2 * ((sum([g.exponents()[0][m] for m in [1, 2]])) % 2)
        for g in dhom(num).monomials()
    ])
    ep_denom = set([
        ZZ(1) - 2 * ((sum([g.exponents()[0][m] for m in [1, 2]])) % 2)
        for g in dhom(denom).monomials()
    ])

    if (n == infinity):
        hom_num = R(num.subs(x=x**4, y=y**2, z=z**2))
        hom_denom = R(denom.subs(x=x**4, y=y**2, z=z**2))
    else:
        n = ZZ(n)
        hom_num = R(num.subs(x=x**4, y=y**(2 * n), z=z**(2 * (n - 2))))
        hom_denom = R(denom.subs(x=x**4, y=y**(2 * n), z=z**(2 * (n - 2))))

    # Determine whether the denominator of f is homogeneous
    if (len(ep_denom) == 1 and dhom(hom_denom).is_homogeneous()):
        elem = True
    else:
        #       elem,  h**o,  k,    ep,   analytic_type
        return (False, False, None, None, None)

    # Determine whether f is homogeneous
    if (len(ep_num) == 1 and dhom(hom_num).is_homogeneous()):
        h**o = True
        if (n == infinity):
            weight = (dhom(hom_num).degree() - dhom(hom_denom).degree())
        else:
            weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) / (n -
                                                                            2)
        ep = ep_num.pop() / ep_denom.pop()
    # TODO: decompose f (resp. its degrees) into homogeneous parts
    else:
        h**o = False
        weight = None
        ep = None

    # Note that we intentionally leave out the d-factor!
    if (n == infinity):
        finf_pol = (x - y**2)
    else:
        finf_pol = x**n - y**2

    # Determine whether f is modular
    if not ((num.degree(z) > 0) or (denom.degree(z) > 0)):
        analytic_type = analytic_type.reduce_to("mero")

    # Determine whether f is holomorphic
    if (dhom(denom).is_constant()):
        analytic_type = analytic_type.reduce_to(["quasi", "holo"])
        # Determine whether f is cuspidal in the sense that finf divides it...
        # Bug in singular: finf_pol.divides(1.0) fails over RR
        if (not dhom(num).is_constant() and finf_pol.divides(num)):
            if (n != infinity or x.divides(num)):
                analytic_type = analytic_type.reduce_to(["quasi", "cusp"])
    else:
        # -> Because of a bug with singular in some cases
        try:
            while (finf_pol.divides(denom)):
                # a simple "denom /= finf_pol" is strangely not enough for non-exact rings
                # and dividing would/may result with an element of the quotient ring of the polynomial ring
                denom = denom.quo_rem(finf_pol)[0]
                denom = R(denom)
            if (n == infinity):
                while (x.divides(denom)):
                    # a simple "denom /= x" is strangely not enough for non-exact rings
                    # and dividing would/may result with an element of the quotient ring of the polynomial ring
                    denom = denom.quo_rem(x)[0]
                    denom = R(denom)
        except TypeError:
            pass

        # Determine whether f is weakly holomorphic in the sense that at most powers of finf occur in denom
        if (dhom(denom).is_constant()):
            analytic_type = analytic_type.reduce_to(["quasi", "weak"])

    return (elem, h**o, weight, ep, analytic_type)
Beispiel #33
0
class FormsRing_abstract(Parent):
    r"""
    Abstract (Hecke) forms ring.

    This should never be called directly. Instead one should
    instantiate one of the derived classes of this class.
    """

    from graded_ring_element import FormsRingElement
    Element = FormsRingElement

    from analytic_type import AnalyticType
    AT = AnalyticType()

    def __init__(self, group, base_ring, red_hom):
        r"""
        Abstract (Hecke) forms ring.

        INPUT:

        - ``group``       - The Hecke triangle group (default: ``HeckeTriangleGroup(3)``)
        - ``base_ring``   - The base_ring (default: ``ZZ``).
        - ``red_hom``     - If True then results of binary operations are considered
                            homogeneous whenever it makes sense (default: False).
                            This is mainly used by the (Hecke) forms.

        OUTPUT:

        The corresponding abstract (Hecke) forms ring.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: MR = ModularFormsRing(group=5, base_ring=ZZ, red_hom=True)
            sage: MR
            ModularFormsRing(n=5) over Integer Ring
            sage: MR.group()
            Hecke triangle group for n = 5
            sage: MR.base_ring()
            Integer Ring
            sage: MR.has_reduce_hom()
            True
            sage: MR.is_homogeneous()
            False
        """

        from graded_ring import canonical_parameters
        (group, base_ring, red_hom) = canonical_parameters(group, base_ring, red_hom)

        if (group == infinity):
            raise NotImplementedError

        #if (not group.is_arithmetic() and base_ring.characteristic()>0):
        #    raise NotImplementedError
        #if (base_ring.characteristic().divides(2*group.n()*(group.n()-2))):
        #    raise NotImplementedError
        if (base_ring.characteristic() > 0):
            raise NotImplementedError
        self._group               = group
        self._red_hom             = red_hom
        self._base_ring           = base_ring
        self._coeff_ring          = FractionField(PolynomialRing(base_ring,'d'))
        self._pol_ring            = PolynomialRing(base_ring,'x,y,z,d')
        self._rat_field           = FractionField(self._pol_ring)

        # default values
        self._weight              = None
        self._ep                  = None
        self._analytic_type       = self.AT(["quasi", "mero"])

        self.default_prec(10)
        self.disp_prec(5)
        self.default_num_prec(53)

        #super(FormsRing_abstract, self).__init__(self.coeff_ring())

    def _repr_(self):
        r"""
        Return the string representation of ``self``.

        EXAMPLES::

            sage: from graded_ring import QModularFormsRing
            sage: QModularFormsRing(group=4)
            QuasiModularFormsRing(n=4) over Integer Ring
        """

        return "{}FormsRing(n={}) over {}".format(self._analytic_type.analytic_space_name(), self._group.n(), self._base_ring)

    def _latex_(self):
        r"""
        Return the LaTeX representation of ``self``.

        EXAMPLES::

            sage: from graded_ring import QWeakModularFormsRing
            sage: latex(QWeakModularFormsRing())
            \mathcal{ QM^! }_{n=3}(\Bold{Z})
        """

        from sage.misc.latex import latex
        return "\\mathcal{{ {} }}_{{n={}}}({})".format(self._analytic_type.latex_space_name(), self._group.n(), latex(self._base_ring))

    def _element_constructor_(self, x):
        r"""
        Return ``x`` coerced/converted into this forms ring.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: MR = ModularFormsRing()
            sage: (x,y,z,d) = MR.pol_ring().gens()

            sage: MR(x^3)
            f_rho^3

            sage: el = MR.Delta().full_reduce()
            sage: MR(el)
            f_rho^3*d - f_i^2*d
            sage: el.parent() == MR
            False
            sage: MR(el).parent() == MR
            True
        """

        from graded_ring_element import FormsRingElement
        if isinstance(x, FormsRingElement):
            x = self._rat_field(x._rat)
        else:
            x = self._rat_field(x)
        return self.element_class(self, x)

    def _coerce_map_from_(self, S):
        r"""
        Return whether or not there exists a coercion from ``S`` to ``self``.

        EXAMPLES::

            sage: from graded_ring import QWeakModularFormsRing, ModularFormsRing, CuspFormsRing
            sage: MR1 = QWeakModularFormsRing(base_ring=CC)
            sage: MR2 = ModularFormsRing()
            sage: MR3 = CuspFormsRing()
            sage: MR3.has_coerce_map_from(MR2)
            False
            sage: MR1.has_coerce_map_from(MR2)
            True
            sage: MR2.has_coerce_map_from(MR3)
            True
            sage: MR3.has_coerce_map_from(ZZ)
            False
            sage: MR1.has_coerce_map_from(ZZ)
            True

            sage: from space import ModularForms, CuspForms
            sage: MF2 = ModularForms(k=6, ep=-1)
            sage: MF3 = CuspForms(k=12, ep=1)
            sage: MR1.has_coerce_map_from(MF2)
            True
            sage: MR2.has_coerce_map_from(MF3)
            True
        """

        from space import FormsSpace_abstract
        if (    isinstance(S, FormsRing_abstract)\
            and self._group         == S._group\
            and self._analytic_type >= S._analytic_type\
            and self.base_ring().has_coerce_map_from(S.base_ring()) ):
                return True
        # TODO: This case never occurs: remove it?
        elif isinstance(S, FormsSpace_abstract):
            return self._coerce_map_from_(S.graded_ring())
        elif (self.AT("holo") <= self._analytic_type) and (self.coeff_ring().has_coerce_map_from(S)):
            return True
        else:
            return False

    def _an_element_(self):
        r"""
        Return an element of ``self``.

        EXAMPLES::

            sage: from graded_ring import CuspFormsRing
            sage: from space import WeakModularForms
            sage: CuspFormsRing().an_element()
            f_rho^3*d - f_i^2*d
            sage: CuspFormsRing().an_element() == CuspFormsRing().Delta()
            True
            sage: WeakModularForms().an_element()
            O(q^5)
            sage: WeakModularForms().an_element() == WeakModularForms().zero()
            True
        """

        return self(self.Delta())

    def default_prec(self, prec = None):
        r"""
        Set the default precision ``prec`` for the Fourier expansion.
        If ``prec=None`` (default) then the current default precision is returned instead.

        Note: This is also used as the default precision for
        the Fourier expansion when evaluating forms.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: from space import ModularForms
            sage: MR = ModularFormsRing()
            sage: MR.default_prec(3)
            sage: MR.default_prec()
            3
            sage: MR.Delta().q_expansion_fixed_d()
            q - 24*q^2 + O(q^3)
            sage: MF = ModularForms(k=4)
            sage: MF.default_prec(2)
            sage: MF.E4()
            1 + 240*q + O(q^2)
            sage: MF.default_prec()
            2
        """

        if (prec is not None):
            self._prec = ZZ(prec)
        else:
            return self._prec

    def disp_prec(self, prec = None):
        r"""
        Set the maximal display precision to ``prec``.
        If ``prec="max"`` the precision is set to the default precision.
        If ``prec=None`` (default) then the current display precision is returned instead.

        Note: This is used for displaying/representing (elements of)
        ``self`` as Fourier expansions.

        EXAMPLES::

            sage: from space import ModularForms
            sage: MF = ModularForms(k=4)
            sage: MF.default_prec(5)
            sage: MF.disp_prec(3)
            sage: MF.disp_prec()
            3
            sage: MF.E4()
            1 + 240*q + 2160*q^2 + O(q^3)
            sage: MF.disp_prec("max")
            sage: MF.E4()
            1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5)
        """

        if (prec == "max"):
            self._disp_prec = self._prec;
        elif (prec is not None):
            self._disp_prec = ZZ(prec)
        else:
            return self._disp_prec

    def default_num_prec(self, prec = None):
        r"""
        Set the default numerical precision to ``prec`` (default: ``53``).
        If ``prec=None`` (default) the current default numerical
        precision is returned instead.

        EXAMPLES::

            sage: from space import ModularForms
            sage: MF = ModularForms(k=6)
            sage: MF.default_prec(20)
            sage: MF.default_num_prec(10)
            sage: MF.default_num_prec()
            10
            sage: E6 = MF.E6()
            sage: E6(i)                                   # rel tol 1e-4
            -0.0020
            sage: MF.default_num_prec(100)
            sage: E6(i)                                   # rel tol 1e-25
            0.00000000000000000000000000000

            sage: MF = ModularForms(group=5, k=4/3)
            sage: F_rho = MF.F_rho()
            sage: F_rho.q_expansion(prec=2)[1]
            7/(100*d)
            sage: MF.default_num_prec(10)
            sage: F_rho.q_expansion_fixed_d(prec=2)[1]    # rel tol 1e-1
            9.9
            sage: MF.default_num_prec(100)
            sage: F_rho.q_expansion_fixed_d(prec=2)[1]    # rel tol 1e-25
            9.9259324351079591527601778294
        """

        if (prec is not None):
            self._num_prec = ZZ(prec)
        else:
            return self._num_prec

    def change_ring(self, new_base_ring):
        r"""
        Return the same space as ``self`` but over a new base ring ``new_base_ring``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().change_ring(CC)
            ModularFormsRing(n=3) over Complex Field with 53 bits of precision
        """

        return self.__class__.__base__(self._group, new_base_ring, self._red_hom)

    def graded_ring(self):
        r"""
        Return the graded ring containing ``self``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing, CuspFormsRing
            sage: from space import CuspForms

            sage: MR = ModularFormsRing(group=5)
            sage: MR.graded_ring() == MR
            True

            sage: CF=CuspForms(k=12)
            sage: CF.graded_ring() == CuspFormsRing()
            False
            sage: CF.graded_ring() == CuspFormsRing(red_hom=True)
            True

            sage: CF.subspace([CF.Delta()]).graded_ring() == CuspFormsRing(red_hom=True)
            True
        """

        return self.extend_type(ring=True)

    def extend_type(self, analytic_type=None, ring=False):
        r"""
        Return a new space which contains (elements of) ``self`` with the analytic type
        of ``self`` extended by ``analytic_type``, possibly extended to a graded ring
        in case ``ring`` is ``True``.

        INPUT:

        - ``analytic_type``   - An ``AnalyticType`` or something which coerces into it (default: ``None``).
        - ``ring``            - Whether to extend to a graded ring (default: ``False``).

        OUTPUT:

        The new extended space.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: from space import CuspForms

            sage: MR = ModularFormsRing(group=5)
            sage: MR.extend_type(["quasi", "weak"])
            QuasiWeakModularFormsRing(n=5) over Integer Ring

            sage: CF=CuspForms(k=12)
            sage: CF.extend_type("holo")
            ModularForms(n=3, k=12, ep=1) over Integer Ring
            sage: CF.extend_type("quasi", ring=True)
            QuasiCuspFormsRing(n=3) over Integer Ring

            sage: CF.subspace([CF.Delta()]).extend_type()
            CuspForms(n=3, k=12, ep=1) over Integer Ring
        """

        if analytic_type == None:
            analytic_type = self._analytic_type
        else:
            analytic_type = self._analytic_type.extend_by(analytic_type)

        if (ring or not self.is_homogeneous()):
            return FormsRing(analytic_type, group=self.group(), base_ring=self.base_ring(), red_hom=self.has_reduce_hom())
        else:
            return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=self.weight(), ep=self.ep())

    def reduce_type(self, analytic_type=None, degree=None):
        r"""
        Return a new space with analytic properties shared by both ``self`` and ``analytic_type``,
        possibly reduced to its homogeneous space of the given ``degree`` (if ``degree`` is set).
        Elements of the new space are contained in ``self``.

        INPUT:

        - ``analytic_type``   - An ``AnalyticType`` or something which coerces into it (default: ``None``).
        - ``degree``          - ``None`` (default) or the degree of the homogeneous component to which
                                ``self`` should be reduced.

        OUTPUT:

        The new reduced space.

        EXAMPLES::

            sage: from graded_ring import QModularFormsRing
            sage: from space import QModularForms

            sage: MR = QModularFormsRing()
            sage: MR.reduce_type(["quasi", "cusp"])
            QuasiCuspFormsRing(n=3) over Integer Ring

            sage: MR.reduce_type("cusp", degree=(12,1))
            CuspForms(n=3, k=12, ep=1) over Integer Ring

            sage: MF=QModularForms(k=6)
            sage: MF.reduce_type("holo")
            ModularForms(n=3, k=6, ep=-1) over Integer Ring

            sage: MF.reduce_type([])
            ZeroForms(n=3, k=6, ep=-1) over Integer Ring
        """

        if analytic_type == None:
            analytic_type = self._analytic_type
        else:
            analytic_type = self._analytic_type.reduce_to(analytic_type)

        if (degree == None and not self.is_homogeneous()):
            return FormsRing(analytic_type, group=self.group(), base_ring=self.base_ring(), red_hom=self.has_reduce_hom())
        elif (degree == None):
            return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=self.weight(), ep=self.ep())
        else:
            (weight, ep) = degree
            if (self.is_homogeneous() and (weight != self.weight() or ep!=self.ep())):
                analytic_type = self._analytic_type.reduce_to([])
            return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=weight, ep=ep)

    def construction(self):
        r"""
        Return a functor that constructs ``self`` (used by the coercion machinery).

        EXAMPLES::

        sage: from graded_ring import ModularFormsRing
        sage: ModularFormsRing().construction()
        (ModularFormsRingFunctor(n=3), BaseFacade(Integer Ring))
        """

        from functors import FormsRingFunctor, BaseFacade
        return FormsRingFunctor(self._analytic_type, self._group, self._red_hom), BaseFacade(self._base_ring)

    @cached_method
    def group(self):
        r"""
        Return the (Hecke triangle) group of ``self``.

        EXAMPLES::

        sage: from graded_ring import ModularFormsRing
        sage: MR = ModularFormsRing(group=7)
        sage: MR.group()
        Hecke triangle group for n = 7

        sage: from space import CuspForms
        sage: CF = CuspForms(group=7, k=4/5)
        sage: CF.group()
        Hecke triangle group for n = 7
        """

        return self._group

    @cached_method
    def hecke_n(self):
        r"""
        Return the parameter ``n`` of the
        (Hecke triangle) group of ``self``.

        EXAMPLES::

        sage: from graded_ring import ModularFormsRing
        sage: MR = ModularFormsRing(group=7)
        sage: MR.hecke_n()
        7

        sage: from space import CuspForms
        sage: CF = CuspForms(group=7, k=4/5)
        sage: CF.hecke_n()
        7
        """

        return self._group.n()

    @cached_method
    def base_ring(self):
        r"""
        Return base ring of ``self``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().base_ring()
            Integer Ring

            sage: from space import CuspForms
            sage: CuspForms(k=12, base_ring=AA).base_ring()
            Algebraic Real Field
        """

        return self._base_ring

    @cached_method
    def coeff_ring(self):
        r"""
        Return coefficient ring of ``self``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().coeff_ring()
            Fraction Field of Univariate Polynomial Ring in d over Integer Ring

            sage: from space import CuspForms
            sage: CuspForms(k=12, base_ring=AA).coeff_ring()
            Fraction Field of Univariate Polynomial Ring in d over Algebraic Real Field
        """

        return self._coeff_ring

    @cached_method
    def pol_ring(self):
        r"""
        Return the underlying polynomial ring used
        by ``self``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().pol_ring()
            Multivariate Polynomial Ring in x, y, z, d over Integer Ring

            sage: from space import CuspForms
            sage: CuspForms(k=12, base_ring=AA).pol_ring()
            Multivariate Polynomial Ring in x, y, z, d over Algebraic Real Field
        """

        return self._pol_ring

    @cached_method
    def rat_field(self):
        r"""
        Return the underlying rational field used by
        ``self`` to construct/represent elements.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().rat_field()
            Fraction Field of Multivariate Polynomial Ring in x, y, z, d over Integer Ring

            sage: from space import CuspForms
            sage: CuspForms(k=12, base_ring=AA).rat_field()
            Fraction Field of Multivariate Polynomial Ring in x, y, z, d over Algebraic Real Field
        """

        return self._rat_field

    @cached_method
    def diff_alg(self):
        r"""
        Return the algebra of differential operators
        (over QQ) which is used on rational functions
        representing elements of ``self``.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().diff_alg()
            Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1}

            sage: from space import CuspForms
            sage: CuspForms(k=12, base_ring=AA).diff_alg()
            Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1}
        """

        # We only use two operators for now which do not involve 'd', so for performance
        # reason we choose FractionField(base_ring) instead of self.coeff_ring().
        free_alg         = FreeAlgebra(FractionField(ZZ),6,'X,Y,Z,dX,dY,dZ')
        (X,Y,Z,dX,dY,dZ) = free_alg.gens()
        diff_alg         = free_alg.g_algebra({dX*X:1+X*dX,dY*Y:1+Y*dY,dZ*Z:1+Z*dZ})
        
        return diff_alg

    @cached_method
    def _derivative_op(self):
        r"""
        Return the differential operator in ``self.diff_alg()``
        corresponding to the derivative of forms.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing(group=7)._derivative_op()
            -1/2*X^6*dY - 5/28*X^5*dZ + 1/7*X*Z*dX + 1/2*Y*Z*dY + 5/28*Z^2*dZ - 1/7*Y*dX
        """

        (X,Y,Z,dX,dY,dZ) = self.diff_alg().gens()
        return   1/self._group.n() * (X*Z-Y)*dX\
               + ZZ(1)/ZZ(2) * (Y*Z-X**(self._group.n()-1))*dY\
               + (self._group.n()-2) / (4*self._group.n()) * (Z**2-X**(self._group.n()-2))*dZ

    @cached_method
    def _serre_derivative_op(self):
        r"""
        Return the differential operator in ``self.diff_alg()``
        corresponding to the serre derivative of forms.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing(group=8)._serre_derivative_op()
            -1/2*X^7*dY - 3/16*X^6*dZ - 3/16*Z^2*dZ - 1/8*Y*dX
        """

        (X,Y,Z,dX,dY,dZ) = self.diff_alg().gens()
        return - 1/self._group.n() * Y*dX\
               - ZZ(1)/ZZ(2) * X**(self._group.n()-1)*dY\
               - (self._group.n()-2) / (4*self._group.n()) * (Z**2+X**(self._group.n()-2))*dZ

    @cached_method
    def has_reduce_hom(self):
        r"""
        Return whether the method ``reduce`` should reduce
        homogeneous elements to the corresponding homogeneous space.

        This is mainly used by binary operations on homogeneous
        spaces which temporarily produce an element of ``self``
        but want to consider it as a homogeneous element
        (also see ``reduce``).

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().has_reduce_hom()
            False
            sage: ModularFormsRing(red_hom=True).has_reduce_hom()
            True

            sage: from space import ModularForms
            sage: ModularForms(k=6).has_reduce_hom()
            True
            sage: ModularForms(k=6).graded_ring().has_reduce_hom()
            True
        """

        return self._red_hom

    def is_homogeneous(self):
        r"""
        Return whether ``self`` is homogeneous component.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().is_homogeneous()
            False

            sage: from space import ModularForms
            sage: ModularForms(k=6).is_homogeneous()
            True
        """

        return self._weight != None

    def is_modular(self):
        r"""
        Return whether ``self`` only contains modular elements.

        EXAMPLES::

            sage: from graded_ring import QWeakModularFormsRing, CuspFormsRing
            sage: QWeakModularFormsRing().is_modular()
            False
            sage: CuspFormsRing(group=7).is_modular()
            True

            sage: from space import QWeakModularForms, CuspForms
            sage: QWeakModularForms(k=10).is_modular()
            False
            sage: CuspForms(group=7, k=12, base_ring=AA).is_modular()
            True
        """

        return not (self.AT("quasi") <= self._analytic_type)

    def is_weakly_holomorphic(self):
        r"""
        Return whether ``self`` only contains weakly
        holomorphic modular elements.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing, CuspFormsRing
            sage: QMModularFormsRing().is_weakly_holomorphic()
            False
            sage: QWeakModularFormsRing().is_weakly_holomorphic()
            True

            sage: from space import MModularForms, CuspForms
            sage: MModularForms(k=10).is_weakly_holomorphic()
            False
            sage: CuspForms(group=7, k=12, base_ring=AA).is_weakly_holomorphic()
            True
        """

        return (self.AT("weak", "quasi") >= self._analytic_type)

    def is_holomorphic(self):
        r"""
        Return whether ``self`` only contains holomorphic
        modular elements.

        EXAMPLES::

            sage: from graded_ring import QWeakModularFormsRing, QModularFormsRing
            sage: QWeakModularFormsRing().is_holomorphic()
            False
            sage: QModularFormsRing().is_holomorphic()
            True

            sage: from space import WeakModularForms, CuspForms
            sage: WeakModularForms(k=10).is_holomorphic()
            False
            sage: CuspForms(group=7, k=12, base_ring=AA).is_holomorphic()
            True
        """

        return (self.AT("holo", "quasi") >= self._analytic_type)

    def is_cuspidal(self):
        r"""
        Return whether ``self`` only contains cuspidal elements.

        EXAMPLES::

            sage: from graded_ring import QModularFormsRing, QCuspFormsRing
            sage: QModularFormsRing().is_cuspidal()
            False
            sage: QCuspFormsRing().is_cuspidal()
            True

            sage: from space import ModularForms, QCuspForms
            sage: ModularForms(k=12).is_cuspidal()
            False
            sage: QCuspForms(k=12).is_cuspidal()
            True
        """

        return (self.AT("cusp", "quasi") >= self._analytic_type)

    def is_zerospace(self):
        r"""
        Return whether ``self`` is the (0-dimensional) zero space.

        EXAMPLES::

            sage: from graded_ring import ModularFormsRing
            sage: ModularFormsRing().is_zerospace()
            False

            sage: from space import ModularForms, CuspForms
            sage: ModularForms(k=12).is_zerospace()
            False
            sage: CuspForms(k=12).reduce_type([]).is_zerospace()
            True
        """

        return (self.AT(["quasi"]) >= self._analytic_type)

    def analytic_type(self):
        r"""
        Return the analytic type of ``self``.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing
            sage: QMModularFormsRing().analytic_type()
            quasi meromorphic modular
            sage: QWeakModularFormsRing().analytic_type()
            quasi weakly holomorphic modular

            sage: from space import MModularForms, CuspForms
            sage: MModularForms(k=10).analytic_type()
            meromorphic modular
            sage: CuspForms(group=7, k=12, base_ring=AA).analytic_type()
            cuspidal
        """

        return self._analytic_type

    def homogeneous_space(self, k, ep):
        r"""
        Return the homogeneous component of degree (``k``, ``e``) of ``self``.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing
            sage: QMModularFormsRing(group=7).homogeneous_space(k=2, ep=-1)
            QuasiMeromorphicModularForms(n=7, k=2, ep=-1) over Integer Ring
        """

        return self.reduce_type(degree = (k,ep))

    @cached_method
    def J_inv(self):
        r"""
        Return the J-invariant (Hauptmodul) of the group of ``self``.
        It is normalized such that ``J_inv(infinity) = infinity``,
        it has real Fourier coefficients starting with ``d > 0`` and ``J_inv(i) = 1``

        It lies in a (weak) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing
            sage: MR = WeakModularFormsRing(group=7)
            sage: J_inv = MR.J_inv()
            sage: J_inv in MR
            True
            sage: CuspFormsRing(group=7).J_inv() == J_inv
            True
            sage: J_inv
            f_rho^7/(f_rho^7 - f_i^2)
            sage: QMModularFormsRing(group=7).J_inv() == QMModularFormsRing(group=7)(J_inv)
            True

            sage: from space import WeakModularForms, CuspForms
            sage: MF = WeakModularForms(group=5, k=0)
            sage: J_inv = MF.J_inv()
            sage: J_inv in MF
            True
            sage: WeakModularFormsRing(group=5, red_hom=True).J_inv() == J_inv
            True
            sage: CuspForms(group=5, k=12).J_inv() == J_inv
            True
            sage: MF.disp_prec(3)
            sage: J_inv
            d*q^-1 + 79/200 + 42877/(640000*d)*q + 12957/(2000000*d^2)*q^2 + O(q^3)

            sage: WeakModularForms().J_inv()
            1/1728*q^-1 + 31/72 + 1823/16*q + 335840/27*q^2 + 16005555/32*q^3 + 11716352*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("weak", ring=True)(x**self._group.n()/(x**self._group.n()-y**2)).reduce()

    @cached_method
    def j_inv(self):
        r"""
        Return the j-invariant (Hauptmodul) of the group of ``self``.
        It is normalized such that ``j_inv(infinity) = infinity``,
        and such that it has real Fourier coefficients starting with ``1``.

        It lies in a (weak) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing
            sage: MR = WeakModularFormsRing(group=7)
            sage: j_inv = MR.j_inv()
            sage: j_inv in MR
            True
            sage: CuspFormsRing(group=7).j_inv() == j_inv
            True
            sage: j_inv
            f_rho^7/(f_rho^7*d - f_i^2*d)
            sage: QMModularFormsRing(group=7).j_inv() == QMModularFormsRing(group=7)(j_inv)
            True

            sage: from space import WeakModularForms, CuspForms
            sage: MF = WeakModularForms(group=5, k=0)
            sage: j_inv = MF.j_inv()
            sage: j_inv in MF
            True
            sage: WeakModularFormsRing(group=5, red_hom=True).j_inv() == j_inv
            True
            sage: CuspForms(group=5, k=12).j_inv() == j_inv
            True
            sage: MF.disp_prec(3)
            sage: j_inv
            q^-1 + 79/(200*d) + 42877/(640000*d^2)*q + 12957/(2000000*d^3)*q^2 + O(q^3)

            sage: WeakModularForms().j_inv()
            q^-1 + 744 + 196884*q + 21493760*q^2 + 864299970*q^3 + 20245856256*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("weak", ring=True)(1/d*x**self._group.n()/(x**self._group.n()-y**2)).reduce()

    @cached_method
    def F_rho(self):
        r"""
        Return the generator ``F_rho`` of the graded ring of ``self``.
        Up to the group action ``F_rho`` has exactly one simple zero at ``rho``. ``F_rho`` is
        normalized such that its first nontrivial Fourier coefficient is ``1``.

        The polynomial variable ``x`` exactly corresponds to ``F_rho``.

        It lies in a (cuspidal) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing
            sage: MR = ModularFormsRing(group=7)
            sage: F_rho = MR.F_rho()
            sage: F_rho in MR
            True
            sage: CuspFormsRing(group=7).F_rho() == F_rho
            True
            sage: F_rho
            f_rho
            sage: QMModularFormsRing(group=7).F_rho() == QMModularFormsRing(group=7)(F_rho)
            True

            sage: from space import ModularForms, CuspForms
            sage: MF = ModularForms(group=5, k=4/3)
            sage: F_rho = MF.F_rho()
            sage: F_rho in MF
            True
            sage: ModularFormsRing(group=5, red_hom=True).F_rho() == F_rho
            True
            sage: CuspForms(group=5, k=12).F_rho() == F_rho
            True
            sage: MF.disp_prec(3)
            sage: F_rho
            1 + 7/(100*d)*q + 21/(160000*d^2)*q^2 + O(q^3)

            sage: ModularForms(k=4).F_rho() == ModularForms(k=4).E4()
            True
            sage: ModularForms(k=4).F_rho()
            1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("holo", ring=True)(x).reduce()

    @cached_method
    def F_i(self):
        r"""
        Return the generator ``F_i`` of the graded ring of ``self``.
        Up to the group action ``F_i`` has exactly one simple zero at ``i``. ``F_i`` is
        normalized such that its first nontrivial Fourier coefficient is ``1``.

        The polynomial variable ``y`` exactly corresponds to ``F_i``.

        It lies in a (holomorphic) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing
            sage: MR = ModularFormsRing(group=7)
            sage: F_i = MR.F_i()
            sage: F_i in MR
            True
            sage: CuspFormsRing(group=7).F_i() == F_i
            True
            sage: F_i
            f_i
            sage: QMModularFormsRing(group=7).F_i() == QMModularFormsRing(group=7)(F_i)
            True

            sage: from space import ModularForms, CuspForms
            sage: MF = ModularForms(group=5, k=10/3)
            sage: F_i = MF.F_i()
            sage: F_i in MF
            True
            sage: ModularFormsRing(group=5, red_hom=True).F_i() == F_i
            True
            sage: CuspForms(group=5, k=12).F_i() == F_i
            True
            sage: MF.disp_prec(3)
            sage: F_i
            1 - 13/(40*d)*q - 351/(64000*d^2)*q^2 + O(q^3)

            sage: ModularForms(k=6).F_i() == ModularForms(k=4).E6()
            True
            sage: ModularForms(k=6).F_i()
            1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("holo", ring=True)(y).reduce()

    @cached_method
    def F_inf(self):
        r"""
        Return the first nontrivial cusp form ``F_inf`` of the graded ring of ``self``.
        Up to the group action ``F_inf`` has exactly one simple zero at ``infinity``.
        ``F_inf`` is normalized such that its first nontrivial Fourier coefficient is ``1``.

        It lies in a (holomorphic) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, CuspFormsRing
            sage: MR = CuspFormsRing(group=7)
            sage: F_inf = MR.F_inf()
            sage: F_inf in MR
            True
            sage: F_inf
            f_rho^7*d - f_i^2*d
            sage: QMModularFormsRing(group=7).F_inf() == QMModularFormsRing(group=7)(F_inf)
            True

            sage: from space import CuspForms
            sage: MF = CuspForms(group=5, k=20/3)
            sage: F_inf = MF.F_inf()
            sage: F_inf in MF
            True
            sage: CuspFormsRing(group=5, red_hom=True).F_inf() == F_inf
            True
            sage: CuspForms(group=5, k=0).F_inf() == F_inf
            True
            sage: MF.disp_prec(3)
            sage: F_inf
            q - 9/(200*d)*q^2 + O(q^3)

            sage: CuspForms(k=12).F_inf() == CuspForms(k=12).Delta()
            True
            sage: CuspForms(k=12).F_inf()
            q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("cusp", ring=True)(d*(x**self._group.n()-y**2)).reduce()

    @cached_method
    def G_inv(self):
        r"""
        If ``2`` divides ``n``: Return the G-invariant of the group of ``self``.

        The G-invariant is analogous to the G-invariant but has multiplier ``-1``.
        I.e. ``G_inv(-1/t) = -G_inv(t)``. It is a holomorphic square root
        of ``J_inv*(J_inv-1)`` with real Fourier coefficients.

        If ``2`` does not divide ``n`` the function doesn't exist and an exception is raised.

        It lies in a (weak) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing
            sage: MR = WeakModularFormsRing(group=8)
            sage: G_inv = MR.G_inv()
            sage: G_inv in MR
            True
            sage: CuspFormsRing(group=8).G_inv() == G_inv
            True
            sage: G_inv
            f_rho^4*f_i*d/(f_rho^8 - f_i^2)
            sage: QMModularFormsRing(group=8).G_inv() == QMModularFormsRing(group=8)(G_inv)
            True

            sage: from space import WeakModularForms, CuspForms
            sage: MF = WeakModularForms(group=8, k=0, ep=-1)
            sage: G_inv = MF.G_inv()
            sage: G_inv in MF
            True
            sage: WeakModularFormsRing(group=8, red_hom=True).G_inv() == G_inv
            True
            sage: CuspForms(group=8, k=12, ep=1).G_inv() == G_inv
            True
            sage: MF.disp_prec(3)
            sage: G_inv
            d^2*q^-1 - 15*d/128 - 15139/262144*q - 11575/(1572864*d)*q^2 + O(q^3)

            sage: WeakModularForms(group=4, k=0, ep=-1).G_inv()
            1/65536*q^-1 - 3/8192 - 955/16384*q - 49/32*q^2 - 608799/32768*q^3 - 659/4*q^4 + O(q^5)
        """

        if (ZZ(2).divides(self._group.n())):
            (x,y,z,d) = self._pol_ring.gens()
            return self.extend_type("weak", ring=True)(d*y*x**(self._group.n()/ZZ(2))/(x**self._group.n()-y**2)).reduce()
        else:
           raise Exception("G_inv doesn't exists for n={}.".format(self._group.n()))

    @cached_method
    def g_inv(self):
        r"""
        If ``2`` divides ``n``: Return the g-invariant of the group of ``self``.

        The g-invariant is analogous to the j-invariant but has multiplier ``-1``.
        I.e. ``g_inv(-1/t) = -g_inv(t)``. It is a (normalized) holomorphic square root
        of ``J_inv*(J_inv-1)``, normalized such that its first nontrivial Fourier coefficient is ``1``.

        If ``2`` does not divide ``n`` the function doesn't exist and an exception is raised.

        It lies in a (weak) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing
            sage: MR = WeakModularFormsRing(group=8)
            sage: g_inv = MR.g_inv()
            sage: g_inv in MR
            True
            sage: CuspFormsRing(group=8).g_inv() == g_inv
            True
            sage: g_inv
            f_rho^4*f_i/(f_rho^8*d - f_i^2*d)
            sage: QMModularFormsRing(group=8).g_inv() == QMModularFormsRing(group=8)(g_inv)
            True

            sage: from space import WeakModularForms, CuspForms
            sage: MF = WeakModularForms(group=8, k=0, ep=-1)
            sage: g_inv = MF.g_inv()
            sage: g_inv in MF
            True
            sage: WeakModularFormsRing(group=8, red_hom=True).g_inv() == g_inv
            True
            sage: CuspForms(group=8, k=12, ep=1).g_inv() == g_inv
            True
            sage: MF.disp_prec(3)
            sage: g_inv
            q^-1 - 15/(128*d) - 15139/(262144*d^2)*q - 11575/(1572864*d^3)*q^2 + O(q^3)

            sage: WeakModularForms(group=4, k=0, ep=-1).g_inv()
            q^-1 - 24 - 3820*q - 100352*q^2 - 1217598*q^3 - 10797056*q^4 + O(q^5)
        """

        if (ZZ(2).divides(self._group.n())):
            (x,y,z,d) = self._pol_ring.gens()
            return self.extend_type("weak", ring=True)(1/d*y*x**(self._group.n()/ZZ(2))/(x**self._group.n()-y**2)).reduce()
        else:
           raise Exception("g_inv doesn't exists for n={}.".format(self._group.n()))

    @cached_method
    def E4(self):
        r"""
        Return the normalized Eisenstein series of weight ``4`` of the graded ring of ``self``.
        It is equal to ``F_rho^(n-2)``.

        It lies in a (holomorphic) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing
            sage: MR = ModularFormsRing(group=7)
            sage: E4 = MR.E4()
            sage: E4 in MR
            True
            sage: CuspFormsRing(group=7).E4() == E4
            True
            sage: E4
            f_rho^5
            sage: QMModularFormsRing(group=7).E4() == QMModularFormsRing(group=7)(E4)
            True

            sage: from space import ModularForms, CuspForms
            sage: MF = ModularForms(group=5, k=4)
            sage: E4 = MF.E4()
            sage: E4 in MF
            True
            sage: ModularFormsRing(group=5, red_hom=True).E4() == E4
            True
            sage: CuspForms(group=5, k=12).E4() == E4
            True
            sage: MF.disp_prec(3)
            sage: E4
            1 + 21/(100*d)*q + 483/(32000*d^2)*q^2 + O(q^3)

            sage: ModularForms(k=4).F_rho() == ModularForms(k=4).E4()
            True
            sage: ModularForms(k=4).E4()
            1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("holo", ring=True)(x**(self._group.n()-2)).reduce()

    @cached_method
    def E6(self):
        r"""
        Return the normalized Eisenstein series of weight ``6`` of the graded ring of ``self``,
        It is equal to ``F_rho^(n-3) * F_i``.

        It lies in a (holomorphic) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing
            sage: MR = ModularFormsRing(group=7)
            sage: E6 = MR.E6()
            sage: E6 in MR
            True
            sage: CuspFormsRing(group=7).E6() == E6
            True
            sage: E6
            f_rho^4*f_i
            sage: QMModularFormsRing(group=7).E6() == QMModularFormsRing(group=7)(E6)
            True

            sage: from space import ModularForms, CuspForms
            sage: MF = ModularForms(group=5, k=6)
            sage: E6 = MF.E6()
            sage: E6 in MF
            True
            sage: ModularFormsRing(group=5, red_hom=True).E6() == E6
            True
            sage: CuspForms(group=5, k=12).E6() == E6
            True
            sage: MF.disp_prec(3)
            sage: E6
            1 - 37/(200*d)*q - 14663/(320000*d^2)*q^2 + O(q^3)

            sage: ModularForms(k=6).F_i() == ModularForms(k=6).E6()
            True
            sage: ModularForms(k=6).E6()
            1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("holo", ring=True)(x**(self._group.n()-3)*y).reduce()

    @cached_method
    def Delta(self):
        r"""
        Return an analog of the Delta-function of the graded ring of ``self``.
        It is a cusp form of weight ``12`` and is equal to
        ``d*(E4^3 - E6^2)`` or (in terms of the generators) ``d*x^(2*n-6)*(x^n - y^2)``.

        It lies in a (cuspidal) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, CuspFormsRing
            sage: MR = CuspFormsRing(group=7)
            sage: Delta = MR.Delta()
            sage: Delta in MR
            True
            sage: Delta
            f_rho^15*d - f_rho^8*f_i^2*d
            sage: QMModularFormsRing(group=7).Delta() == QMModularFormsRing(group=7)(Delta)
            True

            sage: from space import CuspForms, ModularForms
            sage: MF = CuspForms(group=5, k=12)
            sage: Delta = MF.Delta()
            sage: Delta in MF
            True
            sage: CuspFormsRing(group=5, red_hom=True).Delta() == Delta
            True
            sage: CuspForms(group=5, k=0).Delta() == Delta
            True
            sage: MF.disp_prec(3)
            sage: Delta
            q + 47/(200*d)*q^2 + O(q^3)

            sage: d = ModularForms(group=5).coeff_ring().gen()
            sage: Delta == (d*(ModularForms(group=5).E4()^3-ModularForms(group=5).E6()^2))
            True

            sage: CuspForms(k=12).F_inf() == CuspForms(k=12).Delta()
            True
            sage: CuspForms(k=12).Delta()
            q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type("cusp", ring=True)(d*x**(2*self._group.n()-6)*(x**self._group.n()-y**2)).reduce()

    @cached_method
    def E2(self):
        r"""
        Return the normalized quasi holomorphic Eisenstein series of weight ``2`` of the
        graded ring of ``self``. It is also a generator of the graded ring of
        ``self`` and  the polynomial variable ``z`` exactly corresponds to ``E2``.

        It lies in a (quasi holomorphic) extension of the graded ring of ``self``.
        In case ``has_reduce_hom`` is ``True`` it is given as an element of
        the corresponding homogeneous space.

        EXAMPLES::

            sage: from graded_ring import QMModularFormsRing, QModularFormsRing, CuspFormsRing
            sage: MR = QModularFormsRing(group=7)
            sage: E2 = MR.E2()
            sage: E2 in MR
            True
            sage: CuspFormsRing(group=7).E2() == E2
            True
            sage: E2
            E2
            sage: QMModularFormsRing(group=7).E2() == QMModularFormsRing(group=7)(E2)
            True

            sage: from space import QModularForms, CuspForms
            sage: MF = QModularForms(group=5, k=2)
            sage: E2 = MF.E2()
            sage: E2 in MF
            True
            sage: QModularFormsRing(group=5, red_hom=True).E2() == E2
            True
            sage: CuspForms(group=5, k=12, ep=1).E2() == E2
            True
            sage: MF.disp_prec(3)
            sage: E2
            1 - 9/(200*d)*q - 369/(320000*d^2)*q^2 + O(q^3)

            sage: F_inf = MF.F_inf()
            sage: E2 == F_inf.derivative() / F_inf
            True

            sage: QModularForms(k=2).E2()
            1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 + O(q^5)
        """

        (x,y,z,d) = self._pol_ring.gens()
        return self.extend_type(["holo", "quasi"], ring=True)(z).reduce()