def _find_scaling_L_ratio(self):
        r"""
        This function is use to set ``_scaling``, the factor used to adjust the
        scalar multiple of the modular symbol.
        If `[0]`, the modular symbol evaluated at 0, is non-zero, we can just scale
        it with respect to the approximation of the L-value. It is known that
        the quotient is a rational number with small denominator.
        Otherwise we try to scale using quadratic twists.

        ``_scaling`` will be set to a rational non-zero multiple if we succeed and to 1 otherwise.
        Even if we fail we scale at least to make up the difference between the periods
        of the `X_0`-optimal curve and our given curve `E` in the isogeny class.

        EXAMPLES::

            sage: m = EllipticCurve('11a1').modular_symbol(implementation="sage")
            sage: m._scaling
            1/5
            sage: m = EllipticCurve('11a2').modular_symbol(implementation="sage")
            sage: m._scaling
            1
            sage: m = EllipticCurve('11a3').modular_symbol(implementation="sage")
            sage: m._scaling
            1/25
            sage: m = EllipticCurve('37a1').modular_symbol(implementation="sage")
            sage: m._scaling
            -1
            sage: m = EllipticCurve('37a1').modular_symbol()
            sage: m._scaling
            1
            sage: m = EllipticCurve('389a1').modular_symbol()
            sage: m._scaling
            1
            sage: m = EllipticCurve('389a1').modular_symbol(implementation="sage")
            sage: m._scaling
            1
            sage: m = EllipticCurve('196a1').modular_symbol(implementation="sage")
            sage: m._scaling
            1

        Some harder cases fail::

            sage: m = EllipticCurve('121b1').modular_symbol(implementation="sage")
            Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2
            sage: m._scaling
            1

        TESTS::

            sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1']
            sage: for la in rk0:  # long time (3s on sage.math, 2011)
            ....:          E = EllipticCurve(la)
            ....:          me = E.modular_symbol(implementation="eclib")
            ....:          ms = E.modular_symbol(implementation="sage")
            ....:          print("{} {} {}".format(E.lseries().L_ratio()*E.real_components(), me(0), ms(0)))
            1/5 1/5 1/5
            1 1 1
            1/4 1/4 1/4
            1/3 1/3 1/3
            2/3 2/3 2/3

            sage: rk1 = ['37a1','43a1','53a1', '91b1','91b2','91b3']
            sage: [EllipticCurve(la).modular_symbol()(0) for la in rk1]  # long time (1s on sage.math, 2011)
            [0, 0, 0, 0, 0, 0]
            sage: for la in rk1:  # long time (8s on sage.math, 2011)
            ....:       E = EllipticCurve(la)
            ....:       m = E.modular_symbol()
            ....:       lp = E.padic_lseries(5)
            ....:       for D in [5,17,12,8]:
            ....:           ED = E.quadratic_twist(D)
            ....:           md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)])
            ....:           etaD = lp._quotient_of_periods_to_twist(D)
            ....:           assert ED.lseries().L_ratio()*ED.real_components() * etaD == md

        """
        E = self._E
        self._scaling = 1  # initial value, may be changed later.
        self._failed_to_scale = False

        if self._sign == 1:
            at0 = self(0)
            if at0 != 0:
                l1 = self.__lalg__(1)
                if at0 != l1:
                    verbose('scale modular symbols by %s' % (l1 / at0))
                    self._scaling = l1 / at0
            else:
                # if [0] = 0, we can still hope to scale it correctly by considering twists of E
                Dlist = [
                    5, 8, 12, 13, 17, 21, 24, 28, 29, 33, 37, 40, 41, 44, 53,
                    56, 57, 60, 61, 65, 69, 73, 76, 77, 85, 88, 89, 92, 93, 97
                ]  # a list of positive fundamental discriminants
                j = 0
                at0 = 0
                # computes [0]+ for the twist of E by D until one value is non-zero
                while j < 30 and at0 == 0:
                    D = Dlist[j]
                    # the following line checks if the twist of the newform of E by D is a newform
                    # this is to avoid that we 'twist back'
                    if all(
                            valuation(E.conductor(), ell) <= valuation(D, ell)
                            for ell in prime_divisors(D)):
                        at0 = sum([
                            kronecker_symbol(D, u) * self(ZZ(u) / D)
                            for u in range(1, abs(D))
                        ])
                    j += 1
                if j == 30 and at0 == 0:  # curves like "121b1", "225a1", "225e1", "256a1", "256b1", "289a1", "361a1", "400a1", "400c1", "400h1", "441b1", "441c1", "441d1", "441f1 .. will arrive here
                    print(
                        "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2"
                    )
                    self._failed_to_scale = True
                else:
                    l1 = self.__lalg__(D)
                    if at0 != l1:
                        verbose('scale modular symbols by %s found at D=%s ' %
                                (l1 / at0, D),
                                level=2)
                        self._scaling = l1 / at0

        else:  # that is when sign = -1
            Dlist = [
                -3, -4, -7, -8, -11, -15, -19, -20, -23, -24, -31, -35, -39,
                -40, -43, -47, -51, -52, -55, -56, -59, -67, -68, -71, -79,
                -83, -84, -87, -88, -91
            ]  # a list of negative fundamental discriminants
            j = 0
            at0 = 0
            while j < 30 and at0 == 0:
                # computes [0]+ for the twist of E by D until one value is non-zero
                D = Dlist[j]
                if all(
                        valuation(E.conductor(), ell) <= valuation(D, ell)
                        for ell in prime_divisors(D)):
                    at0 = -sum([
                        kronecker_symbol(D, u) * self(ZZ(u) / D)
                        for u in range(1, abs(D))
                    ])
                j += 1
            if j == 30 and at0 == 0:  # no more hope for a normalization
                print(
                    "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2"
                )
                self._failed_to_scale = True
            else:
                l1 = self.__lalg__(D)
                if at0 != l1:
                    verbose('scale modular symbols by %s' % (l1 / at0))
                    self._scaling = l1 / at0
Esempio n. 2
0
    def _tate(self, proof=None, globally=False):
        r"""
        Tate's algorithm for an elliptic curve over a number field.

        Computes both local reduction data at a prime ideal and a
        local minimal model.

        The model is not required to be integral on input.  If `P` is
        principal, uses a generator as uniformizer, so it will not
        affect integrality or minimality at other primes.  If `P` is not
        principal, the minimal model returned will preserve
        integrality at other primes, but not minimality.

        The optional argument globally, when set to True, tells the algorithm to use the generator of the prime ideal if it is principal. Otherwise just any uniformizer will be used.

        .. NOTE::

            Called only by ``EllipticCurveLocalData.__init__()``.

        OUTPUT:

        (tuple) ``(Emin, p, val_disc, fp, KS, cp)`` where:

        - ``Emin`` (EllipticCurve) is a model (integral and) minimal at P
        - ``p`` (int) is the residue characteristic
        - ``val_disc`` (int) is the valuation of the local minimal discriminant
        - ``fp`` (int) is the valuation of the conductor
        - ``KS`` (string) is the Kodaira symbol
        - ``cp`` (int) is the Tamagawa number


        EXAMPLES (this raised a type error in sage prior to 4.4.4, see :trac:`7930`) ::

            sage: E = EllipticCurve('99d1')

            sage: R.<X> = QQ[]
            sage: K.<t> = NumberField(X^3 + X^2 - 2*X - 1)
            sage: L.<s> = NumberField(X^3 + X^2 - 36*X - 4)

            sage: EK = E.base_extend(K)
            sage: toK = EK.torsion_order()
            sage: da = EK.local_data()  # indirect doctest

            sage: EL = E.base_extend(L)
            sage: da = EL.local_data()  # indirect doctest

        EXAMPLES:

        The following example shows that the bug at :trac:`9324` is fixed::

            sage: K.<a> = NumberField(x^2-x+6)
            sage: E = EllipticCurve([0,0,0,-53160*a-43995,-5067640*a+19402006])
            sage: E.conductor() # indirect doctest
            Fractional ideal (18, 6*a)

        The following example shows that the bug at :trac:`9417` is fixed::

            sage: K.<a> = NumberField(x^2+18*x+1)
            sage: E = EllipticCurve(K, [0, -36, 0, 320, 0])
            sage: E.tamagawa_number(K.ideal(2))
            4

        This is to show that the bug :trac:`11630` is fixed. (The computation of the class group would produce a warning)::

            sage: K.<t> = NumberField(x^7-2*x+177)
            sage: E = EllipticCurve([0,1,0,t,t])
            sage: P = K.ideal(2,t^3 + t + 1)
            sage: E.local_data(P).kodaira_symbol()
            II

        """
        E = self._curve
        P = self._prime
        K = E.base_ring()
        OK = K.maximal_order()
        t = verbose("Running Tate's algorithm with P = %s" % P, level=1)
        F = OK.residue_field(P)
        p = F.characteristic()

        # In case P is not principal we mostly use a uniformiser which
        # is globally integral (with positive valuation at some other
        # primes); for this to work, it is essential that we can
        # reduce (mod P) elements of K which are not integral (but are
        # P-integral).  However, if the model is non-minimal and we
        # end up dividing a_i by pi^i then at that point we use a
        # uniformiser pi which has non-positive valuation at all other
        # primes, so that we can divide by it without losing
        # integrality at other primes.

        if globally:
            principal_flag = P.is_principal()
        else:
            principal_flag = False

        if (K is QQ) or principal_flag:
            pi = P.gens_reduced()[0]
            verbose("P is principal, generator pi = %s" % pi, t, 1)
        else:
            pi = K.uniformizer(P, 'positive')
            verbose("uniformizer pi = %s" % pi, t, 1)
        pi2 = pi * pi
        pi3 = pi * pi2
        pi4 = pi * pi3
        pi_neg = None
        prime = pi if K is QQ else P

        pval = lambda x: x.valuation(prime)
        pdiv = lambda x: x.is_zero() or pval(x) > 0
        # Since ResidueField is cached in a way that
        # does not care much about embeddings of number
        # fields, it can happen that F.p.ring() is different
        # from K. This is a problem: If F.p.ring() has no
        # embedding but K has, then there is no coercion
        # from F.p.ring().maximal_order() to K. But it is
        # no problem to do an explicit conversion in that
        # case (Simon King, trac ticket #8800).

        from sage.categories.pushout import pushout, CoercionException
        try:
            if hasattr(F.p.ring(), 'maximal_order'):  # it is not ZZ
                pushout(F.p.ring().maximal_order(), K)
            pinv = lambda x: F.lift(~F(x))
            proot = lambda x, e: F.lift(
                F(x).nth_root(e, extend=False, all=True)[0])
            preduce = lambda x: F.lift(F(x))
        except CoercionException:  # the pushout does not exist, we need conversion
            pinv = lambda x: K(F.lift(~F(x)))
            proot = lambda x, e: K(
                F.lift(F(x).nth_root(e, extend=False, all=True)[0]))
            preduce = lambda x: K(F.lift(F(x)))

        def _pquadroots(a, b, c):
            r"""
            Local function returning True iff `ax^2 + bx + c` has roots modulo `P`
            """
            (a, b, c) = (F(a), F(b), F(c))
            if a == 0:
                return (b != 0) or (c == 0)
            elif p == 2:
                return len(PolynomialRing(F, "x")([c, b, a]).roots()) > 0
            else:
                return (b**2 - 4 * a * c).is_square()

        def _pcubicroots(b, c, d):
            r"""
            Local function returning the number of roots of `x^3 +
            b*x^2 + c*x + d` modulo `P`, counting multiplicities
            """

            return sum([
                rr[1] for rr in PolynomialRing(F, 'x')
                ([F(d), F(c), F(b), F(1)]).roots()
            ], 0)

        if p == 2:
            halfmodp = OK(Integer(0))
        else:
            halfmodp = pinv(Integer(2))

        A = E.a_invariants()
        A = [0, A[0], A[1], A[2], A[3], 0, A[4]]
        indices = [1, 2, 3, 4, 6]
        if min([pval(a) for a in A if a != 0]) < 0:
            verbose(
                "Non-integral model at P: valuations are %s; making integral" %
                ([pval(a) for a in A if a != 0]), t, 1)
            e = 0
            for i in range(7):
                if A[i] != 0:
                    e = max(e, (-pval(A[i]) / i).ceil())
            pie = pi**e
            for i in range(7):
                if A[i] != 0:
                    A[i] *= pie**i
            verbose(
                "P-integral model is %s, with valuations %s" %
                ([A[i] for i in indices], [pval(A[i]) for i in indices]), t, 1)

        split = None  # only relevant for multiplicative reduction

        (a1, a2, a3, a4, a6) = (A[1], A[2], A[3], A[4], A[6])
        while True:
            C = EllipticCurve([a1, a2, a3, a4, a6])
            (b2, b4, b6, b8) = C.b_invariants()
            (c4, c6) = C.c_invariants()
            delta = C.discriminant()
            val_disc = pval(delta)

            if val_disc == 0:
                ## Good reduction already
                cp = 1
                fp = 0
                KS = KodairaSymbol("I0")
                break  #return

            # Otherwise, we change coordinates so that p | a3, a4, a6
            if p == 2:
                if pdiv(b2):
                    r = proot(a4, 2)
                    t = proot(((r + a2) * r + a4) * r + a6, 2)
                else:
                    temp = pinv(a1)
                    r = temp * a3
                    t = temp * (a4 + r * r)
            elif p == 3:
                if pdiv(b2):
                    r = proot(-b6, 3)
                else:
                    r = -pinv(b2) * b4
                t = a1 * r + a3
            else:
                if pdiv(c4):
                    r = -pinv(12) * b2
                else:
                    r = -pinv(12 * c4) * (c6 + b2 * c4)
                t = -halfmodp * (a1 * r + a3)
            r = preduce(r)
            t = preduce(t)
            verbose("Before first transform C = %s" % C)
            verbose("[a1,a2,a3,a4,a6] = %s" % ([a1, a2, a3, a4, a6]))
            C = C.rst_transform(r, 0, t)
            (a1, a2, a3, a4, a6) = C.a_invariants()
            (b2, b4, b6, b8) = C.b_invariants()
            if min([pval(a) for a in (a1, a2, a3, a4, a6) if a != 0]) < 0:
                raise RuntimeError("Non-integral model after first transform!")
            verbose(
                "After first transform %s\n, [a1,a2,a3,a4,a6] = %s\n, valuations = %s"
                % ([r, 0, t], [a1, a2, a3, a4, a6],
                   [pval(a1), pval(a2),
                    pval(a3), pval(a4),
                    pval(a6)]), t, 2)
            if pval(a3) == 0:
                raise RuntimeError(
                    "p does not divide a3 after first transform!")
            if pval(a4) == 0:
                raise RuntimeError(
                    "p does not divide a4 after first transform!")
            if pval(a6) == 0:
                raise RuntimeError(
                    "p does not divide a6 after first transform!")

            # Now we test for Types In, II, III, IV
            # NB the c invariants never change.

            if not pdiv(c4):
                # Multiplicative reduction: Type In (n = val_disc)
                split = False
                if _pquadroots(1, a1, -a2):
                    cp = val_disc
                    split = True
                elif Integer(2).divides(val_disc):
                    cp = 2
                else:
                    cp = 1
                KS = KodairaSymbol("I%s" % val_disc)
                fp = 1
                break  #return

            # Additive reduction

            if pval(a6) < 2:
                ## Type II
                KS = KodairaSymbol("II")
                fp = val_disc
                cp = 1
                break  #return
            if pval(b8) < 3:
                ## Type III
                KS = KodairaSymbol("III")
                fp = val_disc - 1
                cp = 2
                break  #return
            if pval(b6) < 3:
                ## Type IV
                cp = 1
                a3t = preduce(a3 / pi)
                a6t = preduce(a6 / pi2)
                if _pquadroots(1, a3t, -a6t):
                    cp = 3
                KS = KodairaSymbol("IV")
                fp = val_disc - 2
                break  #return

            # If our curve is none of these types, we change coords so that
            # p | a1, a2;  p^2 | a3, a4;  p^3 | a6
            if p == 2:
                s = proot(a2, 2)  # so s^2=a2 (mod pi)
                t = pi * proot(a6 / pi2, 2)  # so t^2=a6 (mod pi^3)
            elif p == 3:
                s = a1  # so a1'=2s+a1=3a1=0 (mod pi)
                t = a3  # so a3'=2t+a3=3a3=0 (mod pi^2)
            else:
                s = -a1 * halfmodp  # so a1'=2s+a1=0 (mod pi)
                t = -a3 * halfmodp  # so a3'=2t+a3=0 (mod pi^2)
            C = C.rst_transform(0, s, t)
            (a1, a2, a3, a4, a6) = C.a_invariants()

            verbose(
                "After second transform %s\n[a1, a2, a3, a4, a6] = %s\nValuations: %s"
                % ([0, s, t], [a1, a2, a3, a4, a6],
                   [pval(a1), pval(a2),
                    pval(a3), pval(a4),
                    pval(a6)]), t, 2)
            if pval(a1) == 0:
                raise RuntimeError(
                    "p does not divide a1 after second transform!")
            if pval(a2) == 0:
                raise RuntimeError(
                    "p does not divide a2 after second transform!")
            if pval(a3) < 2:
                raise RuntimeError(
                    "p^2 does not divide a3 after second transform!")
            if pval(a4) < 2:
                raise RuntimeError(
                    "p^2 does not divide a4 after second transform!")
            if pval(a6) < 3:
                raise RuntimeError(
                    "p^3 does not divide a6 after second transform!")
            if min(pval(a1), pval(a2), pval(a3), pval(a4), pval(a6)) < 0:
                raise RuntimeError(
                    "Non-integral model after second transform!")

            # Analyze roots of the cubic T^3 + bT^2 + cT + d = 0 mod P, where
            # b = a2/p, c = a4/p^2, d = a6/p^3
            b = preduce(a2 / pi)
            c = preduce(a4 / pi2)
            d = preduce(a6 / pi3)
            bb = b * b
            cc = c * c
            bc = b * c
            w = 27 * d * d - bb * cc + 4 * b * bb * d - 18 * bc * d + 4 * c * cc
            x = 3 * c - bb
            if pdiv(w):
                if pdiv(x):
                    sw = 3
                else:
                    sw = 2
            else:
                sw = 1
            verbose(
                "Analyzing roots of cubic T^3 + %s*T^2 + %s*T + %s, case %s" %
                (b, c, d, sw), t, 1)
            if sw == 1:
                ## Three distinct roots - Type I*0
                verbose("Distinct roots", t, 1)
                KS = KodairaSymbol("I0*")
                cp = 1 + _pcubicroots(b, c, d)
                fp = val_disc - 4
                break  #return
            elif sw == 2:
                ## One double root - Type I*m for some m
                verbose("One double root", t, 1)
                ## Change coords so that the double root is T = 0 mod p
                if p == 2:
                    r = proot(c, 2)
                elif p == 3:
                    r = c * pinv(b)
                else:
                    r = (bc - 9 * d) * pinv(2 * x)
                r = pi * preduce(r)
                C = C.rst_transform(r, 0, 0)
                (a1, a2, a3, a4, a6) = C.a_invariants()

                # The rest of this branch is just to compute cp, fp, KS.
                # We use pi to keep transforms integral.
                ix = 3
                iy = 3
                mx = pi2
                my = mx
                while True:
                    a2t = preduce(a2 / pi)
                    a3t = preduce(a3 / my)
                    a4t = preduce(a4 / (pi * mx))
                    a6t = preduce(a6 / (mx * my))
                    if pdiv(a3t * a3t + 4 * a6t):
                        if p == 2:
                            t = my * proot(a6t, 2)
                        else:
                            t = my * preduce(-a3t * halfmodp)
                        C = C.rst_transform(0, 0, t)
                        (a1, a2, a3, a4, a6) = C.a_invariants()

                        my *= pi
                        iy += 1
                        a2t = preduce(a2 / pi)
                        a3t = preduce(a3 / my)
                        a4t = preduce(a4 / (pi * mx))
                        a6t = preduce(a6 / (mx * my))
                        if pdiv(a4t * a4t - 4 * a6t * a2t):
                            if p == 2:
                                r = mx * proot(a6t * pinv(a2t), 2)
                            else:
                                r = mx * preduce(-a4t * pinv(2 * a2t))
                            C = C.rst_transform(r, 0, 0)
                            (a1, a2, a3, a4, a6) = C.a_invariants()

                            mx *= pi
                            ix += 1  # and stay in loop
                        else:
                            if _pquadroots(a2t, a4t, a6t):
                                cp = 4
                            else:
                                cp = 2
                            break  # exit loop
                    else:
                        if _pquadroots(1, a3t, -a6t):
                            cp = 4
                        else:
                            cp = 2
                        break
                KS = KodairaSymbol("I%s*" % (ix + iy - 5))
                fp = val_disc - ix - iy + 1
                break  #return
            else:  # sw == 3
                ## The cubic has a triple root
                verbose("Triple root", t, 1)
                ## First we change coordinates so that T = 0 mod p
                if p == 2:
                    r = b
                elif p == 3:
                    r = proot(-d, 3)
                else:
                    r = -b * pinv(3)
                r = pi * preduce(r)
                C = C.rst_transform(r, 0, 0)
                (a1, a2, a3, a4, a6) = C.a_invariants()

                verbose(
                    "After third transform %s\n[a1,a2,a3,a4,a6] = %s\nValuations: %s"
                    % ([r, 0, 0], [a1, a2, a3, a4, a6],
                       [pval(ai) for ai in [a1, a2, a3, a4, a6]]), t, 2)
                if min(pval(ai) for ai in [a1, a2, a3, a4, a6]) < 0:
                    raise RuntimeError(
                        "Non-integral model after third transform!")
                if pval(a2) < 2 or pval(a4) < 3 or pval(a6) < 4:
                    raise RuntimeError(
                        "Cubic after transform does not have a triple root at 0"
                    )
                a3t = preduce(a3 / pi2)
                a6t = preduce(a6 / pi4)
                # We test for Type IV*
                if not pdiv(a3t * a3t + 4 * a6t):
                    cp = 3 if _pquadroots(1, a3t, -a6t) else 1
                    KS = KodairaSymbol("IV*")
                    fp = val_disc - 6
                    break  #return
                # Now change coordinates so that p^3|a3, p^5|a6
                if p == 2:
                    t = -pi2 * proot(a6t, 2)
                else:
                    t = pi2 * preduce(-a3t * halfmodp)
                C = C.rst_transform(0, 0, t)
                (a1, a2, a3, a4, a6) = C.a_invariants()

                # We test for types III* and II*
                if pval(a4) < 4:
                    ## Type III*
                    KS = KodairaSymbol("III*")
                    fp = val_disc - 7
                    cp = 2
                    break  #return
                if pval(a6) < 6:
                    ## Type II*
                    KS = KodairaSymbol("II*")
                    fp = val_disc - 8
                    cp = 1
                    break  #return
                if pi_neg is None:
                    if principal_flag:
                        pi_neg = pi
                    else:
                        pi_neg = K.uniformizer(P, 'negative')
                    pi_neg2 = pi_neg * pi_neg
                    pi_neg3 = pi_neg * pi_neg2
                    pi_neg4 = pi_neg * pi_neg3
                    pi_neg6 = pi_neg4 * pi_neg2
                a1 /= pi_neg
                a2 /= pi_neg2
                a3 /= pi_neg3
                a4 /= pi_neg4
                a6 /= pi_neg6
                verbose(
                    "Non-minimal equation, dividing out...\nNew model is %s" %
                    ([a1, a2, a3, a4, a6]), t, 1)
        return (C, p, val_disc, fp, KS, cp, split)
Esempio n. 3
0
    def complement(self, bound=None):
        """
        Return the largest Hecke-stable complement of this space.

        EXAMPLES::

            sage: M = ModularSymbols(15, 6).cuspidal_subspace()
            sage: M.complement()
            Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 20 for Gamma_0(15) of weight 6 with sign 0 over Rational Field
            sage: E = EllipticCurve("128a")
            sage: ME = E.modular_symbol_space()
            sage: ME.complement()
            Modular Symbols subspace of dimension 17 of Modular Symbols space of dimension 18 for Gamma_0(128) of weight 2 with sign 1 over Rational Field
        """

        if self.dual_free_module.is_in_cache():
            D = self.dual_free_module()
            V = D.basis_matrix().right_kernel()
            return self.submodule(V, check=False)

        if self.is_ambient():
            return self.ambient_hecke_module().zero_submodule()

        if self.is_zero():
            return self.ambient_hecke_module()

        if self.is_full_hecke_module():
            anemic = False
        else:
            anemic = True

        # TODO: optimize in some cases by computing image of
        # complementary factor instead of kernel...?
        verbose("computing")
        N = self.level()
        A = self.ambient_hecke_module()
        V = A.free_module()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0:
                    p = arith.next_prime(p)
            verbose("using T_%s" % p)
            f = self.hecke_polynomial(p)
            T = A.hecke_matrix(p)
            g = T.charpoly('x')
            V = T.kernel_on(V, poly=g // f, check=False)
            if V.rank() + self.rank() <= A.rank():
                break
            p = arith.next_prime(p)
            if p > bound:  # to avoid computing Hecke bound unless necessary
                break

        if V.rank() + self.rank() == A.rank():
            C = A.submodule(V, check=False)
            return C

        # first attempt to compute the complement failed, we now try
        # the following naive approach: decompose the ambient space,
        # decompose self, and sum the pieces of ambient that are not
        # subspaces of self
        verbose("falling back on naive algorithm")
        D = A.decomposition()
        C = A.zero_submodule()
        for X in D:
            if self.intersection(X).dimension() == 0:
                C = C + X
        if C.rank() + self.rank() == A.rank():
            return C

        # failed miserably
        raise RuntimeError(
            "Computation of complementary space failed (cut down to rank %s, but should have cut down to rank %s)."
            % (V.rank(), A.rank() - self.rank()))
Esempio n. 4
0
    def __init__(self,
                 monic_polynomial,
                 names='X',
                 iterate=True,
                 warning=True):
        r"""
        Python constructor.

        EXAMPLES::

            sage: from sage.algebras.splitting_algebra import SplittingAlgebra
            sage: Lw.<w> = LaurentPolynomialRing(ZZ)
            sage: PuvLw.<u,v> = Lw[]; t = polygen(PuvLw)
            sage: S.<x, y> = SplittingAlgebra(t^3 - u*t^2 + v*t - w, warning=False)
            sage: TestSuite(S).run()
        """

        # ---------------------------------------------------------------------------------
        # checking input parameters
        # ---------------------------------------------------------------------------------

        base_ring = monic_polynomial.base_ring()
        if not monic_polynomial.is_monic():
            raise ValueError("given polynomial must be monic")
        deg = monic_polynomial.degree()

        from sage.structure.category_object import normalize_names
        self._root_names = normalize_names(deg - 1, names)
        root_names = list(self._root_names)
        verbose(
            "Create splitting algebra to base ring %s and polynomial %s (%s %s)"
            % (base_ring, monic_polynomial, iterate, warning))

        self._defining_polynomial = monic_polynomial
        self._iterate = iterate

        try:
            if not base_ring.is_integral_domain():
                raise TypeError("base_ring must be an integral domain")
        except NotImplementedError:
            from sage.rings.ring import Ring
            if not isinstance(base_ring, Ring):
                raise TypeError("base_ring must be an instance of ring")
            if warning:
                warn('Assuming %s to be an integral domain!' % (base_ring))

        if deg < 1:
            raise ValueError("the degree of the polynomial must positive")

        self._splitting_roots = []
        self._coefficients_list = []
        self._invertible_elements = {}

        if isinstance(base_ring, SplittingAlgebra):
            self._invertible_elements = base_ring._invertible_elements

        # ------------------------------------------------------------------------------------
        # taking next root_name
        # ------------------------------------------------------------------------------------
        root_name = root_names[0]
        p = monic_polynomial.change_variable_name(root_name)
        P = p.parent()

        self._set_modulus_irreducible_ = False
        try:
            if not p.is_irreducible():
                raise ValueError("monic_polynomial must be irreducible")
        except (NotImplementedError, AttributeError):
            # assuming this has been checked mathematically before
            self._set_modulus_irreducible_ = True
            if warning:
                warn('Asuming %s to have maximal Galois group!' %
                     (monic_polynomial))
                warning = False  # one warning must be enough

        verbose("P %s defined:" % (P))

        if deg > 2 and iterate:
            # ------------------------------------------------------------------------------------
            # successive solution via recursion (on base_ring_step)
            # ------------------------------------------------------------------------------------
            base_ring_step = SplittingAlgebra(monic_polynomial,
                                              tuple(root_names),
                                              iterate=False,
                                              warning=False)
            first_root = base_ring_step.gen()

            verbose("base_ring_step %s defined:" % (base_ring_step))

            # ------------------------------------------------------------------------------------
            # splitting first root off
            # ------------------------------------------------------------------------------------
            from copy import copy
            root_names_reduces = copy(root_names)
            root_names_reduces.remove(root_name)

            P = base_ring_step[root_names_reduces[0]]
            p = P(monic_polynomial.dict())
            q, r = p.quo_rem((P.gen() - first_root))

            verbose("Invoking recursion with: %s" % (q, ))

            SplittingAlgebra.__init__(self,
                                      q,
                                      root_names_reduces,
                                      warning=False)

            splitting_roots = base_ring_step._splitting_roots + self._splitting_roots
            coefficients_list = base_ring_step._coefficients_list + self._coefficients_list

            verbose("Adding roots: %s" % (splitting_roots))

            self._splitting_roots = splitting_roots
            self._coefficients_list = coefficients_list
        else:
            PolynomialQuotientRing_domain.__init__(self, P, p, root_name)

            first_root = self.gen()
            self._splitting_roots.append(first_root)
            self._coefficients_list = [
                monic_polynomial.coefficients(sparse=False)
            ]

            if not iterate:
                verbose("pre ring defined splitting_roots: %s" %
                        (self._splitting_roots))
                return

            verbose("final ring defined splitting_roots: %s" %
                    (self._splitting_roots))

        if deg == 2:
            coefficients = monic_polynomial.coefficients(sparse=False)
            lin_coeff = coefficients[1]
            self._splitting_roots.append(-lin_coeff - first_root)

        self._root_names = names
        self._splitting_roots = [self(root) for root in self._splitting_roots]
        verbose("splitting_roots: %s embedded" % (self._splitting_roots))

        # -------------------------------------------------------------------------------------------
        # try to calculate inverses of the roots. This is possible if the original polynomial
        # has an invertible constant term. For example let cf = [-w, v,-u, 1] that is
        # p = h^3 -u*h^2 + v*h -w, than u = x + y + z, v = x*y + x*z + y*z, w = x*y*z. If
        # w is invertible then 1/x = (v -(u-x)*x)/w, 1/y = (v -(u-y)*y)/w, 1/z = (v -(u-z)*z)/w
        # -------------------------------------------------------------------------------------------
        # first find the polynomial with invertible constant coefficient
        # -------------------------------------------------------------------------------------------
        cf0_inv = None
        for cf in self._coefficients_list:
            cf0 = cf[0]
            try:
                cf0_inv = ~(cf[0])
                cf0_inv = self(cf0_inv)
                verbose("invertible coefficient: %s found" % (cf0_inv))
                break
            except NotImplementedError:
                verbose("constant coefficient: %s not invertibe" % (cf0))

        # ----------------------------------------------------------------------------------
        # assuming that cf splits into linear factors over self and the _splitting_roots
        # are its roots we can calculate inverses
        # ----------------------------------------------------------------------------------
        if cf0_inv is not None:
            deg_cf = len(cf) - 1
            pf = P(cf)
            for root in self._splitting_roots:
                check = self(pf)
                if not check.is_zero():
                    continue
                root_inv = self.one()
                for pos in range(deg_cf - 1):
                    root_inv = (-1)**(pos + 1) * cf[deg_cf - pos -
                                                    1] - root_inv * root
                verbose("inverse %s of root %s" % (root_inv, root))
                root_inv = (-1)**(deg_cf) * cf0_inv * root_inv
                self._invertible_elements.update({root: root_inv})
                verbose("adding inverse %s of root %s" % (root_inv, root))
            invert_items = [(k, v)
                            for k, v in self._invertible_elements.items()]
            for k, v in invert_items:
                self._invertible_elements.update({v: k})
        return
Esempio n. 5
0
def solve_with_extension(monic_polynomial,
                         root_names=None,
                         var='x',
                         flatten=False,
                         warning=True):
    r"""
    Return all roots of a monic polynomial in its base ring or in an appropriate
    extension ring, as far as possible.

    INPUT:

    - ``monic_polynomial`` -- the monic polynomial whose roots should be created
    - ``root_names``  -- names for the indeterminates needed to define the
      splitting algebra of the ``monic_polynomial`` (if necessary and possible)
    - ``var``  -- (default: ``'x'``) for the indeterminate needed to define the
      splitting field of the ``monic_polynomial`` (if necessary and possible)
    - ``flatten`` -- (default: ``True``) if ``True`` the roots will not be
      given as a list of pairs ``(root, multiplicity)`` but as a list of
      roots repeated according to their multiplicity
    - ``warning`` -- (default: ``True``) can be used (by setting to ``False``)
      to suppress a warning which will be thrown whenever it cannot be checked
      that the Galois group of ``monic_polynomial`` is maximal

    OUTPUT:

    List of tuples ``(root, multiplicity)`` respectively list of roots repeated
    according to their multiplicity if option ``flatten`` is ``True``.

    EXAMPLES::

        sage: from sage.algebras.splitting_algebra import solve_with_extension
        sage: t = polygen(ZZ)
        sage: p = t^2 -2*t +1
        sage: solve_with_extension(p, flatten=True )
        [1, 1]
        sage: solve_with_extension(p)
        [(1, 2)]

        sage: cp5 = cyclotomic_polynomial(5, var='T').change_ring(UniversalCyclotomicField())
        sage: solve_with_extension(cp5)
        [(E(5), 1), (E(5)^4, 1), (E(5)^2, 1), (E(5)^3, 1)]
        sage: _[0][0].parent()
        Universal Cyclotomic Field
    """
    def create_roots(monic_polynomial, warning=True):
        r"""
        This internal function creates all roots of a polynomial in an
        appropriate extension ring assuming that none of the roots is
        contained its base ring.

        It first tries to create the splitting field of the given polynomial.
        If this is not faithful the splitting algebra will be created.

        INPUT:

        - ``monic_polynomial`` -- the monic polynomial whose roots should
          be created
        - ``warning`` -- (default: ``True``) can be used (by setting to ``False``)
          to suppress a warning which will be thrown whenever it cannot be
          checked that the Galois group of ``monic_polynomial`` is maximal
        """
        parent = monic_polynomial.parent()
        base_ring = parent.base_ring()

        try:
            ext_field, embed = monic_polynomial.splitting_field(var, map=True)

            if embed.domain() != base_ring:
                # in this case the SplittingAlgebra is preferred
                raise NotImplementedError

            # -------------------------------------------------------------------------------------
            # in some cases the embedding of the base_ring in ext_field can not be obtained
            # as coercion
            # -------------------------------------------------------------------------------------
            reset_coercion = False
            from sage.rings.number_field.number_field import NumberField_generic
            if isinstance(base_ring, NumberField_generic):
                reset_coercion = True
            elif base_ring.is_finite() and not base_ring.is_prime_field():
                reset_coercion = True
            if reset_coercion:
                ext_field._unset_coercions_used()
                ext_field.register_coercion(embed)
                ext_field.register_conversion(embed)

            verbose("splitting field %s defined" % (ext_field))
            pol_emb = monic_polynomial.change_ring(ext_field)
            roots = pol_emb.roots()
        except NotImplementedError:
            ext_ring = SplittingAlgebra(monic_polynomial,
                                        name_list,
                                        warning=warning)
            verbose("splitting algebra %s defined" % (ext_ring))
            roots = [(r, 1) for r in ext_ring.splitting_roots()]
        return roots

    deg_pol = monic_polynomial.degree()
    if not root_names:
        from sage.structure.category_object import normalize_names
        root_names = normalize_names(deg_pol - 1, 'r')
    name_list = list(root_names)
    root_list = []
    try:
        root_list = monic_polynomial.roots()
    except (TypeError, ValueError, NotImplementedError):
        pass

    if not root_list:
        # ------------------------------------------------------------------------------
        # no roots found: find roots in an appropriate extension ring
        # ------------------------------------------------------------------------------
        verbose("no roots in base_ring")
        if len(name_list) > deg_pol - 1:
            name_list = [name_list[i] for i in range(deg_pol - 1)]
        roots = create_roots(monic_polynomial, warning=warning)

    else:
        # ------------------------------------------------------------------------------
        # root calculation was possible but maybe some more roots in an apropriate
        # extension ring can be constructed.
        # ------------------------------------------------------------------------------
        num_roots = sum(m for r, m in root_list)
        if num_roots < deg_pol:
            h = monic_polynomial.variables()[0]
            divisor = monic_polynomial.base_ring().one()
            for r, m in root_list:
                divisor *= (h - r)**m
            q, r = monic_polynomial.quo_rem(divisor)
            if len(name_list) > deg_pol - num_roots - 1:
                name_list = [
                    name_list[i] for i in range(deg_pol - num_roots - 1)
                ]
            verbose("%d root found in base ring, now solving %s" %
                    (num_roots, q))
            missing_roots = create_roots(q, warning=True)
            roots = root_list + missing_roots
        else:
            roots = root_list
            verbose("all roots in base ring")

    if flatten:
        from sage.misc.flatten import flatten
        return flatten([[rt] * m for rt, m in roots])
    return roots
Esempio n. 6
0
    def _find_generators(self, maxweight, start_gens, start_weight):
        r"""
        For internal use. This function is called by :meth:`generators` and
        :meth:`gen_forms`: it returns a list of triples `(k, f, F)` where `F`
        is a modular form of weight `k` and `f` is its `q`-expansion coerced
        into the base ring of self.

        INPUT:

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

        OUTPUT:

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

        EXAMPLES::

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

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

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

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

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

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

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

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

        while k <= maxweight:

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

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

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

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

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

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

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

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

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

        return G
Esempio n. 7
0
    def _compute_q_expansion_basis(self, prec=None):
        r"""
        Compute q-expansion basis using Schaeffer's algorithm.

        EXAMPLES::

            sage: CuspForms(GammaH(31, [7]), 1).q_expansion_basis() # indirect doctest
            [
            q - q^2 - q^5 + O(q^6)
            ]

        A more elaborate example (two Galois-conjugate characters each giving a
        2-dimensional space)::

            sage: CuspForms(GammaH(124, [85]), 1).q_expansion_basis()
            [
            q - q^4 - q^6 + O(q^7),
            q^2 + O(q^7),
            q^3 + O(q^7),
            q^5 - q^6 + O(q^7)
            ]
        """
        if prec is None:
            prec = self.prec()
        else:
            prec = Integer(prec)

        chars=self.group().characters_mod_H(sign=-1, galois_orbits=True)

        B = []
        dim = 0
        for c in chars:
            chi = c.minimize_base_ring()
            Bchi = [weight1.modular_ratio_to_prec(chi, f, prec)
                for f in weight1.hecke_stable_subspace(chi) ]
            if Bchi == []:
                continue
            if chi.base_ring() == QQ:
                B += [f.padded_list(prec) for f in Bchi]
                dim += len(Bchi)
            else:
                d = chi.base_ring().degree()
                dim += d * len(Bchi)
                for f in Bchi:
                    w = f.padded_list(prec)
                    for i in range(d):
                        B.append([x[i] for x in w])

        basis_mat = Matrix(QQ, B)
        if basis_mat.is_zero():
            return []
        # Daft thing: "transformation=True" parameter to echelonize
        # is ignored for rational matrices!
        big_mat = basis_mat.augment(identity_matrix(dim))
        big_mat.echelonize()
        c = big_mat.pivots()[-1]

        echelon_basis_mat = big_mat[:, :prec]

        R = self._q_expansion_ring()

        if c >= prec:
            verbose("Precision %s insufficient to determine basis" % prec, level=1)
        else:
            verbose("Minimal precision for basis: %s" % (c+1), level=1)
            t = big_mat[:, prec:]
            assert echelon_basis_mat == t * basis_mat
            self.__transformation_matrix = t
            self._char_basis = [R(f.list(), c+1) for f in basis_mat.rows()]

        return [R(f.list(), prec) for f in echelon_basis_mat.rows() if f != 0]
Esempio n. 8
0
def saturation(A, proof=True, p=0, max_dets=5):
    """
    Compute a saturation matrix of A.

    INPUT:

    - A     -- a matrix over ZZ
    - proof -- bool (default: True)
    - p     -- int (default: 0); if not 0 only guarantees that output is
      p-saturated
    - max_dets -- int (default: 4) max number of dets of submatrices to
      compute.

    OUTPUT:

    matrix -- saturation of the matrix A.

    EXAMPLES::

        sage: from sage.matrix.matrix_integer_dense_saturation import saturation
        sage: A = matrix(ZZ, 2, 2, [3,2,3,4]); B = matrix(ZZ, 2,3,[1,2,3,4,5,6]); C = A*B
        sage: C
        [11 16 21]
        [19 26 33]
        sage: C.index_in_saturation()
        18
        sage: S = saturation(C); S
        [11 16 21]
        [-2 -3 -4]
        sage: S.index_in_saturation()
        1
        sage: saturation(C, proof=False)
        [11 16 21]
        [-2 -3 -4]
        sage: saturation(C, p=2)
        [11 16 21]
        [-2 -3 -4]
        sage: saturation(C, p=2, max_dets=1)
        [11 16 21]
        [-2 -3 -4]
    """
    # Find a submatrix of full rank and instead saturate that matrix.
    r = A.rank()
    if A.is_square() and r == A.nrows():
        return identity_matrix(ZZ, r)
    if A.nrows() > r:
        P = []
        while len(P) < r:
            P = matrix_integer_dense_hnf.probable_pivot_rows(A)
        A = A.matrix_from_rows(P)

    # Factor out all common factors from all rows, just in case.
    A = copy(A)
    A._factor_out_common_factors_from_each_row()

    if A.nrows() <= 1:
        return A

    A, zero_cols = A._delete_zero_columns()

    if max_dets > 0:
        # Take the GCD of at most num_dets randomly chosen determinants.
        nr = A.nrows(); nc = A.ncols()
        d = 0
        trials = min(binomial(nc, nr), max_dets)
        already_tried = []
        while len(already_tried) < trials:
            v = random_sublist_of_size(nc, nr)
            tm = verbose('saturation -- checking det condition on submatrix')
            d = gcd(d, A.matrix_from_columns(v).determinant(proof=proof))
            verbose('saturation -- got det down to %s'%d, tm)
            if gcd(d, p) == 1:
                return A._insert_zero_columns(zero_cols)
            already_tried.append(v)

        if gcd(d, p) == 1:
            # already p-saturated
            return A._insert_zero_columns(zero_cols)

        # Factor and p-saturate at each p.
        # This is not a good algorithm, because all the HNF's in it are really slow!
        #
        #tm = verbose('factoring gcd %s of determinants'%d)
        #limit = 2**31-1
        #F = d.factor(limit = limit)
        #D = [p for p, e in F if p <= limit]
        #B = [n for n, e in F if n > limit]  # all big factors -- there will only be at most one
        #assert len(B) <= 1
        #C = B[0]
        #for p in D:
        #    A = p_saturation(A, p=p, proof=proof)

    # This is a really simple but powerful algorithm.
    # FACT: If A is a matrix of full rank, then hnf(transpose(A))^(-1)*A is a saturation of A.
    # To make this practical we use solve_system_with_difficult_last_row, since the
    # last column of HNF's are typically the only really big ones.
    B = A.transpose().hermite_form(include_zero_rows=False, proof=proof)
    B = B.transpose()

    # Now compute B^(-1) * A
    C = solve_system_with_difficult_last_row(B, A)
    return C.change_ring(ZZ)._insert_zero_columns(zero_cols)
Esempio n. 9
0
def det_given_divisor(A, d, proof=True, stabilize=2):
    """
    Given a divisor d of the determinant of A, compute the determinant of A.

    INPUT:

    - ``A`` -- a square integer matrix
    - ``d`` -- a nonzero integer that is assumed to divide the determinant of A
    - ``proof`` -- bool (default: True) compute det modulo enough primes
      so that the determinant is computed provably correctly (via the
      Hadamard bound).  It would be VERY hard for ``det()`` to fail even
      with proof=False.
    - ``stabilize`` -- int (default: 2) if proof = False, then compute
      the determinant modulo `p` until ``stabilize`` successive modulo
      determinant computations stabilize.

    OUTPUT:

    integer -- determinant

    EXAMPLES::

        sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
        sage: a = matrix(ZZ,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2])
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 3)
        -30
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False)
        -30
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 3, proof=False, stabilize=1)
        -30
        sage: a.det()
        -30

    Here we illustrate proof=False giving a wrong answer::

        sage: p = matrix_integer_dense_hnf.max_det_prime(2)
        sage: q = previous_prime(p)
        sage: a = matrix(ZZ, 2, [p, 0, 0, q])
        sage: p * q
        70368442188091
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=2)
        0

    This still works, because we do not work modulo primes that divide
    the determinant bound, which is found using a p-adic algorithm::

        sage: a.det(proof=False, stabilize=2)
        70368442188091

    3 primes is enough::

        sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=3)
        70368442188091
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=False, stabilize=5)
        70368442188091
        sage: matrix_integer_dense_hnf.det_given_divisor(a, 1, proof=True)
        70368442188091

    TESTS::

        sage: m = diagonal_matrix(ZZ, 68, [2]*66 + [1,1])
        sage: m.det()
        73786976294838206464
    """
    p = max_det_prime(A.nrows())
    z_mod = []
    moduli = []
    assert d != 0
    z_so_far = 1
    N_so_far = 1
    if proof:
        N = 1
        B = (2 * 10**A.hadamard_bound()) // d + 1
        dd = d
        # bad verbose statement, since computing the log overflows!
        est = int(RR(B).log() / RR(p).log()) + 1
        cnt = 1
        verbose("Multimodular det -- need to use about %s primes." % est,
                level=1)
        while N < B:
            if d % p != 0:
                tm = cputime()
                dd, z_so_far, N_so_far = det_from_modp_and_divisor(
                    A, d, p, z_mod, moduli, z_so_far, N_so_far)
                N *= p
                verbose(
                    "computed det mod p=%s which is %s (of about %s)" %
                    (p, cnt, est), tm)
            p = previous_prime(p)
            cnt += 1
        return dd
    else:
        val = []
        while True:
            if d % p:
                tm = cputime()
                dd, z_so_far, N_so_far = det_from_modp_and_divisor(
                    A, d, p, z_mod, moduli, z_so_far, N_so_far)
                verbose("computed det mod %s" % p, tm)
                val.append(dd)
                if len(val) >= stabilize and len(set(val[-stabilize:])) == 1:
                    return val[-1]
            p = previous_prime(p)
Esempio n. 10
0
def theta_series_degree_2(Q, prec):
    r"""
    Compute the theta series of degree 2 for the quadratic form Q.

    INPUT:

    - ``prec`` -- an integer.

    OUTPUT:

    dictionary, where:

    - keys are `{\rm GL}_2(\ZZ)`-reduced binary quadratic forms (given as triples of
      coefficients)
    - values are coefficients

    EXAMPLES::

        sage: Q2 = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1])
        sage: S = Q2.theta_series_degree_2(10)
        sage: S[(0,0,2)]
        24
        sage: S[(1,0,1)]
        144
        sage: S[(1,1,1)]
        192

    AUTHORS:

    - Gonzalo Tornaria (2010-03-23)

    REFERENCE:

    - Raum, Ryan, Skoruppa, Tornaria, 'On Formal Siegel Modular Forms'
      (preprint)
    """
    from sage.misc.verbose import verbose

    if Q.base_ring() != ZZ:
        raise TypeError("The quadratic form must be integral")
    if not Q.is_positive_definite():
        raise ValueError("The quadratic form must be positive definite")
    try:
        X = ZZ(prec - 1)  # maximum discriminant
    except TypeError:
        raise TypeError("prec is not an integer")

    if X < -1:
        raise ValueError("prec must be >= 0")

    if X == -1:
        return {}

    V = ZZ**Q.dim()
    H = Q.Hessian_matrix()

    t = cputime()
    max = int(floor((X + 1) / 4))
    v_list = (Q.vectors_by_length(max))  # assume a>0
    v_list = [[V(_) for _ in vs] for vs in v_list]  # coerce vectors into V
    verbose("Computed vectors_by_length", t)

    # Deal with the singular part
    coeffs = {(0, 0, 0): ZZ(1)}
    for i in range(1, max + 1):
        coeffs[(0, 0, i)] = ZZ(2) * len(v_list[i])

    # Now deal with the non-singular part
    a_max = int(floor(sqrt(X / 3)))
    for a in range(1, a_max + 1):
        t = cputime()
        c_max = int(floor((a * a + X) / (4 * a)))
        for c in range(a, c_max + 1):
            for v1 in v_list[a]:
                v1_H = v1 * H

                def B_v1(v):
                    return v1_H * v2

                for v2 in v_list[c]:
                    b = abs(B_v1(v2))
                    if b <= a and 4 * a * c - b * b <= X:
                        qf = (a, b, c)
                        count = ZZ(4) if b == 0 else ZZ(2)
                        coeffs[qf] = coeffs.get(qf, ZZ(0)) + count
        verbose("done a = %d" % a, t)

    return coeffs
Esempio n. 11
0
def solve_system_with_difficult_last_row(B, A):
    """
    Solve the matrix equation B*Z = A when the last row of $B$
    contains huge entries.

    INPUT:

    - B -- a square n x n nonsingular matrix with painful big bottom row.
    - A -- an n x k matrix.

    OUTPUT:

    the unique solution to B*Z = A.

    EXAMPLES::

        sage: from sage.matrix.matrix_integer_dense_saturation import solve_system_with_difficult_last_row
        sage: B = matrix(ZZ, 3, [1,2,3, 3,-1,2,939239082,39202803080,2939028038402834]); A = matrix(ZZ,3,2,[1,2,4,3,-1,0])
        sage: X = solve_system_with_difficult_last_row(B, A); X
        [  290668794698843/226075992027744         468068726971/409557956572]
        [-226078357385539/1582531944194208       1228691305937/2866905696004]
        [      2365357795/1582531944194208           -17436221/2866905696004]
        sage: B*X == A
        True
    """
    # See the comments in the function of the same name in matrix_integer_dense_hnf.py.
    # This function is just a generalization of that one to A a matrix.
    C = copy(B)
    while True:
        C[C.nrows()-1] = random_matrix(ZZ,1,C.ncols()).row(0)
        try:
            X = C.solve_right(A)
        except ValueError:
            verbose("Try difficult solve again with different random vector")
        else:
            break
    D = B.matrix_from_rows(range(C.nrows()-1))
    N = D._rational_kernel_flint()
    if N.ncols() != 1:
        verbose("Difficult solve quickly failed.  Using direct approach.")
        return B.solve_right(A)

    tm = verbose("Recover correct linear combinations")
    k = N.matrix_from_columns([0])

    # The sought for solution Z to B*Z = A is some linear combination
    #       Z = X + alpha*k
    #  Let w be the last row of B; then Z satisfies
    #       w * Z = A'
    # where A' is the last row of A.  Thus
    #       w * (X + alpha*k) = A'
    # so    w * X + alpha*w*k = A'
    # so    alpha*w*k  = A' - w*X.
    w = B[-1]  # last row of B
    A_prime = A[-1]  # last row of A
    lhs = w*k
    rhs = A_prime - w * X

    if lhs[0] == 0:
        verbose("Difficult solve quickly failed.  Using direct approach.")
        return B.solve_right(A)

    for i in range(X.ncols()):
        alpha = rhs[i] / lhs[0]
        X.set_column(i, (X.matrix_from_columns([i]) + alpha*k).list())
    verbose("Done getting linear combinations.", tm)
    return X
Esempio n. 12
0
    def points(self, **kwds):
        r"""
        Return some or all rational points of an affine scheme.

        For dimension 0 subschemes points are determined through a groebner
        basis calculation. For schemes or subschemes with dimension greater than 1
        points are determined through enumeration up to the specified bound.

        Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound
        are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the field
        is infinite or not.

        For number fields, this uses the
        Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for
        computing algebraic numbers up to a given height [DK2013]_.

        The algorithm requires floating point arithmetic, so the user is
        allowed to specify the precision for such calculations.
        Additionally, due to floating point issues, points
        slightly larger than the bound may be returned. This can be controlled
        by lowering the tolerance.


        INPUT:

        kwds:

        - ``bound`` - real number (optional, default: 0). The bound for the
          height of the coordinates. Only used for subschemes with
          dimension at least 1.

        - ``zero_tolerance`` - positive real number (optional, default=10^(-10)).
          For numerically inexact fields, points are on the subscheme if they
          satisfy the equations to within tolerance.

        - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4
          for enumeration over number fields.

        - ``precision`` - the precision to use for computing the elements of
          bounded height of number fields.

        OUTPUT:

        - a list of rational points of a affine scheme

        .. WARNING::

           For numerically inexact fields such as ComplexField or RealField the
           list of points returned is very likely to be incomplete. It may also
           contain repeated points due to tolerance.

        EXAMPLES: The bug reported at #11526 is fixed::

            sage: A2 = AffineSpace(ZZ, 2)
            sage: F = GF(3)
            sage: A2(F).points()
            [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

        ::

            sage: A.<x,y> = ZZ[]
            sage: I = A.ideal(x^2-y^2-1)
            sage: V = AffineSpace(ZZ, 2)
            sage: X = V.subscheme(I)
            sage: M = X(ZZ)
            sage: M.points(bound=1)
            [(-1, 0), (1, 0)]

        ::

            sage: u = QQ['u'].0
            sage: K.<v> = NumberField(u^2 + 3)
            sage: A.<x,y> = AffineSpace(K, 2)
            sage: len(A(K).points(bound=2))
            1849

        ::

            sage: A.<x,y> = AffineSpace(QQ, 2)
            sage: E = A.subscheme([x^2 + y^2 - 1, y^2 - x^3 + x^2 + x - 1])
            sage: E(A.base_ring()).points()
            [(-1, 0), (0, -1), (0, 1), (1, 0)]

        ::

            sage: A.<x,y> = AffineSpace(CC, 2)
            sage: E = A.subscheme([y^3 - x^3 - x^2, x*y])
            sage: E(A.base_ring()).points()
            verbose 0 (...: affine_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.
            [(-1.00000000000000, 0.000000000000000),
            (0.000000000000000, 0.000000000000000)]

        ::

            sage: A.<x1,x2> = AffineSpace(CDF, 2)
            sage: E = A.subscheme([x1^2 + x2^2 + x1*x2, x1 + x2])
            sage: E(A.base_ring()).points()
            verbose 0 (...: affine_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.
            [(0.0, 0.0)]
        """
        from sage.schemes.affine.affine_space import is_AffineSpace

        X = self.codomain()
        if not is_AffineSpace(X) and X.base_ring() in Fields():
            if hasattr(X.base_ring(), 'precision'):
                numerical = True
                verbose(
                    "Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.",
                    level=0)
                zero_tol = RR(kwds.pop('zero_tolerance', 10**(-10)))
                if zero_tol <= 0:
                    raise ValueError("tolerance must be positive")
            else:
                numerical = False
            # Then X must be a subscheme
            dim_ideal = X.defining_ideal().dimension()
            if dim_ideal < 0:  # no points
                return []
            if dim_ideal == 0:  # if X zero-dimensional
                rat_points = []
                AS = X.ambient_space()
                N = AS.dimension_relative()
                BR = X.base_ring()
                #need a lexicographic ordering for elimination
                R = PolynomialRing(BR, N, AS.gens(), order='lex')
                I = R.ideal(X.defining_polynomials())
                I0 = R.ideal(0)
                #Determine the points through elimination
                #This is much faster than using the I.variety() function on each affine chart.
                G = I.groebner_basis()
                if G != [1]:
                    P = {}
                    points = [P]
                    #work backwards from solving each equation for the possible
                    #values of the next coordinate
                    for i in range(len(G) - 1, -1, -1):
                        new_points = []
                        good = 0
                        for P in points:
                            #substitute in our dictionary entry that has the values
                            #of coordinates known so far. This results in a single
                            #variable polynomial (by elimination)
                            L = G[i].substitute(P)
                            if R(L).degree() > 0:
                                if numerical:
                                    for pol in L.univariate_polynomial().roots(
                                            multiplicities=False):
                                        r = L.variables()[0]
                                        varindex = R.gens().index(r)
                                        P.update({R.gen(varindex): pol})
                                        new_points.append(copy(P))
                                        good = 1
                                else:
                                    L = L.factor()
                                    #the linear factors give the possible rational values of
                                    #this coordinate
                                    for pol, pow in L:
                                        if pol.degree() == 1 and len(
                                                pol.variables()) == 1:
                                            good = 1
                                            r = pol.variables()[0]
                                            varindex = R.gens().index(r)
                                            #add this coordinates information to
                                            #each dictionary entry
                                            P.update({
                                                R.gen(varindex):
                                                -pol.constant_coefficient() /
                                                pol.monomial_coefficient(r)
                                            })
                                            new_points.append(copy(P))
                            else:
                                new_points.append(P)
                                good = 1
                        if good:
                            points = new_points
                    #the dictionary entries now have values for all coordinates
                    #they are the rational solutions to the equations
                    #make them into affine points
                    for i in range(len(points)):
                        if numerical:
                            if len(points[i]) == N:
                                S = AS([points[i][R.gen(j)] for j in range(N)])
                                if all(
                                        g(list(S)) < zero_tol
                                        for g in X.defining_polynomials()):
                                    rat_points.append(S)
                        else:
                            if len(points[i]) == N and I.subs(points[i]) == I0:
                                S = X([points[i][R.gen(j)] for j in range(N)])
                                rat_points.append(S)

                rat_points = sorted(rat_points)
                return rat_points
        R = self.value_ring()
        B = kwds.pop('bound', 0)
        tol = kwds.pop('tolerance', 1e-2)
        prec = kwds.pop('precision', 53)
        if is_RationalField(R) or R == ZZ:
            if not B > 0:
                raise TypeError("a positive bound B (= %s) must be specified" %
                                B)
            from sage.schemes.affine.affine_rational_point import enum_affine_rational_field
            return enum_affine_rational_field(self, B)
        if R in NumberFields():
            if not B > 0:
                raise TypeError("a positive bound B (= %s) must be specified" %
                                B)
            from sage.schemes.affine.affine_rational_point import enum_affine_number_field
            return enum_affine_number_field(self,
                                            bound=B,
                                            tolerance=tol,
                                            precision=prec)
        elif is_FiniteField(R):
            from sage.schemes.affine.affine_rational_point import enum_affine_finite_field
            return enum_affine_finite_field(self)
        else:
            raise TypeError("unable to enumerate points over %s" % R)
Esempio n. 13
0
    def mac_lane_approximants(self,
                              G,
                              assume_squarefree=False,
                              require_final_EF=True,
                              required_precision=-1,
                              require_incomparability=False,
                              require_maximal_degree=False,
                              algorithm="serial"):
        r"""
        Return approximants on `K[x]` for the extensions of this valuation to
        `L=K[x]/(G)`.

        If `G` is an irreducible polynomial, then this corresponds to
        extensions of this valuation to the completion of `L`.

        INPUT:

        - ``G`` -- a monic squarefree integral polynomial in a
          univariate polynomial ring over the domain of this valuation

        - ``assume_squarefree`` -- a boolean (default: ``False``), whether to
          assume that ``G`` is squarefree. If ``True``, the squafreeness of
          ``G`` is not verified though it is necessary when
          ``require_final_EF`` is set for the algorithm to terminate.

        - ``require_final_EF`` -- a boolean (default: ``True``); whether to
          require the returned key polynomials to be in one-to-one
          correspondance to the extensions of this valuation to ``L`` and
          require them to have the ramification index and residue degree of the
          valuations they correspond to.

        - ``required_precision`` -- a number or infinity (default: -1); whether
          to require the last key polynomial of the returned valuations to have
          at least that valuation.

        - ``require_incomparability`` -- a boolean (default: ``False``);
          whether to require require the returned valuations to be incomparable
          (with respect to the partial order on valuations defined by comparing
          them pointwise.)

        - ``require_maximal_degree`` -- a boolean (default: ``False``); whether
          to require the last key polynomial of the returned valuation to have
          maximal degree. This is most relevant when using this algorithm to
          compute approximate factorizations of ``G``, when set to ``True``,
          the last key polynomial has the same degree as the corresponding
          factor.

        - ``algorithm`` -- one of ``"serial"`` or ``"parallel"`` (default:
          ``"serial"``); whether or not to parallelize the algorithm

        EXAMPLES::

            sage: v = QQ.valuation(2)
            sage: R.<x> = QQ[]
            sage: v.mac_lane_approximants(x^2 + 1)
            [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]]
            sage: v.mac_lane_approximants(x^2 + 1, required_precision=infinity)
            [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2, v(x^2 + 1) = +Infinity ]]
            sage: v.mac_lane_approximants(x^2 + x + 1)
            [[ Gauss valuation induced by 2-adic valuation, v(x^2 + x + 1) = +Infinity ]]

        Note that ``G`` does not need to be irreducible. Here, we detect a
        factor `x + 1` and an approximate factor `x + 1` (which is an
        approximation to `x - 1`)::

            sage: v.mac_lane_approximants(x^2 - 1)
            [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = +Infinity ],
             [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1 ]]

        However, it needs to be squarefree::

            sage: v.mac_lane_approximants(x^2)
            Traceback (most recent call last):
            ...
            ValueError: G must be squarefree

        TESTS:

        Some difficult cases provided by Mark van Hoeij::

            sage: k = GF(2)
            sage: K.<x> = FunctionField(k)
            sage: R.<y> = K[]
            sage: F = y^21 + x*y^20 + (x^3 + x + 1)*y^18 + (x^3 + 1)*y^17 + (x^4 + x)*y^16 + (x^7 + x^6 + x^3 + x + 1)*y^15 + x^7*y^14 + (x^8 + x^7 + x^6 + x^4 + x^3 + 1)*y^13 + (x^9 + x^8 + x^4 + 1)*y^12 + (x^11 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2)*y^11 + (x^12 + x^9 + x^8 + x^7 + x^5 + x^3 + x + 1)*y^10 + (x^14 + x^13 + x^10 + x^9 + x^8 + x^7 + x^6 + x^3 + x^2 + 1)*y^9 + (x^13 + x^9 + x^8 + x^6 + x^4 + x^3 + x)*y^8 + (x^16 + x^15 + x^13 + x^12 + x^11 + x^7 + x^3 + x)*y^7 + (x^17 + x^16 + x^13 + x^9 + x^8 + x)*y^6 + (x^17 + x^16 + x^12 + x^7 + x^5 + x^2 + x + 1)*y^5 + (x^19 + x^16 + x^15 + x^12 + x^6 + x^5 + x^3 + 1)*y^4 + (x^18 + x^15 + x^12 + x^10 + x^9 + x^7 + x^4 + x)*y^3 + (x^22 + x^21 + x^20 + x^18 + x^13 + x^12 + x^9 + x^8 + x^7 + x^5 + x^4 + x^3)*y^2 + (x^23 + x^22 + x^20 + x^17 + x^15 + x^14 + x^12 + x^9)*y + x^25 + x^23 + x^19 + x^17 + x^15 + x^13 + x^11 + x^5
            sage: x = K._ring.gen()
            sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x,1))
            sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed
            [[ Gauss valuation induced by (x)-adic valuation, v(y + x + 1) = 3/2 ],
             [ Gauss valuation induced by (x)-adic valuation, v(y) = 1 ],
             [ Gauss valuation induced by (x)-adic valuation, v(y) = 4/3 ],
             [ Gauss valuation induced by (x)-adic valuation, v(y^15 + y^13 + y^12 + y^10 + y^9 + y^8 + y^4 + y^3 + y^2 + y + 1) = 1 ]]
            sage: v0 = K.valuation(GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x+1,1))
            sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed
            [[ Gauss valuation induced by (x + 1)-adic valuation, v(y + x^2 + 1) = 7/2 ],
             [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 3/4 ],
             [ Gauss valuation induced by (x + 1)-adic valuation, v(y) = 7/2 ],
             [ Gauss valuation induced by (x + 1)-adic valuation, v(y^13 + y^12 + y^10 + y^7 + y^6 + y^3 + 1) = 1 ]]
            sage: v0 = valuations.FunctionFieldValuation(K, GaussValuation(K._ring, valuations.TrivialValuation(k)).augmentation(x^3+x^2+1,1))
            sage: v0.mac_lane_approximants(F, assume_squarefree=True) # assumes squarefree for speed
            [[ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y + x^3 + x^2 + x) = 2, v(y^2 + (x^6 + x^4 + 1)*y + x^14 + x^10 + x^9 + x^8 + x^5 + x^4 + x^3 + x^2 + x) = 5 ],
             [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^2 + (x^2 + x)*y + 1) = 1 ],
             [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + (x + 1)*y^2 + (x + 1)*y + x^2 + x + 1) = 1 ],
             [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^3 + x^2*y + x) = 1 ],
             [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^4 + (x + 1)*y^3 + x^2*y^2 + (x^2 + x)*y + x) = 1 ],   
             [ Gauss valuation induced by (x^3 + x^2 + 1)-adic valuation, v(y^7 + x^2*y^6 + (x + 1)*y^4 + x^2*y^3 + (x^2 + x + 1)*y^2 + x^2*y + x) = 1 ]]

        Cases with trivial residue field extensions::

            sage: K.<x> = FunctionField(QQ)
            sage: S.<y> = K[]
            sage: F = y^2 - x^2 - x^3 - 3
            sage: v0 = GaussValuation(K._ring, QQ.valuation(3))
            sage: v1 = v0.augmentation(K._ring.gen(),1/3)
            sage: mu0 = valuations.FunctionFieldValuation(K, v1)
            sage: mu0.mac_lane_approximants(F)
            [[ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + 2*x) = 2/3 ],
             [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ]]

        Over a complete base field::

            sage: k=Qp(2,10)
            sage: v = k.valuation()

            sage: R.<x>=k[]
            sage: G = x
            sage: v.mac_lane_approximants(G)
            [Gauss valuation induced by 2-adic valuation]
            sage: v.mac_lane_approximants(G, required_precision = infinity)
            [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = +Infinity ]]

            sage: G = x^2 + 1
            sage: v.mac_lane_approximants(G)
            [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2 ]]
            sage: v.mac_lane_approximants(G, required_precision = infinity)
            [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x + 1 + O(2^10)) = 1/2, v((1 + O(2^10))*x^2 + 1 + O(2^10)) = +Infinity ]]

            sage: G = x^4 + 2*x^3 + 2*x^2 - 2*x + 2
            sage: v.mac_lane_approximants(G)
            [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4 ]]
            sage: v.mac_lane_approximants(G, required_precision=infinity)
            [[ Gauss valuation induced by 2-adic valuation, v((1 + O(2^10))*x) = 1/4, v((1 + O(2^10))*x^4 + (2 + O(2^11))*x^3 + (2 + O(2^11))*x^2 + (2 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^9 + 2^10 + O(2^11))*x + 2 + O(2^11)) = +Infinity ]]

        The factorization of primes in the Gaussian integers can be read off
        the Mac Lane approximants::

            sage: v0 = QQ.valuation(2)
            sage: R.<x> = QQ[]
            sage: G = x^2 + 1
            sage: v0.mac_lane_approximants(G)
            [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/2 ]]

            sage: v0 = QQ.valuation(3)
            sage: v0.mac_lane_approximants(G)
            [[ Gauss valuation induced by 3-adic valuation, v(x^2 + 1) = +Infinity ]]

            sage: v0 = QQ.valuation(5)
            sage: v0.mac_lane_approximants(G)
            [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ],
             [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]]
            sage: v0.mac_lane_approximants(G, required_precision = 10)
            [[ Gauss valuation induced by 5-adic valuation, v(x + 3116/237) = 10 ],
             [ Gauss valuation induced by 5-adic valuation, v(x - 3116/237) = 10 ]]

        The same example over the 5-adic numbers. In the quadratic extension
        `\QQ[x]/(x^2+1)`, 5 factors `-(x - 2)(x + 2)`, this behaviour can be
        read off the Mac Lane approximants::

            sage: k=Qp(5,4)
            sage: v = k.valuation()
            sage: R.<x>=k[]
            sage: G = x^2 + 1
            sage: v1,v2 = v.mac_lane_approximants(G); v1,v2
            ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + O(5^4)) = 1 ],
             [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + O(5^4)) = 1 ])
            sage: w1, w2 = v.mac_lane_approximants(G, required_precision = 2); w1,w2
            ([ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + O(5^4)) = 2 ],
             [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + O(5^4)) = 2 ])

        Note how the latter give a better approximation to the factors of `x^2 + 1`::

            sage: v1.phi() * v2.phi() - G
            O(5^4)*x^2 + (5 + O(5^4))*x + 5 + O(5^4)
            sage: w1.phi() * w2.phi() - G
            O(5^4)*x^2 + (5^2 + O(5^4))*x + 5^3 + O(5^4)

        In this example, the process stops with a factorization of `x^2 + 1`::

            sage: v.mac_lane_approximants(G, required_precision=infinity)
            [[ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) = +Infinity ],
             [ Gauss valuation induced by 5-adic valuation, v((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)) = +Infinity ]]

        This obviously cannot happen over the rationals where we only get an
        approximate factorization::

            sage: v = QQ.valuation(5)
            sage: R.<x>=QQ[]
            sage: G = x^2 + 1
            sage: v.mac_lane_approximants(G)
            [[ Gauss valuation induced by 5-adic valuation, v(x + 2) = 1 ],
             [ Gauss valuation induced by 5-adic valuation, v(x + 3) = 1 ]]
            sage: v.mac_lane_approximants(G, required_precision=5)
            [[ Gauss valuation induced by 5-adic valuation, v(x + 79/3) = 5 ],
             [ Gauss valuation induced by 5-adic valuation, v(x - 79/3) = 5 ]]

        Initial versions ran into problems with the trivial residue field
        extensions in this case::

            sage: K = Qp(3, 20, print_mode='digits')
            sage: R.<T> = K[]

            sage: alpha = T^3/4
            sage: G = 3^3*T^3*(alpha^4 - alpha)^2 - (4*alpha^3 - 1)^3
            sage: G = G/G.leading_coefficient()
            sage: K.valuation().mac_lane_approximants(G)
            [[ Gauss valuation induced by 3-adic valuation, v(...1*T + ...2) = 1/9, v(...1*T^9 + ...20*T^8 + ...210*T^7 + ...20*T^6 + ...20*T^5 + ...10*T^4 + ...220*T^3 + ...20*T^2 + ...110*T + ...122) = 55/27 ]]

        A similar example::

            sage: R.<x> = QQ[]
            sage: v = QQ.valuation(3)
            sage: G = (x^3 + 3)^3 - 81
            sage: v.mac_lane_approximants(G)
            [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 + 3*x + 3) = 13/9 ]]

        Another problematic case::

            sage: R.<x> = QQ[] 
            sage: Delta = x^12 + 20*x^11 + 154*x^10 + 664*x^9 + 1873*x^8 + 3808*x^7 + 5980*x^6 + 7560*x^5 + 7799*x^4 + 6508*x^3 + 4290*x^2 + 2224*x + 887 
            sage: K.<theta> = NumberField(x^6 + 108) 
            sage: K.is_galois()
            True
            sage: vK = QQ.valuation(2).extension(K)
            sage: vK(2) 
            1 
            sage: vK(theta) 
            1/3
            sage: G=Delta.change_ring(K) 
            sage: vK.mac_lane_approximants(G)
            [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 1/2*theta^4 + 3*theta + 1) = 3/2 ],
             [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 1/2*theta^4 + theta + 1) = 3/2 ],
             [ Gauss valuation induced by 2-adic valuation, v(x + 1) = 1/4, v(x^4 + 2*theta + 1) = 3/2 ]]

        An easy case that produced the wrong error at some point::

            sage: R.<x> = QQ[]
            sage: v = QQ.valuation(2)
            sage: v.mac_lane_approximants(x^2 - 1/2)
            Traceback (most recent call last):
            ...
            ValueError: G must be integral

        Some examples that Sebastian Pauli used in a talk at Sage Days 87.

        ::

            sage: R = ZpFM(3, 7, print_mode='terse')
            sage: S.<x> = R[]
            sage: v = R.valuation()
            sage: f = x^4 + 234
            sage: len(v.mac_lane_approximants(f, assume_squarefree=True)) # is_squarefree() is not properly implemented yet
            2

        ::

            sage: R = ZpFM(2, 50, print_mode='terse')
            sage: S.<x> = R[]
            sage: f = (x^32 + 16)*(x^32 + 16 + 2^16*x^2) + 2^34
            sage: v = R.valuation()
            sage: len(v.mac_lane_approximants(f, assume_squarefree=True)) # is_squarefree() is not properly implemented yet
            2

        A case that triggered an assertion at some point::

            sage: v = QQ.valuation(3)
            sage: R.<x> = QQ[]
            sage: f = x^36 + 60552000*x^33 + 268157412*x^30 + 173881701*x^27 + 266324841*x^24 + 83125683*x^21 + 111803814*x^18 + 31925826*x^15 + 205726716*x^12 +17990262*x^9 + 351459648*x^6 + 127014399*x^3 + 359254116
            sage: v.mac_lane_approximants(f)
            [[ Gauss valuation induced by 3-adic valuation, v(x) = 1/3, v(x^3 - 3) = 3/2, v(x^12 - 3*x^9 + 54*x^6 + 27/2*x^3 + 405/2) = 13/2, v(x^36 + 60552000*x^33 + 268157412*x^30 + 173881701*x^27 + 266324841*x^24 + 83125683*x^21 + 111803814*x^18 + 31925826*x^15 + 205726716*x^12 + 17990262*x^9 + 351459648*x^6 + 127014399*x^3 + 359254116) = +Infinity ]]

        """
        R = G.parent()
        if R.base_ring() is not self.domain():
            raise ValueError(
                "G must be defined over the domain of this valuation")

        from sage.misc.verbose import verbose
        verbose("Approximants of %r on %r towards %r" %
                (self, self.domain(), G),
                level=3)

        from sage.rings.valuation.gauss_valuation import GaussValuation

        if not all(self(c) >= 0 for c in G.coefficients()):
            raise ValueError("G must be integral")

        if require_maximal_degree:
            # we can only assert maximality of degrees when E and F are final
            require_final_EF = True

        if not assume_squarefree:
            if require_final_EF and not G.is_squarefree():
                raise ValueError("G must be squarefree")
            else:
                # if only required_precision is set, we do not need to check
                # whether G is squarefree. If G is not squarefree, we compute
                # valuations corresponding to approximants for all the
                # squarefree factors of G (up to required_precision.)
                pass

        def is_sufficient(leaf, others):
            if leaf.valuation.mu() < required_precision:
                return False
            if require_final_EF and not leaf.ef:
                return False
            if require_maximal_degree and leaf.valuation.phi().degree(
            ) != leaf.valuation.E() * leaf.valuation.F():
                return False
            if require_incomparability:
                if any(leaf.valuation <= o.valuation for o in others):
                    return False
            return True

        seed = MacLaneApproximantNode(GaussValuation(R, self), None,
                                      G.degree() == 1, G.degree(), None, None)
        seed.forced_leaf = is_sufficient(seed, [])

        def create_children(node):
            new_leafs = []
            if node.forced_leaf:
                return new_leafs
            augmentations = node.valuation.mac_lane_step(
                G,
                report_degree_bounds_and_caches=True,
                coefficients=node.coefficients,
                valuations=node.valuations,
                check=False,
                # We do not want to see augmentations that are
                # already part of other branches of the tree of
                # valuations for obvious performance reasons and
                # also because the principal_part_bound would be
                # incorrect for these.
                allow_equivalent_key=node.valuation.is_gauss_valuation(),
                # The length of an edge in the Newton polygon in
                # one MacLane step bounds the length of the
                # principal part (i.e., the part with negative
                # slopes) of the Newton polygons in the next
                # MacLane step. Therefore, mac_lane_step does not
                # need to compute valuations for coefficients
                # beyond that bound as they do not contribute any
                # augmentations.
                principal_part_bound=node.principal_part_bound)
            for w, bound, principal_part_bound, coefficients, valuations in augmentations:
                ef = bound == w.E() * w.F()
                new_leafs.append(
                    MacLaneApproximantNode(w, node, ef, principal_part_bound,
                                           coefficients, valuations))
            for leaf in new_leafs:
                if is_sufficient(leaf,
                                 [l for l in new_leafs if l is not leaf]):
                    leaf.forced_leaf = True
            return new_leafs

        def reduce_tree(v, w):
            return v + w

        from sage.all import RecursivelyEnumeratedSet
        tree = RecursivelyEnumeratedSet([seed],
                                        successors=create_children,
                                        structure='forest',
                                        enumeration='breadth')
        # this is a tad faster but annoying for profiling / debugging
        if algorithm == 'parallel':
            nodes = tree.map_reduce(map_function=lambda x: [x], reduce_init=[])
        elif algorithm == 'serial':
            from sage.parallel.map_reduce import RESetMapReduce
            nodes = RESetMapReduce(forest=tree,
                                   map_function=lambda x: [x],
                                   reduce_init=[]).run_serial()
        else:
            raise NotImplementedError(algorithm)
        leafs = set([node.valuation for node in nodes])
        for node in nodes:
            if node.parent is None:
                continue
            v = node.parent.valuation
            if v in leafs:
                leafs.remove(v)

        # The order of the leafs is not predictable in parallel mode and in
        # serial mode it depends on the hash functions and so on the underlying
        # architecture (32/64 bit). There is no natural ordering on these
        # valuations but it is very convenient for doctesting to return them in
        # some stable order, so we just order them by their string
        # representation which should be very fast.
        try:
            ret = sorted(leafs, key=str)
        except Exception:
            # if for some reason the valuation can not be printed, we leave them unsorted
            ret = list(leafs)

        return ret
    def _find_scaling_period(self):
        r"""
        Uses the integral period map of the modular symbol implementation in sage
        in order to determine the scaling. The resulting modular symbol is correct
        only for the `X_0`-optimal curve, at least up to a possible factor +- a
        power of 2.

        EXAMPLES::

            sage: E = EllipticCurve('11a1')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (1/5, 1/2)
            sage: E = EllipticCurve('11a2')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (1, 5/2)
            sage: E = EllipticCurve('121b2')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (0, 0, 0, 11/2, 11/2, 11/2, 11/2, -3, 3/2, 1/2, -1, 2)

        TESTS::

            sage: E = EllipticCurve('19a1')
            sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none')
            sage: m._find_scaling_period()
            sage: m._scaling
            1

            sage: E = EllipticCurve('19a2')
            sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none')
            sage: m._scaling
            1
            sage: m._find_scaling_period()
            sage: m._scaling
            3
        """
        P = self._modsym.integral_period_mapping()
        self._e = P.matrix().transpose().row(0)
        self._e /= 2
        E = self._E
        try:
            crla = parse_cremona_label(E.label())
        except RuntimeError:  # raised when curve is outside of the table
            print(
                "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number."
            )
            self._scaling = 1
        else:
            cr0 = Integer(crla[0]).str() + crla[1] + '1'
            E0 = EllipticCurve(cr0)
            if self._sign == 1:
                q = E0.period_lattice().basis()[0] / E.period_lattice().basis(
                )[0]
            else:
                q = E0.period_lattice().basis()[1].imag() / E.period_lattice(
                ).basis()[1].imag()
                if E0.real_components() == 1:
                    q *= 2
                if E.real_components() == 1:
                    q /= 2
            q = QQ((q * 200).round()) / 200
            verbose('scale modular symbols by %s' % q)
            self._scaling = q
        c = self(0)  #  required, to change base point from oo to 0
        if c < 0:
            c *= -1
            self._scaling *= -1
        self._at_zero = c
        self._e *= self._scaling
Esempio n. 15
0
    def cuspidal_submodule_q_expansion_basis(self, weight, prec=None):
        r"""
        Calculate a basis of `q`-expansions for the space of cusp forms of
        weight ``weight`` for this group.

        INPUT:

        - ``weight`` (integer) -- the weight
        - ``prec`` (integer or None) -- precision of `q`-expansions to return

        ALGORITHM: Uses the method :meth:`cuspidal_ideal_generators` to
        calculate generators of the ideal of cusp forms inside this ring. Then
        multiply these up to weight ``weight`` using the generators of the
        whole modular form space returned by :meth:`q_expansion_basis`.

        EXAMPLES::

            sage: R = ModularFormsRing(Gamma0(3))
            sage: R.cuspidal_submodule_q_expansion_basis(20)
            [q - 8532*q^6 - 88442*q^7 + O(q^8), q^2 + 207*q^6 + 24516*q^7 + O(q^8), q^3 + 456*q^6 + O(q^8), q^4 - 135*q^6 - 926*q^7 + O(q^8), q^5 + 18*q^6 + 135*q^7 + O(q^8)]

        We compute a basis of a space of very large weight, quickly (using this
        module) and slowly (using modular symbols), and verify that the answers
        are the same. ::

            sage: A = R.cuspidal_submodule_q_expansion_basis(80, prec=30)  # long time (1s on sage.math, 2013)
            sage: B = R.modular_forms_of_weight(80).cuspidal_submodule().q_expansion_basis(prec=30)  # long time (19s on sage.math, 2013)
            sage: A == B # long time
            True
        """
        d = self.modular_forms_of_weight(
            weight).cuspidal_submodule().dimension()
        if d == 0:
            return []

        minprec = self.modular_forms_of_weight(weight).sturm_bound()
        if prec is None:
            prec = working_prec = minprec
        else:
            working_prec = max(prec, minprec)

        gen_weight = min(6, weight)

        while True:
            verbose(
                "Trying to generate the %s-dimensional cuspidal submodule at weight %s using generators of weight up to %s"
                % (d, weight, gen_weight))
            G = self.cuspidal_ideal_generators(maxweight=gen_weight,
                                               prec=working_prec)

            flist = []
            for (j, f, F) in G:
                for g in self.q_expansion_basis(weight - j, prec=working_prec):
                    flist.append(g * f)

            A = self.base_ring()**working_prec
            W = A.span([A(f.padded_list(working_prec)) for f in flist])
            if W.rank() == d and (self.base_ring().is_field()
                                  or W.index_in_saturation() == 1):
                break
            else:
                gen_weight += 1
                verbose(
                    "Need more generators: trying again with generators of weight up to %s"
                    % gen_weight)

        R = G[0][1].parent()
        return [R(list(x), prec=prec) for x in W.gens()]
Esempio n. 16
0
def hnf(A, include_zero_rows=True, proof=True):
    """
    Return the Hermite Normal Form of a general integer matrix A,
    along with the pivot columns.

    INPUT:

    - A -- an n x m matrix A over the integers.
    - include_zero_rows -- bool (default: True) whether or not to include zero
      rows in the output matrix
    - proof -- whether or not to prove the result correct.

    OUTPUT:

    - matrix -- the Hermite normal form of A
    - pivots -- the pivot column positions of A

    EXAMPLES::

        sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
        sage: a = matrix(ZZ,3,5,[-2, -6, -3, -17, -1, 2, -1, -1, -2, -1, -2, -2, -6, 9, 2])
        sage: matrix_integer_dense_hnf.hnf(a)
        (
        [   2    0   26  -75  -10]
        [   0    1   27  -73   -9]
        [   0    0   37 -106  -13], [0, 1, 2]
        )
        sage: matrix_integer_dense_hnf.hnf(a.transpose())
        (
        [1 0 0]
        [0 1 0]
        [0 0 1]
        [0 0 0]
        [0 0 0], [0, 1, 2]
        )
        sage: matrix_integer_dense_hnf.hnf(a.transpose(), include_zero_rows=False)
        (
        [1 0 0]
        [0 1 0]
        [0 0 1], [0, 1, 2]
        )
    """
    if A.nrows() <= 1:
        np = A.nonzero_positions()
        if not np:
            pivots = []
            if not include_zero_rows:
                A = A.new_matrix(0)  # 0 rows
        else:
            i, j = np[0]
            if A[i, j] < 0:
                A = -A
            pivots = [j]
        return A, pivots

    if not proof:
        H, pivots = probable_hnf(A,
                                 include_zero_rows=include_zero_rows,
                                 proof=False)
        if not include_zero_rows and len(pivots) > H.nrows():
            return H.matrix_from_rows(range(len(pivots))), pivots

    while True:
        H, pivots = probable_hnf(A,
                                 include_zero_rows=include_zero_rows,
                                 proof=True)
        if is_in_hnf_form(H, pivots):
            if not include_zero_rows and len(pivots) > H.nrows():
                H = H.matrix_from_rows(range(len(pivots)))
            return H, pivots
        verbose(
            "After attempt the return matrix is not in HNF form since pivots must have been wrong.  We try again."
        )
Esempio n. 17
0
def _span_of_forms_in_weight(forms,
                             weight,
                             prec,
                             stop_dim=None,
                             use_random=False):
    r"""
    Utility function. Given a nonempty list of pairs ``(k,f)``, where `k` is an
    integer and `f` is a power series, and a weight l, return all weight l
    forms obtained by multiplying together the given forms.

    INPUT:

    - ``forms`` -- list of pairs `(k, f)` with k an integer and f a power
      series (all over the same base ring)
    - ``weight`` -- an integer
    - ``prec`` -- an integer (less than or equal to the precision of all the
      forms in ``forms``) -- precision to use in power series computations.
    - ``stop_dim`` -- an integer: stop as soon as we have enough forms to span
      a submodule of this rank (a saturated one if the base ring is `\ZZ`).
      Ignored if ``use_random`` is False.
    - ``use_random`` -- which algorithm to use. If True, tries random products
      of the generators of the appropriate weight until a large enough
      submodule is found (determined by ``stop_dim``). If False, just tries
      everything.

    Note that if the given forms do generate the whole space, then
    ``use_random=True`` will often be quicker (particularly if the weight is
    large); but if the forms don't generate, the randomized algorithm is no
    help and will actually be substantially slower, because it needs to do
    repeated echelon form calls to check if vectors are in a submodule, while
    the non-randomized algorithm just echelonizes one enormous matrix at the
    end.

    EXAMPLES::

        sage: import sage.modular.modform.ring as f
        sage: forms = [(4, 240*eisenstein_series_qexp(4,5)), (6,504*eisenstein_series_qexp(6,5))]
        sage: f._span_of_forms_in_weight(forms, 12, prec=5)
        Vector space of degree 5 and dimension 2 over Rational Field
        Basis matrix:
        [        1         0    196560  16773120 398034000]
        [        0         1       -24       252     -1472]
        sage: f._span_of_forms_in_weight(forms, 24, prec=5)
        Vector space of degree 5 and dimension 3 over Rational Field
        Basis matrix:
        [          1           0           0    52416000 39007332000]
        [          0           1           0      195660    12080128]
        [          0           0           1         -48        1080]
        sage: ModularForms(1, 24).q_echelon_basis(prec=5)
        [
        1 + 52416000*q^3 + 39007332000*q^4 + O(q^5),
        q + 195660*q^3 + 12080128*q^4 + O(q^5),
        q^2 - 48*q^3 + 1080*q^4 + O(q^5)
        ]

    Test the alternative randomized algorithm::

        sage: f._span_of_forms_in_weight(forms, 24, prec=5, use_random=True, stop_dim=3)
        Vector space of degree 5 and dimension 3 over Rational Field
        Basis matrix:
        [          1           0           0    52416000 39007332000]
        [          0           1           0      195660    12080128]
        [          0           0           1         -48        1080]
    """
    t = verbose('multiplying forms up to weight %s' % weight)
    # Algorithm: run through the monomials of the appropriate weight, and build
    # up the vector space they span.

    n = len(forms)
    R = forms[0][1].base_ring()
    V = R**prec
    W = V.zero_submodule()
    shortforms = [f[1].truncate_powerseries(prec) for f in forms]

    # List of weights
    from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
    wts = list(WeightedIntegerVectors(weight, [f[0] for f in forms]))
    t = verbose("calculated weight list", t)
    N = len(wts)

    if use_random:
        if stop_dim is None:
            raise ValueError("stop_dim must be provided if use_random is True")
        shuffle(wts)

        for c in range(N):
            w = V(
                prod(shortforms[i]**wts[c][i]
                     for i in range(n)).padded_list(prec))
            if w in W:
                continue
            W = V.span(list(W.gens()) + [w])
            if stop_dim and W.rank() == stop_dim:
                if R != ZZ or W.index_in_saturation() == 1:
                    verbose("Succeeded after %s of %s" % (c, N), t)
                    return W
        verbose("Nothing worked", t)
        return W
    else:
        G = [
            V(prod(forms[i][1]**c[i] for i in range(n)).padded_list(prec))
            for c in wts
        ]
        t = verbose('found %s candidates' % N, t)
        W = V.span(G)
        verbose('span has dimension %s' % W.rank(), t)
        return W
Esempio n. 18
0
def solve_system_with_difficult_last_row(B, a):
    """
    Solve B*x = a when the last row of `B` contains huge entries using
    a clever trick that reduces the problem to solve C*x = a where `C`
    is `B` but with the last row replaced by something small, along
    with one easy null space computation.  The latter are both solved
    `p`-adically.

    INPUT:

    - B -- a square n x n nonsingular matrix with painful big bottom row.
    - a -- an n x 1 column matrix

    OUTPUT:

    - the unique solution to B*x = a.

    EXAMPLES::

        sage: from sage.matrix.matrix_integer_dense_hnf import solve_system_with_difficult_last_row
        sage: B = matrix(ZZ, 3, [1,2,4, 3,-4,7, 939082,2930982,132902384098234])
        sage: a = matrix(ZZ,3,1, [1,2,5])
        sage: z = solve_system_with_difficult_last_row(B, a)
        sage: z
        [ 106321906985474/132902379815497]
        [132902385037291/1329023798154970]
        [        -5221794/664511899077485]
        sage: B*z
        [1]
        [2]
        [5]
    """
    # Here's how:
    # 1. We make a copy of B but with the last *nasty* row of B replaced
    #    by a random very nice row.
    C = copy(B)
    while True:
        C[C.nrows() - 1] = random_matrix(ZZ, 1, C.ncols()).row(0)
        # 2. Then we find the unique solution to C * x = a
        try:
            x = C.solve_right(a)
        except ValueError:
            verbose("Try difficult solve again with different random vector")
        else:
            break

    # 3. We next delete the last row of B and find a basis vector k
    #    for the 1-dimensional kernel.
    D = B.matrix_from_rows(range(C.nrows() - 1))
    N = D._rational_kernel_iml()
    if N.ncols() != 1:
        verbose("Try difficult solve again with different random vector")
        return solve_system_with_difficult_last_row(B, a)

    k = N.matrix_from_columns([0])

    # 4. The sought for solution z to B*z = a is some linear combination
    #
    #       z = x + alpha*k
    #
    #  of x and k, where k is the above fixed basis for the kernel of D.
    #  Setting w to be the last row of B, this column vector z satisfies
    #
    #       w * z = a'
    #
    # where a' is the last entry of a.  Thus
    #
    #       w * (x + alpha*k) = a'
    #
    # so    w * x + alpha*w*k = a'
    # so    alpha*w*k  = a' - w*x.

    w = B[-1]  # last row of B
    a_prime = a[-1]
    lhs = w * k
    rhs = a_prime - w * x

    if lhs[0] == 0:
        verbose("Try difficult solve again with different random vector")
        return solve_system_with_difficult_last_row(B, a)

    alpha = rhs[0] / lhs[0]
    z = x + alpha * k
    return z
Esempio n. 19
0
    def q_expansion_basis(self, weight, prec=None, use_random=True):
        r"""
        Calculate a basis of q-expansions for the space of modular forms of the
        given weight for this group, calculated using the ring generators given
        by ``find_generators``.

        INPUT:

        - ``weight`` (integer) -- the weight
        - ``prec`` (integer or ``None``, default: ``None``) -- power series
          precision. If ``None``, the precision defaults to the Sturm bound for
          the requested level and weight.
        - ``use_random`` (boolean, default: True) -- whether or not to use a
          randomized algorithm when building up the space of forms at the given
          weight from known generators of small weight.

        EXAMPLES::

            sage: m = ModularFormsRing(Gamma0(4))
            sage: m.q_expansion_basis(2,10)
            [1 + 24*q^2 + 24*q^4 + 96*q^6 + 24*q^8 + O(q^10),
            q + 4*q^3 + 6*q^5 + 8*q^7 + 13*q^9 + O(q^10)]
            sage: m.q_expansion_basis(3,10)
            []

            sage: X = ModularFormsRing(SL2Z)
            sage: X.q_expansion_basis(12, 10)
            [1 + 196560*q^2 + 16773120*q^3 + 398034000*q^4 + 4629381120*q^5 + 34417656000*q^6 + 187489935360*q^7 + 814879774800*q^8 + 2975551488000*q^9 + O(q^10),
            q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 - 16744*q^7 + 84480*q^8 - 113643*q^9 + O(q^10)]

        We calculate a basis of a massive modular forms space, in two ways.
        Using this module is about twice as fast as Sage's generic code. ::

            sage: A = ModularFormsRing(11).q_expansion_basis(30, prec=40) # long time (5s)
            sage: B = ModularForms(Gamma0(11), 30).q_echelon_basis(prec=40) # long time (9s)
            sage: A == B # long time
            True

        Check that absurdly small values of ``prec`` don't mess things up::

            sage: ModularFormsRing(11).q_expansion_basis(10, prec=5)
            [1 + O(q^5), q + O(q^5), q^2 + O(q^5), q^3 + O(q^5), q^4 + O(q^5), O(q^5), O(q^5), O(q^5), O(q^5), O(q^5)]
        """
        d = self.modular_forms_of_weight(weight).dimension()
        if d == 0:
            return []

        if prec is None:
            prec = self.modular_forms_of_weight(weight).sturm_bound()

        working_prec = max(prec,
                           self.modular_forms_of_weight(weight).sturm_bound())

        gen_weight = min(6, weight)

        while True:
            verbose(
                "Trying to generate the %s-dimensional space at weight %s using generators of weight up to %s"
                % (d, weight, gen_weight))
            G = self.generators(maxweight=gen_weight, prec=working_prec)
            V = _span_of_forms_in_weight(G,
                                         weight,
                                         prec=working_prec,
                                         use_random=use_random,
                                         stop_dim=d)
            if V.rank() == d and (self.base_ring().is_field()
                                  or V.index_in_saturation() == 1):
                break
            else:
                gen_weight += 1
                verbose(
                    "Need more generators: trying again with generators of weight up to %s"
                    % gen_weight)

        R = G[0][1].parent()
        return [R(list(x), prec=prec) for x in V.gens()]
Esempio n. 20
0
def hnf_square(A, proof):
    """
    INPUT:

    - a nonsingular n x n matrix A over the integers.

    OUTPUT:

    - the Hermite normal form of A.

    EXAMPLES::

        sage: import sage.matrix.matrix_integer_dense_hnf as hnf
        sage: A = matrix(ZZ, 3, [-21, -7, 5, 1,20,-7, -1,1,-1])
        sage: hnf.hnf_square(A, False)
        [ 1  6 29]
        [ 0  7 28]
        [ 0  0 46]
        sage: A.echelon_form()
        [ 1  6 29]
        [ 0  7 28]
        [ 0  0 46]
    """
    n = A.nrows()
    m = A.ncols()
    if n != m:
        raise ValueError("A must be square.")

    # Small cases -- do not use this algorithm
    if n <= 3:
        return A.echelon_form(algorithm="pari")

    if A.rank() < A.nrows():
        raise ValueError("matrix must have full rank")

    t = verbose("starting slicings")
    B = A.matrix_from_rows(range(m - 2)).matrix_from_columns(range(n - 1))
    c = A.matrix_from_rows([m - 2]).matrix_from_columns(range(n - 1))
    d = A.matrix_from_rows([m - 1]).matrix_from_columns(range(n - 1))
    b = A.matrix_from_columns([n - 1]).matrix_from_rows(range(m - 2))
    verbose("done slicing", t)

    try:
        d1, d2 = double_det(B, c, d, proof=proof)
    except (ValueError, ZeroDivisionError):
        d1 = B.stack(c).det(proof=proof)
        d2 = B.stack(d).det(proof=proof)

    g, k, l = d1._xgcd(d2, minimal=True)

    W = B.stack(k * c + l * d)
    verbose("submatrix det: g=%s" % g)
    CUTOFF = 2147483647  # 2^31-1
    if g == 0:
        # Big trouble -- matrix is not invertible
        # Since we have no good conditioning code at present,
        # in this case we just fall back to using pari.
        H = W.echelon_form(algorithm='pari')
    elif 2 * g > CUTOFF:
        # Unlikely that g will be large on even slightly random input
        # if it is, we fallback to the traditional algorithm.
        # A nasty example is A = n*random_matrix(ZZ,m), where
        # this algorithm gets killed.  This is not random input though.
        f = W.gcd()
        g = g / (f**W.nrows())
        if 2 * g <= CUTOFF:
            verbose(
                "Found common factor of %s -- dividing out; get new g = %s" %
                (f, g))
            W0 = (W / f).change_ring(ZZ)
            H = W0._hnf_mod(2 * g)
            H *= f
        else:
            verbose(
                "Falling back to PARI HNF since input matrix is ill conditioned for p-adic hnf algorithm."
            )
            # We need more clever preconditioning?
            # It is important to *not* just do the submatrix, since
            # the whole rest of the algorithm will likely be very slow in
            # weird cases where the det is large.
            # E.g., matrix all of whose rows but 1 are multiplied by some
            # fixed scalar n.
            raise NotImplementedError("fallback to PARI!")
            # H = W.hermite_form(algorithm='pari')
    else:
        H = W._hnf_mod(2 * g)

    x = add_column(
        W, H, b.stack(matrix(1, 1,
                             [k * A[m - 2, m - 1] + l * A[m - 1, m - 1]])),
        proof)
    Hprime = H.augment(x)
    pivots = pivots_of_hnf_matrix(Hprime)

    Hprime, pivots = add_row(Hprime,
                             A.matrix_from_rows([m - 2]),
                             pivots,
                             include_zero_rows=False)
    Hprime, pivots = add_row(Hprime,
                             A.matrix_from_rows([m - 1]),
                             pivots,
                             include_zero_rows=False)
    return Hprime.matrix_from_rows(range(m))
def siegel_product(self, u):
    """
    Computes the infinite product of local densities of the quadratic
    form for the number `u`.

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.theta_series(11)
        1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11)

        sage: Q.siegel_product(1)
        8
        sage: Q.siegel_product(2)      ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =(
        24
        sage: Q.siegel_product(3)
        32
        sage: Q.siegel_product(5)
        48
        sage: Q.siegel_product(6)
        96
        sage: Q.siegel_product(7)
        64
        sage: Q.siegel_product(9)
        104

        sage: Q.local_density(2,1)
        1
        sage: M = 4; len([v  for v in mrange([M,M,M,M])  if Q(v) % M == 1]) / M^3
        1
        sage: M = 16; len([v  for v in mrange([M,M,M,M])  if Q(v) % M == 1]) / M^3  # long time (2s on sage.math, 2014)
        1

        sage: Q.local_density(2,2)
        3/2
        sage: M = 4; len([v  for v in mrange([M,M,M,M])  if Q(v) % M == 2]) / M^3
        3/2
        sage: M = 16; len([v  for v in mrange([M,M,M,M])  if Q(v) % M == 2]) / M^3  # long time (2s on sage.math, 2014)
        3/2

    TESTS::

        sage: [1] + [Q.siegel_product(ZZ(a))  for a in range(1,11)] == Q.theta_series(11).list()  # long time (2s on sage.math, 2014)
        True
    """
    ## Protect u (since it fails often if it's an just an int!)
    u = ZZ(u)

    n = self.dim()
    d = self.det(
    )  ## ??? Warning: This is a factor of 2^n larger than it should be!

    # DIAGNOSTIC
    verbose("n = " + str(n))
    verbose("d = " + str(d))
    verbose("In siegel_product:  d = " + str(d) + "\n")

    ## Product of "bad" places to omit
    S = 2 * d * u

    ## DIAGNOSTIC
    verbose("siegel_product Break 1. \n")
    verbose(" u = " + str(u) + "\n")

    ## Make the odd generic factors
    if ((n % 2) == 1):
        m = (n - 1) // 2
        d1 = fundamental_discriminant(
            ((-1)**m) * 2 * d *
            u)  ## Replaced d by 2d here to compensate for the determinant
        f = abs(
            d1)  ## gaining an odd power of 2 by using the matrix of 2Q instead
        ## of the matrix of Q.
        ##  --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u);

        ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1)
        factor1 = 1
        for i in range(1, m + 1):
            factor1 *= 2 * i - 1
        for i in range(m + 1, 2 * m + 1):
            factor1 *= i

        genericfactor = factor1 * ((u / f) ** m) \
            * QQ(sqrt((2 ** n) *  f) / (u * d)) \
            * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m))

    ## DIAGNOSTIC
    verbose("siegel_product Break 2. \n")

    ## Make the even generic factor
    if ((n % 2) == 0):
        m = n // 2
        d1 = fundamental_discriminant(((-1)**m) * d)
        f = abs(d1)

        ## DIAGNOSTIC
        #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl;
        #cout << " f = " << f << " and d1 = " << d1 << endl;


        genericfactor = m / QQ(sqrt(f*d)) \
            * ((u/2) ** (m-1)) * (f ** m) \
            / abs(QuadraticBernoulliNumber(m, d1)) \
            * (2 ** m)                                               ## This last factor compensates for using the matrix of 2*Q

    ##return genericfactor

    ## Omit the generic factors in S and compute them separately
    omit = 1
    include = 1

    S_divisors = prime_divisors(S)

    ## DIAGNOSTIC
    #cout << "\n S is " << S << endl;
    #cout << " The Prime divisors of S are :";
    #PrintV(S_divisors);

    for p in S_divisors:
        Q_normal = self.local_normal_form(p)

        ## DIAGNOSTIC
        verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" +
                str(d1) + "/" + str(p) + ") is " +
                str(kronecker_symbol(d1, p)) + "\n")

        omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m)))

        ## DIAGNOSTIC
        verbose(" omit = " + str(omit) + "\n")
        verbose(" Q_normal is \n" + str(Q_normal) + "\n")
        verbose(" Q_normal = \n" + str(Q_normal))
        verbose(" p = " + str(p) + "\n")
        verbose(" u = " + str(u) + "\n")
        verbose(" include = " + str(include) + "\n")

        include *= Q_normal.local_density(p, u)

        ## DIAGNOSTIC
        #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl;

        ## DIAGNOSTIC
        verbose("    ---  Exiting loop \n")

    #// ****************  Important *******************
    #// Additional fix (only included for n=4) to deal
    #// with the power of 2 introduced at the real place
    #// by working with Q instead of 2*Q.  This needs to
    #// be done for all other n as well...
    #/*
    #if (n==4)
    #  genericfactor = 4 * genericfactor;
    #*/

    ## DIAGNOSTIC
    #cout << endl;
    #cout << " generic factor = " << genericfactor << endl;
    #cout << " omit = " << omit << endl;
    #cout << " include = " << include << endl;
    #cout << endl;

    ## DIAGNOSTIC
    #//  cout << "siegel_product Break 3. " << endl;

    ## Return the final factor (and divide by 2 if n=2)
    if n == 2:
        return genericfactor * omit * include / 2
    else:
        return genericfactor * omit * include
Esempio n. 22
0
def extract_ones_data(H, pivots):
    """
    Compute ones data and corresponding submatrices of H.

    This is used to optimized the :func:`add_row` function.

    INPUT:

    - H -- a matrix in HNF
    - pivots -- list of all pivot column positions of H

    OUTPUT:

    C, D, E, onecol, onerow, non_onecol, non_onerow
    where onecol, onerow, non_onecol, non_onerow are as for
    the ones function, and C, D, E are matrices:

    - C -- submatrix of all non-onecol columns and onecol rows
    - D -- all non-onecol columns and other rows
    - E -- inverse of D

    If D is not invertible or there are 0 or more than 2 non onecols,
    then C, D, and E are set to None.

    EXAMPLES::

        sage: H = matrix(ZZ, 3, 4, [1, 0, 0, 7, 0, 1, 5, 2, 0, 0, 6, 6])
        sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
        sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,2])
        (
        [0]
        [5], [6], [1/6], [0, 1], [0, 1], [2], [2]
        )

    Here we get None's since the (2,2) position submatrix is not invertible.
        sage: H = matrix(ZZ, 3, 5, [1, 0, 0, 45, -36, 0, 1, 0, 131, -107, 0, 0, 0, 178, -145]); H
        [   1    0    0   45  -36]
        [   0    1    0  131 -107]
        [   0    0    0  178 -145]
        sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
        sage: matrix_integer_dense_hnf.extract_ones_data(H, [0,1,3])
        (None, None, None, [0, 1], [0, 1], [2], [2])
    """
    onecol, onerow, non_onecol, non_onerow = ones(H, pivots)
    verbose('extract_ones -- got submatrix of size %s' % len(non_onecol))
    if len(non_onecol) in [1, 2]:
        # Extract submatrix of all non-onecol columns and onecol rows
        C = H.matrix_from_rows_and_columns(onerow, non_onecol)
        # Extract submatrix of all non-onecol columns and other rows
        D = H.matrix_from_rows_and_columns(non_onerow, non_onecol).transpose()
        tt = verbose("extract ones -- INVERT %s x %s" %
                     (len(non_onerow), len(non_onecol)),
                     level=1)
        try:
            E = D**(-1)
        except ZeroDivisionError:
            C = D = E = None
        verbose("done inverting", tt, level=1)
        return C, D, E, onecol, onerow, non_onecol, non_onerow
    else:
        return None, None, None, onecol, onerow, non_onecol, non_onerow
Esempio n. 23
0
    def hom(self, im_gens, codomain=None, check=True, base_map=None):
        r"""
        This version keeps track with the special recursive structure
        of :class:`SplittingAlgebra`

        Type ``Ring.hom?`` to see the general documentation of this method.
        Here you see just special examples for the current class.

        EXAMPLES::

            sage: from sage.algebras.splitting_algebra import SplittingAlgebra
            sage: L.<u, v, w> = LaurentPolynomialRing(ZZ); x = polygen(L)
            sage: S = SplittingAlgebra(x^3 - u*x^2 + v*x - w, ('X', 'Y'))
            sage: P.<x, y, z> = PolynomialRing(ZZ)
            sage: F = FractionField(P)
            sage: im_gens = [F(g) for g in [y, x, x + y + z, x*y+x*z+y*z, x*y*z]]
            sage: f = S.hom(im_gens)
            sage: f(u), f(v), f(w)
            (x + y + z, x*y + x*z + y*z, x*y*z)
            sage: roots = S.splitting_roots(); roots
            [X, Y, -Y - X + u]
            sage: [f(r) for r in roots]
            [x, y, z]
        """
        base_ring = self.base_ring()

        if not isinstance(im_gens, (list, tuple)):
            im_gens = [im_gens]

        all_gens = self.gens_dict_recursive()
        if len(im_gens) != len(all_gens):
            return super(SplittingAlgebra, self).hom(im_gens,
                                                     codomain=codomain,
                                                     check=check,
                                                     base_map=base_map)

        num_gens = len(self.gens())
        im_gens_start = [
            img for img in im_gens if im_gens.index(img) < num_gens
        ]
        im_gens_end = [
            img for img in im_gens if im_gens.index(img) >= num_gens
        ]

        if not im_gens_end:
            return super(SplittingAlgebra, self).hom(im_gens,
                                                     codomain=codomain,
                                                     check=check,
                                                     base_map=base_map)

        verbose('base %s im_gens_end %s codomain %s check %s base_map %s' %
                (base_ring, im_gens_end, codomain, check, base_map))
        hom_on_base_recurs = base_ring.hom(im_gens_end,
                                           codomain=codomain,
                                           check=check,
                                           base_map=base_map)
        verbose('hom_on_base_recurs %s' % (hom_on_base_recurs))

        cover_ring = self.cover_ring()
        hom_from_cover = cover_ring.hom(im_gens_start,
                                        codomain=codomain,
                                        check=check,
                                        base_map=hom_on_base_recurs)
        lift = self.lifting_map()
        return hom_from_cover * lift
Esempio n. 24
0
def probable_hnf(A, include_zero_rows, proof):
    """
    Return the HNF of A or raise an exception if something involving
    the randomized nature of the algorithm goes wrong along the way.

    Calling this function again a few times should result it in it
    working, at least if proof=True.

    INPUT:

    - A -- a matrix
    - include_zero_rows -- bool
    - proof -- bool

    OUTPUT:

    the Hermite normal form of A.
    cols -- pivot columns

    EXAMPLES::

        sage: a = matrix(ZZ,4,3,[-1, -1, -1, -20, 4, 1, -1, 1, 2,1,2,3])
        sage: import sage.matrix.matrix_integer_dense_hnf as matrix_integer_dense_hnf
        sage: matrix_integer_dense_hnf.probable_hnf(a, True, True)
        (
        [1 0 0]
        [0 1 0]
        [0 0 1]
        [0 0 0], [0, 1, 2]
        )
        sage: matrix_integer_dense_hnf.probable_hnf(a, False, True)
        (
        [1 0 0]
        [0 1 0]
        [0 0 1], [0, 1, 2]
        )
        sage: matrix_integer_dense_hnf.probable_hnf(a, False, False)
        (
        [1 0 0]
        [0 1 0]
        [0 0 1], [0, 1, 2]
        )
    """
    # Find left-most full rank submatrix by working modulo a prime
    rows = list(probable_pivot_rows(A))
    B = A.matrix_from_rows(rows)
    cols = list(probable_pivot_columns(B))
    C = B.matrix_from_columns(cols)
    # Now C is a submatrix of A that has full rank and is square.

    # We compute the HNF of C, which is a square nonsingular matrix.
    try:
        H = hnf_square(C, proof=proof)
    except NotImplementedError:
        # raise
        # this signals that we must fallback to PARI
        verbose(
            "generic random modular HNF algorithm failed -- we fall back to PARI"
        )
        H = A.hermite_form(algorithm='pari',
                           include_zero_rows=include_zero_rows,
                           proof=proof)
        return H, H.pivots()

    # The transformation matrix to HNF is the unique
    # matrix U such that U * C = H, i.e., U = H*C^(-1).

    if len(cols) < B.ncols():
        # We compute the HNF of B by multiplying the matrix D
        # got from the columns not in C by U:
        # We want to compute X = U*D.  But U = H*C^(-1),
        # so X = U*D = H*C^(-1)*D.
        # So C*H^(-1)*X = D

        # find y s.t C*y = D
        #   H^(-1)*X = y ===> X = H*y
        #
        cols_set = set(cols)
        cols2 = [i for i in range(B.ncols()) if i not in cols_set]
        D = B.matrix_from_columns(cols2)
        Y = C.solve_right(D)
        H2 = H * Y
        H2 = H2.change_ring(ZZ)

        # The HNF of B is got by assembling together
        # the matrices H and H2.
        H = interleave_matrices(H, H2, cols, cols2)

    pivots = pivots_of_hnf_matrix(H)

    # Now H is the HNF of the matrix B.
    # Finally we add all remaining rows of A to H using
    # the add_row function.

    C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(
        H, cols)
    if not proof and len(non_onecol) == 0:
        # Identity matrix -- done
        verbose("hnf -- got identity matrix -- early abort (0)")
        if include_zero_rows:
            H = pad_zeros(H, A.nrows())
        return H, pivots

    rows_set = set(rows)
    for i in range(A.nrows()):
        if i not in rows_set:
            v = A.matrix_from_rows([i])
            if v == 0:
                continue
            if E is None:
                H, pivots = add_row(H, v, pivots, include_zero_rows=False)
                C, D, E, onecol, onerow, non_onecol, non_onerow = extract_ones_data(
                    H, pivots)
                if not proof and len(non_onecol) == 0:
                    # Identity matrix -- done
                    verbose("hnf -- got identity matrix -- early abort (1)")
                    if include_zero_rows:
                        H = pad_zeros(H, A.nrows())
                    return H, pivots
            else:
                z = A.matrix_from_rows_and_columns([i], non_onecol)
                w = A.matrix_from_rows_and_columns([i], onecol)
                tt = verbose("checking denom (%s x %s)" %
                             (D.nrows(), D.ncols()))
                Y = (z - w * C).transpose()
                k = E * Y
                verbose("done checking denom", tt)
                if k.denominator() != 1:
                    H, pivots = add_row(H, v, pivots, include_zero_rows=False)
                    D = H.matrix_from_rows_and_columns(non_onerow,
                                                       non_onecol).transpose()
                nn = ones(H, pivots)
                if not proof and len(nn[2]) == 0:
                    verbose("hnf -- got identity matrix -- early abort (2)")
                    if include_zero_rows:
                        H = pad_zeros(H, A.nrows())
                    return H, pivots

    if include_zero_rows:
        H = pad_zeros(H, A.nrows())
    return H, pivots
Esempio n. 25
0
    def points(self, **kwds):
        """
        Return some or all rational points of a projective scheme.

        For dimension 0 subschemes points are determined through a groebner
        basis calculation. For schemes or subschemes with dimension greater than 1
        points are determined through enumeration up to the specified bound.

        INPUT:

        kwds:

        - ``bound`` - real number (optional, default=0). The bound for the coordinates for
          subschemes with dimension at least 1.

        - ``precision`` - integer (optional, default=53). The precision to use to
          compute the elements of bounded height for number fields.

        - ``point_tolerance`` - positive real number (optional, default=10^(-10)).
          For numerically inexact fields, two points are considered the same
          if their coordinates are within tolerance.

        - ``zero_tolerance`` - positive real number (optional, default=10^(-10)).
          For numerically inexact fields, points are on the subscheme if they
          satisfy the equations to within tolerance.

        - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4
          for enumeration over number fields.

        OUTPUT:

        - a list of rational points of a projective scheme

        .. WARNING::
        
            For numerically inexact fields such as ComplexField or RealField the
            list of points returned is very likely to be incomplete. It may also
            contain repeated points due to tolerances.

        EXAMPLES::

            sage: P.<x,y> = ProjectiveSpace(QQ,1)
            sage: P(QQ).points(bound=4)
            [(-4 : 1), (-3 : 1), (-2 : 1), (-3/2 : 1), (-4/3 : 1), (-1 : 1),
            (-3/4 : 1), (-2/3 : 1), (-1/2 : 1), (-1/3 : 1), (-1/4 : 1), (0 : 1),
            (1/4 : 1), (1/3 : 1), (1/2 : 1), (2/3 : 1), (3/4 : 1), (1 : 0), (1 : 1),
            (4/3 : 1), (3/2 : 1), (2 : 1), (3 : 1), (4 : 1)]

        ::

            sage: u = QQ['u'].0
            sage: K.<v> = NumberField(u^2 + 3)
            sage: P.<x,y,z> = ProjectiveSpace(K,2)
            sage: len(P(K).points(bound=1.8))
            381

        ::

            sage: P1 = ProjectiveSpace(GF(2),1)
            sage: F.<a> = GF(4,'a')
            sage: P1(F).points()
            [(0 : 1), (1 : 0), (1 : 1), (a : 1), (a + 1 : 1)]

        ::

            sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
            sage: E = P.subscheme([(y^3-y*z^2) - (x^3-x*z^2),(y^3-y*z^2) + (x^3-x*z^2)])
            sage: E(P.base_ring()).points()
            [(-1 : -1 : 1), (-1 : 0 : 1), (-1 : 1 : 1), (0 : -1 : 1), (0 : 0 : 1), (0 : 1 : 1),
            (1 : -1 : 1), (1 : 0 : 1), (1 : 1 : 1)]

        ::

            sage: P.<x,y,z> = ProjectiveSpace(CC, 2)
            sage: E = P.subscheme([y^3 - x^3 - x*z^2, x*y*z])
            sage: L=E(P.base_ring()).points(); sorted(L, key=str)
            verbose 0 (71: projective_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.
            [(-0.500000000000000 + 0.866025403784439*I : 1.00000000000000 : 0.000000000000000),
            (-0.500000000000000 - 0.866025403784439*I : 1.00000000000000 : 0.000000000000000),
            (-1.00000000000000*I : 0.000000000000000 : 1.00000000000000),
            (0.000000000000000 : 0.000000000000000 : 1.00000000000000),
            (1.00000000000000 : 1.00000000000000 : 0.000000000000000),
            (1.00000000000000*I : 0.000000000000000 : 1.00000000000000)]
            sage: L[0].codomain()
            Projective Space of dimension 2 over Complex Field with 53 bits of precision

        ::

            sage: P.<x,y,z> = ProjectiveSpace(CDF, 2)
            sage: E = P.subscheme([y^2 + x^2 + z^2, x*y*z])
            sage: len(E(P.base_ring()).points())
            verbose 0 (71: projective_homset.py, points) Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.
            6
        """
        from sage.schemes.projective.projective_space import is_ProjectiveSpace
        X = self.codomain()
        if not is_ProjectiveSpace(X) and X.base_ring() in Fields():
            if hasattr(X.base_ring(), 'precision'):
                numerical = True
                verbose(
                    "Warning: computations in the numerical fields are inexact;points may be computed partially or incorrectly.",
                    level=0)
                pt_tol = RR(kwds.pop('point_tolerance', 10**(-10)))
                zero_tol = RR(kwds.pop('zero_tolerance', 10**(-10)))
                if pt_tol <= 0 or zero_tol <= 0:
                    raise ValueError("tolerance must be positive")
            else:
                numerical = False
            #Then it must be a subscheme
            dim_ideal = X.defining_ideal().dimension()
            if dim_ideal < 1:  # no points
                return []
            if dim_ideal == 1:  # if X zero-dimensional
                rat_points = set()
                PS = X.ambient_space()
                N = PS.dimension_relative()
                BR = X.base_ring()
                #need a lexicographic ordering for elimination
                R = PolynomialRing(BR, N + 1, PS.variable_names(), order='lex')
                I = R.ideal(X.defining_polynomials())
                I0 = R.ideal(0)
                #Determine the points through elimination
                #This is much faster than using the I.variety() function on each affine chart.
                for k in range(N + 1):
                    #create the elimination ideal for the kth affine patch
                    G = I.substitute({R.gen(k): 1}).groebner_basis()
                    if G != [1]:
                        P = {}
                        #keep track that we know the kth coordinate is 1
                        P.update({R.gen(k): 1})
                        points = [P]
                        #work backwards from solving each equation for the possible
                        #values of the next coordinate
                        for i in range(len(G) - 1, -1, -1):
                            new_points = []
                            good = 0
                            for P in points:
                                #substitute in our dictionary entry that has the values
                                #of coordinates known so far. This results in a single
                                #variable polynomial (by elimination)
                                L = G[i].substitute(P)
                                if R(L).degree() > 0:
                                    if numerical:
                                        for pol in L.univariate_polynomial(
                                        ).roots(multiplicities=False):
                                            good = 1
                                            r = L.variables()[0]
                                            varindex = R.gens().index(r)
                                            P.update({R.gen(varindex): pol})
                                            new_points.append(copy(P))
                                    else:
                                        L = L.factor()
                                        #the linear factors give the possible rational values of
                                        #this coordinate
                                        for pol, pow in L:
                                            if pol.degree() == 1 and len(
                                                    pol.variables()) == 1:
                                                good = 1
                                                r = pol.variables()[0]
                                                varindex = R.gens().index(r)
                                                #add this coordinates information to
                                                #each dictionary entry
                                                P.update({
                                                    R.gen(varindex):
                                                    -pol.constant_coefficient(
                                                    ) /
                                                    pol.monomial_coefficient(r)
                                                })
                                                new_points.append(copy(P))
                                else:
                                    new_points.append(P)
                                    good = 1
                            if good:
                                points = new_points
                        #the dictionary entries now have values for all coordinates
                        #they are the rational solutions to the equations
                        #make them into projective points
                        for i in range(len(points)):
                            if numerical:
                                if len(points[i]) == N + 1:
                                    S = PS([
                                        points[i][R.gen(j)]
                                        for j in range(N + 1)
                                    ])
                                    S.normalize_coordinates()
                                    if all(
                                            g(list(S)) < zero_tol
                                            for g in X.defining_polynomials()):
                                        rat_points.add(S)
                            else:
                                if len(points[i]) == N + 1 and I.subs(
                                        points[i]) == I0:
                                    S = X([
                                        points[i][R.gen(j)]
                                        for j in range(N + 1)
                                    ])
                                    S.normalize_coordinates()
                                    rat_points.add(S)

                # remove duplicate element using tolerance
                if numerical:
                    dupl_points = list(rat_points)
                    for i in range(len(dupl_points)):
                        u = dupl_points[i]
                        for j in range(i + 1, len(dupl_points)):
                            v = dupl_points[j]
                            if all((u[k] - v[k]).abs() < pt_tol
                                   for k in range(len(u))):
                                rat_points.remove(u)
                                break

                rat_points = sorted(rat_points)
                return rat_points
        R = self.value_ring()
        B = kwds.pop('bound', 0)
        tol = kwds.pop('tolerance', 1e-2)
        prec = kwds.pop('precision', 53)
        if is_RationalField(R):
            if not B > 0:
                raise TypeError("a positive bound B (= %s) must be specified" %
                                B)
            if isinstance(X, AlgebraicScheme_subscheme
                          ):  # sieve should only be called for subschemes
                from sage.schemes.projective.projective_rational_point import sieve
                return sieve(X, B)
            else:
                from sage.schemes.projective.projective_rational_point import enum_projective_rational_field
                return enum_projective_rational_field(self, B)
        elif R in NumberFields():
            if not B > 0:
                raise TypeError("a positive bound B (= %s) must be specified" %
                                B)
            from sage.schemes.projective.projective_rational_point import enum_projective_number_field
            return enum_projective_number_field(self,
                                                bound=B,
                                                tolerance=tol,
                                                precision=prec)
        elif is_FiniteField(R):
            from sage.schemes.projective.projective_rational_point import enum_projective_finite_field
            return enum_projective_finite_field(self.extended_codomain())
        else:
            raise TypeError("unable to enumerate points over %s" % R)
Esempio n. 26
0
    def L_ratio(self):
        r"""
        Return the ratio `L(E,1) / \Omega` as an exact rational number.

        The result is *provably* correct if the Manin
        constant of the associated optimal quotient is `\leq 2`.  This
        hypothesis on the Manin constant is true for all semistable
        curves (i.e., squarefree conductor), by a theorem of Mazur
        from his *Rational Isogenies of Prime Degree* paper.

        EXAMPLES::

            sage: E = EllipticCurve([0, -1, 1, -10, -20])   # 11A  = X_0(11)
            sage: E.lseries().L_ratio()
            1/5
            sage: E = EllipticCurve([0, -1, 1, 0, 0])       # X_1(11)
            sage: E.lseries().L_ratio()
            1/25
            sage: E = EllipticCurve([0, 0, 1, -1, 0])       # 37A  (rank 1)
            sage: E.lseries().L_ratio()
            0
            sage: E = EllipticCurve([0, 1, 1, -2, 0])       # 389A (rank 2)
            sage: E.lseries().L_ratio()
            0
            sage: E = EllipticCurve([0, 0, 1, -38, 90])     # 361A (CM curve))
            sage: E.lseries().L_ratio()
            0
            sage: E = EllipticCurve([0,-1,1,-2,-1])         # 141C (13-isogeny)
            sage: E.lseries().L_ratio()
            1
            sage: E = EllipticCurve(RationalField(), [1, 0, 0, 1/24624, 1/886464])
            sage: E.lseries().L_ratio()
            2

        See :trac:`3651` and :trac:`15299`::

            sage: EllipticCurve([0,0,0,-193^2,0]).sha().an()
            4
            sage: EllipticCurve([1, 0, 1, -131, 558]).sha().an()  # long time
            1.00000000000000

        ALGORITHM: Compute the root number.  If it is -1 then `L(E,s)`
        vanishes to odd order at 1, hence vanishes.  If it is +1, use
        a result about modular symbols and Mazur's *Rational Isogenies*
        paper to determine a provably correct bound (assuming Manin
        constant is <= 2) so that we can determine whether `L(E,1) = 0`.

        AUTHOR: William Stein, 2005-04-20.
        """
        if not self.__E.is_minimal():
            return self.__E.minimal_model().lseries().L_ratio()

        QQ = RationalField()
        if self.__E.root_number() == -1:
            return QQ.zero()

        # Even root number.  Decide if L(E,1) = 0.  If E is a modular
        # *OPTIMAL* quotient of J_0(N) elliptic curve, we know that T *
        # L(E,1)/omega is an integer n, where T is the order of the
        # image of the rational torsion point (0)-(oo) in E(Q), and
        # omega is the least real Neron period.  (This is proved in my
        # Ph.D. thesis, but is probably well known.)  We can easily
        # compute omega to very high precision using AGM.  So to prove
        # that L(E,1) = 0 we compute T/omega * L(E,1) to sufficient
        # precision to determine it as an integer.  If eps is the
        # error in computation of L(E,1), then the error in computing
        # the product is (2T/Omega_E) * eps, and we need this to be
        # less than 0.5, i.e.,
        #          (2T/Omega_E) * eps < 0.5,
        # so
        #          eps < 0.5 * Omega_E / (2T) = Omega_E / (4*T).
        #
        # Since in general E need not be optimal, we have to choose
        # eps = Omega_E/(8*t*B), where t is the exponent of E(Q)_tor,
        # and is a multiple of the degree of an isogeny between E
        # and the optimal curve.
        #
        # NOTE: We *do* have to worry about the Manin constant, since
        # we are using the Neron model to compute omega, not the
        # newform.  My theorem replaces the omega above by omega/c,
        # where c is the Manin constant, and the bound must be
        # correspondingly smaller.  If the level is square free, then
        # the Manin constant is 1 or 2, so there's no problem (since
        # we took 8 instead of 4 in the denominator).  If the level
        # is divisible by a square, then the Manin constant could
        # be a divisible by an arbitrary power of that prime, except
        # that Edixhoven claims the primes that appear are <= 7.

        t = self.__E.torsion_subgroup().order()
        omega = self.__E.period_lattice().basis()[0]
        d = self.__E._multiple_of_degree_of_isogeny_to_optimal_curve()
        C = 8 * d * t
        eps = omega / C

        sqrtN = 2 * self.__E.conductor().isqrt()
        k = sqrtN + 10
        while True:
            L1, error_bound = self.at1(k)
            if error_bound < eps:
                n = (L1 * C / omega).round()
                quo = QQ((n, C))
                return quo / self.__E.real_components()
            k += sqrtN
            verbose("Increasing precision to %s terms." % k)
Esempio n. 27
0
def splitting_field(poly,
                    name,
                    map=False,
                    degree_multiple=None,
                    abort_degree=None,
                    simplify=True,
                    simplify_all=False):
    r"""
    Compute the splitting field of a given polynomial, defined over a
    number field.

    INPUT:

    - ``poly`` -- a monic polynomial over a number field

    - ``name`` -- a variable name for the number field

    - ``map`` -- (default: ``False``) also return an embedding of
      ``poly`` into the resulting field. Note that computing this
      embedding might be expensive.

    - ``degree_multiple`` -- a multiple of the absolute degree of
      the splitting field.  If ``degree_multiple`` equals the actual
      degree, this can enormously speed up the computation.

    - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort`
      if it can be determined that the absolute degree of the splitting
      field is strictly larger than ``abort_degree``.

    - ``simplify`` -- (default: ``True``) during the algorithm, try
      to find a simpler defining polynomial for the intermediate
      number fields using PARI's ``polred()``.  This usually speeds
      up the computation but can also considerably slow it down.
      Try and see what works best in the given situation.

    - ``simplify_all`` -- (default: ``False``) If ``True``, simplify
      intermediate fields and also the resulting number field.

    OUTPUT:

    If ``map`` is ``False``, the splitting field as an absolute number
    field.  If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi``
    is an embedding of the base field in ``K``.

    EXAMPLES::

        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = (x^3 + 2).splitting_field(); K
        Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1
        sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K
        Number Field in a with defining polynomial x^3 - 3*x + 1

    The ``simplify`` and ``simplify_all`` flags usually yield
    fields defined by polynomials with smaller coefficients.
    By default, ``simplify`` is True and ``simplify_all`` is False.

    ::

        sage: (x^4 - x + 1).splitting_field('a', simplify=False)
        Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991
        sage: (x^4 - x + 1).splitting_field('a', simplify=True)
        Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919
        sage: (x^4 - x + 1).splitting_field('a', simplify_all=True)
        Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1

    Reducible polynomials also work::

        sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3)
        sage: pol.splitting_field('a', simplify_all=True)
        Number Field in a with defining polynomial x^8 - x^4 + 1

    Relative situation::

        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = NumberField(x^3 + 2)
        sage: S.<t> = PolynomialRing(K)
        sage: L.<b> = (t^2 - a).splitting_field()
        sage: L
        Number Field in b with defining polynomial t^6 + 2

    With ``map=True``, we also get the embedding of the base field
    into the splitting field::

        sage: L.<b>, phi = (t^2 - a).splitting_field(map=True)
        sage: phi
        Ring morphism:
          From: Number Field in a with defining polynomial x^3 + 2
          To:   Number Field in b with defining polynomial t^6 + 2
          Defn: a |--> b^2
        sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1]
        Ring morphism:
          From: Rational Field
          To:   Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1
          Defn: 1 |--> 1

    We can enable verbose messages::

        sage: from sage.misc.verbose import set_verbose
        sage: set_verbose(2)
        sage: K.<a> = (x^3 - x + 1).splitting_field()
        verbose 1 (...: splitting_field.py, splitting_field) Starting field: y
        verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)]
        verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...)
        verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)]
        verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6]
        verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23
        verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...)
        verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...)
        verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...)
        verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: []
        verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...)
        verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)]
        verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6]
        verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1
        verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...)
        sage: set_verbose(0)

    Try all Galois groups in degree 4. We use a quadratic base field
    such that ``polgalois()`` cannot be used::

        sage: R.<x> = PolynomialRing(QuadraticField(-11))
        sage: C2C2pol = x^4 - 10*x^2 + 1
        sage: C2C2pol.splitting_field('x')
        Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824
        sage: C4pol = x^4 + x^3 + x^2 + x + 1
        sage: C4pol.splitting_field('x')
        Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81
        sage: D8pol = x^4 - 2
        sage: D8pol.splitting_field('x')
        Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961
        sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21
        sage: A4pol.splitting_field('x')
        Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173
        sage: S4pol = x^4 + x + 1
        sage: S4pol.splitting_field('x')
        Number Field in x with defining polynomial x^48 ...

    Some bigger examples::

        sage: R.<x> = PolynomialRing(QQ)
        sage: pol15 = chebyshev_T(31, x) - 1    # 2^30*(x-1)*minpoly(cos(2*pi/31))^2
        sage: pol15.splitting_field('a')
        Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1
        sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12
        sage: pol48.splitting_field('a')
        Number Field in a with defining polynomial x^48 ...

    If you somehow know the degree of the field in advance, you
    should add a ``degree_multiple`` argument.  This can speed up the
    computation, in particular for polynomials of degree >= 12 or
    for relative extensions::

        sage: pol15.splitting_field('a', degree_multiple=15)
        Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1

    A value for ``degree_multiple`` which isn't actually a
    multiple of the absolute degree of the splitting field can
    either result in a wrong answer or the following exception::

        sage: pol48.splitting_field('a', degree_multiple=20)
        Traceback (most recent call last):
        ...
        ValueError: inconsistent degree_multiple in splitting_field()

    Compute the Galois closure as the splitting field of the defining polynomial::

        sage: R.<x> = PolynomialRing(QQ)
        sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12
        sage: K.<a> = NumberField(pol48)
        sage: L.<b> = pol48.change_ring(K).splitting_field()
        sage: L
        Number Field in b with defining polynomial x^48 ...

    Try all Galois groups over `\QQ` in degree 5 except for `S_5`
    (the latter is infeasible with the current implementation)::

        sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1
        sage: C5pol.splitting_field('x')
        Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1
        sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1
        sage: D10pol.splitting_field('x')
        Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401
        sage: AGL_1_5pol = x^5 - 2
        sage: AGL_1_5pol.splitting_field('x')
        Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25
        sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2
        sage: A5pol.splitting_field('x')
        Number Field in x with defining polynomial x^60 ...

    We can use the ``abort_degree`` option if we don't want to compute
    fields of too large degree (this can be used to check whether the
    splitting field has small degree)::

        sage: (x^5+x+3).splitting_field('b', abort_degree=119)
        Traceback (most recent call last):
        ...
        SplittingFieldAbort: degree of splitting field equals 120
        sage: (x^10+x+3).splitting_field('b', abort_degree=60)  # long time (10s on sage.math, 2014)
        Traceback (most recent call last):
        ...
        SplittingFieldAbort: degree of splitting field is a multiple of 180

    Use the ``degree_divisor`` attribute to recover the divisor of the
    degree of the splitting field or ``degree_multiple`` to recover a
    multiple::

        sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort
        sage: try:  # long time (4s on sage.math, 2014)
        ....:     (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False)
        ....: except SplittingFieldAbort as e:
        ....:     print(e.degree_divisor)
        ....:     print(e.degree_multiple)
        120
        1440

    TESTS::

        sage: from sage.rings.number_field.splitting_field import splitting_field
        sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True)
        (Number Field in x with defining polynomial x, Ring morphism:
          From: Rational Field
          To:   Number Field in x with defining polynomial x
          Defn: 1 |--> 1)
    """
    from sage.misc.all import cputime
    from sage.misc.verbose import verbose

    degree_multiple = Integer(degree_multiple or 0)
    abort_degree = Integer(abort_degree or 0)

    # Kpol = PARI polynomial in y defining the extension found so far
    F = poly.base_ring()
    if is_RationalField(F):
        Kpol = pari("'y")
    else:
        Kpol = F.pari_polynomial("y")
    # Fgen = the generator of F as element of Q[y]/Kpol
    # (only needed if map=True)
    if map:
        Fgen = F.gen().__pari__()
    verbose("Starting field: %s" % Kpol)

    # L and Lred are lists of SplittingData.
    # L contains polynomials which are irreducible over K,
    # Lred contains polynomials which need to be factored.
    L = []
    Lred = [SplittingData(poly._pari_with_name(), degree_multiple)]

    # Main loop, handle polynomials one by one
    while True:
        # Absolute degree of current field K
        absolute_degree = Integer(Kpol.poldegree())

        # Compute minimum relative degree of splitting field
        rel_degree_divisor = Integer(1)
        for splitting in L:
            rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree())

        # Check for early aborts
        abort_rel_degree = abort_degree // absolute_degree
        if abort_rel_degree and rel_degree_divisor > abort_rel_degree:
            raise SplittingFieldAbort(absolute_degree * rel_degree_divisor,
                                      degree_multiple)

        # First, factor polynomials in Lred and store the result in L
        verbose("SplittingData to factor: %s" %
                [s._repr_tuple() for s in Lred])
        t = cputime()
        for splitting in Lred:
            m = splitting.dm.gcd(degree_multiple).gcd(
                factorial(splitting.poldegree()))
            if m == 1:
                continue
            factors = Kpol.nffactor(splitting.pol)[0]
            for q in factors:
                d = q.poldegree()
                fac = factorial(d)
                # Multiple of the degree of the splitting field of q,
                # note that the degree equals fac iff the Galois group is S_n.
                mq = m.gcd(fac)
                if mq == 1:
                    continue
                # Multiple of the degree of the splitting field of q
                # over the field defined by adding square root of the
                # discriminant.
                # If the Galois group is contained in A_n, then mq_alt is
                # also the degree multiple over the current field K.
                # Here, we have equality if the Galois group is A_n.
                mq_alt = mq.gcd(fac // 2)

                # If we are over Q, then use PARI's polgalois() to compute
                # these degrees exactly.
                if absolute_degree == 1:
                    try:
                        G = q.polgalois()
                    except PariError:
                        pass
                    else:
                        mq = Integer(G[0])
                        mq_alt = mq // 2 if (G[1] == -1) else mq

                # In degree 4, use the cubic resolvent to refine the
                # degree bounds.
                if d == 4 and mq >= 12:  # mq equals 12 or 24
                    # Compute cubic resolvent
                    a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev()
                    assert a4 == 1
                    cubicpol = pari([
                        4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0,
                        -a2, 1
                    ]).Polrev()
                    cubicfactors = Kpol.nffactor(cubicpol)[0]
                    if len(cubicfactors) == 1:  # A4 or S4
                        # After adding a root of the cubic resolvent,
                        # the degree of the extension defined by q
                        # is a factor 3 smaller.
                        L.append(SplittingData(cubicpol, 3))
                        rel_degree_divisor = rel_degree_divisor.lcm(3)
                        mq = mq // 3  # 4 or 8
                        mq_alt = 4
                    elif len(cubicfactors) == 2:  # C4 or D8
                        # The irreducible degree 2 factor is
                        # equivalent to x^2 - q.poldisc().
                        discpol = cubicfactors[1]
                        L.append(SplittingData(discpol, 2))
                        mq = mq_alt = 4
                    else:  # C2 x C2
                        mq = mq_alt = 4

                if mq > mq_alt >= 3:
                    # Add quadratic resolvent x^2 - D to decrease
                    # the degree multiple by a factor 2.
                    discpol = pari([-q.poldisc(), 0, 1]).Polrev()
                    discfactors = Kpol.nffactor(discpol)[0]
                    if len(discfactors) == 1:
                        # Discriminant is not a square
                        L.append(SplittingData(discpol, 2))
                        rel_degree_divisor = rel_degree_divisor.lcm(2)
                    mq = mq_alt

                L.append(SplittingData(q, mq))
                rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree())
                if abort_rel_degree and rel_degree_divisor > abort_rel_degree:
                    raise SplittingFieldAbort(
                        absolute_degree * rel_degree_divisor, degree_multiple)
        verbose("Done factoring", t, level=2)

        if len(L) == 0:  # Nothing left to do
            break

        # Recompute absolute degree multiple
        new_degree_multiple = absolute_degree
        for splitting in L:
            new_degree_multiple *= splitting.dm
        degree_multiple = new_degree_multiple.gcd(degree_multiple)

        # Absolute degree divisor
        degree_divisor = rel_degree_divisor * absolute_degree

        # Sort according to degree to handle low degrees first
        L.sort(key=lambda x: x.key())
        verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L])
        verbose("Bounds for absolute degree: [%s, %s]" %
                (degree_divisor, degree_multiple))

        # Check consistency
        if degree_multiple % degree_divisor != 0:
            raise ValueError(
                "inconsistent degree_multiple in splitting_field()")
        for splitting in L:
            # The degree of the splitting field must be a multiple of
            # the degree of the polynomial. Only do this check for
            # SplittingData with minimal dm, because the higher dm are
            # defined as relative degree over the splitting field of
            # the polynomials with lesser dm.
            if splitting.dm > L[0].dm:
                break
            if splitting.dm % splitting.poldegree() != 0:
                raise ValueError(
                    "inconsistent degree_multiple in splitting_field()")

        # Add a root of f = L[0] to construct the field N = K[x]/f(x)
        splitting = L[0]
        f = splitting.pol
        verbose("Handling polynomial %s" % (f.lift()), level=2)
        t = cputime()
        Npol, KtoN, k = Kpol.rnfequation(f, flag=1)

        # Make Npol monic integral primitive, store in Mpol
        # (after this, we don't need Npol anymore, only Mpol)
        Mdiv = pari(1)
        Mpol = Npol
        while True:
            denom = Integer(Mpol.pollead())
            if denom == 1:
                break
            denom = pari(denom.factor().radical_value())
            Mpol = (Mpol * (denom**Mpol.poldegree())).subst(
                "x",
                pari([0, 1 / denom]).Polrev("x"))
            Mpol /= Mpol.content()
            Mdiv *= denom

        # We are finished for sure if we hit the degree bound
        finished = (Mpol.poldegree() >= degree_multiple)

        if simplify_all or (simplify and not finished):
            # Find a simpler defining polynomial Lpol for Mpol
            verbose("New field before simplifying: %s" % Mpol, t)
            t = cputime()
            M = Mpol.polred(flag=3)
            n = len(M[0]) - 1
            Lpol = M[1][n].change_variable_name("y")
            LtoM = M[0][n].change_variable_name("y").Mod(
                Mpol.change_variable_name("y"))
            MtoL = LtoM.modreverse()
        else:
            # Lpol = Mpol
            Lpol = Mpol.change_variable_name("y")
            MtoL = pari("'y")

        NtoL = MtoL / Mdiv
        KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol)
        Kpol = Lpol  # New Kpol (for next iteration)
        verbose("New field: %s" % Kpol, t)
        if map:
            t = cputime()
            Fgen = Fgen.lift().subst("y", KtoL)
            verbose("Computed generator of F in K", t, level=2)
        if finished:
            break

        t = cputime()

        # Convert f and elements of L from K to L and store in L
        # (if the polynomial is certain to remain irreducible) or Lred.
        Lold = L[1:]
        L = []
        Lred = []

        # First add f divided by the linear factor we obtained,
        # mg is the new degree multiple.
        mg = splitting.dm // f.poldegree()
        if mg > 1:
            g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()]
            g = pari(g).Polrev()
            g /= pari([k * KtoL - NtoL, 1]).Polrev()  # divide linear factor
            Lred.append(SplittingData(g, mg))

        for splitting in Lold:
            g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()]
            g = pari(g).Polrev()
            mg = splitting.dm
            if Integer(g.poldegree()).gcd(
                    f.poldegree()) == 1:  # linearly disjoint fields
                L.append(SplittingData(g, mg))
            else:
                Lred.append(SplittingData(g, mg))
        verbose("Converted polynomials to new field", t, level=2)

    # Convert Kpol to Sage and construct the absolute number field
    Kpol = PolynomialRing(RationalField(),
                          name=poly.variable_name())(Kpol / Kpol.pollead())
    K = NumberField(Kpol, name)
    if map:
        return K, F.hom(Fgen, K)
    else:
        return K
Esempio n. 28
0
    def cuspidal_ideal_generators(self, maxweight=8, prec=None):
        r"""
        Calculate generators for the ideal of cuspidal forms in this ring, as a
        module over the whole ring.

        EXAMPLES::

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

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

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

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

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

            flist = []

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

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

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

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

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

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

        self.__cached_cusp_maxweight = maxweight
        self.__cached_cusp_gens = G

        if prec is None:
            return G
        elif prec <= working_prec:
            return [(k, f.truncate_powerseries(prec), F) for k, f, F in G]
        else:
            # user wants increased precision, so we may as well cache that
            Gnew = [(k, F.qexp(prec).change_ring(self.base_ring()), F)
                    for k, f, F in G]
            self.__cached_cusp_gens = Gnew
            return Gnew
Esempio n. 29
0
    def dual_free_module(self, bound=None, anemic=True, use_star=True):
        r"""
        Compute embedded dual free module if possible. In general this won't be
        possible, e.g., if this space is not Hecke equivariant, possibly if it
        is not cuspidal, or if the characteristic is not 0. In all these cases
        we raise a RuntimeError exception.

        If use_star is True (which is the default), we also use the +/-
        eigenspaces for the star operator to find the dual free module of self.
        If self does not have a star involution, use_star will automatically be
        set to False.

        EXAMPLES::

            sage: M = ModularSymbols(11, 2)
            sage: M.dual_free_module()
            Vector space of dimension 3 over Rational Field
            sage: Mpc = M.plus_submodule().cuspidal_submodule()
            sage: Mcp = M.cuspidal_submodule().plus_submodule()
            sage: Mcp.dual_free_module() == Mpc.dual_free_module()
            True
            sage: Mpc.dual_free_module()
            Vector space of degree 3 and dimension 1 over Rational Field
            Basis matrix:
            [  1 5/2   5]

            sage: M = ModularSymbols(35,2).cuspidal_submodule()
            sage: M.dual_free_module(use_star=False)
            Vector space of degree 9 and dimension 6 over Rational Field
            Basis matrix:
            [   1    0    0    0   -1    0    0    4   -2]
            [   0    1    0    0    0    0    0 -1/2  1/2]
            [   0    0    1    0    0    0    0 -1/2  1/2]
            [   0    0    0    1   -1    0    0    1    0]
            [   0    0    0    0    0    1    0   -2    1]
            [   0    0    0    0    0    0    1   -2    1]

            sage: M = ModularSymbols(40,2)
            sage: Mmc = M.minus_submodule().cuspidal_submodule()
            sage: Mcm = M.cuspidal_submodule().minus_submodule()
            sage: Mcm.dual_free_module() == Mmc.dual_free_module()
            True
            sage: Mcm.dual_free_module()
            Vector space of degree 13 and dimension 3 over Rational Field
            Basis matrix:
            [ 0  1  0  0  0  0  1  0 -1 -1  1 -1  0]
            [ 0  0  1  0 -1  0 -1  0  1  0  0  0  0]
            [ 0  0  0  0  0  1  1  0 -1  0  0  0  0]

            sage: M = ModularSymbols(43).cuspidal_submodule()
            sage: S = M[0].plus_submodule() + M[1].minus_submodule()
            sage: S.dual_free_module(use_star=False)
            Traceback (most recent call last):
            ...
            RuntimeError: Computation of complementary space failed (cut down to rank 7, but should have cut down to rank 4).
            sage: S.dual_free_module().dimension() == S.dimension()
            True

        We test that :trac:`5080` is fixed::

            sage: EllipticCurve('128a').congruence_number()
            32

        """

        # if we know the complement we can read off the dual module
        if self.complement.is_in_cache():
            verbose(
                'This module knows its complement already -- cheating in dual_free_module'
            )
            C = self.complement()
            V = C.basis_matrix().right_kernel()
            return V

        verbose("computing dual")

        A = self.ambient_hecke_module()

        if self.dimension() == 0:
            return A.zero_submodule()

        if A.dimension() == self.dimension():
            return A.free_module()

        # ALGORITHM: Compute the char poly of each Hecke operator on
        # the submodule, then use it to cut out a submodule of the
        # dual.  If the dimension cuts down to the dimension of self
        # terminate with success.  If it stays larger beyond the Sturm
        # bound, raise a RuntimeError exception.

        # In the case that the sign of self is not 1, we need to use
        # the star involution as well as the Hecke operators in order
        # to find the dual of self.
        #
        # Note that one needs to comment out the line caching the
        # result of this computation below in order to get meaningful
        # timings.

        # If the star involution doesn't make sense for self, then we
        # can't use it.
        if not hasattr(self, 'star_eigenvalues'):
            use_star = False

        if use_star:
            # If the star involution has both + and - eigenspaces on self,
            # then we compute the dual on each eigenspace, then put them
            # together.
            if len(self.star_eigenvalues()) == 2:
                V = self.plus_submodule(compute_dual = False).dual_free_module() + \
                    self.minus_submodule(compute_dual = False).dual_free_module()
                return V

            # At this point, we know that self is an eigenspace for star.
            V = A.sign_submodule(self.sign()).dual_free_module()
        else:
            V = A.free_module()

        N = self.level()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0:
                    p = arith.next_prime(p)
            verbose("using T_%s" % p)
            f = self.hecke_polynomial(p)
            T = A.dual_hecke_matrix(p)
            V = T.kernel_on(V, poly=f, check=False)
            if V.dimension() <= self.dimension():
                break
            p = arith.next_prime(p)
            if p > bound:
                break

        if V.rank() == self.rank():
            return V
        else:
            # Failed to reduce V to the appropriate dimension
            W = self.complement()
            V2 = W.basis_matrix().right_kernel()
            if V2.rank() == self.rank():
                return V2
            else:
                raise RuntimeError("Computation of embedded dual vector space failed " + \
                  "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank()))
Esempio n. 30
0
    def _eigenvectors(self):
        r"""
        Find numerical approximations to simultaneous eigenvectors in
        self.modular_symbols() for all T_p in self._tp.

        EXAMPLES::

            sage: n = numerical_eigenforms(61)
            sage: n._eigenvectors() # random order
            [              1.0    0.289473640239    0.176788851952    0.336707726757  2.4182243084e-16]
            [                0  -0.0702748344418    0.491416161212    0.155925712173    0.707106781187]
            [                0    0.413171180356    0.141163094698   0.0923242547901    0.707106781187]
            [                0    0.826342360711    0.282326189397     0.18464850958 6.79812569682e-16]
            [                0      0.2402380858    0.792225196393    0.905370774276 4.70805946682e-16]

        TESTS:

        This tests if this routine selects only eigenvectors with
        multiplicity one.  Two of the eigenvalues are
        (roughly) -92.21 and -90.30 so if we set ``eps = 2.0``
        then they should compare as equal, causing both eigenvectors
        to be absent from the matrix returned.  The remaining eigenvalues
        (ostensibly unique) are visible in the test, which should be
        independent of which eigenvectors are returned, but it does presume
        an ordering of these eigenvectors for the test to succeed.
        This exercises a correction in :trac:`8018`. ::

            sage: n = numerical_eigenforms(61, eps=2.0)
            sage: evectors = n._eigenvectors()
            sage: evalues = diagonal_matrix(CDF, [-283.0, 142.0, 108.522012456])
            sage: diff = n._hecke_matrix*evectors - evectors*evalues
            sage: sum([abs(diff[i,j]) for i in range(5) for j in range(3)]) < 1.0e-9
            True
        """
        verbose('Finding eigenvector basis')
        M = self.modular_symbols()

        tp = self._tp
        p = tp[0]
        t = M.T(p).matrix()
        for p in tp[1:]:
            t += randint(-50, 50) * M.T(p).matrix()

        self._hecke_matrix = t

        global scipy
        if scipy is None:
            import scipy
        import scipy.linalg
        evals, eig = scipy.linalg.eig(self._hecke_matrix.numpy(),
                                      right=True,
                                      left=False)
        B = matrix(eig)
        v = [CDF(evals[i]) for i in range(len(evals))]

        # Determine the eigenvectors with eigenvalues of multiplicity
        # one, with equality controlled by the value of eps
        # Keep just these eigenvectors
        eps = self._eps
        w = []
        for i in range(len(v)):
            e = v[i]
            uniq = True
            for j in range(len(v)):
                if uniq and i != j and abs(e - v[j]) < eps:
                    uniq = False
            if uniq:
                w.append(i)
        return B.matrix_from_columns(w)