Esempio n. 1
0
    def is_principal(self, proof=None):
        """
        Return True if this ideal is principal.  If so, set
        self.__reduced_generators, with length one.

        EXAMPLES::

            sage: K.<a, b> = NumberField([x^2 - 23, x^2 + 1])
            sage: I = K.ideal([7, (-1/2*b - 3/2)*a + 3/2*b + 9/2])
            sage: I.is_principal()
            True
            sage: I # random
            Fractional ideal ((1/2*b + 1/2)*a - 3/2*b - 3/2)
        """
        proof = get_flag(proof, "number_field")
        try:
            return self.__is_principal
        except AttributeError:
            self.__is_principal = self.absolute_ideal().is_principal(proof=proof)
            if self.__is_principal:
                abs_ideal = self.absolute_ideal()
                from_abs = abs_ideal.number_field().structure()[0]
                g = from_abs(abs_ideal.gens_reduced()[0])
                self.__reduced_generators = tuple([g])
            return self.__is_principal
Esempio n. 2
0
    def is_principal(self, proof=None):
        """
        Return True if this ideal is principal.  If so, set
        self.__reduced_generators, with length one.

        EXAMPLES::

            sage: K.<a, b> = NumberField([x^2 - 23, x^2 + 1])
            sage: I = K.ideal([7, (-1/2*b - 3/2)*a + 3/2*b + 9/2])
            sage: I.is_principal()
            True
            sage: I # random
            Fractional ideal ((1/2*b + 1/2)*a - 3/2*b - 3/2)
        """
        proof = get_flag(proof, "number_field")
        try:
            return self.__is_principal
        except AttributeError:
            self.__is_principal = self.absolute_ideal().is_principal(proof=proof)
            if self.__is_principal:
                abs_ideal = self.absolute_ideal()
                from_abs = abs_ideal.number_field().structure()[0]
                g = from_abs(abs_ideal.gens_reduced()[0])
                self.__reduced_generators = tuple([g])
            return self.__is_principal
Esempio n. 3
0
    def __init__(self, number_field, proof=True):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            [-1, 6*a - 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3
            sage: UK.gens()
            [-1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K

        # compute the units via pari:
        fu = [K(u) for u in pK.bnfunit()]

        # compute a torsion generator and pick the 'simplest' one:
        n, z = pK.nfrootsof1()
        n = ZZ(n)
        self.__ntu = n
        z = K(z)

        # If we replaced z by another torsion generator we would need
        # to allow for this in the dlog function!  So we do not.

        # Store the actual generators (torsion first):
        gens = [z] + fu
        self.__nfu = len(fu)
        self.__gens = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:
        AbelianGroup_class.__init__(self, 1 + len(fu), [n] + [0] * len(fu), "u")
Esempio n. 4
0
    def __init__(self, number_field, proof=True):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            [-1, 6*a - 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3
            sage: UK.gens()
            [-1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K

        # compute the units via pari:
        fu = [K(u) for u in pK.bnfunit()]

        # compute a torsion generator and pick the 'simplest' one:
        n, z = pK.nfrootsof1()
        n = ZZ(n)
        self.__ntu = n
        z = K(z)

        # If we replaced z by another torsion generator we would need
        # to allow for this in the dlog function!  So we do not.

        # Store the actual generators (torsion first):
        gens = [z] + fu
        self.__nfu = len(fu)
        self.__gens = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:        
        AbelianGroup_class.__init__(self, 1+len(fu), [n]+[0]*len(fu), 'u')
Esempio n. 5
0
def prove_BSD(E, verbosity=0, two_desc='mwrank', proof=None, secs_hi=5,
                 return_BSD=False):
    r"""
    Attempts to prove the Birch and Swinnerton-Dyer conjectural
    formula for `E`, returning a list of primes `p` for which this
    function fails to prove BSD(E,p).  Here, BSD(E,p) is the
    statement: "the Birch and Swinnerton-Dyer formula holds up to a
    rational number coprime to `p`."

    INPUT:

        - ``E`` - an elliptic curve

        - ``verbosity`` - int, how much information about the proof to print.

            - 0 - print nothing
            - 1 - print sketch of proof
            - 2 - print information about remaining primes

        - ``two_desc`` - string (default ``'mwrank'``), what to use for the
          two-descent. Options are ``'mwrank', 'simon', 'sage'``

        - ``proof`` - bool or None (default: None, see
          proof.elliptic_curve or sage.structure.proof). If False, this
          function just immediately returns the empty list.

        - ``secs_hi`` - maximum number of seconds to try to compute the
          Heegner index before switching over to trying to compute the
          Heegner index bound. (Rank 0 only!)

        - ``return_BSD`` - bool (default: False) whether to return an object
          which contains information to reconstruct a proof

    NOTE:

    When printing verbose output, phrases such as "by Mazur" are referring
    to the following list of papers:

    REFERENCES:

    .. [Cha] \B. Cha. Vanishing of some cohomology goups and bounds for the
       Shafarevich-Tate groups of elliptic curves. J. Number Theory, 111:154-
       178, 2005.
    .. [Jetchev] \D. Jetchev. Global divisibility of Heegner points and
       Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826.
    .. [Kato] \K. Kato. p-adic Hodge theory and values of zeta functions of
       modular forms. Astérisque, (295):ix, 117-290, 2004.
    .. [Kolyvagin] \V. A. Kolyvagin. On the structure of Shafarevich-Tate
       groups. Algebraic geometry, 94--121, Lecture Notes in Math., 1479,
       Springer, Berlin, 1991.
    .. [LawsonWuthrich] \T. Lawson and C. Wuthrich, Vanishing of some Galois
       cohomology groups for elliptic curves, :arxiv:`1505.02940`
    .. [LumStein] \A. Lum, W. Stein. Verification of the Birch and
       Swinnerton-Dyer Conjecture for Elliptic Curves with Complex
       Multiplication (unpublished)
    .. [Mazur] \B. Mazur. Modular curves and the Eisenstein ideal. Inst.
       Hautes Études Sci. Publ. Math. No. 47 (1977), 33--186 (1978).
    .. [Rubin] \K. Rubin. The "main conjectures" of Iwasawa theory for
       imaginary quadratic fields. Invent. Math. 103 (1991), no. 1, 25--68.
    .. [SteinWuthrich] \W. Stein and C. Wuthrich, Algorithms
       for the Arithmetic of Elliptic Curves using Iwasawa Theory
       Mathematics of Computation 82 (2013), 1757-1792.
    .. [SteinEtAl] \G. Grigorov, A. Jorza, S. Patrikis, W. Stein,
       C. Tarniţǎ. Computational verification of the Birch and
       Swinnerton-Dyer conjecture for individual elliptic curves.
       Math. Comp. 78 (2009), no. 268, 2397--2425.


    EXAMPLES::

        sage: EllipticCurve('11a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Kolyvagin's bound for p = 5 applies by Lawson-Wuthrich
        True for p = 5 by Kolyvagin bound
        []

        sage: EllipticCurve('14a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        []

        sage: E = EllipticCurve("20a1")
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kato further implies that #Sha[3] is trivial.
        []

        sage: E = EllipticCurve("50b1")
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3, 5} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        Remaining primes:
        p = 5: reducible, not surjective, additive, divides a Tamagawa number
            (no bounds found)
            ord_p(#Sha_an) = 0
        [5]
        sage: E.prove_BSD(two_desc='simon')
        [5]

    A rank two curve::

        sage: E = EllipticCurve('389a')

    We know nothing with proof=True::

        sage: E.prove_BSD()
        Set of all prime numbers: 2, 3, 5, 7, ...

    We (think we) know everything with proof=False::

        sage: E.prove_BSD(proof=False)
        []

    A curve of rank 0 and prime conductor::

        sage: E = EllipticCurve('19a')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        []

        sage: E = EllipticCurve('37a')
        sage: E.rank()
        1
        sage: E._EllipticCurve_rational_field__rank
        (1, True)
        sage: E.analytic_rank = lambda : 0
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: It seems that the rank conjecture does not hold for this curve (Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field)! This may be a counterexample to BSD, but is more likely a bug.

    We test the consistency check for the 2-part of Sha::

        sage: E = EllipticCurve('37a')
        sage: S = E.sha(); S
        Tate-Shafarevich group for the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
        sage: def foo(use_database):
        ....:  return 4
        sage: S.an = foo
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: Apparent contradiction: 0 <= rank(sha[2]) <= 0, but ord_2(sha_an) = 2

    An example with a Tamagawa number at 5::

        sage: E = EllipticCurve('123a1')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Remaining primes:
        p = 5: reducible, not surjective, good ordinary, divides a Tamagawa number
            (no bounds found)
            ord_p(#Sha_an) = 0
        [5]

    A curve for which 3 divides the order of the Tate-Shafarevich group::

        sage: E = EllipticCurve('681b')
        sage: E.prove_BSD(verbosity=2)               # long time
        p = 2: True by 2-descent...
        True for p not in {2, 3} by Kolyvagin....
        Remaining primes:
        p = 3: irreducible, surjective, non-split multiplicative
            (0 <= ord_p <= 2)
            ord_p(#Sha_an) = 2
        [3]

    A curve for which we need to use ``heegner_index_bound``::

        sage: E = EllipticCurve('198b')
        sage: E.prove_BSD(verbosity=1, secs_hi=1)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        [3]

    The ``return_BSD`` option gives an object with detailed information
    about the proof::

        sage: E = EllipticCurve('26b')
        sage: B = E.prove_BSD(return_BSD=True)
        sage: B.two_tor_rk
        0
        sage: B.N
        26
        sage: B.gens
        []
        sage: B.primes
        []
        sage: B.heegner_indexes
        {-23: 2}

    TESTS:

    This was fixed by :trac:`8184` and :trac:`7575`::

        sage: EllipticCurve('438e1').prove_BSD(verbosity=1)
        p = 2: True by 2-descent...
        True for p not in {2} by Kolyvagin.
        []

    ::

        sage: E = EllipticCurve('960d1')
        sage: E.prove_BSD(verbosity=1)  # long time (4s on sage.math, 2011)
        p = 2: True by 2-descent
        True for p not in {2} by Kolyvagin.
        []

    """
    if proof is None:
        from sage.structure.proof.proof import get_flag
        proof = get_flag(proof, "elliptic_curve")
    else:
        proof = bool(proof)
    if not proof:
        return []
    from copy import copy
    BSD = BSD_data()
    # We replace this curve by the optimal curve, which we can do since
    # truth of BSD(E,p) is invariant under isogeny.
    BSD.curve = E.optimal_curve()
    if BSD.curve.has_cm():
        # ensure that CM is by a maximal order
        non_max_j_invs = [-12288000, 54000, 287496, 16581375]
        if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves?
            if verbosity > 0:
                print('CM by non maximal order: switching curves')
            for E in BSD.curve.isogeny_class():
                if E.j_invariant() not in non_max_j_invs:
                    BSD.curve = E
                    break
    BSD.update()
    galrep = BSD.curve.galois_representation()

    if two_desc=='mwrank':
        M = mwrank_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc=='simon':
        M = simon_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc=='sage':
        M = native_two_isogeny_descent_work(BSD.curve, BSD.two_tor_rk)
    else:
        raise NotImplementedError()
    rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens = M
    assert sha2_lower_bd <= sha2_upper_bd
    if gens is not None: gens = BSD.curve.saturation(gens)[0]
    if rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Apparent contradiction: %d <= rank <= %d."%(rank_lower_bd, rank_upper_bd))
    BSD.two_selmer_rank = rank_upper_bd + sha2_lower_bd + BSD.two_tor_rk
    if sha2_upper_bd == sha2_lower_bd:
        BSD.rank = rank_lower_bd
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
    else:
        BSD.rank = BSD.curve.rank(use_database=True)
        sha2_upper_bd -= (BSD.rank - rank_lower_bd)
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
        if verbosity > 0:
            print("Unable to compute the rank exactly -- used database.")
    if rank_lower_bd > 1:
        # We do not know BSD(E,p) for even a single p, since it's
        # an open problem to show that L^r(E,1)/(Reg*Omega) is
        # rational for any curve with r >= 2.
        from sage.sets.all import Primes
        BSD.primes = Primes()
        if return_BSD:
            BSD.rank = rank_lower_bd
            return BSD
        return BSD.primes
    if (BSD.sha_an.ord(2) == 0) != (BSD.bounds[2][1] == 0):
        raise RuntimeError("Apparent contradiction: %d <= rank(sha[2]) <= %d, but ord_2(sha_an) = %d"%(sha2_lower_bd, sha2_upper_bd, BSD.sha_an.ord(2)))
    if BSD.bounds[2][0] == BSD.sha_an.ord(2) and BSD.sha_an.ord(2) == BSD.bounds[2][1]:
        if verbosity > 0:
            print('p = 2: True by 2-descent')
        BSD.primes = []
        BSD.bounds.pop(2)
        BSD.proof[2] = ['2-descent']
    else:
        BSD.primes = [2]
        BSD.proof[2] = [('2-descent',)+BSD.bounds[2]]
    if len(gens) > rank_lower_bd or \
       rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Something went wrong with 2-descent.")
    if BSD.rank != len(gens):
        gens = BSD.curve.gens(proof=True)
        if BSD.rank != len(gens):
            raise RuntimeError("Could not get generators")
    BSD.gens = [BSD.curve.point(x, check=True) for x in gens]

    if BSD.rank != BSD.curve.analytic_rank():
        raise RuntimeError("It seems that the rank conjecture does not hold for this curve (%s)! This may be a counterexample to BSD, but is more likely a bug."%(BSD.curve))

    # reduce set of remaining primes to a finite set
    import signal
    kolyvagin_primes = []
    heegner_index = None
    if BSD.rank == 0:
        for D in BSD.curve.heegner_discriminants_list(10):
            max_height = max(13,BSD.curve.quadratic_twist(D).CPS_height_bound())
            heegner_primes = -1
            while heegner_primes == -1:
                if max_height > 21: break
                heegner_primes, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height)
                max_height += 1
            if isinstance(heegner_primes, list):
                break
        if not isinstance(heegner_primes, list):
            raise RuntimeError("Tried 10 Heegner discriminants, and heegner_index_bound failed each time.")
        if exact is not False:
            heegner_index = exact
            BSD.heegner_indexes[D] = exact
        else:
            BSD.heegner_index_upper_bound[D] = max(heegner_primes+[1])
        if 2 in heegner_primes:
            heegner_primes.remove(2)
    else: # rank 1
        for D in BSD.curve.heegner_discriminants_list(10):
            I = BSD.curve.heegner_index(D)
            J = I.is_int()
            if J[0] and J[1]>0:
                I = J[1]
            else:
                J = (2*I).is_int()
                if J[0] and J[1]>0:
                    I = J[1]
                else:
                    continue
            heegner_index = I
            BSD.heegner_indexes[D] = I
            break
        heegner_primes = [p for p in arith.prime_divisors(heegner_index) if p!=2]

    assert BSD.sha_an in ZZ and BSD.sha_an > 0
    if BSD.curve.has_cm():
        if BSD.curve.analytic_rank() == 0:
            if verbosity > 0:
                print('    p >= 5: true by Rubin')
            BSD.primes.append(3)
        else:
            K = rings.QuadraticField(BSD.curve.cm_discriminant(), 'a')
            D_K = K.disc()
            D_E = BSD.curve.discriminant()
            if len(K.factor(3)) == 1: # 3 does not split in K
                BSD.primes.append(3)
            for p in arith.prime_divisors(D_K):
                if p >= 5:
                    BSD.primes.append(p)
            for p in arith.prime_divisors(D_E):
                if p >= 5 and D_K%p and len(K.factor(p)) == 1: # p is inert in K
                    BSD.primes.append(p)
            for p in heegner_primes:
                if p >= 5 and D_E%p != 0 and D_K%p != 0 and len(K.factor(p)) == 1: # p is good for E and inert in K
                    kolyvagin_primes.append(p)
            for p in arith.prime_divisors(BSD.sha_an):
                if p >= 5 and D_K%p != 0 and len(K.factor(p)) == 1:
                    if BSD.curve.is_good(p):
                        if verbosity > 2 and p in heegner_primes and heegner_index is None:
                            print('ALERT: Prime p (%d) >= 5 dividing sha_an, good for E, inert in K, in heegner_primes, should not divide the actual Heegner index')
                        # Note that the following check is not entirely
                        # exhaustive, in case there is a p not dividing
                        # the Heegner index in heegner_primes,
                        # for which only an outer bound was computed
                        if p not in heegner_primes:
                            raise RuntimeError("p = %d divides sha_an, is of good reduction for E, inert in K, and does not divide the Heegner index. This may be a counterexample to BSD, but is more likely a bug. %s"%(p,BSD.curve))
            if verbosity > 0:
                print('True for p not in {%s} by Kolyvagin (via Stein & Lum -- unpublished) and Rubin.' % str(list(set(BSD.primes).union(set(kolyvagin_primes))))[1:-1])
        BSD.proof['finite'] = copy(BSD.primes)
    else: # no CM
        # do some tricks to get to a finite set without calling bound_kolyvagin
        BSD.primes += [p for p in galrep.non_surjective() if p != 2]
        for p in heegner_primes:
            if p not in BSD.primes:
                BSD.primes.append(p)
        for p in arith.prime_divisors(BSD.sha_an):
            if p not in BSD.primes and p != 2:
                BSD.primes.append(p)
        if verbosity > 0:
            s = str(BSD.primes)[1:-1]
            if 2 not in BSD.primes:
                if len(s) == 0: s = '2'
                else: s = '2, '+s
            print('True for p not in {' + s + '} by Kolyvagin.')
        BSD.proof['finite'] = copy(BSD.primes)
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_surjective(p) and not BSD.curve.has_additive_reduction(p):
                if BSD.curve.has_nonsplit_multiplicative_reduction(p):
                    if BSD.rank > 0:
                        continue
                if p==3:
                    if (not (BSD.curve.is_ordinary(p) and BSD.curve.is_good(p))) and (not BSD.curve.has_split_multiplicative_reduction(p)):
                        continue
                    if BSD.rank > 0:
                        continue
                if verbosity > 1:
                    print('    p = %d: Trying p_primary_bound' % p)
                p_bound = BSD.Sha.p_primary_bound(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Stein-Wuthrich', p_bound))
                else:
                    BSD.proof[p] = [('Stein-Wuthrich', p_bound)]
                if BSD.sha_an.ord(p) == 0 and p_bound == 0:
                    if verbosity > 0:
                        print('True for p=%d by Stein-Wuthrich.' % p)
                    primes_to_remove.append(p)
                else:
                    if p in BSD.bounds:
                        BSD.bounds[p][1] = min(BSD.bounds[p][1], p_bound)
                    else:
                        BSD.bounds[p] = (0, p_bound)
                    print('Analytic %d-rank is '%p + str(BSD.sha_an.ord(p)) + ', actual %d-rank is at most %d.' % (p, p_bound))
                    print('    by Stein-Wuthrich.\n')
        for p in primes_to_remove:
            BSD.primes.remove(p)
        kolyvagin_primes = []
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_surjective(p):
                kolyvagin_primes.append(p)
        for p in kolyvagin_primes:
            BSD.primes.remove(p)
    # apply other hypotheses which imply Kolyvagin's bound holds
    bounded_primes = []
    D_K = rings.QuadraticField(D, 'a').disc()

    # Cha's hypothesis
    for p in BSD.primes:
        if p == 2: continue
        if D_K%p != 0 and BSD.N%(p**2) != 0 and galrep.is_irreducible(p):
            if verbosity > 0:
                print('Kolyvagin\'s bound for p = %d applies by Cha.' % p)
            if p in BSD.proof:
                BSD.proof[p].append('Cha')
            else:
                BSD.proof[p] = ['Cha']
            kolyvagin_primes.append(p)
    # Stein et al replaced
    for p in BSD.primes:
        # the lemma about the vanishing of H^1 is false in Stein et al for p=5 and 11
        # here is the correction from Lawson-Wuthrich. Especially Theorem 14 in
        # [LawsonWuthrich] above.
        if p in kolyvagin_primes or p == 2 or D_K % p == 0:
            continue
        crit_lw = False
        if p > 11 or p == 7:
            crit_lw = True
        elif p == 11:
            if BSD.N != 121 or BSD.curve.label() != "121c2":
                crit_lw = True
        elif galrep.is_irreducible(p):
            crit_lw = True
        else:
            phis = BSD.curve.isogenies_prime_degree(p)
            if len(phis) != 1:
                crit_lw = True
            else:
                C = phis[0].codomain()
                if p == 3:
                    if BSD.curve.torsion_order() % p != 0 and C.torsion_order() % p != 0:
                        crit_lw = True
                else:  # p == 5
                    Et = BSD.curve.quadratic_twist(5)
                    if Et.torsion_order() % p != 0 and C.torsion_order() % p != 0:
                        crite_lw = True
        if crit_lw:
            if verbosity > 0:
                print('Kolyvagin\'s bound for p = %d applies by Lawson-Wuthrich' % p)
            kolyvagin_primes.append(p)
            if p in BSD.proof:
                BSD.proof[p].append('Lawson-Wuthrich')
            else:
                BSD.proof[p] = ['Lawson-Wuthrich']
    for p in kolyvagin_primes:
        if p in BSD.primes:
            BSD.primes.remove(p)

    # apply Kolyvagin's bound
    primes_to_remove = []
    for p in kolyvagin_primes:
        if p == 2: continue
        if p not in heegner_primes:
            ord_p_bound = 0
        elif heegner_index is not None: # p must divide heegner_index
            ord_p_bound = 2*heegner_index.ord(p)
            # Here Jetchev's results apply.
            m_max = max([BSD.curve.tamagawa_number(q).ord(p) for q in BSD.N.prime_divisors()])
            if m_max > 0:
                if verbosity > 0:
                    print('Jetchev\'s results apply (at p = %d) with m_max =' % p, m_max)
                if p in BSD.proof:
                    BSD.proof[p].append(('Jetchev',m_max))
                else:
                    BSD.proof[p] = [('Jetchev',m_max)]
            ord_p_bound -= 2*m_max
        else: # Heegner index is None
            for D in BSD.heegner_index_upper_bound:
                M = BSD.heegner_index_upper_bound[D]
                ord_p_bound = 0
                while p**(ord_p_bound+1) <= M**2:
                    ord_p_bound += 1
                # now ord_p_bound is one on I_K!!!
                ord_p_bound *= 2 # by Kolyvagin, now ord_p_bound is one on #Sha
                break
        if p in BSD.proof:
            BSD.proof[p].append(('Kolyvagin',ord_p_bound))
        else:
            BSD.proof[p] = [('Kolyvagin',ord_p_bound)]
        if BSD.sha_an.ord(p) == 0 and ord_p_bound == 0:
            if verbosity > 0:
                print('True for p = %d by Kolyvagin bound' % p)
            primes_to_remove.append(p)
        elif BSD.sha_an.ord(p) > ord_p_bound:
            raise RuntimeError("p = %d: ord_p_bound == %d, but sha_an.ord(p) == %d. This appears to be a counterexample to BSD, but is more likely a bug."%(p,ord_p_bound,BSD.sha_an.ord(p)))
        else: # BSD.sha_an.ord(p) <= ord_p_bound != 0:
            if p in BSD.bounds:
                low = BSD.bounds[p][0]
                BSD.bounds[p] = (low, min(BSD.bounds[p][1], ord_p_bound))
            else:
                BSD.bounds[p] = (0, ord_p_bound)
    for p in primes_to_remove:
        kolyvagin_primes.remove(p)
    BSD.primes = list( set(BSD.primes).union(set(kolyvagin_primes)) )

    # Kato's bound
    if BSD.rank == 0 and not BSD.curve.has_cm():
        L_over_Omega = BSD.curve.lseries().L_ratio()
        kato_primes = BSD.Sha.bound_kato()
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2: continue
            if p not in kato_primes:
                if verbosity > 0:
                    print('Kato further implies that #Sha[%d] is trivial.' % p)
                primes_to_remove.append(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Kato',0))
                else:
                    BSD.proof[p] = [('Kato',0)]
            if p not in [2,3] and BSD.N%p != 0:
                if galrep.is_surjective(p):
                    bd = L_over_Omega.valuation(p)
                    if verbosity > 1:
                        print('Kato implies that ord_p(#Sha[%d]) <= %d ' % (p, bd))
                    if p in BSD.proof:
                        BSD.proof[p].append(('Kato',bd))
                    else:
                        BSD.proof[p] = [('Kato',bd)]
                    if p in BSD.bounds:
                        low = BSD.bounds[p][0]
                        BSD.bounds[p][1] = (low, min(BSD.bounds[p][1], bd))
                    else:
                        BSD.bounds[p] = (0, bd)
        for p in primes_to_remove:
            BSD.primes.remove(p)

    # Mazur
    primes_to_remove = []
    if BSD.N.is_prime():
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_reducible(p):
                primes_to_remove.append(p)
                if verbosity > 0:
                    print('True for p=%s by Mazur' % p)
        for p in primes_to_remove:
            BSD.primes.remove(p)
            if p in BSD.proof:
                BSD.proof[p].append('Mazur')
            else:
                BSD.proof[p] = ['Mazur']

    BSD.primes.sort()

    # Try harder to compute the Heegner index, where it matters
    if heegner_index is None:
        if max_height < 18:
            max_height = 18
        for D in BSD.heegner_index_upper_bound:
            M = BSD.heegner_index_upper_bound[D]
            for p in kolyvagin_primes:
                if p not in BSD.primes or p == 3: continue
                if verbosity > 0:
                    print('    p = %d: Trying harder for Heegner index' % p)
                obt = 0
                while p**(BSD.sha_an.ord(p)/2+1) <= M and max_height < 22:
                    if verbosity > 2:
                        print('    trying max_height =', max_height)
                    old_bound = M
                    M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_hi)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print('    heegner index =', M)
                    else:
                        M = max(M+[1])
                        if verbosity > 2:
                            print('    bound =', M)
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = min(M,BSD.heegner_index_upper_bound[D])
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn+1) <= M:
                    expn += 1
                if 2*expn < upp:
                    upp = 2*expn
                    BSD.bounds[p] = (low,upp)
                    if verbosity > 0:
                        print('    got better bound on ord_p =', upp)
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print('    proven!')
                            BSD.primes.remove(p)
            break
        for p in kolyvagin_primes:
            if p not in BSD.primes or p == 3: continue
            for D in BSD.curve.heegner_discriminants_list(4):
                if D in BSD.heegner_index_upper_bound: continue
                print('    discriminant', D)
                if verbosity > 0:
                    print('p = %d: Trying discriminant = %d for Heegner index' % (p, D))
                max_height = max(10, BSD.curve.quadratic_twist(D).CPS_height_bound())
                obt = 0
                while True:
                    if verbosity > 2:
                        print('    trying max_height =', max_height)
                    old_bound = M
                    if p**(BSD.sha_an.ord(p)/2+1) > M or max_height >= 22:
                        break
                    M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_hi)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print('    heegner index =', M)
                    else:
                        M = max(M+[1])
                        if verbosity > 2:
                            print('    bound =', M)
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = M
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn+1) <= M:
                    expn += 1
                if 2*expn < upp:
                    upp = 2*expn
                    BSD.bounds[p] = (low,upp)
                    if verbosity > 0:
                        print('    got better bound =', upp)
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print('    proven!')
                            BSD.primes.remove(p)
                            break

    # print some extra information
    if verbosity > 1:
        if len(BSD.primes) > 0:
            print('Remaining primes:')
        for p in BSD.primes:
            s = 'p = ' + str(p) + ': '
            if galrep.is_irreducible(p):
                s += 'ir'
            s += 'reducible, '
            if not galrep.is_surjective(p):
                s += 'not '
            s += 'surjective, '
            a_p = BSD.curve.an(p)
            if BSD.curve.is_good(p):
                if a_p%p != 0:
                    s += 'good ordinary'
                else:
                    s += 'good, non-ordinary'
            else:
                assert BSD.curve.is_minimal()
                if a_p == 0:
                    s += 'additive'
                elif a_p == 1:
                    s += 'split multiplicative'
                elif a_p == -1:
                    s += 'non-split multiplicative'
            if BSD.curve.tamagawa_product()%p==0:
                s += ', divides a Tamagawa number'
            if p in BSD.bounds:
                s += '\n    (%d <= ord_p <= %d)'%BSD.bounds[p]
            else:
                s += '\n    (no bounds found)'
            s += '\n    ord_p(#Sha_an) = %d'%BSD.sha_an.ord(p)
            if heegner_index is None:
                may_divide = True
                for D in BSD.heegner_index_upper_bound:
                    if p > BSD.heegner_index_upper_bound[D] or p not in kolyvagin_primes:
                        may_divide = False
                if may_divide:
                    s += '\n    may divide the Heegner index, for which only a bound was computed'
            print(s)

    if BSD.curve.has_cm():
        if BSD.rank == 1:
            BSD.proof['reason_finite'] = 'Rubin&Kolyvagin'
        else:
            BSD.proof['reason_finite'] = 'Rubin'
    else:
        BSD.proof['reason_finite'] = 'Kolyvagin'
    # reduce memory footprint of BSD object:
    BSD.curve = BSD.curve.label()
    BSD.Sha = None
    return BSD if return_BSD else BSD.primes
Esempio n. 6
0
def discriminants_with_bounded_class_number(hmax, B=None, proof=None):
    r"""
    Return dictionary with keys class numbers `h\le hmax` and values the
    list of all pairs `(D, f)`, with `D<0` a fundamental discriminant such
    that `Df^2` has class number `h`.  If the optional bound `B` is given,
    return only those pairs with fundamental `|D| \le B`, though `f` can
    still be arbitrarily large.

    INPUT:

    - ``hmax`` -- integer
    - `B` -- integer or None; if None returns all pairs
    - ``proof`` -- this code calls the PARI function ``qfbclassno``, so it
      could give wrong answers when ``proof``==``False``.  The default is
      whatever ``proof.number_field()`` is.  If ``proof==False`` and `B` is
      ``None``, at least the number of discriminants is correct, since it
      is double checked with Watkins's table.

    OUTPUT:

    - dictionary

    In case `B` is not given, we use Mark Watkins's: "Class numbers of
    imaginary quadratic fields" to compute a `B` that captures all `h`
    up to `hmax` (only available for `hmax\le100`).

    EXAMPLES::

        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(3)
        sage: sorted(v)
        [1, 2, 3]
        sage: v[1]
        [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
        sage: v[2]
        [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
        sage: v[3]
        [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2), (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1), (-643, 1), (-883, 1), (-907, 1)]
        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(8, proof=False)
        sage: sorted(len(v[h]) for h in v)
        [13, 25, 29, 29, 38, 84, 101, 208]

    Find all class numbers for discriminant up to 50::

        sage: sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
        {1: [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2)], 4: [(-3, 13), (-3, 11), (-3, 8), (-4, 10), (-4, 8), (-4, 7), (-4, 6), (-7, 8), (-7, 6), (-7, 3), (-8, 6), (-8, 4), (-11, 5), (-15, 4), (-19, 5), (-19, 3), (-20, 3), (-20, 2), (-24, 2), (-35, 3), (-39, 2), (-39, 1), (-40, 2), (-43, 3)], 5: [(-47, 2), (-47, 1)]}
    """
    # imports that are needed only for this function
    from sage.structure.proof.proof import get_flag

    # deal with input defaults and type checking
    proof = get_flag(proof, 'number_field')
    hmax = Integer(hmax)

    # T stores the output
    T = {}

    # Easy case -- instead of giving error, give meaningful output
    if hmax < 1:
        return T

    if B is None:
        # Determine how far we have to go by applying Watkins's theorem.
        v = [
            largest_fundamental_disc_with_class_number(h)
            for h in range(1, hmax + 1)
        ]
        B = max([b for b, _ in v])
        fund_count = [0] + [cnt for _, cnt in v]
    else:
        # Nothing to do -- set to None so we can use this later to know not
        # to do a double check about how many we find.
        fund_count = None
        B = Integer(B)

    if B <= 2:
        # This is an easy special case, since there are no fundamental discriminants
        # this small.
        return T

    # This lower bound gets used in an inner loop below.
    from math import log

    def lb(f):
        """Lower bound on euler_phi."""
        # 1.79 > e^gamma = 1.7810724...
        if f <= 1: return 0  # don't do log(log(1)) = log(0)
        return f / (1.79 * log(log(f)) + 3.0 / log(log(f)))

    for D in range(-B, -2):
        D = Integer(D)
        if is_fundamental_discriminant(D):
            h_D = D.class_number(proof)
            # For each fundamental discriminant D, loop through the f's such
            # that h(D*f^2) could possibly be <= hmax.  As explained to me by Cremona,
            # we have h(D*f^2) >= (1/c)*h(D)*phi_D(f) >= (1/c)*h(D)*euler_phi(f), where
            # phi_D(f) is like euler_phi(f) but the factor (1-1/p) is replaced
            # by a factor of (1-kr(D,p)*p), where kr(D/p) is the Kronecker symbol.
            # The factor c is 1 unless D=-4 and f>1 (when c=2) or D=-3 and f>1 (when c=3).
            # Since (1-1/p) <= 1 and (1-1/p) <= (1+1/p), we see that
            #     euler_phi(f) <= phi_D(f).
            #
            # We have the following analytic lower bound on euler_phi:
            #
            #     euler_phi(f) >= lb(f) = f / (exp(euler_gamma)*log(log(n)) + 3/log(log(n))).
            #
            # See Theorem 8 of Peter Clark's
            #   http://math.uga.edu/~pete/4400arithmeticorders.pdf
            # which is a consequence of Theorem 15 of
            # [Rosser and Schoenfeld, 1962].
            #
            # By Calculus, we see that the lb(f) is an increasing function of f >= 2.
            #
            # NOTE: You can visibly "see" that it is a lower bound in Sage with
            #   lb(n) = n/(exp(euler_gamma)*log(log(n)) + 3/log(log(n)))
            #   plot(lb, (n, 1, 10^4), color='red') + plot(lambda x: euler_phi(int(x)), 1, 10^4).show()
            #
            # So we consider f=1,2,..., until the first f with lb(f)*h_D > c*h_max.
            # (Note that lb(f) is <= 0 for f=1,2, so nothing special is needed there.)
            #
            # TODO: Maybe we could do better using a bound for phi_D(f).
            #
            f = Integer(1)
            chmax = hmax
            if D == -3:
                chmax *= 3
            else:
                if D == -4:
                    chmax *= 2
            while lb(f) * h_D <= chmax:
                if f == 1:
                    h = h_D
                else:
                    h = (D * f * f).class_number(proof)
                # If the class number of this order is within the range, then
                # use it.  (NOTE: In some cases there is a simple relation between
                # the class number for D and D*f^2, and this could be used to
                # optimize this inner loop a little.)
                if h <= hmax:
                    z = (D, f)
                    if h in T:
                        T[h].append(z)
                    else:
                        T[h] = [z]
                f += 1

    for h in T:
        T[h] = list(reversed(T[h]))

    if fund_count is not None:
        # Double check that we found the right number of fundamental
        # discriminants; we might as well, since Watkins provides this
        # data.
        for h in T:
            if len([DD for DD, ff in T[h] if ff == 1]) != fund_count[h]:
                raise RuntimeError(
                    "number of discriminants inconsistent with Watkins's table"
                )

    return T
Esempio n. 7
0
File: cm.py Progetto: mcognetta/sage
def discriminants_with_bounded_class_number(hmax, B=None, proof=None):
    """
    Return dictionary with keys class numbers `h\le hmax` and values the
    list of all pairs `(D, f)`, with `D<0` a fundamental discriminant such
    that `Df^2` has class number `h`.  If the optional bound `B` is given,
    return only those pairs with fundamental `|D| \le B`, though `f` can
    still be arbitrarily large.

    INPUT:

    - ``hmax`` -- integer
    - `B` -- integer or None; if None returns all pairs
    - ``proof`` -- this code calls the PARI function ``qfbclassno``, so it
      could give wrong answers when ``proof``==``False``.  The default is
      whatever ``proof.number_field()`` is.  If ``proof==False`` and `B` is
      ``None``, at least the number of discriminants is correct, since it
      is double checked with Watkins's table.

    OUTPUT:

    - dictionary

    In case `B` is not given, we use Mark Watkins's: "Class numbers of
    imaginary quadratic fields" to compute a `B` that captures all `h`
    up to `hmax` (only available for `hmax\le100`).

    EXAMPLES::

        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(3)
        sage: list(v)
        [1, 2, 3]
        sage: v[1]
        [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
        sage: v[2]
        [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
        sage: v[3]
        [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2), (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1), (-643, 1), (-883, 1), (-907, 1)]
        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(8, proof=False)
        sage: [len(v[h]) for h in v]
        [13, 29, 25, 84, 29, 101, 38, 208]

    Find all class numbers for discriminant up to 50::

        sage: sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
        {1: [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2)], 4: [(-3, 13), (-3, 11), (-3, 8), (-4, 10), (-4, 8), (-4, 7), (-4, 6), (-7, 8), (-7, 6), (-7, 3), (-8, 6), (-8, 4), (-11, 5), (-15, 4), (-19, 5), (-19, 3), (-20, 3), (-20, 2), (-24, 2), (-35, 3), (-39, 2), (-39, 1), (-40, 2), (-43, 3)], 5: [(-47, 2), (-47, 1)]}
    """
    # imports that are needed only for this function
    from sage.structure.proof.proof import get_flag
    import math
    from sage.misc.functional import round

    # deal with input defaults and type checking
    proof = get_flag(proof, 'number_field')
    hmax = Integer(hmax)

    # T stores the output
    T = {}

    # Easy case -- instead of giving error, give meaningful output
    if hmax < 1:
        return T

    if B is None:
        # Determine how far we have to go by applying Watkins's theorem.
        v = [largest_fundamental_disc_with_class_number(h) for h in range(1, hmax+1)]
        B = max([b for b,_ in v])
        fund_count = [0] + [cnt for _,cnt in v]
    else:
        # Nothing to do -- set to None so we can use this later to know not
        # to do a double check about how many we find.
        fund_count = None
        B = Integer(B)

    if B <= 2:
        # This is an easy special case, since there are no fundamental discriminants
        # this small.
        return T

    # This lower bound gets used in an inner loop below.
    from math import log
    def lb(f):
        """Lower bound on euler_phi."""
        # 1.79 > e^gamma = 1.7810724...
        if f <= 1: return 0  # don't do log(log(1)) = log(0)
        return f/(1.79*log(log(f)) + 3.0/log(log(f)))

    for D in range(-B, -2):
        D = Integer(D)
        if is_fundamental_discriminant(D):
            h_D = D.class_number(proof)
            # For each fundamental discriminant D, loop through the f's such
            # that h(D*f^2) could possibly be <= hmax.  As explained to me by Cremona,
            # we have h(D*f^2) >= (1/c)*h(D)*phi_D(f) >= (1/c)*h(D)*euler_phi(f), where
            # phi_D(f) is like euler_phi(f) but the factor (1-1/p) is replaced
            # by a factor of (1-kr(D,p)*p), where kr(D/p) is the Kronecker symbol.
            # The factor c is 1 unless D=-4 and f>1 (when c=2) or D=-3 and f>1 (when c=3).
            # Since (1-1/p) <= 1 and (1-1/p) <= (1+1/p), we see that
            #     euler_phi(f) <= phi_D(f).
            #
            # We have the following analytic lower bound on euler_phi:
            #
            #     euler_phi(f) >= lb(f) = f / (exp(euler_gamma)*log(log(n)) + 3/log(log(n))).
            #
            # See Theorem 8 of Peter Clark's
            #   http://math.uga.edu/~pete/4400arithmeticorders.pdf
            # which is a consequence of Theorem 15 of
            # [Rosser and Schoenfeld, 1962].
            #
            # By Calculus, we see that the lb(f) is an increasing function of f >= 2.
            #
            # NOTE: You can visibly "see" that it is a lower bound in Sage with
            #   lb(n) = n/(exp(euler_gamma)*log(log(n)) + 3/log(log(n)))
            #   plot(lb, (n, 1, 10^4), color='red') + plot(lambda x: euler_phi(int(x)), 1, 10^4).show()
            #
            # So we consider f=1,2,..., until the first f with lb(f)*h_D > c*h_max.
            # (Note that lb(f) is <= 0 for f=1,2, so nothing special is needed there.)
            #
            # TODO: Maybe we could do better using a bound for for phi_D(f).
            #
            f = Integer(1)
            chmax=hmax
            if D==-3:
                chmax*=3
            else:
                if D==-4:
                    chmax*=2
            while lb(f)*h_D <= chmax:
                if f == 1:
                    h = h_D
                else:
                    h = (D*f*f).class_number(proof)
                # If the class number of this order is within the range, then
                # use it.  (NOTE: In some cases there is a simple relation between
                # the class number for D and D*f^2, and this could be used to
                # optimize this inner loop a little.)
                if h <= hmax:
                    z = (D, f)
                    if h in T:
                        T[h].append(z)
                    else:
                        T[h] = [z]
                f += 1

    for h in T:
        T[h] = list(reversed(T[h]))

    if fund_count is not None:
        # Double check that we found the right number of fundamental
        # discriminants; we might as well, since Watkins provides this
        # data.
        for h in T:
            if len([D for D,f in T[h] if f==1]) != fund_count[h]:
                raise RuntimeError("number of discriminants inconsistent with Watkins's table")

    return T
Esempio n. 8
0
    def __init__(self, number_field, proof=True, S=None):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag
        - ``S`` - tuple of prime ideals, or an ideal, or a single
          ideal or element from which an ideal can be constructed, in
          which case the support is used.  If None, the global unit
          group is constructed; otherwise, the S-unit group is
          constructed.

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            (u0, u1)
            sage: UK.gens_values()
            [-1, 6*a - 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3
            sage: UK.gens()
            (u,)
            sage: UK.gens_values()
            [-1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens()
            (u0, u1, u2, u3, u4, u5)
            sage: UK.gens_values() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            sage: SUK = UnitGroup(K,S=2); SUK
            S-unit group with structure C26 x Z x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 with S = (Fractional ideal (2),)

            """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K
        self.__pari_number_field = pK

        # process the parameter S:
        if not S:
            S = self.__S = ()
        else:
            if isinstance(S, list):
                S = tuple(S)
            if not isinstance(S, tuple):
                try:
                    S = tuple(K.ideal(S).prime_factors())
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
            else:
                try:
                    S = tuple(K.ideal(P) for P in S)
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
                if not all([P.is_prime() for P in S]):
                    raise ValueError(
                        "Not all elements of %s are prime ideals" % (S, ))
            self.__S = S
            self.__pS = pS = [P.pari_prime() for P in S]

        # compute the fundamental units via pari:
        fu = [K(u) for u in pK.bnfunit()]
        self.__nfu = len(fu)

        # compute the additional S-unit generators:
        if S:
            self.__S_unit_data = pK.bnfsunit(pS)
            su = [K(u) for u in self.__S_unit_data[0]]
        else:
            su = []
        self.__nsu = len(su)
        self.__rank = self.__nfu + self.__nsu

        # compute a torsion generator and pick the 'simplest' one:
        n, z = pK.nfrootsof1()
        n = ZZ(n)
        self.__ntu = n
        z = K(z)

        # If we replaced z by another torsion generator we would need
        # to allow for this in the dlog function!  So we do not.

        # Store the actual generators (torsion first):
        gens = [z] + fu + su
        values = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:
        gens_orders = tuple([ZZ(n)] + [ZZ(0)] * (self.__rank))
        AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values,
                                              number_field)
Esempio n. 9
0
    def __init__(self, number_field, proof=True, S=None):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag
        - ``S`` - tuple of prime ideals, or an ideal, or a single
          ideal or element from which an ideal can be constructed, in
          which case the support is used.  If None, the global unit
          group is constructed; otherwise, the S-unit group is
          constructed.

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            (u0, u1)
            sage: UK.gens_values()
            [-1, 6*a - 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3 with a = 1.732050807568878?*I
            sage: UK.gens()
            (u,)
            sage: UK.gens_values()
            [1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens()
            (u0, u1, u2, u3, u4, u5)
            sage: UK.gens_values() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            sage: SUK = UnitGroup(K,S=2); SUK
            S-unit group with structure C26 x Z x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 with S = (Fractional ideal (2),)

        TESTS:

        Number fields defined by non-monic and non-integral
        polynomials are supported (:trac:`252`);
        the representation depends on the PARI version::

            sage: K.<a> = NumberField(7/9*x^3 + 7/3*x^2 - 56*x + 123)
            sage: K.unit_group()
            Unit group with structure C2 x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123
            sage: UnitGroup(K, S=tuple(K.primes_above(7)))
            S-unit group with structure C2 x Z x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123 with S = (Fractional ideal (...),)
            sage: K.primes_above(7)[0] in (7/225*a^2 - 7/75*a - 42/25, 28/225*a^2 + 77/75*a - 133/25)
            True

        Conversion from unit group to a number field and back
        gives the right results (:trac:`25874`)::

            sage: K = QuadraticField(-3).composite_fields(QuadraticField(2))[0]
            sage: U = K.unit_group()
            sage: tuple(U(K(u)) for u in U.gens()) == U.gens()
            True
            sage: US = K.S_unit_group(3)
            sage: tuple(US(K(u)) for u in US.gens()) == US.gens()
            True

        """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K
        self.__pari_number_field = pK

        # process the parameter S:
        if not S:
            S = self.__S = ()
        else:
            if isinstance(S, list):
                S = tuple(S)
            if not isinstance(S, tuple):
                try:
                    S = tuple(K.ideal(S).prime_factors())
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
            else:
                try:
                    S = tuple(K.ideal(P) for P in S)
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
                if not all(P.is_prime() for P in S):
                    raise ValueError(
                        "Not all elements of %s are prime ideals" % (S, ))
            self.__S = S
            self.__pS = pS = [P.pari_prime() for P in S]

        # compute the fundamental units via pari:
        fu = [K(u, check=False) for u in pK.bnfunit()]
        self.__nfu = len(fu)

        # compute the additional S-unit generators:
        if S:
            self.__S_unit_data = pK.bnfsunit(pS)
            su = [K(u, check=False) for u in self.__S_unit_data[0]]
        else:
            su = []
        self.__nsu = len(su)
        self.__rank = self.__nfu + self.__nsu

        # compute a torsion generator and pick the 'simplest' one:
        n, z = pK[7][
            3]  # number of roots of unity and bnf.tu as in pari documentation
        n = ZZ(n)
        self.__ntu = n
        z = K(z, check=False)

        # If we replaced z by another torsion generator we would need
        # to allow for this in the dlog function!  So we do not.

        # Store the actual generators (torsion first):
        gens = [z] + fu + su
        values = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:
        gens_orders = tuple([ZZ(n)] + [ZZ(0)] * (self.__rank))
        AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values,
                                              number_field)
Esempio n. 10
0
def egros_get_j(S=[], proof=None, verbose=False):
    r"""
    Returns a list of rational `j` such that all elliptic curves
    defined over `\QQ` with good reduction outside `S` have
    `j`-invariant in the list, sorted by height.

    INPUT:

    - ``S`` -- list of primes (default: empty list).

    - ``proof`` -- ``True``/``False`` (default ``True``): the MW basis for
      auxiliary curves will be computed with this proof flag.

    - ``verbose`` -- ``True``/``False`` (default ``False````): if ``True``, some
      details of the computation will be output.

    .. note::

        Proof flag: The algorithm used requires determining all
        S-integral points on several auxiliary curves, which in turn
        requires the computation of their generators.  This is not
        always possible (even in theory) using current knowledge.

        The value of this flag is passed to the function which
        computes generators of various auxiliary elliptic curves, in
        order to find their S-integral points.  Set to ``False`` if the
        default (``True``) causes warning messages, but note that you can
        then not rely on the set of invariants returned being
        complete.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.ell_egros import egros_get_j
        sage: egros_get_j([])
        [1728]
        sage: egros_get_j([2])  # long time (3s on sage.math, 2013)
        [128, 432, -864, 1728, 3375/2, -3456, 6912, 8000, 10976, -35937/4, 287496, -784446336, -189613868625/128]
        sage: egros_get_j([3])  # long time (3s on sage.math, 2013)
        [0, -576, 1536, 1728, -5184, -13824, 21952/9, -41472, 140608/3, -12288000]
        sage: jlist=egros_get_j([2,3]); len(jlist) # long time (30s)
        83

    """
    if not all([p.is_prime() for p in S]):
        raise ValueError("Elements of S must be prime.")

        if proof is None:
            from sage.structure.proof.proof import get_flag
            proof = get_flag(proof, "elliptic_curve")
        else:
            proof = bool(proof)

    if verbose:
        import sys  # so we can flush stdout for debugging

    SS = [-1] + S

    jlist=[]
    wcount=0
    nw = 6**len(S) * 2

    if verbose:
        print "Finding possible j invariants for S = ",S
        print "Using ", nw, " twists of base curve"
        sys.stdout.flush()

    for ei in xmrange([6]*len(S) + [2]):
        w = prod([p**e for p,e in zip(reversed(SS),ei)],QQ(1))
        wcount+=1
        if verbose:
            print "Curve #",wcount, "/",nw,":";
            print "w = ",w,"=",w.factor()
            sys.stdout.flush()
        a6 = -1728*w
        d2 = 0
        d3 = 0
        u0 = (2**d2)*(3**d3)
        E = EllipticCurve([0,0,0,0,a6])
        # This curve may not be minimal at 2 or 3, but the
        # S-integral_points function requires minimality at primes in
        # S, so we find a new model which is p-minimal at both 2 and 3
        # if they are in S.  Note that the isomorphism between models
        # will preserve S-integrality of points.
        E2 = E.local_minimal_model(2) if 2 in S else E
        E23 = E2.local_minimal_model(3) if 3 in S else E2
        urst = E23.isomorphism_to(E)

        try:
            pts = E23.S_integral_points(S,proof=proof)
        except RuntimeError:
            pts=[]
            print "Failed to find S-integral points on ",E23.ainvs()
            if proof:
                if verbose:
                    print "--trying again with proof=False"
                    sys.stdout.flush()
                pts = E23.S_integral_points(S,proof=False)
                if verbose:
                    print "--done"
        if verbose:
            print len(pts), " S-integral points: ",pts
            sys.stdout.flush()
        for P in pts:
            P = urst(P)
            x = P[0]
            y = P[1]
            j = x**3 /w
            assert j-1728 == y**2 /w
            if is_possible_j(j,S):
                if not j in jlist:
                    if verbose:
                        print "Adding possible j = ",j
                        sys.stdout.flush()
                    jlist += [j]
            else:
                if True: #verbose:
                    print "Discarding illegal j = ",j
                    sys.stdout.flush()
    return sorted(jlist, key=lambda j: j.height())
Esempio n. 11
0
    def __init__(self, number_field, proof=True, S=None):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag
        - ``S`` - tuple of prime ideals, or an ideal, or a single
          ideal or element from which an ideal can be constructed, in
          which case the support is used.  If None, the global unit
          group is constructed; otherwise, the S-unit group is
          constructed.

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            (u0, u1)
            sage: UK.gens_values()
            [-1, 6*a - 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3
            sage: UK.gens()
            (u,)
            sage: UK.gens_values()
            [-1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens()
            (u0, u1, u2, u3, u4, u5)
            sage: UK.gens_values() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            sage: SUK = UnitGroup(K,S=2); SUK
            S-unit group with structure C26 x Z x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 with S = (Fractional ideal (2),)

            """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K
        self.__pari_number_field = pK

        # process the parameter S:
        if not S:
            S = self.__S = ()
        else:
            if type(S)==list:
                S = tuple(S)
            if not type(S)==tuple:
                try:
                    S = tuple(K.ideal(S).prime_factors())
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s"%(S,))
            else:
                try:
                    S = tuple(K.ideal(P) for P in S)
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s"%(S,))
                if not all([P.is_prime() for P in S]):
                    raise ValueError("Not all elements of %s are prime ideals"%(S,))
            self.__S = S
            self.__pS = pS = [P.pari_prime() for P in S]

        # compute the fundamental units via pari:
        fu = [K(u) for u in pK.bnfunit()]
        self.__nfu = len(fu)

        # compute the additional S-unit generators:
        if S:
            self.__S_unit_data = pK.bnfsunit(pS)
            su = [K(u) for u in self.__S_unit_data[0]]
        else:
            su = []
        self.__nsu = len(su)
        self.__rank = self.__nfu + self.__nsu

        # compute a torsion generator and pick the 'simplest' one:
        n, z = pK.nfrootsof1()
        n = ZZ(n)
        self.__ntu = n
        z = K(z)

        # If we replaced z by another torsion generator we would need
        # to allow for this in the dlog function!  So we do not.

        # Store the actual generators (torsion first):
        gens = [z] + fu + su
        values = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:
        gens_orders = tuple([ZZ(n)]+[ZZ(0)]*(self.__rank))
        AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values, number_field)
Esempio n. 12
0
def prove_BSD(E, verbosity=0, two_desc='mwrank', proof=None, secs_hi=5,
                 return_BSD=False):
    r"""
    Attempts to prove the Birch and Swinnerton-Dyer conjectural
    formula for `E`, returning a list of primes `p` for which this
    function fails to prove BSD(E,p).  Here, BSD(E,p) is the
    statement: "the Birch and Swinnerton-Dyer formula holds up to a
    rational number coprime to `p`."

    INPUT:

        - ``E`` - an elliptic curve

        - ``verbosity`` - int, how much information about the proof to print.

            - 0 - print nothing
            - 1 - print sketch of proof
            - 2 - print information about remaining primes

        - ``two_desc`` - string (default ``'mwrank'``), what to use for the
          two-descent. Options are ``'mwrank', 'simon', 'sage'``

        - ``proof`` - bool or None (default: None, see
          proof.elliptic_curve or sage.structure.proof). If False, this
          function just immediately returns the empty list.

        - ``secs_hi`` - maximum number of seconds to try to compute the
          Heegner index before switching over to trying to compute the
          Heegner index bound. (Rank 0 only!)

        - ``return_BSD`` - bool (default: False) whether to return an object
          which contains information to reconstruct a proof

    NOTE:

    When printing verbose output, phrases such as "by Mazur" are referring
    to the following list of papers:

    REFERENCES:

    .. [Cha] B. Cha. Vanishing of some cohomology goups and bounds for the
       Shafarevich-Tate groups of elliptic curves. J. Number Theory, 111:154-
       178, 2005.
    .. [Jetchev] D. Jetchev. Global divisibility of Heegner points and
       Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826.
    .. [Kato] K. Kato. p-adic Hodge theory and values of zeta functions of
       modular forms. Astérisque, (295):ix, 117-290, 2004.
    .. [Kolyvagin] V. A. Kolyvagin. On the structure of Shafarevich-Tate
       groups. Algebraic geometry, 94--121, Lecture Notes in Math., 1479,
       Springer, Berlin, 1991.
    .. [LumStein] A. Lum, W. Stein. Verification of the Birch and
       Swinnerton-Dyer Conjecture for Elliptic Curves with Complex
       Multiplication (unpublished)
    .. [Mazur] B. Mazur. Modular curves and the Eisenstein ideal. Inst.
       Hautes Études Sci. Publ. Math. No. 47 (1977), 33--186 (1978).
    .. [Rubin] K. Rubin. The "main conjectures" of Iwasawa theory for
       imaginary quadratic fields. Invent. Math. 103 (1991), no. 1, 25--68.
    .. [SteinWuthrich] W. Stein and C. Wuthrich, Algorithms
       for the Arithmetic of Elliptic Curves using Iwasawa Theory
       Mathematics of Computation 82 (2013), 1757-1792.
    .. [SteinEtAl] G. Grigorov, A. Jorza, S. Patrikis, W. Stein,
       C. Tarniţǎ. Computational verification of the Birch and
       Swinnerton-Dyer conjecture for individual elliptic curves.
       Math. Comp. 78 (2009), no. 268, 2397--2425.


    EXAMPLES::

        sage: EllipticCurve('11a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Kato further implies that #Sha[5] is trivial.
        []

        sage: EllipticCurve('14a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kato further implies that #Sha[3] is trivial.
        []

        sage: E = EllipticCurve("50b1")
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3, 5} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Stein et al.
        True for p = 3 by Kolyvagin bound
        Remaining primes:
        p = 5: reducible, not surjective, additive, divides a Tamagawa number
            (no bounds found)
            ord_p(#Sha_an) = 0
        [5]
        sage: E.prove_BSD(two_desc='simon')
        [5]

    A rank two curve::

        sage: E = EllipticCurve('389a')

    We know nothing with proof=True::

        sage: E.prove_BSD()
        Set of all prime numbers: 2, 3, 5, 7, ...

    We (think we) know everything with proof=False::

        sage: E.prove_BSD(proof=False)
        []

    A curve of rank 0 and prime conductor::

        sage: E = EllipticCurve('19a')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kato further implies that #Sha[3] is trivial.
        []

        sage: E = EllipticCurve('37a')
        sage: E.rank()
        1
        sage: E._EllipticCurve_rational_field__rank
        {True: 1}
        sage: E.analytic_rank = lambda : 0
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: It seems that the rank conjecture does not hold for this curve (Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field)! This may be a counterexample to BSD, but is more likely a bug.

    We test the consistency check for the 2-part of Sha::

        sage: E = EllipticCurve('37a')
        sage: S = E.sha(); S
        Tate-Shafarevich group for the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
        sage: def foo(use_database):
        ...    return 4
        sage: S.an = foo
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: Apparent contradiction: 0 <= rank(sha[2]) <= 0, but ord_2(sha_an) = 2

    An example with a Tamagawa number at 5::

        sage: E = EllipticCurve('123a1')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Remaining primes:
        p = 5: reducible, not surjective, good ordinary, divides a Tamagawa number
            (no bounds found)
            ord_p(#Sha_an) = 0
        [5]

    A curve for which 3 divides the order of the Tate-Shafarevich group::

        sage: E = EllipticCurve('681b')
        sage: E.prove_BSD(verbosity=2)               # long time
        p = 2: True by 2-descent...
        True for p not in {2, 3} by Kolyvagin....
        Remaining primes:
        p = 3: irreducible, surjective, non-split multiplicative
            (0 <= ord_p <= 2)
            ord_p(#Sha_an) = 2
        [3]

    A curve for which we need to use ``heegner_index_bound``::

        sage: E = EllipticCurve('198b')
        sage: E.prove_BSD(verbosity=1, secs_hi=1)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        [3]

    The ``return_BSD`` option gives an object with detailed information
    about the proof::

        sage: E = EllipticCurve('26b')
        sage: B = E.prove_BSD(return_BSD=True)
        sage: B.two_tor_rk
        0
        sage: B.N
        26
        sage: B.gens
        []
        sage: B.primes
        []
        sage: B.heegner_indexes
        {-23: 2}

    TESTS:

    This was fixed by trac #8184 and #7575::

        sage: EllipticCurve('438e1').prove_BSD(verbosity=1)
        p = 2: True by 2-descent...
        True for p not in {2} by Kolyvagin.
        []

    ::

        sage: E = EllipticCurve('960d1')
        sage: E.prove_BSD(verbosity=1)  # long time (4s on sage.math, 2011)
        p = 2: True by 2-descent
        True for p not in {2} by Kolyvagin.
        []

    """
    if proof is None:
        from sage.structure.proof.proof import get_flag
        proof = get_flag(proof, "elliptic_curve")
    else:
        proof = bool(proof)
    if not proof:
        return []
    from copy import copy
    BSD = BSD_data()
    # We replace this curve by the optimal curve, which we can do since
    # truth of BSD(E,p) is invariant under isogeny.
    BSD.curve = E.optimal_curve()
    if BSD.curve.has_cm():
        # ensure that CM is by a maximal order
        non_max_j_invs = [-12288000, 54000, 287496, 16581375]
        if BSD.curve.j_invariant() in non_max_j_invs: # is this possible for optimal curves?
            if verbosity > 0:
                print 'CM by non maximal order: switching curves'
            for E in BSD.curve.isogeny_class():
                if E.j_invariant() not in non_max_j_invs:
                    BSD.curve = E
                    break
    BSD.update()
    galrep = BSD.curve.galois_representation()

    if two_desc=='mwrank':
        M = mwrank_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc=='simon':
        M = simon_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc=='sage':
        M = native_two_isogeny_descent_work(BSD.curve, BSD.two_tor_rk)
    else:
        raise NotImplementedError()
    rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens = M
    assert sha2_lower_bd <= sha2_upper_bd
    if gens is not None: gens = BSD.curve.saturation(gens)[0]
    if rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Apparent contradiction: %d <= rank <= %d."%(rank_lower_bd, rank_upper_bd))
    BSD.two_selmer_rank = rank_upper_bd + sha2_lower_bd + BSD.two_tor_rk
    if sha2_upper_bd == sha2_lower_bd:
        BSD.rank = rank_lower_bd
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
    else:
        BSD.rank = BSD.curve.rank(use_database=True)
        sha2_upper_bd -= (BSD.rank - rank_lower_bd)
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
        if verbosity > 0:
            print "Unable to compute the rank exactly -- used database."
    if rank_lower_bd > 1:
        # We do not know BSD(E,p) for even a single p, since it's
        # an open problem to show that L^r(E,1)/(Reg*Omega) is
        # rational for any curve with r >= 2.
        from sage.sets.all import Primes
        BSD.primes = Primes()
        if return_BSD:
            BSD.rank = rank_lower_bd
            return BSD
        return BSD.primes
    if (BSD.sha_an.ord(2) == 0) != (BSD.bounds[2][1] == 0):
        raise RuntimeError("Apparent contradiction: %d <= rank(sha[2]) <= %d, but ord_2(sha_an) = %d"%(sha2_lower_bd, sha2_upper_bd, BSD.sha_an.ord(2)))
    if BSD.bounds[2][0] == BSD.sha_an.ord(2) and BSD.sha_an.ord(2) == BSD.bounds[2][1]:
        if verbosity > 0:
            print 'p = 2: True by 2-descent'
        BSD.primes = []
        BSD.bounds.pop(2)
        BSD.proof[2] = ['2-descent']
    else:
        BSD.primes = [2]
        BSD.proof[2] = [('2-descent',)+BSD.bounds[2]]
    if len(gens) > rank_lower_bd or \
       rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Something went wrong with 2-descent.")
    if BSD.rank != len(gens):
        if BSD.rank != len(BSD.curve._EllipticCurve_rational_field__gens[True]):
            raise RuntimeError("Could not get generators")
        gens = BSD.curve._EllipticCurve_rational_field__gens[True]
    BSD.gens = [BSD.curve.point(x, check=True) for x in gens]

    if BSD.rank != BSD.curve.analytic_rank():
        raise RuntimeError("It seems that the rank conjecture does not hold for this curve (%s)! This may be a counterexample to BSD, but is more likely a bug."%(BSD.curve))

    # reduce set of remaining primes to a finite set
    import signal
    kolyvagin_primes = []
    heegner_index = None
    if BSD.rank == 0:
        for D in BSD.curve.heegner_discriminants_list(10):
            max_height = max(13,BSD.curve.quadratic_twist(D).CPS_height_bound())
            heegner_primes = -1
            while heegner_primes == -1:
                if max_height > 21: break
                heegner_primes, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height)
                max_height += 1
            if isinstance(heegner_primes, list):
                break
        if not isinstance(heegner_primes, list):
            raise RuntimeError("Tried 10 Heegner discriminants, and heegner_index_bound failed each time.")
        if exact is not False:
            heegner_index = exact
            BSD.heegner_indexes[D] = exact
        else:
            BSD.heegner_index_upper_bound[D] = max(heegner_primes+[1])
        if 2 in heegner_primes:
            heegner_primes.remove(2)
    else: # rank 1
        for D in BSD.curve.heegner_discriminants_list(10):
            I = BSD.curve.heegner_index(D)
            J = I.is_int()
            if J[0] and J[1]>0:
                I = J[1]
            else:
                J = (2*I).is_int()
                if J[0] and J[1]>0:
                    I = J[1]
                else:
                    continue
            heegner_index = I
            BSD.heegner_indexes[D] = I
            break
        heegner_primes = [p for p in arith.prime_divisors(heegner_index) if p!=2]

    assert BSD.sha_an in ZZ and BSD.sha_an > 0
    if BSD.curve.has_cm():
        if BSD.curve.analytic_rank() == 0:
            if verbosity > 0:
                print '    p >= 5: true by Rubin'
            BSD.primes.append(3)
        else:
            K = rings.QuadraticField(BSD.curve.cm_discriminant(), 'a')
            D_K = K.disc()
            D_E = BSD.curve.discriminant()
            if len(K.factor(3)) == 1: # 3 does not split in K
                BSD.primes.append(3)
            for p in arith.prime_divisors(D_K):
                if p >= 5:
                    BSD.primes.append(p)
            for p in arith.prime_divisors(D_E):
                if p >= 5 and D_K%p and len(K.factor(p)) == 1: # p is inert in K
                    BSD.primes.append(p)
            for p in heegner_primes:
                if p >= 5 and D_E%p != 0 and D_K%p != 0 and len(K.factor(p)) == 1: # p is good for E and inert in K
                    kolyvagin_primes.append(p)
            for p in arith.prime_divisors(BSD.sha_an):
                if p >= 5 and D_K%p != 0 and len(K.factor(p)) == 1:
                    if BSD.curve.is_good(p):
                        if verbosity > 2 and p in heegner_primes and heegner_index is None:
                            print 'ALERT: Prime p (%d) >= 5 dividing sha_an, good for E, inert in K, in heegner_primes, should not divide the actual Heegner index'
                        # Note that the following check is not entirely
                        # exhaustive, in case there is a p not dividing
                        # the Heegner index in heegner_primes,
                        # for which only an outer bound was computed
                        if p not in heegner_primes:
                            raise RuntimeError("p = %d divides sha_an, is of good reduction for E, inert in K, and does not divide the Heegner index. This may be a counterexample to BSD, but is more likely a bug. %s"%(p,BSD.curve))
            if verbosity > 0:
                print 'True for p not in {%s} by Kolyvagin (via Stein & Lum -- unpublished) and Rubin.'%str(list(set(BSD.primes).union(set(kolyvagin_primes))))[1:-1]
        BSD.proof['finite'] = copy(BSD.primes)
    else: # no CM
        # do some tricks to get to a finite set without calling bound_kolyvagin
        BSD.primes += [p for p in galrep.non_surjective() if p != 2]
        for p in heegner_primes:
            if p not in BSD.primes:
                BSD.primes.append(p)
        for p in arith.prime_divisors(BSD.sha_an):
            if p not in BSD.primes and p != 2:
                BSD.primes.append(p)
        if verbosity > 0:
            s = str(BSD.primes)[1:-1]
            if 2 not in BSD.primes:
                if len(s) == 0: s = '2'
                else: s = '2, '+s
            print 'True for p not in {' + s + '} by Kolyvagin.'
        BSD.proof['finite'] = copy(BSD.primes)
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_surjective(p) and not BSD.curve.has_additive_reduction(p):
                if BSD.curve.has_nonsplit_multiplicative_reduction(p):
                    if BSD.rank > 0:
                        continue
                if p==3:
                    if (not (BSD.curve.is_ordinary(p) and BSD.curve.is_good(p))) and (not BSD.curve.has_split_multiplicative_reduction(p)):
                        continue
                    if BSD.rank > 0:
                        continue
                if verbosity > 1:
                    print '    p = %d: Trying p_primary_bound'%p
                p_bound = BSD.Sha.p_primary_bound(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Stein-Wuthrich', p_bound))
                else:
                    BSD.proof[p] = [('Stein-Wuthrich', p_bound)]
                if BSD.sha_an.ord(p) == 0 and p_bound == 0:
                    if verbosity > 0:
                        print 'True for p=%d by Stein-Wuthrich.'%p
                    primes_to_remove.append(p)
                else:
                    if p in BSD.bounds:
                        BSD.bounds[p][1] = min(BSD.bounds[p][1], p_bound)
                    else:
                        BSD.bounds[p] = (0, p_bound)
                    print 'Analytic %d-rank is '%p + str(BSD.sha_an.ord(p)) + ', actual %d-rank is at most %d.'%(p, p_bound)
                    print '    by Stein-Wuthrich.\n'
        for p in primes_to_remove:
            BSD.primes.remove(p)
        kolyvagin_primes = []
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_surjective(p):
                kolyvagin_primes.append(p)
        for p in kolyvagin_primes:
            BSD.primes.remove(p)
    # apply other hypotheses which imply Kolyvagin's bound holds
    bounded_primes = []
    D_K = rings.QuadraticField(D, 'a').disc()

    # Cha's hypothesis
    for p in BSD.primes:
        if p == 2: continue
        if D_K%p != 0 and BSD.N%(p**2) != 0 and galrep.is_irreducible(p):
            if verbosity > 0:
                print 'Kolyvagin\'s bound for p = %d applies by Cha.'%p
            if p in BSD.proof:
                BSD.proof[p].append('Cha')
            else:
                BSD.proof[p] = ['Cha']
            kolyvagin_primes.append(p)
    # Stein et al.
    if not BSD.curve.has_cm():
        L = arith.lcm([F.torsion_order() for F in BSD.curve.isogeny_class()])
        for p in BSD.primes:
            if p in kolyvagin_primes or p == 2: continue
            if L%p != 0:
                if len(arith.prime_divisors(D_K)) == 1:
                    if D_K%p == 0: continue
                if verbosity > 0:
                    print 'Kolyvagin\'s bound for p = %d applies by Stein et al.'%p
                kolyvagin_primes.append(p)
                if p in BSD.proof:
                    BSD.proof[p].append('Stein et al.')
                else:
                    BSD.proof[p] = ['Stein et al.']
    for p in kolyvagin_primes:
        if p in BSD.primes:
            BSD.primes.remove(p)

    # apply Kolyvagin's bound
    primes_to_remove = []
    for p in kolyvagin_primes:
        if p == 2: continue
        if p not in heegner_primes:
            ord_p_bound = 0
        elif heegner_index is not None: # p must divide heegner_index
            ord_p_bound = 2*heegner_index.ord(p)
            # Here Jetchev's results apply.
            m_max = max([BSD.curve.tamagawa_number(q).ord(p) for q in BSD.N.prime_divisors()])
            if m_max > 0:
                if verbosity > 0:
                    print 'Jetchev\'s results apply (at p = %d) with m_max ='%p, m_max
                if p in BSD.proof:
                    BSD.proof[p].append(('Jetchev',m_max))
                else:
                    BSD.proof[p] = [('Jetchev',m_max)]
            ord_p_bound -= 2*m_max
        else: # Heegner index is None
            for D in BSD.heegner_index_upper_bound:
                M = BSD.heegner_index_upper_bound[D]
                ord_p_bound = 0
                while p**(ord_p_bound+1) <= M**2:
                    ord_p_bound += 1
                # now ord_p_bound is one on I_K!!!
                ord_p_bound *= 2 # by Kolyvagin, now ord_p_bound is one on #Sha
                break
        if p in BSD.proof:
            BSD.proof[p].append(('Kolyvagin',ord_p_bound))
        else:
            BSD.proof[p] = [('Kolyvagin',ord_p_bound)]
        if BSD.sha_an.ord(p) == 0 and ord_p_bound == 0:
            if verbosity > 0:
                print 'True for p = %d by Kolyvagin bound'%p
            primes_to_remove.append(p)
        elif BSD.sha_an.ord(p) > ord_p_bound:
            raise RuntimeError("p = %d: ord_p_bound == %d, but sha_an.ord(p) == %d. This appears to be a counterexample to BSD, but is more likely a bug."%(p,ord_p_bound,BSD.sha_an.ord(p)))
        else: # BSD.sha_an.ord(p) <= ord_p_bound != 0:
            if p in BSD.bounds:
                low = BSD.bounds[p][0]
                BSD.bounds[p] = (low, min(BSD.bounds[p][1], ord_p_bound))
            else:
                BSD.bounds[p] = (0, ord_p_bound)
    for p in primes_to_remove:
        kolyvagin_primes.remove(p)
    BSD.primes = list( set(BSD.primes).union(set(kolyvagin_primes)) )

    # Kato's bound
    if BSD.rank == 0 and not BSD.curve.has_cm():
        L_over_Omega = BSD.curve.lseries().L_ratio()
        kato_primes = BSD.Sha.bound_kato()
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2: continue
            if p not in kato_primes:
                if verbosity > 0:
                    print 'Kato further implies that #Sha[%d] is trivial.'%p
                primes_to_remove.append(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Kato',0))
                else:
                    BSD.proof[p] = [('Kato',0)]
            if p not in [2,3] and BSD.N%p != 0:
                if galrep.is_surjective(p):
                    bd = L_over_Omega.valuation(p)
                    if verbosity > 1:
                        print 'Kato implies that ord_p(#Sha[%d]) <= %d '%(p,bd)
                    if p in BSD.proof:
                        BSD.proof[p].append(('Kato',bd))
                    else:
                        BSD.proof[p] = [('Kato',bd)]
                    if p in BSD.bounds:
                        low = BSD.bounds[p][0]
                        BSD.bounds[p][1] = (low, min(BSD.bounds[p][1], bd))
                    else:
                        BSD.bounds[p] = (0, bd)
        for p in primes_to_remove:
            BSD.primes.remove(p)

    # Mazur
    primes_to_remove = []
    if BSD.N.is_prime():
        for p in BSD.primes:
            if p == 2: continue
            if galrep.is_reducible(p):
                primes_to_remove.append(p)
                if verbosity > 0:
                    print 'True for p=%s by Mazur'%p
        for p in primes_to_remove:
            BSD.primes.remove(p)
            if p in BSD.proof:
                BSD.proof[p].append('Mazur')
            else:
                BSD.proof[p] = ['Mazur']

    BSD.primes.sort()

    # Try harder to compute the Heegner index, where it matters
    if heegner_index is None:
        if max_height < 18:
            max_height = 18
        for D in BSD.heegner_index_upper_bound:
            M = BSD.heegner_index_upper_bound[D]
            for p in kolyvagin_primes:
                if p not in BSD.primes or p == 3: continue
                if verbosity > 0:
                    print '    p = %d: Trying harder for Heegner index'%p
                obt = 0
                while p**(BSD.sha_an.ord(p)/2+1) <= M and max_height < 22:
                    if verbosity > 2:
                        print '    trying max_height =', max_height
                    old_bound = M
                    M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print '    heegner index =', M
                    else:
                        M = max(M+[1])
                        if verbosity > 2:
                            print '    bound =', M
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = min(M,BSD.heegner_index_upper_bound[D])
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn+1) <= M:
                    expn += 1
                if 2*expn < upp:
                    upp = 2*expn
                    BSD.bounds[p] = (low,upp)
                    if verbosity > 0:
                        print '    got better bound on ord_p =', upp
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print '    proven!'
                            BSD.primes.remove(p)
            break
        for p in kolyvagin_primes:
            if p not in BSD.primes or p == 3: continue
            for D in BSD.curve.heegner_discriminants_list(4):
                if D in BSD.heegner_index_upper_bound: continue
                print '    discriminant', D
                if verbosity > 0:
                    print 'p = %d: Trying discriminant = %d for Heegner index'%(p,D)
                max_height = max(10, BSD.curve.quadratic_twist(D).CPS_height_bound())
                obt = 0
                while True:
                    if verbosity > 2:
                        print '    trying max_height =', max_height
                    old_bound = M
                    if p**(BSD.sha_an.ord(p)/2+1) > M or max_height >= 22:
                        break
                    M, _, exact = BSD.curve.heegner_index_bound(D, max_height=max_height, secs_dc=secs_dc)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print '    heegner index =', M
                    else:
                        M = max(M+[1])
                        if verbosity > 2:
                            print '    bound =', M
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = M
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn+1) <= M:
                    expn += 1
                if 2*expn < upp:
                    upp = 2*expn
                    BSD.bounds[p] = (low,upp)
                    if verbosity > 0:
                        print '    got better bound =', upp
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print '    proven!'
                            BSD.primes.remove(p)
                            break

    # print some extra information
    if verbosity > 1:
        if len(BSD.primes) > 0:
            print 'Remaining primes:'
        for p in BSD.primes:
            s = 'p = ' + str(p) + ': '
            if galrep.is_irreducible(p):
                s += 'ir'
            s += 'reducible, '
            if not galrep.is_surjective(p):
                s += 'not '
            s += 'surjective, '
            a_p = BSD.curve.an(p)
            if BSD.curve.is_good(p):
                if a_p%p != 0:
                    s += 'good ordinary'
                else:
                    s += 'good, non-ordinary'
            else:
                assert BSD.curve.is_minimal()
                if a_p == 0:
                    s += 'additive'
                elif a_p == 1:
                    s += 'split multiplicative'
                elif a_p == -1:
                    s += 'non-split multiplicative'
            if BSD.curve.tamagawa_product()%p==0:
                s += ', divides a Tamagawa number'
            if p in BSD.bounds:
                s += '\n    (%d <= ord_p <= %d)'%BSD.bounds[p]
            else:
                s += '\n    (no bounds found)'
            s += '\n    ord_p(#Sha_an) = %d'%BSD.sha_an.ord(p)
            if heegner_index is None:
                may_divide = True
                for D in BSD.heegner_index_upper_bound:
                    if p > BSD.heegner_index_upper_bound[D] or p not in kolyvagin_primes:
                        may_divide = False
                if may_divide:
                    s += '\n    may divide the Heegner index, for which only a bound was computed'
            print s

    if BSD.curve.has_cm():
        if BSD.rank == 1:
            BSD.proof['reason_finite'] = 'Rubin&Kolyvagin'
        else:
            BSD.proof['reason_finite'] = 'Rubin'
    else:
        BSD.proof['reason_finite'] = 'Kolyvagin'
    # reduce memory footprint of BSD object:
    BSD.curve = BSD.curve.label()
    BSD.Sha = None
    return BSD if return_BSD else BSD.primes
Esempio n. 13
0
def egros_get_j(S=[], proof=None, verbose=False):
    r"""
    Returns a list of rational `j` such that all elliptic curves
    defined over `Q` with good reduction outside `S` have
    `j`-invariant in the list, sorted by height.

    INPUT:

        -  ``S`` - list of primes (default: empty list).

        - ``proof`` - True/False (default True): the MW basis for
          auxiliary curves will be computed with this proof flag.

        - ``verbose`` - True/False (default False): if True, some
          details of the computation will be output.

    .. note::

        Proof flag: The algorithm used requires determining all
        S-integral points on several auxiliary curves, which in turn
        requires the computation of their generators.  This is not
        always possible (even in theory) using current knowledge.

        The value of this flag is passed to the function which
        computes generators of various auxiliary elliptic curves, in
        order to find their S-integral points.  Set to False if the
        default (True) causes warning messages, but note that you can
        then not rely on the set of invariants returned being
        complete.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.ell_egros import egros_get_j
        sage: egros_get_j([])
        [1728]
        sage: egros_get_j([2])
        [128, 432, -864, 1728, 3375/2, -3456, 6912, 8000, 10976, -35937/4, 287496, -784446336, -189613868625/128]
        sage: egros_get_j([3])
        [0, -576, 1536, 1728, -5184, -13824, 21952/9, -41472, 140608/3, -12288000]
        sage: jlist=egros_get_j([2,3]); len(jlist) # long time (30s)
        83

    """
    if not all([p.is_prime() for p in S]):
        raise ValueError, "Elements of S must be prime."

        if proof is None:
            from sage.structure.proof.proof import get_flag
            proof = get_flag(proof, "elliptic_curve")
        else:
            proof = bool(proof)

    if verbose:
        import sys  # so we can flush stdout for debugging

    SS = [-1] + S

    jlist = []
    wcount = 0
    nw = 6**len(S) * 2

    if verbose:
        print "Finding possible j invariants for S = ", S
        print "Using ", nw, " twists of base curve"
        sys.stdout.flush()

    for ei in xmrange([6] * len(S) + [2]):
        w = prod([p**e for p, e in zip(reversed(SS), ei)], QQ(1))
        wcount += 1
        if verbose:
            print "Curve #", wcount, "/", nw, ":"
            print "w = ", w, "=", w.factor()
            sys.stdout.flush()
        a6 = -1728 * w
        d2 = 0
        d3 = 0
        u0 = (2**d2) * (3**d3)
        E = EllipticCurve([0, 0, 0, 0, a6])
        # This curve may not be minimal at 2 or 3, but the
        # S-integral_points function requires minimality at primes in
        # S, so we find a new model which is p-minimal at both 2 and 3
        # if they are in S.  Note that the isomorphism between models
        # will preserve S-integrality of points.
        E2 = E.local_minimal_model(2) if 2 in S else E
        E23 = E2.local_minimal_model(3) if 3 in S else E2
        urst = E23.isomorphism_to(E)

        try:
            pts = E23.S_integral_points(S, proof=proof)
        except RuntimeError:
            pts = []
            print "Failed to find S-integral points on ", E23.ainvs()
            if proof:
                if verbose:
                    print "--trying again with proof=False"
                    sys.stdout.flush()
                pts = E23.S_integral_points(S, proof=False)
                if verbose:
                    print "--done"
        if verbose:
            print len(pts), " S-integral points: ", pts
            sys.stdout.flush()
        for P in pts:
            P = urst(P)
            x = P[0]
            y = P[1]
            j = x**3 / w
            assert j - 1728 == y**2 / w
            if is_possible_j(j, S):
                if not j in jlist:
                    if verbose:
                        print "Adding possible j = ", j
                        sys.stdout.flush()
                    jlist += [j]
            else:
                if True:  #verbose:
                    print "Discarding illegal j = ", j
                    sys.stdout.flush()
    height_cmp = lambda j1, j2: cmp(j1.height(), j2.height())
    jlist.sort(cmp=height_cmp)
    return jlist
Esempio n. 14
0
    def __init__(self, number_field, proof=True, S=None):
        """
        Create a unit group of a number field.

        INPUT:

        - ``number_field`` - a number field
        - ``proof`` - boolean (default True): proof flag
        - ``S`` - tuple of prime ideals, or an ideal, or a single
          ideal or element from which an ideal can be constructed, in
          which case the support is used.  If None, the global unit
          group is constructed; otherwise, the S-unit group is
          constructed.

        The proof flag is passed to pari via the ``pari_bnf()`` function
        which computes the unit group.  See the documentation for the
        number_field module.

        EXAMPLES::

            sage: x = polygen(QQ)
            sage: K.<a> = NumberField(x^2-38)
            sage: UK = K.unit_group(); UK
            Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38
            sage: UK.gens()
            (u0, u1)
            sage: UK.gens_values()
            [-1, -6*a + 37]

            sage: K.<a> = QuadraticField(-3)
            sage: UK = K.unit_group(); UK
            Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3 with a = 1.732050807568878?*I
            sage: UK.gens()
            (u,)
            sage: UK.gens_values()
            [-1/2*a + 1/2]

            sage: K.<z> = CyclotomicField(13)
            sage: UK = K.unit_group(); UK
            Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12
            sage: UK.gens()
            (u0, u1, u2, u3, u4, u5)
            sage: UK.gens_values() # random
            [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z]
            sage: SUK = UnitGroup(K,S=2); SUK
            S-unit group with structure C26 x Z x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 with S = (Fractional ideal (2),)

        TESTS:

        Number fields defined by non-monic and non-integral
        polynomials are supported (:trac:`252`);
        the representation depends on the PARI version::

            sage: K.<a> = NumberField(7/9*x^3 + 7/3*x^2 - 56*x + 123)
            sage: K.unit_group()
            Unit group with structure C2 x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123
            sage: UnitGroup(K, S=tuple(K.primes_above(7)))
            S-unit group with structure C2 x Z x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123 with S = (Fractional ideal (...),)
            sage: K.primes_above(7)[0] in (7/225*a^2 - 7/75*a - 42/25, 28/225*a^2 + 77/75*a - 133/25)
            True

        Conversion from unit group to a number field and back
        gives the right results (:trac:`25874`)::

            sage: K = QuadraticField(-3).composite_fields(QuadraticField(2))[0]
            sage: U = K.unit_group()
            sage: tuple(U(K(u)) for u in U.gens()) == U.gens()
            True
            sage: US = K.S_unit_group(3)
            sage: tuple(US(K(u)) for u in US.gens()) == US.gens()
            True

        """
        proof = get_flag(proof, "number_field")
        K = number_field
        pK = K.pari_bnf(proof)
        self.__number_field = K
        self.__pari_number_field = pK

        # process the parameter S:
        if not S:
            S = self.__S = ()
        else:
            if isinstance(S, list):
                S = tuple(S)
            if not isinstance(S, tuple):
                try:
                    S = tuple(K.ideal(S).prime_factors())
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
            else:
                try:
                    S = tuple(K.ideal(P) for P in S)
                except (NameError, TypeError, ValueError):
                    raise ValueError("Cannot make a set of primes from %s" %
                                     (S, ))
                if not all(P.is_prime() for P in S):
                    raise ValueError(
                        "Not all elements of %s are prime ideals" % (S, ))
            self.__S = S
            self.__pS = pS = [P.pari_prime() for P in S]

        # compute the fundamental units via pari:
        fu = [K(u, check=False) for u in pK.bnf_get_fu()]
        self.__nfu = len(fu)

        # compute the additional S-unit generators:
        if S:
            self.__S_unit_data = pK.bnfunits(pS)
        else:
            self.__S_unit_data = pK.bnfunits()
        # TODO: converting the factored matrix representation of bnfunits into polynomial
        # form is a *big* waste of time
        su_fu_tu = [
            pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0]
        ]

        self.__nfu = len(pK.bnf_get_fu())  # number of fundamental units
        self.__nsu = len(su_fu_tu) - self.__nfu - 1  # number of S-units
        self.__ntu = pK.bnf_get_tu()[0]  # order of torsion
        self.__rank = self.__nfu + self.__nsu

        # Move the torsion unit first, then fundamental units then S-units
        gens = [K(u, check=False) for u in su_fu_tu]
        gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu]

        # Construct the abstract group:
        gens_orders = tuple([ZZ(self.__ntu)] + [ZZ(0)] * (self.__rank))
        AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', gens,
                                              number_field)
Esempio n. 15
0
def prove_BSD(E,
              verbosity=0,
              two_desc='mwrank',
              proof=None,
              secs_hi=5,
              return_BSD=False):
    r"""
    Attempt to prove the Birch and Swinnerton-Dyer conjectural
    formula for `E`, returning a list of primes `p` for which this
    function fails to prove BSD(E,p).

    Here, BSD(E,p) is the
    statement: "the Birch and Swinnerton-Dyer formula holds up to a
    rational number coprime to `p`."

    INPUT:

    - ``E`` - an elliptic curve

    - ``verbosity`` - int, how much information about the proof to print.

        - 0 - print nothing
        - 1 - print sketch of proof
        - 2 - print information about remaining primes

    - ``two_desc`` - string (default ``'mwrank'``), what to use for the
      two-descent. Options are ``'mwrank', 'simon', 'sage'``

    - ``proof`` - bool or None (default: None, see
      proof.elliptic_curve or sage.structure.proof). If False, this
      function just immediately returns the empty list.

    - ``secs_hi`` - maximum number of seconds to try to compute the
      Heegner index before switching over to trying to compute the
      Heegner index bound. (Rank 0 only!)

    - ``return_BSD`` - bool (default: False) whether to return an object
      which contains information to reconstruct a proof

    .. NOTE::

        When printing verbose output, phrases such as "by Mazur" are referring
        to the following list of papers:

    REFERENCES:

    - [Cha2005]_
    - [Jet2008]_
    - [Kat2004]_
    - [Kol1991]_
    - [LW2015]_
    - [LS]
    - [Maz1978]_
    - [Rub1991]_
    - [SW2013]_
    - [GJPST2009]_

    EXAMPLES::

        sage: EllipticCurve('11a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Kolyvagin's bound for p = 5 applies by Lawson-Wuthrich
        True for p = 5 by Kolyvagin bound
        []

        sage: EllipticCurve('14a').prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        []

        sage: E = EllipticCurve("20a1")
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kato further implies that #Sha[3] is trivial.
        []

        sage: E = EllipticCurve("50b1")
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3, 5} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        Kolyvagin's bound for p = 5 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        True for p = 5 by Kolyvagin bound
        []
        sage: E.prove_BSD(two_desc='simon')
        []

    A rank two curve::

        sage: E = EllipticCurve('389a')

    We know nothing with proof=True::

        sage: E.prove_BSD()
        Set of all prime numbers: 2, 3, 5, 7, ...

    We (think we) know everything with proof=False::

        sage: E.prove_BSD(proof=False)
        []

    A curve of rank 0 and prime conductor::

        sage: E = EllipticCurve('19a')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        Kolyvagin's bound for p = 3 applies by Lawson-Wuthrich
        True for p = 3 by Kolyvagin bound
        []

        sage: E = EllipticCurve('37a')
        sage: E.rank()
        1
        sage: E._EllipticCurve_rational_field__rank
        (1, True)
        sage: E.analytic_rank = lambda : 0
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: It seems that the rank conjecture does not hold for this curve (Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field)! This may be a counterexample to BSD, but is more likely a bug.

    We test the consistency check for the 2-part of Sha::

        sage: E = EllipticCurve('37a')
        sage: S = E.sha(); S
        Tate-Shafarevich group for the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
        sage: def foo(use_database):
        ....:  return 4
        sage: S.an = foo
        sage: E.prove_BSD()
        Traceback (most recent call last):
        ...
        RuntimeError: Apparent contradiction: 0 <= rank(sha[2]) <= 0, but ord_2(sha_an) = 2

    An example with a Tamagawa number at 5::

        sage: E = EllipticCurve('123a1')
        sage: E.prove_BSD(verbosity=2)
        p = 2: True by 2-descent
        True for p not in {2, 5} by Kolyvagin.
        Kolyvagin's bound for p = 5 applies by Lawson-Wuthrich
        True for p = 5 by Kolyvagin bound
        []

    A curve for which 3 divides the order of the Tate-Shafarevich group::

        sage: E = EllipticCurve('681b')
        sage: E.prove_BSD(verbosity=2)               # long time
        p = 2: True by 2-descent...
        True for p not in {2, 3} by Kolyvagin....
        Remaining primes:
        p = 3: irreducible, surjective, non-split multiplicative
            (0 <= ord_p <= 2)
            ord_p(#Sha_an) = 2
        [3]

    A curve for which we need to use ``heegner_index_bound``::

        sage: E = EllipticCurve('198b')
        sage: E.prove_BSD(verbosity=1, secs_hi=1)
        p = 2: True by 2-descent
        True for p not in {2, 3} by Kolyvagin.
        [3]

    The ``return_BSD`` option gives an object with detailed information
    about the proof::

        sage: E = EllipticCurve('26b')
        sage: B = E.prove_BSD(return_BSD=True)
        sage: B.two_tor_rk
        0
        sage: B.N
        26
        sage: B.gens
        []
        sage: B.primes
        []
        sage: B.heegner_indexes
        {-23: 2}

    TESTS:

    This was fixed by :trac:`8184` and :trac:`7575`::

        sage: EllipticCurve('438e1').prove_BSD(verbosity=1)
        p = 2: True by 2-descent...
        True for p not in {2} by Kolyvagin.
        []

    ::

        sage: E = EllipticCurve('960d1')
        sage: E.prove_BSD(verbosity=1)  # long time (4s on sage.math, 2011)
        p = 2: True by 2-descent
        True for p not in {2} by Kolyvagin.
        []

    """
    if proof is None:
        from sage.structure.proof.proof import get_flag
        proof = get_flag(proof, "elliptic_curve")
    else:
        proof = bool(proof)
    if not proof:
        return []
    from copy import copy
    BSD = BSD_data()
    # We replace this curve by the optimal curve, which we can do since
    # truth of BSD(E,p) is invariant under isogeny.
    BSD.curve = E.optimal_curve()
    if BSD.curve.has_cm():
        # ensure that CM is by a maximal order
        non_max_j_invs = [-12288000, 54000, 287496, 16581375]
        if BSD.curve.j_invariant(
        ) in non_max_j_invs:  # is this possible for optimal curves?
            if verbosity > 0:
                print('CM by non maximal order: switching curves')
            for E in BSD.curve.isogeny_class():
                if E.j_invariant() not in non_max_j_invs:
                    BSD.curve = E
                    break
    BSD.update()
    galrep = BSD.curve.galois_representation()

    if two_desc == 'mwrank':
        M = mwrank_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc == 'simon':
        M = simon_two_descent_work(BSD.curve, BSD.two_tor_rk)
    elif two_desc == 'sage':
        M = native_two_isogeny_descent_work(BSD.curve, BSD.two_tor_rk)
    else:
        raise NotImplementedError()
    rank_lower_bd, rank_upper_bd, sha2_lower_bd, sha2_upper_bd, gens = M
    assert sha2_lower_bd <= sha2_upper_bd
    if gens is not None:
        gens = BSD.curve.saturation(gens)[0]
    if rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Apparent contradiction: %d <= rank <= %d." %
                           (rank_lower_bd, rank_upper_bd))
    BSD.two_selmer_rank = rank_upper_bd + sha2_lower_bd + BSD.two_tor_rk
    if sha2_upper_bd == sha2_lower_bd:
        BSD.rank = rank_lower_bd
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
    else:
        BSD.rank = BSD.curve.rank(use_database=True)
        sha2_upper_bd -= (BSD.rank - rank_lower_bd)
        BSD.bounds[2] = (sha2_lower_bd, sha2_upper_bd)
        if verbosity > 0:
            print("Unable to compute the rank exactly -- used database.")
    if rank_lower_bd > 1:
        # We do not know BSD(E,p) for even a single p, since it's
        # an open problem to show that L^r(E,1)/(Reg*Omega) is
        # rational for any curve with r >= 2.
        from sage.sets.all import Primes
        BSD.primes = Primes()
        if return_BSD:
            BSD.rank = rank_lower_bd
            return BSD
        return BSD.primes
    if (BSD.sha_an.ord(2) == 0) != (BSD.bounds[2][1] == 0):
        raise RuntimeError(
            "Apparent contradiction: %d <= rank(sha[2]) <= %d, but ord_2(sha_an) = %d"
            % (sha2_lower_bd, sha2_upper_bd, BSD.sha_an.ord(2)))
    if BSD.bounds[2][0] == BSD.sha_an.ord(2) and BSD.sha_an.ord(
            2) == BSD.bounds[2][1]:
        if verbosity > 0:
            print('p = 2: True by 2-descent')
        BSD.primes = []
        BSD.bounds.pop(2)
        BSD.proof[2] = ['2-descent']
    else:
        BSD.primes = [2]
        BSD.proof[2] = [('2-descent', ) + BSD.bounds[2]]
    if len(gens) > rank_lower_bd or rank_lower_bd > rank_upper_bd:
        raise RuntimeError("Something went wrong with 2-descent.")
    if BSD.rank != len(gens):
        gens = BSD.curve.gens(proof=True)
        if BSD.rank != len(gens):
            raise RuntimeError("Could not get generators")
    BSD.gens = [BSD.curve.point(x, check=True) for x in gens]

    if BSD.rank != BSD.curve.analytic_rank():
        raise RuntimeError(
            "It seems that the rank conjecture does not hold for this curve (%s)! This may be a counterexample to BSD, but is more likely a bug."
            % BSD.curve)

    # reduce set of remaining primes to a finite set
    kolyvagin_primes = []
    heegner_index = None
    if BSD.rank == 0:
        for D in BSD.curve.heegner_discriminants_list(10):
            max_height = max(13,
                             BSD.curve.quadratic_twist(D).CPS_height_bound())
            heegner_primes = -1
            while heegner_primes == -1:
                if max_height > 21:
                    break
                heegner_primes, _, exact = BSD.curve.heegner_index_bound(
                    D, max_height=max_height)
                max_height += 1
            if isinstance(heegner_primes, list):
                break
        if not isinstance(heegner_primes, list):
            raise RuntimeError(
                "Tried 10 Heegner discriminants, and heegner_index_bound failed each time."
            )
        if exact is not False:
            heegner_index = exact
            BSD.heegner_indexes[D] = exact
        else:
            BSD.heegner_index_upper_bound[D] = max(heegner_primes + [1])
        if 2 in heegner_primes:
            heegner_primes.remove(2)
    else:  # rank 1
        for D in BSD.curve.heegner_discriminants_list(10):
            I = BSD.curve.heegner_index(D)
            J = I.is_int()
            if J[0] and J[1] > 0:
                I = J[1]
            else:
                J = (2 * I).is_int()
                if J[0] and J[1] > 0:
                    I = J[1]
                else:
                    continue
            heegner_index = I
            BSD.heegner_indexes[D] = I
            break
        heegner_primes = [p for p in prime_divisors(heegner_index) if p != 2]

    assert BSD.sha_an in ZZ and BSD.sha_an > 0
    if BSD.curve.has_cm():
        if BSD.curve.analytic_rank() == 0:
            if verbosity > 0:
                print('    p >= 5: true by Rubin')
            BSD.primes.append(3)
        else:
            K = QuadraticField(BSD.curve.cm_discriminant(), 'a')
            D_K = K.disc()
            D_E = BSD.curve.discriminant()
            if len(K.factor(3)) == 1:  # 3 does not split in K
                BSD.primes.append(3)
            for p in prime_divisors(D_K):
                if p >= 5:
                    BSD.primes.append(p)
            for p in prime_divisors(D_E):
                if p >= 5 and D_K % p and len(K.factor(p)) == 1:
                    # p is inert in K
                    BSD.primes.append(p)
            for p in heegner_primes:
                if p >= 5 and D_E % p and D_K % p and len(K.factor(p)) == 1:
                    # p is good for E and inert in K
                    kolyvagin_primes.append(p)
            for p in prime_divisors(BSD.sha_an):
                if p >= 5 and D_K % p and len(K.factor(p)) == 1:
                    if BSD.curve.is_good(p):
                        if verbosity > 2 and p in heegner_primes and heegner_index is None:
                            print(
                                'ALERT: Prime p (%d) >= 5 dividing sha_an, good for E, inert in K, in heegner_primes, should not divide the actual Heegner index'
                            )
                        # Note that the following check is not entirely
                        # exhaustive, in case there is a p not dividing
                        # the Heegner index in heegner_primes,
                        # for which only an outer bound was computed
                        if p not in heegner_primes:
                            raise RuntimeError(
                                "p = %d divides sha_an, is of good reduction for E, inert in K, and does not divide the Heegner index. This may be a counterexample to BSD, but is more likely a bug. %s"
                                % (p, BSD.curve))
            if verbosity > 0:
                print(
                    'True for p not in {%s} by Kolyvagin (via Stein & Lum -- unpublished) and Rubin.'
                    % str(list(set(BSD.primes).union(
                        set(kolyvagin_primes))))[1:-1])
        BSD.proof['finite'] = copy(BSD.primes)
    else:  # no CM
        # do some tricks to get to a finite set without calling bound_kolyvagin
        BSD.primes += [p for p in galrep.non_surjective() if p != 2]
        for p in heegner_primes:
            if p not in BSD.primes:
                BSD.primes.append(p)
        for p in prime_divisors(BSD.sha_an):
            if p not in BSD.primes and p != 2:
                BSD.primes.append(p)
        if verbosity > 0:
            s = str(BSD.primes)[1:-1]
            if 2 not in BSD.primes:
                if not s:
                    s = '2'
                else:
                    s = '2, ' + s
            print('True for p not in {' + s + '} by Kolyvagin.')
        BSD.proof['finite'] = copy(BSD.primes)
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2:
                continue
            if galrep.is_surjective(
                    p) and not BSD.curve.has_additive_reduction(p):
                if BSD.curve.has_nonsplit_multiplicative_reduction(p):
                    if BSD.rank > 0:
                        continue
                if p == 3:
                    if (not (BSD.curve.is_ordinary(p) and BSD.curve.is_good(p))
                        ) and (not BSD.curve.
                               has_split_multiplicative_reduction(p)):
                        continue
                    if BSD.rank > 0:
                        continue
                if verbosity > 1:
                    print('    p = %d: Trying p_primary_bound' % p)
                p_bound = BSD.Sha.p_primary_bound(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Stein-Wuthrich', p_bound))
                else:
                    BSD.proof[p] = [('Stein-Wuthrich', p_bound)]
                if BSD.sha_an.ord(p) == 0 and p_bound == 0:
                    if verbosity > 0:
                        print('True for p=%d by Stein-Wuthrich.' % p)
                    primes_to_remove.append(p)
                else:
                    if p in BSD.bounds:
                        BSD.bounds[p][1] = min(BSD.bounds[p][1], p_bound)
                    else:
                        BSD.bounds[p] = (0, p_bound)
                    print('Analytic %d-rank is ' % p + str(BSD.sha_an.ord(p)) +
                          ', actual %d-rank is at most %d.' % (p, p_bound))
                    print('    by Stein-Wuthrich.\n')
        for p in primes_to_remove:
            BSD.primes.remove(p)
        kolyvagin_primes = []
        for p in BSD.primes:
            if p == 2:
                continue
            if galrep.is_surjective(p):
                kolyvagin_primes.append(p)
        for p in kolyvagin_primes:
            BSD.primes.remove(p)
    # apply other hypotheses which imply Kolyvagin's bound holds
    D_K = QuadraticField(D, 'a').disc()

    # Cha's hypothesis
    for p in BSD.primes:
        if p == 2:
            continue
        if D_K % p != 0 and BSD.N % (p**2) != 0 and galrep.is_irreducible(p):
            if verbosity > 0:
                print('Kolyvagin\'s bound for p = %d applies by Cha.' % p)
            if p in BSD.proof:
                BSD.proof[p].append('Cha')
            else:
                BSD.proof[p] = ['Cha']
            kolyvagin_primes.append(p)
    # Stein et al replaced
    for p in BSD.primes:
        # the lemma about the vanishing of H^1 is false in Stein et al for p=5 and 11
        # here is the correction from Lawson-Wuthrich. Especially Theorem 14 in
        # [LW2015] above.
        if p in kolyvagin_primes or p == 2 or D_K % p == 0:
            continue
        crit_lw = False
        if p > 11 or p == 7:
            crit_lw = True
        elif p == 11:
            if BSD.N != 121 or BSD.curve.label() != "121c2":
                crit_lw = True
        elif galrep.is_irreducible(p):
            crit_lw = True
        else:
            phis = BSD.curve.isogenies_prime_degree(p)
            if len(phis) != 1:
                crit_lw = True
            else:
                C = phis[0].codomain()
                if p == 3:
                    if BSD.curve.torsion_order() % p != 0 and C.torsion_order(
                    ) % p != 0:
                        crit_lw = True
                else:  # p == 5
                    Et = BSD.curve.quadratic_twist(5)
                    if Et.torsion_order() % p != 0 and C.torsion_order(
                    ) % p != 0:
                        crit_lw = True
        if crit_lw:
            if verbosity > 0:
                print(
                    'Kolyvagin\'s bound for p = %d applies by Lawson-Wuthrich'
                    % p)
            kolyvagin_primes.append(p)
            if p in BSD.proof:
                BSD.proof[p].append('Lawson-Wuthrich')
            else:
                BSD.proof[p] = ['Lawson-Wuthrich']
    for p in kolyvagin_primes:
        if p in BSD.primes:
            BSD.primes.remove(p)

    # apply Kolyvagin's bound
    primes_to_remove = []
    for p in kolyvagin_primes:
        if p == 2:
            continue
        if p not in heegner_primes:
            ord_p_bound = 0
        elif heegner_index is not None:  # p must divide heegner_index
            ord_p_bound = 2 * heegner_index.ord(p)
            # Here Jetchev's results apply.
            m_max = max([
                BSD.curve.tamagawa_number(q).ord(p)
                for q in BSD.N.prime_divisors()
            ])
            if m_max > 0:
                if verbosity > 0:
                    print(
                        'Jetchev\'s results apply (at p = %d) with m_max =' %
                        p, m_max)
                if p in BSD.proof:
                    BSD.proof[p].append(('Jetchev', m_max))
                else:
                    BSD.proof[p] = [('Jetchev', m_max)]
            ord_p_bound -= 2 * m_max
        else:  # Heegner index is None
            for D in BSD.heegner_index_upper_bound:
                M = BSD.heegner_index_upper_bound[D]
                ord_p_bound = 0
                while p**(ord_p_bound + 1) <= M**2:
                    ord_p_bound += 1
                # now ord_p_bound is one on I_K!!!
                ord_p_bound *= 2  # by Kolyvagin, now ord_p_bound is one on #Sha
                break
        if p in BSD.proof:
            BSD.proof[p].append(('Kolyvagin', ord_p_bound))
        else:
            BSD.proof[p] = [('Kolyvagin', ord_p_bound)]
        if BSD.sha_an.ord(p) == 0 and ord_p_bound == 0:
            if verbosity > 0:
                print('True for p = %d by Kolyvagin bound' % p)
            primes_to_remove.append(p)
        elif BSD.sha_an.ord(p) > ord_p_bound:
            raise RuntimeError(
                "p = %d: ord_p_bound == %d, but sha_an.ord(p) == %d. This appears to be a counterexample to BSD, but is more likely a bug."
                % (p, ord_p_bound, BSD.sha_an.ord(p)))
        else:  # BSD.sha_an.ord(p) <= ord_p_bound != 0:
            if p in BSD.bounds:
                low = BSD.bounds[p][0]
                BSD.bounds[p] = (low, min(BSD.bounds[p][1], ord_p_bound))
            else:
                BSD.bounds[p] = (0, ord_p_bound)
    for p in primes_to_remove:
        kolyvagin_primes.remove(p)
    BSD.primes = list(set(BSD.primes).union(set(kolyvagin_primes)))

    # Kato's bound
    if BSD.rank == 0 and not BSD.curve.has_cm():
        L_over_Omega = BSD.curve.lseries().L_ratio()
        kato_primes = BSD.Sha.bound_kato()
        primes_to_remove = []
        for p in BSD.primes:
            if p == 2:
                continue
            if p not in kato_primes:
                if verbosity > 0:
                    print('Kato further implies that #Sha[%d] is trivial.' % p)
                primes_to_remove.append(p)
                if p in BSD.proof:
                    BSD.proof[p].append(('Kato', 0))
                else:
                    BSD.proof[p] = [('Kato', 0)]
            if p not in [2, 3] and BSD.N % p != 0:
                if galrep.is_surjective(p):
                    bd = L_over_Omega.valuation(p)
                    if verbosity > 1:
                        print('Kato implies that ord_p(#Sha[%d]) <= %d ' %
                              (p, bd))
                    if p in BSD.proof:
                        BSD.proof[p].append(('Kato', bd))
                    else:
                        BSD.proof[p] = [('Kato', bd)]
                    if p in BSD.bounds:
                        low = BSD.bounds[p][0]
                        BSD.bounds[p][1] = (low, min(BSD.bounds[p][1], bd))
                    else:
                        BSD.bounds[p] = (0, bd)
        for p in primes_to_remove:
            BSD.primes.remove(p)

    # Mazur
    primes_to_remove = []
    if BSD.N.is_prime():
        for p in BSD.primes:
            if p == 2:
                continue
            if galrep.is_reducible(p):
                primes_to_remove.append(p)
                if verbosity > 0:
                    print('True for p=%s by Mazur' % p)
        for p in primes_to_remove:
            BSD.primes.remove(p)
            if p in BSD.proof:
                BSD.proof[p].append('Mazur')
            else:
                BSD.proof[p] = ['Mazur']

    BSD.primes.sort()

    # Try harder to compute the Heegner index, where it matters
    if heegner_index is None:
        if max_height < 18:
            max_height = 18
        for D in BSD.heegner_index_upper_bound:
            M = BSD.heegner_index_upper_bound[D]
            for p in kolyvagin_primes:
                if p not in BSD.primes or p == 3:
                    continue
                if verbosity > 0:
                    print('    p = %d: Trying harder for Heegner index' % p)
                obt = 0
                while p**(BSD.sha_an.ord(p) / 2 + 1) <= M and max_height < 22:
                    if verbosity > 2:
                        print('    trying max_height =', max_height)
                    old_bound = M
                    M, _, exact = BSD.curve.heegner_index_bound(
                        D, max_height=max_height, secs_dc=secs_hi)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print('    heegner index =', M)
                    else:
                        M = max(M + [1])
                        if verbosity > 2:
                            print('    bound =', M)
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = min(
                    M, BSD.heegner_index_upper_bound[D])
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn + 1) <= M:
                    expn += 1
                if 2 * expn < upp:
                    upp = 2 * expn
                    BSD.bounds[p] = (low, upp)
                    if verbosity > 0:
                        print('    got better bound on ord_p =', upp)
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print('    proven!')
                            BSD.primes.remove(p)
            break
        for p in kolyvagin_primes:
            if p not in BSD.primes or p == 3:
                continue
            for D in BSD.curve.heegner_discriminants_list(4):
                if D in BSD.heegner_index_upper_bound:
                    continue
                print('    discriminant', D)
                if verbosity > 0:
                    print(
                        'p = %d: Trying discriminant = %d for Heegner index' %
                        (p, D))
                max_height = max(
                    10,
                    BSD.curve.quadratic_twist(D).CPS_height_bound())
                obt = 0
                while True:
                    if verbosity > 2:
                        print('    trying max_height =', max_height)
                    old_bound = M
                    if p**(BSD.sha_an.ord(p) / 2 + 1) > M or max_height >= 22:
                        break
                    M, _, exact = BSD.curve.heegner_index_bound(
                        D, max_height=max_height, secs_dc=secs_hi)
                    if M == -1:
                        max_height += 1
                        continue
                    if exact is not False:
                        heegner_index = exact
                        BSD.heegner_indexes[D] = exact
                        M = exact
                        if verbosity > 2:
                            print('    heegner index =', M)
                    else:
                        M = max(M + [1])
                        if verbosity > 2:
                            print('    bound =', M)
                    if old_bound == M:
                        obt += 1
                        if obt == 2:
                            break
                    max_height += 1
                BSD.heegner_index_upper_bound[D] = M
                low, upp = BSD.bounds[p]
                expn = 0
                while p**(expn + 1) <= M:
                    expn += 1
                if 2 * expn < upp:
                    upp = 2 * expn
                    BSD.bounds[p] = (low, upp)
                    if verbosity > 0:
                        print('    got better bound =', upp)
                    if low == upp:
                        if upp != BSD.sha_an.ord(p):
                            raise RuntimeError
                        else:
                            if verbosity > 0:
                                print('    proven!')
                            BSD.primes.remove(p)
                            break

    # print some extra information
    if verbosity > 1:
        if BSD.primes:
            print('Remaining primes:')
        for p in BSD.primes:
            s = 'p = ' + str(p) + ': '
            if galrep.is_irreducible(p):
                s += 'ir'
            s += 'reducible, '
            if not galrep.is_surjective(p):
                s += 'not '
            s += 'surjective, '
            a_p = BSD.curve.an(p)
            if BSD.curve.is_good(p):
                if a_p % p != 0:
                    s += 'good ordinary'
                else:
                    s += 'good, non-ordinary'
            else:
                assert BSD.curve.is_minimal()
                if a_p == 0:
                    s += 'additive'
                elif a_p == 1:
                    s += 'split multiplicative'
                elif a_p == -1:
                    s += 'non-split multiplicative'
            if BSD.curve.tamagawa_product() % p == 0:
                s += ', divides a Tamagawa number'
            if p in BSD.bounds:
                s += '\n    (%d <= ord_p <= %d)' % BSD.bounds[p]
            else:
                s += '\n    (no bounds found)'
            s += '\n    ord_p(#Sha_an) = %d' % BSD.sha_an.ord(p)
            if heegner_index is None:
                may_divide = True
                for D in BSD.heegner_index_upper_bound:
                    if p > BSD.heegner_index_upper_bound[
                            D] or p not in kolyvagin_primes:
                        may_divide = False
                if may_divide:
                    s += '\n    may divide the Heegner index, for which only a bound was computed'
            print(s)

    if BSD.curve.has_cm():
        if BSD.rank == 1:
            BSD.proof['reason_finite'] = 'Rubin&Kolyvagin'
        else:
            BSD.proof['reason_finite'] = 'Rubin'
    else:
        BSD.proof['reason_finite'] = 'Kolyvagin'
    # reduce memory footprint of BSD object:
    BSD.curve = BSD.curve.label()
    BSD.Sha = None
    return BSD if return_BSD else BSD.primes
Esempio n. 16
0
def _cache_bnfisprincipal(self, proof=None, gens=False):
    r"""
    This function is essentially the implementation of
    :meth:`is_principal`, :meth:`gens_reduced` and
    :meth:`ideal_class_log`.
    INPUT:
    - ``self`` -- an ideal
    - ``proof`` -- proof flag.  If ``proof=False``, assume GRH.
    - ``gens`` -- (default: False) if True, also computes the reduced
      generators of the ideal.
    OUTPUT:
    None.  This function simply caches the results: it sets
    ``_ideal_class_log`` (see :meth:`ideal_class_log`),
    ``_is_principal`` (see :meth:`is_principal`) and
    ``_reduced_generators``.
    TESTS:
    Check that no warnings are triggered from PARI/GP (see :trac:`30801`)::
        sage: K.<a> = NumberField(x^2 - x + 112941801)
        sage: I = K.ideal((112941823, a + 49942513))
        sage: I.is_principal()
        False
    """
    # Since pari_bnf() is cached, this call to pari_bnf() should not
    # influence the run-time much.  Also, this simplifies the handling
    # of the proof flag: if we computed bnfisprincipal() in the past
    # with proof=False, then we do not need to recompute the result.
    # We just need to check correctness of pari_bnf().
    proof = get_flag(proof, "number_field")
    bnf = self.number_field().pari_bnf(proof)

    # If we already have _reduced_generators, no need to compute them again
    if hasattr(self, "_reduced_generators"):
        gens = False

    # Is there something to do?
    if hasattr(self, "_ideal_class_log") and not gens:
        self._is_principal = not any(self._ideal_class_log)
        return

    if not gens:
        v = bnf.bnfisprincipal(self.pari_hnf(), 0)
        self._ideal_class_log = list(v)
        self._is_principal = not any(self._ideal_class_log)
    else:
        # TODO: this is a bit of a waste. We ask bnfisprincipal to compute the compact form and then
        # convert this compact form back into an expanded form.
        # (though calling with 3 instead of 5 most likely triggers an error with memory allocation failure)
        v = bnf.bnfisprincipal(self.pari_hnf(), 5)
        e = v[0]
        t = v[1]
        t = bnf.nfbasistoalg(bnf.nffactorback(t))
        self._ideal_class_log = list(e)
        g = self.number_field()(t)
        self._ideal_log_relation = g
        self._is_principal = not any(self._ideal_class_log)

        if self._is_principal:
            self._reduced_generators = (g, )
        elif gens:
            # Non-principal ideal
            self._reduced_generators = self.gens_two()