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
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")
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')
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
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
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
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)
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)
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())
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)
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
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
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)
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
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()