Path: blob/master/src/sage/schemes/elliptic_curves/heegner.py
8821 views
# -*- coding: utf-8 -*-1r"""2Heegner points on elliptic curves over the rational numbers34AUTHORS:56- William Stein (August 2009)-- most of the initial version78- Robert Bradshaw (July 2009) -- an early version of some specific code910EXAMPLES::1112sage: E = EllipticCurve('433a')13sage: P = E.heegner_point(-8,3)14sage: z = P.point_exact(201); z15(-4/3 : 1/27*a - 4/27 : 1)16sage: parent(z)17Abelian group of points on Elliptic Curve defined by y^2 + x*y = x^3 + 1 over Number Field in a with defining polynomial x^2 - 44*x + 115918sage: parent(z[0]).discriminant()19-320sage: E.quadratic_twist(-3).rank()21122sage: K.<a> = QuadraticField(-8)23sage: K.factor(3)24(Fractional ideal (1/2*a + 1)) * (Fractional ideal (1/2*a - 1))2526Next try an inert prime::2728sage: K.factor(5)29Fractional ideal (5)30sage: P = E.heegner_point(-8,5)31sage: z = P.point_exact(300)32sage: z[0].charpoly().factor()33(x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/100)^234sage: z[1].charpoly().factor()35x^12 - x^11 + 6/5*x^10 - 33/40*x^9 - 89/320*x^8 + 3287/800*x^7 - 5273/1600*x^6 + 993/4000*x^5 + 823/320*x^4 - 2424/625*x^3 + 12059/12500*x^2 + 3329/25000*x + 123251/25000036sage: f = P.x_poly_exact(300); f37x^6 + x^5 - 1/4*x^4 + 19/10*x^3 + 31/20*x^2 - 7/10*x + 49/10038sage: f.discriminant().factor()39-1 * 2^-9 * 5^-9 * 7^2 * 281^2 * 1021^24041We find some Mordell-Weil generators in the rank 1 case using Heegner points::4243sage: E = EllipticCurve('43a'); P = E.heegner_point(-7)44sage: P.x_poly_exact()45x46sage: P.point_exact()47(0 : 0 : 1)4849sage: E = EllipticCurve('997a')50sage: E.rank()51152sage: E.heegner_discriminants_list(10)53[-19, -23, -31, -35, -39, -40, -52, -55, -56, -59]54sage: P = E.heegner_point(-19)55sage: P.x_poly_exact()56x - 141/4957sage: P.point_exact()58(141/49 : -162/343 : 1)5960Here we find that the Heegner point generates a subgroup of index 3::6162sage: E = EllipticCurve('92b1')63sage: E.heegner_discriminants_list(1)64[-7]65sage: P = E.heegner_point(-7); z = P.point_exact(); z66(0 : 1 : 1)67sage: E.regulator()680.049808397298064869sage: z.height()700.44827557568258371sage: P = E(1,1); P # a generator72(1 : 1 : 1)73sage: -3*P74(0 : 1 : 1)75sage: E.tamagawa_product()7637778The above is consistent with the following analytic computation::7980sage: E.heegner_index(-7)813.0000?82"""8384##############################################################################85# Copyright (C) 2005-2009 William Stein <[email protected]>86#87# Distributed under the terms of the GNU General Public License (GPL)88#89# This code is distributed in the hope that it will be useful,90# but WITHOUT ANY WARRANTY; without even the implied warranty of91# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU92# General Public License for more details.93#94# The full text of the GPL is available at:95#96# http://www.gnu.org/licenses/97##############################################################################9899100from sage.misc.all import verbose, prod101from sage.misc.cachefunc import cached_method102103from sage.structure.sage_object import SageObject104105import sage.rings.number_field.number_field as number_field106import sage.rings.arith as arith107import sage.rings.all as rings108from sage.rings.all import (ZZ, GF, QQ, CDF,109Integers, RealField, ComplexField, QuadraticField,110gcd, lcm, is_fundamental_discriminant)111from sage.quadratic_forms.all import (BinaryQF,112BinaryQF_reduced_representatives)113from sage.matrix.all import MatrixSpace, matrix114115from sage.modular.modsym.p1list import P1List116117118##################################################################################119#120# The exported functions, which are in most cases enough to get the121# user going working with Heegner points:122#123# heegner_points -- all of them with given level, discriminant, conducto124# heegner_point -- a specific one125#126##################################################################################127128def heegner_points(N, D=None, c=None):129"""130Return all Heegner points of given level `N`. Can also restrict131to Heegner points with specified discriminant `D` and optionally132conductor `c`.133134INPUT:135136- `N` -- level (positive integer)137138- `D` -- discriminant (negative integer)139140- `c` -- conductor (positive integer)141142EXAMPLES::143144sage: heegner_points(389,-7)145Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]146sage: heegner_points(389,-7,1)147All Heegner points of conductor 1 on X_0(389) associated to QQ[sqrt(-7)]148sage: heegner_points(389,-7,5)149All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]150"""151if D is None and c is None:152return HeegnerPoints_level(N)153if D is not None and c is None:154return HeegnerPoints_level_disc(N, D)155if D is not None and c is not None:156return HeegnerPoints_level_disc_cond(N,D,c)157raise TypeError158159def heegner_point(N, D=None, c=1):160"""161Return a specific Heegner point of level `N` with given162discriminant and conductor. If `D` is not specified, then the163first valid Heegner discriminant is used. If `c` is not given,164then `c=1` is used.165166INPUT:167168- `N` -- level (positive integer)169170- `D` -- discriminant (optional: default first valid `D`)171172- `c` -- conductor (positive integer, optional, default: 1)173174EXAMPLES::175176sage: heegner_point(389)177Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)178sage: heegner_point(389,-7)179Heegner point 1/778*sqrt(-7) - 185/778 of discriminant -7 on X_0(389)180sage: heegner_point(389,-7,5)181Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)182sage: heegner_point(389,-20)183Heegner point 1/778*sqrt(-20) - 165/389 of discriminant -20 on X_0(389)184"""185if D is not None:186return heegner_points(N,D,c)[0]187H = heegner_points(N)188D = H.discriminants(1)[0]189return heegner_points(N,D,c)[0]190191192##################################################################################193#194# Ring class fields, represented as abstract objects. These do not195# derive from number fields, since we do not need to work with their196# elements, and explicitly representing them as number fields would be197# far too difficult.198#199##################################################################################200201class RingClassField(SageObject):202"""203A Ring class field of a quadratic imaginary field of given conductor.204205.. NOTE::206207This is a *ring* class field, not a ray class field. In208general, the ring class field of given conductor is a subfield209of the ray class field of the same conductor.210211EXAMPLES::212213sage: heegner_point(37,-7).ring_class_field()214Hilbert class field of QQ[sqrt(-7)]215sage: heegner_point(37,-7,5).ring_class_field()216Ring class field extension of QQ[sqrt(-7)] of conductor 5217sage: heegner_point(37,-7,55).ring_class_field()218Ring class field extension of QQ[sqrt(-7)] of conductor 55219220TESTS::221222sage: K_c = heegner_point(37,-7).ring_class_field()223sage: type(K_c)224<class 'sage.schemes.elliptic_curves.heegner.RingClassField'>225sage: loads(dumps(K_c)) == K_c226True227"""228def __init__(self, D, c, check=True):229"""230INPUTS:231232- `D` -- discriminant of quadratic imaginary field233234- `c` -- conductor (positive integer coprime to `D`)235236- ``check`` -- bool (default: ``True``); whether to check237validity of input238239EXAMPLES::240241sage: sage.schemes.elliptic_curves.heegner.RingClassField(-7,5, False)242Ring class field extension of QQ[sqrt(-7)] of conductor 5243244"""245if check:246D = ZZ(D); c = ZZ(c)247self.__D = D248self.__c = c249250def __eq__(self, other):251"""252Used for equality testing.253254EXAMPLES::255256sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()257sage: K11 = E.heegner_point(-7,11).ring_class_field()258sage: K5 == K11259False260sage: K5 == K5261True262sage: K11 == 11263False264"""265return isinstance(other, RingClassField) and self.__D == other.__D and self.__c == other.__c266267def __hash__(self):268"""269Used for computing hash of ``self``.270271EXAMPLES::272273sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()274sage: hash(K5)275-3713088127102618519 # 64-bit2761817441385 # 32-bit277sage: hash((-7,5))278-3713088127102618519 # 64-bit2791817441385 # 32-bit280"""281return hash((self.__D, self.__c))282283def conductor(self):284"""285Return the conductor of this ring class field.286287EXAMPLES::288289sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()290sage: K5.conductor()2915292"""293return self.__c294295def discriminant_of_K(self):296"""297Return the discriminant of the quadratic imaginary field `K` contained in ``self``.298299EXAMPLES::300301sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()302sage: K5.discriminant_of_K()303-7304"""305return self.__D306307@cached_method308def ramified_primes(self):309r"""310Return the primes of `\ZZ` that ramify in this ring class field.311312EXAMPLES::313314sage: E = EllipticCurve('389a'); K55 = E.heegner_point(-7,55).ring_class_field()315sage: K55.ramified_primes()316[5, 7, 11]317sage: E.heegner_point(-7).ring_class_field().ramified_primes()318[7]319"""320return arith.prime_divisors(self.__D * self.__c)321322def _repr_(self):323"""324EXAMPLES::325326sage: heegner_point(37,-7,55).ring_class_field()._repr_()327'Ring class field extension of QQ[sqrt(-7)] of conductor 55'328sage: heegner_point(37,-7).ring_class_field()._repr_()329'Hilbert class field of QQ[sqrt(-7)]'330"""331c = self.__c332if c == 1:333return "Hilbert class field of QQ[sqrt(%s)]"%self.__D334else:335return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c)336337@cached_method338def degree_over_K(self):339"""340Return the relative degree of this ring class field over the341quadratic imaginary field `K`.342343EXAMPLES::344345sage: E = EllipticCurve('389a'); P = E.heegner_point(-7,5)346sage: K5 = P.ring_class_field(); K5347Ring class field extension of QQ[sqrt(-7)] of conductor 5348sage: K5.degree_over_K()3496350sage: type(K5.degree_over_K())351<type 'sage.rings.integer.Integer'>352353sage: E = EllipticCurve('389a'); E.heegner_point(-20).ring_class_field().degree_over_K()3542355sage: E.heegner_point(-20,3).ring_class_field().degree_over_K()3564357sage: kronecker(-20,11)358-1359sage: E.heegner_point(-20,11).ring_class_field().degree_over_K()36024361"""362D, c = self.__D, self.__c363364K = self.quadratic_field()365366# Multiply class number by relative degree of the Hilbert class field H over K.367return K.class_number() * self.degree_over_H()368369@cached_method370def degree_over_H(self):371"""372Return the degree of this field over the Hilbert class field `H` of `K`.373374EXAMPLES::375376sage: E = EllipticCurve('389a')377sage: E.heegner_point(-59).ring_class_field().degree_over_H()3781379sage: E.heegner_point(-59).ring_class_field().degree_over_K()3803381sage: QuadraticField(-59,'a').class_number()3823383384Some examples in which prime dividing c is inert::385386sage: heegner_point(37,-7,3).ring_class_field().degree_over_H()3874388sage: heegner_point(37,-7,3^2).ring_class_field().degree_over_H()38912390sage: heegner_point(37,-7,3^3).ring_class_field().degree_over_H()39136392393The prime dividing c is split. For example, in the first case394`O_K/cO_K` is isomorphic to a direct sum of two copies of395``GF(2)``, so the units are trivial::396397sage: heegner_point(37,-7,2).ring_class_field().degree_over_H()3981399sage: heegner_point(37,-7,4).ring_class_field().degree_over_H()4002401sage: heegner_point(37,-7,8).ring_class_field().degree_over_H()4024403404Now c is ramified::405406sage: heegner_point(37,-7,7).ring_class_field().degree_over_H()4077408sage: heegner_point(37,-7,7^2).ring_class_field().degree_over_H()40949410"""411c = self.__c412if c == 1:413return ZZ(1)414415# Let K_c be the ring class field. We have by class field theory that416# Gal(K_c / H) = (O_K/c*O_K)^* / (Z/cZ)^*.417#418# To compute the cardinality of the above Galois group, we419# first reduce to the case that c = p^e is a prime power420# (since the expression is multiplicative in c).421# Of course, note also that #(Z/cZ)^* = phi(c)422#423# Case 1: p splits in O_K. Then424# #(O_K/p^e*O_K)^* = (#(Z/p^eZ)^*)^2 = phi(p^e)^2, so425# #(O_K/p^e*O_K)^*/(Z/p^eZ)^* = phi(p^e) = p^e - p^(e-1)426#427# Case 2: p is inert in O_K. Then428# #(O_K/p^e O_K)^* = p^(2*e)-p^(2*(e-1))429# so #(O_K/p^e*O_K)^*/(Z/p^eZ)^*430# = (p^(2*e)-p^(2*(e-1)))/(p^e-p^(e-1)) = p^e + p^(e-1).431#432# Case 3: p ramified in O_K. Then433# #(O_K/p^e O_K)^* = p^(2*e) - p^(2*e-1),434# so #(O_K/p^e O_K)^*/#(Z/p^eZ)^* = p^e.435#436# Section 4.2 of Cohen's "Advanced Computational Algebraic437# Number Theory" GTM is also relevant, though Cohen is working438# with *ray* class fields and here we want the cardinality439# of the *ring* class field, which is a subfield.440441K = self.quadratic_field()442443n = ZZ(1)444for p, e in c.factor():445F = K.factor(p)446if len(F) == 2:447# split case448n *= p**e - p**(e-1)449else:450if F[0][1] > 1:451# ramified case452n *= p**e453else:454# inert case455n *= p**e + p**(e-1)456return n457458@cached_method459def absolute_degree(self):460r"""461Return the absolute degree of this field over `\QQ`.462463EXAMPLES::464465sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()466sage: K.absolute_degree()46712468sage: K.degree_over_K()4696470"""471return 2*self.degree_over_K()472473degree_over_Q = absolute_degree474475@cached_method476def quadratic_field(self):477r"""478Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.479480EXAMPLES::481482sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()483sage: K.quadratic_field()484Number Field in sqrt_minus_7 with defining polynomial x^2 + 7485"""486D = self.__D487var = 'sqrt_minus_%s'%(-D)488return number_field.QuadraticField(D,var)489490@cached_method491def galois_group(self, base=QQ):492"""493Return the Galois group of ``self`` over base.494495INPUT:496497- ``base`` -- (default: `\QQ`) a subfield of ``self`` or `\QQ`498499EXAMPLES::500501sage: E = EllipticCurve('389a')502sage: A = E.heegner_point(-7,5).ring_class_field()503sage: A.galois_group()504Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5505sage: B = E.heegner_point(-7).ring_class_field()506sage: C = E.heegner_point(-7,15).ring_class_field()507sage: A.galois_group()508Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5509sage: A.galois_group(B)510Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]511sage: A.galois_group().cardinality()51212513sage: A.galois_group(B).cardinality()5146515sage: C.galois_group(A)516Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 15 over Ring class field extension of QQ[sqrt(-7)] of conductor 5517sage: C.galois_group(A).cardinality()5184519520"""521return GaloisGroup(self, base)522523def is_subfield(self, M):524"""525Return ``True`` if this ring class field is a subfield of the ring class field `M`.526If `M` is not a ring class field, then a TypeError is raised.527528EXAMPLES::529530sage: E = EllipticCurve('389a')531sage: A = E.heegner_point(-7,5).ring_class_field()532sage: B = E.heegner_point(-7).ring_class_field()533sage: C = E.heegner_point(-20).ring_class_field()534sage: D = E.heegner_point(-7,15).ring_class_field()535sage: B.is_subfield(A)536True537sage: B.is_subfield(B)538True539sage: B.is_subfield(D)540True541sage: B.is_subfield(C)542False543sage: A.is_subfield(B)544False545sage: A.is_subfield(D)546True547"""548if not isinstance(M, RingClassField):549raise TypeError("M must be a ring class field")550return self.quadratic_field() == M.quadratic_field() and \551M.conductor() % self.conductor() == 0552553##################################################################################554#555# Galois groups of ring class fields556#557##################################################################################558559class GaloisGroup(SageObject):560"""561A Galois group of a ring class field.562563EXAMPLES::564565sage: E = EllipticCurve('389a')566sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G567Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5568sage: G.field()569Ring class field extension of QQ[sqrt(-7)] of conductor 5570sage: G.cardinality()57112572sage: G.complex_conjugation()573Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5574575TESTS::576577sage: G = heegner_point(37,-7).ring_class_field().galois_group()578sage: loads(dumps(G)) == G579True580sage: type(G)581<class 'sage.schemes.elliptic_curves.heegner.GaloisGroup'>582"""583def __init__(self, field, base=QQ):584r"""585INPUT:586587- ``field`` -- a ring class field588589- ``base`` -- subfield of field (default: `\QQ`)590591EXAMPLES::592593sage: K5 = heegner_points(389,-7,5).ring_class_field()594sage: K1 = heegner_points(389,-7,1).ring_class_field()595sage: sage.schemes.elliptic_curves.heegner.GaloisGroup(K5,K1)596Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]597sage: K5.galois_group(K1)598Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]599"""600if not isinstance(field, RingClassField):601raise TypeError("field must be of type RingClassField")602if base != QQ and base != field.quadratic_field():603if not isinstance(base, RingClassField):604raise TypeError("base must be of type RingClassField or QQ or quadratic field")605if not base.is_subfield(field):606raise TypeError("base must be a subfield of field")607self.__field = field608self.__base = base609610def __eq__(self, G):611"""612EXAMPLES::613614sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()615sage: G == G616True617sage: G == 0618False619sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()620sage: G == H621False622"""623return isinstance(G, GaloisGroup) and (G.__field,G.__base) == (self.__field,self.__base)624625def __hash__(self):626"""627Return hash of this Galois group, which is the same as the628hash of the pair, the field and its base.629630EXAMPLES::631632sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()633sage: hash(G)634-6198252699510613726 # 64-bit6351905285410 # 32-bit636sage: hash((G.field(), G.base_field()))637-6198252699510613726 # 64-bit6381905285410 # 32-bit639"""640return hash((self.__field, self.__base))641642def __call__(self, x):643"""644Coerce `x` into ``self``, where `x` is a Galois group element, or645in case ``self`` has base field the Hilbert class field, `x` can646also be an element of the ring of integers.647648INPUT:649650- `x` -- automorphism or quadratic field element651652OUTPUT:653654- automorphism (or TypeError)655656EXAMPLES::657658sage: K5 = heegner_points(389,-52,5).ring_class_field()659sage: K1 = heegner_points(389,-52,1).ring_class_field()660sage: G = K5.galois_group(K1)661sage: G(1)662Class field automorphism defined by x^2 + 325*y^2663sage: G(G[0])664Class field automorphism defined by x^2 + 325*y^2665sage: alpha = 2 + K1.quadratic_field().gen(); alpha666sqrt_minus_52 + 2667sage: G(alpha)668Class field automorphism defined by 14*x^2 - 10*x*y + 25*y^2669670A TypeError is raised when the coercion is not possible::671672sage: G(0)673Traceback (most recent call last):674...675TypeError: x does not define element of (O_K/c*O_K)^*676677"""678if isinstance(x, GaloisAutomorphism) and x.parent() == self:679return x680try:681return self._alpha_to_automorphism(x)682except (ZeroDivisionError, TypeError):683raise TypeError("x does not define element of (O_K/c*O_K)^*")684685def _repr_(self):686"""687Return string representation of this Galois group.688689EXAMPLES::690691sage: E = EllipticCurve('389a')692sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()693sage: G._repr_()694'Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5'695"""696if self.base_field() != QQ:697s = " over %s"%self.base_field()698else:699s = ''700return "Galois group of %s%s"%(self.field(), s)701702def field(self):703"""704Return the ring class field that this Galois group acts on.705706EXAMPLES::707708sage: G = heegner_point(389,-7,5).ring_class_field().galois_group()709sage: G.field()710Ring class field extension of QQ[sqrt(-7)] of conductor 5711"""712return self.__field713714def base_field(self):715"""716Return the base field, which the field fixed by all the717automorphisms in this Galois group.718719EXAMPLES::720721sage: x = heegner_point(37,-7,5)722sage: Kc = x.ring_class_field(); Kc723Ring class field extension of QQ[sqrt(-7)] of conductor 5724sage: K = x.quadratic_field()725sage: G = Kc.galois_group(); G726Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5727sage: G.base_field()728Rational Field729sage: G.cardinality()73012731sage: Kc.absolute_degree()73212733sage: G = Kc.galois_group(K); G734Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Number Field in sqrt_minus_7 with defining polynomial x^2 + 7735sage: G.cardinality()7366737sage: G.base_field()738Number Field in sqrt_minus_7 with defining polynomial x^2 + 7739sage: G = Kc.galois_group(Kc); G740Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Ring class field extension of QQ[sqrt(-7)] of conductor 5741sage: G.cardinality()7421743sage: G.base_field()744Ring class field extension of QQ[sqrt(-7)] of conductor 5745"""746return self.__base747748@cached_method749def kolyvagin_generators(self):750r"""751Assuming this Galois group `G` is of the form752`G=\textrm{Gal}(K_c/K_1)`, with `c=p_1\dots p_n` satisfying the753Kolyvagin hypothesis, this function returns noncanonical754choices of lifts of generators for each of the cyclic factors755of `G` corresponding to the primes dividing `c`. Thus the756`i`-th returned valued is an element of `G` that maps to the757identity element of `\textrm{Gal}(K_p/K_1)` for all `p \neq p_i` and758to a choice of generator of `\textrm{Gal}(K_{p_i}/K_1)`.759760OUTPUT:761762- list of elements of ``self``763764EXAMPLES::765766sage: K3 = heegner_points(389,-52,3).ring_class_field()767sage: K1 = heegner_points(389,-52,1).ring_class_field()768sage: G = K3.galois_group(K1)769sage: G.kolyvagin_generators()770(Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2,)771772sage: K5 = heegner_points(389,-52,5).ring_class_field()773sage: K1 = heegner_points(389,-52,1).ring_class_field()774sage: G = K5.galois_group(K1)775sage: G.kolyvagin_generators()776(Class field automorphism defined by 17*x^2 - 14*x*y + 22*y^2,)777"""778M = self.field()779c = M.conductor()780if not (self._base_is_hilbert_class_field() and self.is_kolyvagin()):781raise ValueError("field must be of the form Gal(K_c/K_1)")782if not c.is_prime():783raise NotImplementedError("only implemented when c is prime")784785# Since c satisfies Kolyvagin and is prime, the group is cyclic,786# so we just find a generator.787for sigma in self:788if sigma.order() == self.cardinality():789return tuple([sigma])790791raise NotImplementedError792793@cached_method794def lift_of_hilbert_class_field_galois_group(self):795r"""796Assuming this Galois group `G` is of the form `G=\textrm{Gal}(K_c/K)`,797this function returns noncanonical choices of lifts of the798elements of the quotient group `\textrm{Gal}(K_1/K)`.799800OUTPUT:801802- tuple of elements of self803804EXAMPLES::805806sage: K5 = heegner_points(389,-52,5).ring_class_field()807sage: G = K5.galois_group(K5.quadratic_field())808sage: G.lift_of_hilbert_class_field_galois_group()809(Class field automorphism defined by x^2 + 325*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 163*y^2)810sage: G.cardinality()81112812sage: K5.quadratic_field().class_number()8132814"""815if not self._base_is_quad_imag_field():816raise ValueError("Galois group must be of the form Gal(K_c/K)")817K = self.base_field()818C = K.class_group()819v = []820lifts = []821for sigma in self:822I = sigma.ideal()823g = C(I)824if g not in v:825v.append(g)826lifts.append(sigma)827return tuple(lifts)828829@cached_method830def _list(self):831r"""832Enumerate the elements of ``self``.833834EXAMPLES::835836Example with order 1 (a special case)::837838sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,1).ring_class_field()839sage: G = F.galois_group(F.quadratic_field())840sage: G._list()841(Class field automorphism defined by x^2 + x*y + 2*y^2,)842843Example over quadratic imaginary field::844845sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()846sage: G = F.galois_group(F.quadratic_field())847sage: G._list()848(Class field automorphism defined by x^2 + x*y + 44*y^2, Class field automorphism defined by 2*x^2 - x*y + 22*y^2, Class field automorphism defined by 2*x^2 + x*y + 22*y^2, Class field automorphism defined by 4*x^2 - x*y + 11*y^2, Class field automorphism defined by 4*x^2 + x*y + 11*y^2, Class field automorphism defined by 7*x^2 + 7*x*y + 8*y^2)849850Example over `\QQ` (it is not implemented yet)::851852sage: K3 = heegner_points(389,-52,3).ring_class_field()853sage: K3.galois_group()._list()854Traceback (most recent call last):855...856NotImplementedError: Galois group over QQ not yet implemented857858Example over Hilbert class field::859860sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()861sage: G = K3.galois_group(K1)862sage: G._list()863(Class field automorphism defined by x^2 + 117*y^2, Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2, Class field automorphism defined by 9*x^2 + 13*y^2, Class field automorphism defined by 9*x^2 + 6*x*y + 14*y^2)864"""865if self._base_is_QQ():866raise NotImplementedError("Galois group over QQ not yet implemented")867elif self._base_is_quad_imag_field():868# Over the quadratic imaginary field, so straightforward869# enumeration of all reduced primitive binary quadratic870# forms of discriminant D*c^2.871D = self.base_field().discriminant()872c = self.field().conductor()873Q = [f for f in BinaryQF_reduced_representatives(D*c*c) if f.is_primitive()]874v = [GaloisAutomorphismQuadraticForm(self, f) for f in Q]875876elif self._base_is_hilbert_class_field() and self.is_kolyvagin():877# Take only the automorphisms in the quad imag case that map to878# a principal ideal.879M = self.field()880K = M.quadratic_field()881v = []882self.__p1_to_automorphism = {}883for sigma in M.galois_group(K)._list():884I = sigma.ideal()885if I.is_principal():886# sigma does define an element of our Galois subgroup.887alpha = sigma.ideal().gens_reduced()[0]888t = GaloisAutomorphismQuadraticForm(self, sigma.quadratic_form(), alpha=alpha)889self.__p1_to_automorphism[t.p1_element()] = t890v.append(t)891else:892raise NotImplementedError("general Galois group not yet implemented")893894v.sort()895assert len(v) == self.cardinality(), "bug enumerating Galois group elements"896return tuple(v)897898def _quadratic_form_to_alpha(self, f):899"""900INPUT:901902- `f` -- a binary quadratic form with discriminant `c^2 D`903904OUTPUT:905906- an element of the ring of integers of the quadratic907imaginary field908909EXAMPLES::910911sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()912sage: G = K3.galois_group(K1)913sage: [G._quadratic_form_to_alpha(s.quadratic_form()) for s in G]914[3/2*sqrt_minus_52, 1/6*sqrt_minus_52 + 1/3, 1/6*sqrt_minus_52, 1/6*sqrt_minus_52 - 1/3]915916What happens when we input a quadratic form that has nothing917to do with `G`::918919sage: G._quadratic_form_to_alpha(BinaryQF([1,2,3]))920Traceback (most recent call last):921...922ValueError: quadratic form has the wrong discriminant923"""924A,B,C = f925K = self.field().quadratic_field()926if f.discriminant() != self.field().conductor()**2 * K.discriminant():927raise ValueError("quadratic form has the wrong discriminant")928929R = K['X']930v = R([C,B,A]).roots()[0][0]931return v932933def _alpha_to_automorphism(self, alpha):934r"""935Assuming ``self`` has base field the Hilbert class field, make an936automorphism from the element `\alpha` of the ring of integers937into ``self``.938939INPUT:940941- `\alpha` -- element of quadratic imaginary field coprime to conductor942943EXAMPLES::944945sage: K3 = heegner_points(389,-52,3).ring_class_field()946sage: K1 = heegner_points(389,-52,1).ring_class_field()947sage: G = K3.galois_group(K1)948sage: G._alpha_to_automorphism(1)949Class field automorphism defined by x^2 + 117*y^2950sage: [G._alpha_to_automorphism(s.alpha()) for s in G] == list(G)951True952"""953if not self._base_is_hilbert_class_field() and self.is_kolyvagin():954raise TypeError("base must be Hilbert class field with Kolyvagin condition on conductor")955R = self.field().quadratic_field().maximal_order()956uv = self._alpha_to_p1_element(R(alpha))957try:958d = self.__p1_to_automorphism959except AttributeError:960self._list() # computes attribute as side-effect961d = self.__p1_to_automorphism962return d[uv]963964965def _alpha_to_p1_element(self, alpha):966r"""967Given an element of the ring of integers that is nonzero968modulo c, return canonical (after our fixed choice of basis)969element of the project line corresponding to it.970971INPUT:972973- `\alpha` -- element of the ring of integers of the974quadratic imaginary field975976OUTPUT:977978- 2-tuple of integers979980EXAMPLES::981982sage: K3 = heegner_points(389,-52,3).ring_class_field()983sage: K1 = heegner_points(389,-52,1).ring_class_field()984sage: G = K3.galois_group(K1)985sage: G._alpha_to_p1_element(1)986(1, 0)987sage: sorted([G._alpha_to_p1_element(s.alpha()) for s in G])988[(0, 1), (1, 0), (1, 1), (1, 2)]989"""990try:991A, P1 = self.__alpha_to_p1_element992except AttributeError:993# todo (optimize) -- this whole function can be massively optimized:994M = self.field()995A = M.quadratic_field().maximal_order().free_module()996P1 = P1List(M.conductor())997self.__alpha_to_p1_element = A, P1998alpha = self.field().quadratic_field()(alpha)999w = A.coordinate_vector(alpha.vector())1000w *= w.denominator()1001w = w.change_ring(ZZ)1002n = arith.gcd(w)1003w /= n1004c = P1.N()1005w = P1.normalize(ZZ(w[0])%c, ZZ(w[1])%c)1006if w == (0,0):1007w = (1,0)1008return w10091010def _p1_element_to_alpha(self, uv):1011"""1012Convert a normalized pair ``uv=(u,v)`` of integers to the1013corresponding element of the ring of integers got by taking `u1014b_0 + v b_1` where `b_0, b_1` are the basis for the ring of1015integers.10161017INPUT:10181019- ``uv`` -- pair of integers10201021OUTPUT:10221023- element of maximal order of quadratic field10241025EXAMPLES::10261027sage: K5 = heegner_points(389,-52,5).ring_class_field()1028sage: K1 = heegner_points(389,-52,1).ring_class_field()1029sage: G = K5.galois_group(K1)1030sage: v = [G._alpha_to_p1_element(s.alpha()) for s in G]1031sage: [G._p1_element_to_alpha(z) for z in v]1032[1, 1/2*sqrt_minus_52, 1/2*sqrt_minus_52 + 1, 2*sqrt_minus_52 + 1, sqrt_minus_52 + 1, 3/2*sqrt_minus_52 + 1]1033sage: [G(G._p1_element_to_alpha(z)) for z in v] == list(G)1034True1035"""1036B = self.field().quadratic_field().maximal_order().basis()1037return uv[0]*B[0] + uv[1]*B[1]103810391040def _base_is_QQ(self):1041r"""1042Return ``True`` if the base field of this ring class field is `\QQ`.10431044EXAMPLES::10451046sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1047sage: M.galois_group(H.quadratic_field())._base_is_QQ()1048False1049sage: M.galois_group(QQ)._base_is_QQ()1050True1051sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_QQ()1052False1053"""1054return self.__base == QQ10551056def _base_is_quad_imag_field(self):1057"""1058Return ``True`` if the base field of this ring class field is the1059quadratic imaginary field `K`.10601061EXAMPLES::10621063sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1064sage: M.galois_group(H.quadratic_field())._base_is_quad_imag_field()1065True1066sage: M.galois_group(QQ)._base_is_quad_imag_field()1067False1068sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_quad_imag_field()1069False1070"""1071return number_field.is_QuadraticField(self.__base)10721073def is_kolyvagin(self):1074"""1075Return ``True`` if conductor `c` is prime to the discriminant of the1076quadratic field, `c` is squarefree and each prime dividing `c`1077is inert.10781079EXAMPLES::10801081sage: K5 = heegner_points(389,-52,5).ring_class_field()1082sage: K1 = heegner_points(389,-52,1).ring_class_field()1083sage: K5.galois_group(K1).is_kolyvagin()1084True1085sage: K7 = heegner_points(389,-52,7).ring_class_field()1086sage: K7.galois_group(K1).is_kolyvagin()1087False1088sage: K25 = heegner_points(389,-52,25).ring_class_field()1089sage: K25.galois_group(K1).is_kolyvagin()1090False1091"""1092M = self.field()1093c = M.conductor()1094D = M.quadratic_field().discriminant()1095if c.gcd(D) != 1: return False1096if not c.is_squarefree(): return False1097for p in c.prime_divisors():1098if not is_inert(D,p):1099return False1100return True11011102def _base_is_hilbert_class_field(self):1103"""1104Return ``True`` if the base field of this ring class field is the1105Hilbert class field of `K` viewed as a ring class field (so1106not of data type QuadraticField).11071108EXAMPLES::11091110sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1111sage: M.galois_group(H.quadratic_field())._base_is_hilbert_class_field()1112False1113sage: M.galois_group(QQ)._base_is_hilbert_class_field()1114False1115sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_hilbert_class_field()1116True1117"""1118M = self.__base1119return isinstance(M, RingClassField) and M.conductor() == 1112011211122def __getitem__(self, i):1123"""1124EXAMPLES::11251126sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()1127sage: G = F.galois_group(F.quadratic_field())1128sage: G[0]1129Class field automorphism defined by x^2 + x*y + 44*y^21130"""1131return self._list()[i]113211331134def __len__(self):1135"""1136EXAMPLES::11371138sage: K5 = heegner_points(389,-52,5).ring_class_field()1139sage: K1 = heegner_points(389,-52,1).ring_class_field()1140sage: G = K5.galois_group(K1)1141sage: G.cardinality()114261143sage: len(G)114461145"""1146return self.cardinality()11471148@cached_method1149def cardinality(self):1150"""1151Return the cardinality of this Galois group.11521153EXAMPLES::11541155sage: E = EllipticCurve('389a')1156sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G1157Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51158sage: G.cardinality()1159121160sage: G = E.heegner_point(-7).ring_class_field().galois_group()1161sage: G.cardinality()116221163sage: G = E.heegner_point(-7,55).ring_class_field().galois_group()1164sage: G.cardinality()11651201166"""1167return self.__field.absolute_degree() // self.__base.absolute_degree()11681169@cached_method1170def complex_conjugation(self):1171"""1172Return the automorphism of ``self`` determined by complex1173conjugation. The base field must be the rational numbers.11741175EXAMPLES::11761177sage: E = EllipticCurve('389a')1178sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()1179sage: G.complex_conjugation()1180Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51181"""1182if self.base_field() != QQ:1183raise ValueError("the base field must be fixed by complex conjugation")1184return GaloisAutomorphismComplexConjugation(self)118511861187##################################################################################1188#1189# Elements of Galois groups1190#1191##################################################################################11921193class GaloisAutomorphism(SageObject):1194"""1195An abstract automorphism of a ring class field.11961197.. TODO::11981199make :class:`GaloisAutomorphism` derive from GroupElement, so1200that one gets powers for free, etc.1201"""1202def __init__(self, parent):1203"""1204INPUT:12051206- ``parent`` -- a group of automorphisms of a ring class field12071208EXAMPLES::12091210sage: G = heegner_points(389,-7,5).ring_class_field().galois_group(); G1211Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51212sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphism(G)1213<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphism'>1214"""1215self.__parent = parent12161217def parent(self):1218"""1219Return the parent of this automorphism, which is a Galois1220group of a ring class field.12211222EXAMPLES::12231224sage: E = EllipticCurve('389a')1225sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()1226sage: s.parent()1227Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51228"""1229return self.__parent12301231def domain(self):1232"""1233Return the domain of this automorphism.12341235EXAMPLES::12361237sage: E = EllipticCurve('389a')1238sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()1239sage: s.domain()1240Ring class field extension of QQ[sqrt(-7)] of conductor 51241"""1242return self.parent().field()12431244class GaloisAutomorphismComplexConjugation(GaloisAutomorphism):1245"""1246The complex conjugation automorphism of a ring class field.12471248EXAMPLES::12491250sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1251sage: conj1252Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51253sage: conj.domain()1254Ring class field extension of QQ[sqrt(-7)] of conductor 512551256TESTS::12571258sage: type(conj)1259<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation'>1260sage: loads(dumps(conj)) == conj1261True1262"""1263def __init__(self, parent):1264"""1265INPUT:12661267- ``parent`` -- a group of automorphisms of a ring class field12681269EXAMPLES::12701271sage: G = heegner_point(37,-7,5).ring_class_field().galois_group()1272sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)1273Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51274"""1275GaloisAutomorphism.__init__(self, parent)12761277def __hash__(self):1278"""1279EXAMPLES::12801281sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()1282sage: conj = G.complex_conjugation(); hash(conj)12831347197483068745902 # 64-bit1284480045230 # 32-bit1285"""1286return hash((self.parent(), 1))12871288def __eq__(self, right):1289"""1290EXAMPLES::12911292sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()1293sage: conj = G.complex_conjugation()1294sage: conj2 = sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)1295sage: conj is conj21296False1297sage: conj == conj21298True1299"""1300return isinstance(right, GaloisAutomorphismComplexConjugation) and \1301self.parent() == right.parent()13021303def _repr_(self):1304"""1305Return print representation of the complex conjugation automorphism.13061307EXAMPLES::13081309sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1310sage: conj._repr_()1311'Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5'1312"""1313return "Complex conjugation automorphism of %s"%self.domain()13141315## def __mul__(self, right):1316## """1317## Return the composition of two automorphisms.13181319## EXAMPLES::13201321## sage: ?1322## """1323## if self.parent() != right.__parent():1324## raise TypeError, "automorphisms must be of the same class field"1325## raise NotImplementedError13261327def __invert__(self):1328"""1329Return the inverse of ``self``, which is just ``self`` again.13301331EXAMPLES::13321333sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1334sage: ~conj1335Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51336"""1337return self13381339def order(self):1340"""1341EXAMPLES::13421343sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1344sage: conj.order()134521346"""1347return ZZ(2)13481349class GaloisAutomorphismQuadraticForm(GaloisAutomorphism):1350"""1351An automorphism of a ring class field defined by a quadratic form.13521353EXAMPLES::13541355sage: H = heegner_points(389,-20,3)1356sage: sigma = H.ring_class_field().galois_group(H.quadratic_field())[0]; sigma1357Class field automorphism defined by x^2 + 45*y^21358sage: type(sigma)1359<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm'>1360sage: loads(dumps(sigma)) == sigma1361True1362"""1363def __init__(self, parent, quadratic_form, alpha=None):1364r"""1365INPUT:13661367- ``parent`` -- a group of automorphisms of a ring class field13681369- ``quadratic_form`` -- a binary quadratic form that1370defines an element of the Galois group of `K_c` over `K`.13711372- ``\alpha`` -- (default: ``None``) optional data that specified1373element corresponding element of `(\mathcal{O}_K /1374c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class field1375theory.13761377EXAMPLES::13781379sage: H = heegner_points(389,-20,3); G = H.ring_class_field().galois_group(H.quadratic_field())1380sage: f = BinaryQF_reduced_representatives(-20*9)[0]1381sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm(G, f)1382Class field automorphism defined by x^2 + 45*y^21383"""1384self.__quadratic_form = quadratic_form.reduced_form()1385self.__alpha = alpha1386GaloisAutomorphism.__init__(self, parent)13871388@cached_method1389def order(self):1390"""1391Return the multiplicative order of this Galois group automorphism.13921393EXAMPLES::13941395sage: K3 = heegner_points(389,-52,3).ring_class_field()1396sage: K1 = heegner_points(389,-52,1).ring_class_field()1397sage: G = K3.galois_group(K1)1398sage: sorted([g.order() for g in G])1399[1, 2, 4, 4]1400sage: K5 = heegner_points(389,-52,5).ring_class_field()1401sage: K1 = heegner_points(389,-52,1).ring_class_field()1402sage: G = K5.galois_group(K1)1403sage: sorted([g.order() for g in G])1404[1, 2, 3, 3, 6, 6]1405"""1406alpha = self.__alpha1407if alpha is None:1408raise NotImplementedError("order only currently implemented when alpha given in construction")1409G = self.parent()1410one = G(1).p1_element()1411ans = ZZ(1)1412z = alpha1413for i in range(G.cardinality()):1414if G._alpha_to_p1_element(z) == one:1415return ans1416ans += 11417z *= alpha1418assert False, "bug in order"14191420def alpha(self):1421r"""1422Optional data that specified element corresponding element of1423`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class1424field theory.14251426This is a generator of the ideal corresponding to this1427automorphism.14281429EXAMPLES::14301431sage: K3 = heegner_points(389,-52,3).ring_class_field()1432sage: K1 = heegner_points(389,-52,1).ring_class_field()1433sage: G = K3.galois_group(K1)1434sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)1435[1, 1/2*sqrt_minus_52 + 1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 - 1]1436sage: sorted([x^2 for x in orb]) # this is just for testing1437[-13, -sqrt_minus_52 - 12, sqrt_minus_52 - 12, 1]14381439sage: K5 = heegner_points(389,-52,5).ring_class_field()1440sage: K1 = heegner_points(389,-52,1).ring_class_field()1441sage: G = K5.galois_group(K1)1442sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)1443[1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 + 1, 1/2*sqrt_minus_52 - 1, 1/2*sqrt_minus_52 - 2, -1/2*sqrt_minus_52 - 2]1444sage: sorted([x^2 for x in orb]) # just for testing1445[-13, -sqrt_minus_52 - 12, sqrt_minus_52 - 12, -2*sqrt_minus_52 - 9, 2*sqrt_minus_52 - 9, 1]14461447"""1448if self.__alpha is None:1449raise ValueError("alpha data not defined")1450return self.__alpha14511452@cached_method1453def p1_element(self):1454r"""1455Return element of the projective line corresponding to this1456automorphism.14571458This only makes sense if this automorphism is in the Galois1459group `\textrm{Gal}(K_c/K_1)`.14601461EXAMPLES::14621463sage: K3 = heegner_points(389,-52,3).ring_class_field()1464sage: K1 = heegner_points(389,-52,1).ring_class_field()1465sage: G = K3.galois_group(K1)1466sage: sorted([g.p1_element() for g in G])1467[(0, 1), (1, 0), (1, 1), (1, 2)]14681469sage: K5 = heegner_points(389,-52,5).ring_class_field()1470sage: K1 = heegner_points(389,-52,1).ring_class_field()1471sage: G = K5.galois_group(K1)1472sage: sorted([g.p1_element() for g in G])1473[(0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]1474"""1475return self.parent()._alpha_to_p1_element(self.__alpha)14761477def __hash__(self):1478"""1479EXAMPLES::14801481sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1482sage: hash(s)14834262582128197601113 # 64-bit1484-1994029223 # 32-bit1485"""1486return hash((self.parent(), tuple(self.__quadratic_form)))14871488def __eq__(self, right):1489"""1490EXAMPLES::14911492sage: H = heegner_points(389,-7,5); s = H.ring_class_field().galois_group(H.quadratic_field())[1]1493sage: s == s1494True1495sage: s == s*s1496False1497sage: s == s*s*s*s*s1498False1499sage: s == s*s*s*s*s*s*s1500True1501"""1502return isinstance(right, GaloisAutomorphismQuadraticForm) and \1503self.parent() == right.parent() and \1504self.quadratic_form().is_equivalent(right.quadratic_form())15051506def __cmp__(self, right):1507"""1508Compare ``self`` and right. Used mainly so that lists of1509automorphisms are sorted consistently between runs.15101511EXAMPLES::15121513sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1514sage: s.__cmp__(s)151501516sage: s.__cmp__(0) != 01517True1518"""1519if not isinstance(right, GaloisAutomorphismQuadraticForm):1520return cmp(type(self), type(right))1521c = cmp(self.parent(), right.parent())1522if c: return c1523if self.quadratic_form().is_equivalent(right.quadratic_form()):1524return 01525return cmp(self.quadratic_form(), right.quadratic_form())15261527def _repr_(self):1528"""1529Return string representation of this automorphism.15301531EXAMPLES::15321533sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1534sage: s._repr_()1535'Class field automorphism defined by x^2 + 45*y^2'15361537"""1538return "Class field automorphism defined by %s"%self.__quadratic_form15391540def __mul__(self, right):1541"""1542Return the composition of two automorphisms.15431544EXAMPLES::15451546sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1547sage: s * s1548Class field automorphism defined by x^2 + 45*y^21549sage: G = s.parent(); list(G)1550[Class field automorphism defined by x^2 + 45*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^2, Class field automorphism defined by 5*x^2 + 9*y^2, Class field automorphism defined by 7*x^2 + 4*x*y + 7*y^2]1551sage: G[0]*G[0]1552Class field automorphism defined by x^2 + 45*y^21553sage: G[1]*G[2] == G[3]1554True1555"""1556if self.parent() != right.parent():1557raise TypeError("automorphisms must be of the same class field")1558if not isinstance(right, GaloisAutomorphismQuadraticForm):1559# TODO: special case when right is complex conjugation1560raise NotImplementedError1561Q = (self.__quadratic_form * right.__quadratic_form).reduced_form()1562if self.__alpha and right.__alpha:1563alpha = self.__alpha * right.__alpha1564else:1565alpha = None1566return GaloisAutomorphismQuadraticForm(self.parent(), Q, alpha=alpha)15671568def quadratic_form(self):1569"""1570Return reduced quadratic form corresponding to this Galois1571automorphism.157215731574EXAMPLES::15751576sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1577sage: s.quadratic_form()1578x^2 + 45*y^21579"""1580return self.__quadratic_form15811582@cached_method1583def ideal(self):1584r"""1585Return ideal of ring of integers of quadratic imaginary field1586corresponding to this quadratic form. This is the ideal15871588`I = \left(A, \frac{-B+ c\sqrt{D}}{2}\right) \mathcal{O}_K`.15891590EXAMPLES::15911592sage: E = EllipticCurve('389a'); F= E.heegner_point(-20,3).ring_class_field()1593sage: G = F.galois_group(F.quadratic_field())1594sage: G[1].ideal()1595Fractional ideal (2, 1/2*sqrt_minus_20 + 1)1596sage: [s.ideal().gens() for s in G]1597[(1, 3/2*sqrt_minus_20), (2, 3/2*sqrt_minus_20 - 1), (5, 3/2*sqrt_minus_20), (7, 3/2*sqrt_minus_20 - 2)]1598"""1599M = self.parent().field()1600K = M.quadratic_field()1601f = self.quadratic_form()1602c = M.conductor()1603sqrtD = K.gen()1604(A,B,C) = f1605if A%c == 0:1606A, C = C, A1607return K.maximal_order().ideal([A, (-B+c*sqrtD)/2])16081609## def __call__(self, z):1610## """1611## Return image of the Heegner point `z` under this automorphism.1612##1613## INPUT:1614##1615## - `z` -- a Heegner point on `X_0(N)` or an elliptic curve1616##1617## OUTPUT:1618##1619## - a Heegner point1620##1621## EXAMPLES::1622##1623## sage: x = heegner_point(389,-20,3); F = x.ring_class_field()1624## sage: sigma = F.galois_group(F.quadratic_field())[1]; sigma1625## Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^21626## sage: sigma(x)1627## Heegner point 3/1556*sqrt(-20) - 495/778 of discriminant -20 and conductor 3 on X_0(389)1628## """1629## if isinstance(z, HeegnerPointOnX0N):1630## if z.ring_class_field() != self.domain():1631## raise NotImplementedError, "class fields must be the same"1632## # TODO -- check more compatibilities?1633## # TODO -- this is surely backwards -- something must be inverted?1634## f = z.quadratic_form() * self.quadratic_form()1635## # TODO -- put f into the correct form with A divisible by N, etc.?1636## # That could be done by looking up reduced form of f in a canonical1637## # list of best reps.1638## N,D,c = z.level(),z.discriminant(),z.conductor()1639## return HeegnerPointOnX0N(N,D,c, f = f)1640## else:1641## raise NotImplementedError16421643##################################################################################1644#1645# Specific Heegner points1646#1647##################################################################################164816491650class HeegnerPoint(SageObject):1651r"""1652A Heegner point of level `N`, discriminant `D` and conductor `c`1653is any point on a modular curve or elliptic curve that is1654concocted in some way from a quadratic imaginary `\tau` in the upper1655half plane with `\Delta(\tau) = D c = \Delta(N \tau)`.16561657EXAMPLES::16581659sage: x = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,13); x1660Heegner point of level 389, discriminant -7, and conductor 131661sage: type(x)1662<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1663sage: loads(dumps(x)) == x1664True1665"""1666def __init__(self, N, D, c):1667"""1668INPUT:16691670- `N` -- (positive integer) the level16711672- `D` -- (negative integer) fundamental discriminant16731674- `c` -- (positive integer) conductor16751676Since this is an abstract base class, no type or compatibility1677checks are done, as those are all assumed to be done in the1678derived class.16791680EXAMPLES::16811682sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1683sage: type(H)1684<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1685"""1686self.__N = N1687self.__D = D1688self.__c = c16891690def __cmp__(self, x):1691"""1692Compare two Heegner points.16931694EXAMPLES::16951696sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1697sage: H.__cmp__(H)169801699"""1700if not isinstance(x, HeegnerPoint):1701raise NotImplementedError1702return cmp((self.__N, self.__D, self.__c), (x.__N, x.__D, x.__c))17031704def _repr_(self):1705"""1706EXAMPLES::17071708sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1709sage: type(H)1710<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1711sage: H._repr_()1712'Heegner point of level 389, discriminant -7, and conductor 5'1713"""1714return "Heegner point of level %s, discriminant %s, and conductor %s"%(1715self.__N, self.__D, self.__c)17161717def __hash__(self):1718"""1719EXAMPLES::17201721sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)1722<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1723sage: hash(H)17246187687223143458874 # 64-bit1725-458201030 # 32-bit1726"""1727return hash((self.__N, self.__D, self.__c))17281729def __eq__(self, right):1730"""1731EXAMPLES::17321733sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)1734<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1735sage: J = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,11)1736sage: H == H1737True1738sage: H == J1739False1740sage: J == H1741False1742sage: H == 01743False1744"""1745if not isinstance(right, HeegnerPoint): return False1746return (self.__N, self.__D, self.__c) == (right.__N, right.__D, right.__c)17471748def level(self):1749"""1750Return the level of this Heegner point, which is the level of the1751modular curve `X_0(N)` on which this is a Heegner point.17521753EXAMPLES::17541755sage: heegner_point(389,-7,5).level()17563891757"""1758return self.__N17591760def conductor(self):1761"""1762Return the conductor of this Heegner point.17631764EXAMPLES::17651766sage: heegner_point(389,-7,5).conductor()176751768sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P1769Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 371770sage: P.conductor()177171772sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.conductor()177351774"""1775return self.__c17761777def discriminant(self):1778"""1779Return the discriminant of the quadratic imaginary field1780associated to this Heegner point.17811782EXAMPLES::17831784sage: heegner_point(389,-7,5).discriminant()1785-71786sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P1787Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 371788sage: P.discriminant()1789-671790sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.discriminant()1791-71792"""1793return self.__D17941795@cached_method1796def quadratic_field(self):1797"""1798Return the quadratic number field of discriminant `D`.17991800EXAMPLES::18011802sage: x = heegner_point(37,-7,5)1803sage: x.quadratic_field()1804Number Field in sqrt_minus_7 with defining polynomial x^2 + 7180518061807sage: E = EllipticCurve('37a'); P = E.heegner_point(-40)1808sage: P.quadratic_field()1809Number Field in sqrt_minus_40 with defining polynomial x^2 + 401810sage: P.quadratic_field() is P.quadratic_field()1811True1812sage: type(P.quadratic_field())1813<class 'sage.rings.number_field.number_field.NumberField_quadratic_with_category'>1814"""1815return self.ring_class_field().quadratic_field()18161817@cached_method1818def quadratic_order(self):1819"""1820Return the order in the quadratic imaginary field of conductor1821`c`, where `c` is the conductor of this Heegner point.18221823EXAMPLES::18241825sage: heegner_point(389,-7,5).quadratic_order()1826Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 71827sage: heegner_point(389,-7,5).quadratic_order().basis()1828[1, 5*sqrt_minus_7]18291830sage: E = EllipticCurve('37a'); P = E.heegner_point(-40,11)1831sage: P.quadratic_order()1832Order in Number Field in sqrt_minus_40 with defining polynomial x^2 + 401833sage: P.quadratic_order().basis()1834[1, 11*sqrt_minus_40]18351836"""1837K = self.quadratic_field()1838return K.order([1,self.conductor()*K.gen()])18391840@cached_method1841def ring_class_field(self):1842"""1843Return the ring class field associated to this Heegner point.1844This is an extension `K_c` over `K`, where `K` is the1845quadratic imaginary field and `c` is the conductor associated1846to this Heegner point. This Heegner point is defined over1847`K_c` and the Galois group `Gal(K_c/K)` acts transitively on1848the Galois conjugates of this Heegner point.18491850EXAMPLES::18511852sage: E = EllipticCurve('389a'); K.<a> = QuadraticField(-5)1853sage: len(K.factor(5))185411855sage: len(K.factor(23))185621857sage: E.heegner_point(-7, 5).ring_class_field().degree_over_K()185861859sage: E.heegner_point(-7, 23).ring_class_field().degree_over_K()1860221861sage: E.heegner_point(-7, 5*23).ring_class_field().degree_over_K()18621321863sage: E.heegner_point(-7, 5^2).ring_class_field().degree_over_K()1864301865sage: E.heegner_point(-7, 7).ring_class_field().degree_over_K()186671867"""1868return RingClassField(self.discriminant(), self.conductor())186918701871##################################################################################1872#1873# Sets of Heegner points1874#1875##################################################################################18761877class HeegnerPoints(SageObject):1878"""1879The set of Heegner points with given parameters.18801881EXAMPLES::18821883sage: H = heegner_points(389); H1884Set of all Heegner points on X_0(389)1885sage: type(H)1886<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>1887sage: isinstance(H, sage.schemes.elliptic_curves.heegner.HeegnerPoints)1888True1889"""1890def __init__(self, N):1891"""1892INPUT:18931894- `N` -- level, a positive integer18951896EXAMPLES::18971898sage: heegner_points(37)1899Set of all Heegner points on X_0(37)1900sage: heegner_points(0)1901Traceback (most recent call last):1902...1903ValueError: N must a positive integer1904"""1905self.__N = ZZ(N)1906if self.__N <= 0:1907raise ValueError("N must a positive integer")19081909def level(self):1910"""1911Return the level `N` of the modular curve `X_0(N)`.19121913EXAMPLES::19141915sage: heegner_points(389).level()19163891917"""1918return self.__N191919201921class HeegnerPoints_level(HeegnerPoints):1922"""1923Return the infinite set of all Heegner points on `X_0(N)` for all1924quadratic imaginary fields.19251926EXAMPLES::19271928sage: H = heegner_points(11); H1929Set of all Heegner points on X_0(11)1930sage: type(H)1931<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>1932sage: loads(dumps(H)) == H1933True1934"""1935def __eq__(self, other):1936"""1937EXAMPLES::19381939sage: H = heegner_points(11)1940sage: H == heegner_points(13)1941False1942sage: H == heegner_points(11)1943True1944sage: H == 01945False1946"""1947return isinstance(other, HeegnerPoints_level) and self.level() == other.level()19481949def _repr_(self):1950"""1951Return string representation of the set of Heegner points.19521953EXAMPLES::19541955sage: heegner_points(389)._repr_()1956'Set of all Heegner points on X_0(389)'1957"""1958return "Set of all Heegner points on X_0(%s)"%self.level()19591960def reduce_mod(self, ell):1961r"""1962Return object that allows for computation with Heegner points1963of level `N` modulo the prime `\ell`, represented using1964quaternion algebras.19651966INPUT:19671968- `\ell` -- prime19691970EXAMPLES::19711972sage: heegner_points(389).reduce_mod(7).quaternion_algebra()1973Quaternion Algebra (-1, -7) with base ring Rational Field1974"""1975return HeegnerQuatAlg(self.level(), ell)19761977def discriminants(self, n=10, weak=False):1978r"""1979Return the first `n` quadratic imaginary discriminants that1980satisfy the Heegner hypothesis for `N`.19811982INPUTS:19831984- `n` -- nonnegative integer19851986- ``weak`` -- bool (default: ``False``); if ``True`` only require1987weak Heegner hypothesis, which is the same as usual but1988without the condition that `\gcd(D,N)=1`.19891990EXAMPLES::19911992sage: X = heegner_points(37)1993sage: X.discriminants(5)1994[-7, -11, -40, -47, -67]19951996The default is 10::19971998sage: X.discriminants()1999[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]2000sage: X.discriminants(15)2001[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -115, -120, -123, -127]20022003The discriminant -111 satisfies only the weak Heegner hypothesis, since it2004is divisible by 37::20052006sage: X.discriminants(15,weak=True)2007[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -111, -115, -120, -123]2008"""2009N = self.level()2010n = ZZ(n)2011v = []2012D = ZZ(-4)2013while len(v) < n:2014D -= 12015if satisfies_weak_heegner_hypothesis(N,D):2016# if not weak, then also require gcd(D,N)=12017if not weak and D.gcd(N) != 1:2018continue2019v.append(D)2020return v20212022class HeegnerPoints_level_disc(HeegnerPoints):2023"""2024Set of Heegner points of given level and all conductors associated2025to a quadratic imaginary field.20262027EXAMPLES::20282029sage: H = heegner_points(389,-7); H2030Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]2031sage: type(H)2032<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc'>2033sage: H._repr_()2034'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'2035sage: H.discriminant()2036-72037sage: H.quadratic_field()2038Number Field in sqrt_minus_7 with defining polynomial x^2 + 72039sage: H.kolyvagin_conductors()2040[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]20412042sage: loads(dumps(H)) == H2043True2044"""2045def __init__(self, N, D):2046"""2047INPUT:20482049- `N` -- positive integer20502051- `D` -- negative fundamental discriminant20522053EXAMPLES::20542055sage: sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc(37,-7)2056Set of all Heegner points on X_0(37) associated to QQ[sqrt(-7)]2057"""2058HeegnerPoints.__init__(self, N)2059D = ZZ(D)2060if not satisfies_weak_heegner_hypothesis(N,D):2061raise ValueError("D (=%s) must satisfy the weak Heegner hypothesis for N (=%s)"%(D,N))2062self.__D = D20632064def __eq__(self, other):2065"""2066EXAMPLES::20672068sage: H = heegner_points(389,-7)2069sage: H == heegner_points(389,-7)2070True2071sage: H == 02072False2073sage: H == heegner_points(389,-11)2074False2075"""2076return isinstance(other, HeegnerPoints_level_disc) and \2077self.level() == other.level() and self.__D == other.__D20782079def _repr_(self):2080"""2081Return string representation of the set of Heegner points for a given2082quadratic field.20832084EXAMPLES::20852086sage: heegner_points(389,-7)._repr_()2087'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'2088"""2089return "Set of all Heegner points on X_0(%s) associated to QQ[sqrt(%s)]"%(2090self.level(), self.discriminant())209120922093def discriminant(self):2094r"""2095Return the discriminant of the quadratic imaginary extension `K`.20962097EXAMPLES::20982099sage: heegner_points(389,-7).discriminant()2100-72101"""2102return self.__D21032104@cached_method2105def quadratic_field(self):2106r"""2107Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.21082109EXAMPLES::21102111sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()2112sage: K.quadratic_field()2113Number Field in sqrt_minus_7 with defining polynomial x^2 + 72114"""2115D = self.__D2116var = 'sqrt_minus_%s'%(-D)2117return number_field.QuadraticField(D,var)21182119def kolyvagin_conductors(self, r=None, n=10, E=None, m=None):2120r"""2121Return the first `n` conductors that are squarefree products2122of distinct primes inert in the quadratic imaginary field2123`K = \QQ(\sqrt{D})`. If `r` is specified, return only2124conductors that are a product of `r` distinct primes all inert2125in `K`. If `r = 0`, always return the list ``[1]``,2126no matter what.21272128If the optional elliptic curve `E` and integer `m` are given,2129then only include conductors `c` such that for each prime2130divisor `p` of `c` we have `m \mid \gcd(a_p(E), p+1)`.21312132INPUT:21332134- `r` -- (default: ``None``) nonnegative integer or ``None``21352136- `n` -- positive integer21372138- `E` -- an elliptic curve21392140- `m` -- a positive integer21412142EXAMPLES::21432144sage: H = heegner_points(389,-7)2145sage: H.kolyvagin_conductors(0)2146[1]2147sage: H.kolyvagin_conductors(1)2148[3, 5, 13, 17, 19, 31, 41, 47, 59, 61]2149sage: H.kolyvagin_conductors(1,15)2150[3, 5, 13, 17, 19, 31, 41, 47, 59, 61, 73, 83, 89, 97, 101]2151sage: H.kolyvagin_conductors(1,5)2152[3, 5, 13, 17, 19]2153sage: H.kolyvagin_conductors(1,5,EllipticCurve('389a'),3)2154[5, 17, 41, 59, 83]2155sage: H.kolyvagin_conductors(2,5,EllipticCurve('389a'),3)2156[85, 205, 295, 415, 697]2157"""2158D = self.__D2159if not satisfies_weak_heegner_hypothesis(self.level(),D):2160raise ValueError("D must satisfy the weak Heegner hypothesis")2161n = ZZ(n)2162if n <= 0:2163raise ValueError("n must be a positive integer")2164if r is not None:2165r = ZZ(r)2166if r < 0:2167raise ValueError("n must be a nonnegative integer")2168if r == 0:2169return [ZZ(1)]21702171c = ZZ(1)2172v = []2173N = self.level()21742175if E is not None:2176m = ZZ(m)21772178while len(v) < n:2179if is_kolyvagin_conductor(N, E, D, r, m, c):2180v.append(c)2181c += 121822183return v218421852186def is_kolyvagin_conductor(N, E, D, r, n, c):2187r"""2188Return ``True`` if `c` is a Kolyvagin conductor for level `N`,2189discriminant `D`, mod `n`, etc., i.e., `c` is divisible by exactly2190`r` prime factors, is coprime to `ND`, each prime dividing `c` is2191inert, and if `E` is not ``None`` then `n | \gcd(p+1, a_p(E))`2192for each prime `p` dividing `c`.21932194INPUT:21952196- `N` -- level (positive integer)21972198- `E` -- elliptic curve or ``None``21992200- `D` -- negative fundamental discriminant22012202- `r` -- number of prime factors (nonnegative integer) or ``None``22032204- `n` -- torsion order (i.e., do we get class in `(E(K_c)/n E(K_c))^{Gal(K_c/K)}`?)22052206- `c` -- conductor (positive integer)22072208EXAMPLES::22092210sage: from sage.schemes.elliptic_curves.heegner import is_kolyvagin_conductor2211sage: is_kolyvagin_conductor(389,None,-7,1,None,5)2212True2213sage: is_kolyvagin_conductor(389,None,-7,1,None,7)2214False2215sage: is_kolyvagin_conductor(389,None,-7,1,None,11)2216False2217sage: is_kolyvagin_conductor(389,EllipticCurve('389a'),-7,1,3,5)2218True2219sage: is_kolyvagin_conductor(389,EllipticCurve('389a'),-7,1,11,5)2220False2221"""2222ND = N*D2223if ND.gcd(c) != 1: return False2224if not c.is_squarefree(): return False2225P = c.prime_factors()2226if r is not None and len(P) != r:2227return False2228# check that each prime in P is inert in K2229for p in P:2230if D.kronecker(p) != -1:2231return False2232if E is not None and n is not None:2233for p in P:2234if (p+1).gcd(E.ap(p)) % n != 0:2235return False2236return True223722382239class HeegnerPoints_level_disc_cond(HeegnerPoints_level, HeegnerPoints_level_disc):2240"""2241The set of Heegner points of given level, discriminant, and conductor.22422243EXAMPLES::22442245sage: H = heegner_points(389,-7,5); H2246All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]2247sage: type(H)2248<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc_cond'>2249sage: H.discriminant()2250-72251sage: H.level()225238922532254sage: len(H.points())2255122256sage: H.points()[0]2257Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)2258sage: H.betas()2259(147, 631)22602261sage: H.quadratic_field()2262Number Field in sqrt_minus_7 with defining polynomial x^2 + 72263sage: H.ring_class_field()2264Ring class field extension of QQ[sqrt(-7)] of conductor 522652266sage: H.kolyvagin_conductors()2267[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]2268sage: H.satisfies_kolyvagin_hypothesis()2269True22702271sage: H = heegner_points(389,-7,5)2272sage: loads(dumps(H)) == H2273True2274"""2275def __init__(self, N, D, c=ZZ(1)):2276"""2277Create set of Heegner points.22782279INPUT:22802281- `N` -- positive integer (the level)22822283- `D` -- negative fundamental discriminant22842285- `c` -- conductor (default: 1)22862287EXAMPLES::22882289sage: H = heegner_points(389,-7,5); H2290All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]2291sage: type(H)2292<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc_cond'>2293"""2294HeegnerPoints_level.__init__(self, N)2295HeegnerPoints_level_disc.__init__(self, N, D)2296self.__c = ZZ(c)22972298def __eq__(self, other):2299"""2300EXAMPLES::23012302sage: H = heegner_points(389,-7, 3)2303sage: H == heegner_points(389,-7, 3)2304True2305sage: H == heegner_points(389,-7, 1)2306False2307sage: H == 02308False2309"""2310return isinstance(other, HeegnerPoints_level_disc_cond) and \2311self.level() == other.level() and self.discriminant() == other.discriminant() \2312and self.conductor() == other.conductor()23132314def _repr_(self):2315"""2316Return string representation of this set of Heegner points.23172318EXAMPLES::23192320sage: H = heegner_points(37,-7,5); H._repr_()2321'All Heegner points of conductor 5 on X_0(37) associated to QQ[sqrt(-7)]'2322"""2323return "All Heegner points of conductor %s on X_0(%s) associated to QQ[sqrt(%s)]"%(2324self.conductor(), self.level(), self.discriminant())23252326def conductor(self):2327"""2328Return the level of the conductor.23292330EXAMPLES::23312332sage: heegner_points(389,-7,5).conductor()233352334"""2335return self.__c23362337@cached_method2338def satisfies_kolyvagin_hypothesis(self):2339"""2340Return ``True`` if ``self`` satisfies the Kolyvagin hypothesis, i.e.,2341that each prime dividing the conductor `c` of ``self`` is inert in2342`K` and coprime to `ND`.23432344EXAMPLES:23452346The prime 5 is inert, but the prime 11 is not::23472348sage: heegner_points(389,-7,5).satisfies_kolyvagin_hypothesis()2349True2350sage: heegner_points(389,-7,11).satisfies_kolyvagin_hypothesis()2351False2352"""2353return is_kolyvagin_conductor(N=self.level(), E=None, D=self.discriminant(),2354r=None, n=None, c=self.conductor())23552356@cached_method2357def ring_class_field(self):2358"""2359Return the ring class field associated to this set of Heegner2360points. This is an extension `K_c` over `K`, where `K` is the2361quadratic imaginary field and `c` the conductor associated to2362this Heegner point. This Heegner point is defined over `K_c`2363and the Galois group `Gal(K_c/K)` acts transitively on the2364Galois conjugates of this Heegner point.23652366EXAMPLES::23672368sage: heegner_points(389,-7,5).ring_class_field()2369Ring class field extension of QQ[sqrt(-7)] of conductor 52370"""2371return RingClassField(self.discriminant(), self.conductor())23722373def __getitem__(self, i):2374"""2375Return the `i`-th Heegner point.23762377EXAMPLES::23782379sage: H = heegner_points(389,-7,5)2380sage: len(H)2381122382sage: H[0]2383Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)2384sage: H[-1]2385Heegner point 5/5446*sqrt(-7) - 757/778 of discriminant -7 and conductor 5 on X_0(389)2386"""2387return self.points()[i]23882389def __len__(self):2390"""2391Return the number of Heegner points.23922393EXAMPLES::23942395sage: len(heegner_points(389,-7,5))23961223972398When the conductor is 1 the length is a power of 2 (number of2399square roots of `D` mod `4N` reduced mod `2N`) times the class2400number::24012402sage: len(heegner_points(389,-20,1))240342404sage: QQ[sqrt(-20)].class_number()240522406"""2407return len(self.points())24082409@cached_method2410def betas(self):2411"""2412Return the square roots of `D c^2` modulo `4 N` all reduced2413mod `2 N`, without multiplicity.24142415EXAMPLES::24162417sage: X = heegner_points(45,-11,1); X2418All Heegner points of conductor 1 on X_0(45) associated to QQ[sqrt(-11)]2419sage: [x.quadratic_form() for x in X]2420[45*x^2 + 13*x*y + y^2,242145*x^2 + 23*x*y + 3*y^2,242245*x^2 + 67*x*y + 25*y^2,242345*x^2 + 77*x*y + 33*y^2]2424sage: X.betas()2425(13, 23, 67, 77)2426sage: X.points(13)2427(Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45),)2428sage: [x.quadratic_form() for x in X.points(13)]2429[45*x^2 + 13*x*y + y^2]2430"""2431c = self.__c2432D = self.discriminant()*c*c2433N = self.level()2434R = Integers(4*N)2435m = 2*N2436return tuple(sorted( set([a%m for a in R(D).sqrt(all=True)]) ))243724382439@cached_method2440def points(self, beta=None):2441r"""2442Return the Heegner points in ``self``. If `\beta` is given,2443return only those Heegner points with given `\beta`, i.e.,2444whose quadratic form has `B` congruent to `\beta` modulo `2 N`.24452446Use ``self.beta()`` to get a list of betas.24472448EXAMPLES::24492450sage: H = heegner_points(389,-7,5); H2451All Heegner points of conductor 5 on X_0(389) associated to QQ[sqrt(-7)]2452sage: H.points()2453(Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389), ..., Heegner point 5/5446*sqrt(-7) - 757/778 of discriminant -7 and conductor 5 on X_0(389))2454sage: H.betas()2455(147, 631)2456sage: [x.tau() for x in H.points(147)]2457[5/778*sqrt_minus_7 - 147/778, 5/1556*sqrt_minus_7 - 147/1556, 5/1556*sqrt_minus_7 - 925/1556, 5/3112*sqrt_minus_7 - 1703/3112, 5/3112*sqrt_minus_7 - 2481/3112, 5/5446*sqrt_minus_7 - 21/778]24582459sage: [x.tau() for x in H.points(631)]2460[5/778*sqrt_minus_7 - 631/778, 5/1556*sqrt_minus_7 - 631/1556, 5/1556*sqrt_minus_7 - 1409/1556, 5/3112*sqrt_minus_7 - 631/3112, 5/3112*sqrt_minus_7 - 1409/3112, 5/5446*sqrt_minus_7 - 757/778]24612462The result is cached and is a tuple (since it is immutable)::24632464sage: H.points() is H.points()2465True2466sage: type(H.points())2467<type 'tuple'>2468"""2469if beta is None:2470SDN = self.betas()2471return tuple(sorted(sum([list(self.points(b)) for b in SDN], [])))24722473c = self.conductor()2474N = self.level()2475D = self.discriminant()2476b = ZZ(beta) % (2*N)24772478disc = D*c*c24792480U = []2481R = []2482h = self.ring_class_field().degree_over_K()2483a = 12484while len(U) < h:2485if c.gcd(a) != 1:2486a += 12487continue2488# todo (optimize) -- replace for over all s with for over solution set2489y = ZZ((b*b - disc)/(4*N))2490for s in Integers(a):2491if N*s*s + b*s + y == 0:2492s = s.lift()2493f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - disc)/(4*a*N)) )2494g = BinaryQF(f).reduced_form()2495assert g.discriminant() == disc2496if g not in U:2497U.append(g)2498R.append(HeegnerPointOnX0N(N,D,c,f))2499if len(U) >= h: break2500a += 12501return tuple(sorted(R))25022503def plot(self, *args, **kwds):2504"""2505Returns plot of all the representatives in the upper half2506plane of the Heegner points in this set of Heegner points.25072508The inputs to this function get passed onto the point command.25092510EXAMPLES::25112512sage: heegner_points(389,-7,5).plot(pointsize=50, rgbcolor='red')2513sage: heegner_points(53,-7,15).plot(pointsize=50, rgbcolor='purple')2514"""2515return sum(z.plot(*args, **kwds) for z in self)251625172518class HeegnerPointOnX0N(HeegnerPoint):2519r"""2520A Heegner point as a point on the modular curve `X_0(N)`, which we2521view as the upper half plane modulo the action of `\Gamma_0(N)`.25222523EXAMPLES::25242525sage: x = heegner_point(37,-7,5); x2526Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)2527sage: type(x)2528<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N'>2529sage: x.level()2530372531sage: x.conductor()253252533sage: x.discriminant()2534-72535sage: x.quadratic_field()2536Number Field in sqrt_minus_7 with defining polynomial x^2 + 72537sage: x.quadratic_form()253837*x^2 + 11*x*y + 2*y^22539sage: x.quadratic_order()2540Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 72541sage: x.tau()25425/74*sqrt_minus_7 - 11/742543sage: loads(dumps(x)) == x2544True2545"""2546def __init__(self, N, D, c=ZZ(1), f=None, check=True):2547r"""2548INPUT:25492550- `N` -- positive integer25512552- `D` -- fundamental discriminant, a negative integer25532554- `c` -- conductor, a positive integer coprime to `N`25552556- `f` -- binary quadratic form, 3-tuple `(A,B,C)` of coefficients2557of `AX^2 + BXY + CY^2`, or element of quadratic imaginary2558field `\QQ(\sqrt{D})` in the upper half plan.25592560- ``check`` -- bool, default: ``True``. should not be used2561except internally.256225632564EXAMPLES::25652566sage: x = heegner_point(389, -7, 5); x2567Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)2568sage: type(x)2569<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N'>2570sage: sage.schemes.elliptic_curves.heegner.HeegnerPointOnX0N(389, -7, 5, None, check=False)2571Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)2572"""2573if check:2574N = ZZ(N); D = ZZ(D); c = ZZ(c)2575if c.gcd(N) != 1:2576raise ValueError("conductor c (=%s) must be coprime to N (=%s)" % (c, N))2577if not satisfies_weak_heegner_hypothesis(N, D):2578raise ValueError("N (=%s) and D (=%s) must satisfy the Heegner hypothesis"%(N, D))2579if f is not None:2580if isinstance(f, tuple):2581if len(f) != 3:2582raise ValueError("if f is a tuple, it must have length 3")2583f = tuple(ZZ(a) for a in f)2584elif isinstance(f, BinaryQF):2585# convert from BinaryQF2586f = tuple(f)2587elif rings.is_NumberFieldElement(f):2588# tau = number field element2589g = f.minpoly()2590if g.degree() != 2:2591raise TypeError("number field element f must have degree 2")2592g *= g.denominator() # make integral2593f = (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))2594else:2595raise TypeError("f must be a 3-tuple, quadratic form, or element of the upper half plane")2596A, B, C = f2597if B*B - 4*A*C != D*c*c:2598raise ValueError("f (=%s) must have discriminant %s"%(f, D*c*c))2599HeegnerPoint.__init__(self, N, D, c)2600if f is None:2601# We know that N|A, so A = N is optimal.2602A = N2603B = ZZ(Integers(4*N)(D*c*c).sqrt(extend=False) % (2*N))2604C = ZZ((B*B - D*c*c)/(4*A))2605f = (A,B,C)2606self.__f = f260726082609def __hash__(self):2610"""2611EXAMPLES::26122613sage: y = EllipticCurve('389a').heegner_point(-7,5)2614sage: hash(y)2615-756867903203770682 # 64-bit2616-274399546 # 32-bit2617"""2618return hash((HeegnerPoint.__hash__(self), self.reduced_quadratic_form()))26192620def __eq__(self, right):2621"""2622EXAMPLES::26232624sage: x1 = EllipticCurve('389a').heegner_point(-7).heegner_point_on_X0N()2625sage: x5 = EllipticCurve('389a').heegner_point(-7,5).heegner_point_on_X0N()2626sage: x1 == x12627True2628sage: x5 == x52629True2630sage: x1 == x52631False2632sage: x1 == 102633False2634"""2635return isinstance(right, HeegnerPointOnX0N) and \2636HeegnerPoint.__eq__(self,right) and \2637self.reduced_quadratic_form() == right.reduced_quadratic_form()26382639def __cmp__(self, x):2640"""2641Compare two Heegner points with character.26422643EXAMPLES::26442645sage: x1 = EllipticCurve('389a').heegner_point(-7).heegner_point_on_X0N()2646sage: x5 = EllipticCurve('389a').heegner_point(-7,5).heegner_point_on_X0N()2647sage: x1.__cmp__(x1)264802649sage: x1.__cmp__(x5)2650-12651sage: x5.__cmp__(x1)265212653"""2654c = HeegnerPoint.__cmp__(self, x)2655if c: return c2656return cmp(self.__f, x.__f)26572658def _repr_(self):2659"""2660Return string representation of this Heegner point.26612662EXAMPLES::26632664sage: x = heegner_point(37,-7,5); x._repr_()2665'Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)'2666"""2667c = self.conductor()2668s = " and conductor %s"%c if c != 1 else ""2669N = self.level()2670D = self.discriminant()2671tau = repr(self.tau()).replace('sqrt_minus_%s'%(-D),'sqrt(%s)'%D)2672return "Heegner point %s of discriminant %s%s on X_0(%s)"%(tau, D, s, N)26732674def atkin_lehner_act(self, Q=None):2675r"""2676Given an integer Q dividing the level N such that `\gcd(Q, N/Q) = 1`, returns the2677image of this Heegner point under the Atkin-Lehner operator `W_Q`.26782679INPUT:26802681- `Q` -- positive divisor of `N`; if not given, default to `N`26822683EXAMPLES::26842685sage: x = heegner_point(389,-7,5)2686sage: x.atkin_lehner_act()2687Heegner point 5/199168*sqrt(-7) - 631/199168 of discriminant -7 and conductor 5 on X_0(389)26882689sage: x = heegner_point(45,D=-11,c=1); x2690Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45)2691sage: x.atkin_lehner_act(5)2692Heegner point 1/90*sqrt(-11) + 23/90 of discriminant -11 on X_0(45)2693sage: y = x.atkin_lehner_act(9); y2694Heegner point 1/90*sqrt(-11) - 23/90 of discriminant -11 on X_0(45)2695sage: z = y.atkin_lehner_act(9); z2696Heegner point 1/90*sqrt(-11) - 13/90 of discriminant -11 on X_0(45)2697sage: z == x2698True2699"""2700N = self.level()2701if Q is None:2702Q = N2703if Q == 1:2704return self # trivial special case2705g, u, v = arith.xgcd(Q*Q, -N)2706if g != Q:2707raise ValueError("Q must divide N and be coprime to N/Q")2708tau = self.tau()2709WQ_tau = ((u*Q*tau + v) / (N*tau + Q))2710return HeegnerPointOnX0N(N, self.discriminant(), self.conductor(), f=WQ_tau, check=True)271127122713@cached_method2714def quadratic_form(self):2715"""2716Return the integral primitive positive-definite binary2717quadratic form associated to this Heegner point.27182719EXAMPLES::27202721sage: heegner_point(389,-7,5).quadratic_form()2722389*x^2 + 147*x*y + 14*y^22723"""2724# It is good/important that this return a copy, since2725# BinaryQF's stupidly are mutable and cannot be made immutable.2726# In particular, they have a stupid reduce method that changes2727# them in place.2728return BinaryQF(self.__f)27292730def reduced_quadratic_form(self):2731"""2732Return reduced binary quadratic corresponding to this Heegner point.27332734EXAMPLES::27352736sage: x = heegner_point(389,-7,5)2737sage: x.quadratic_form()2738389*x^2 + 147*x*y + 14*y^22739sage: x.reduced_quadratic_form()27404*x^2 - x*y + 11*y^22741"""2742return self.quadratic_form().reduced_form()27432744@cached_method2745def tau(self):2746"""2747Return an element tau in the upper half plane that corresponds2748to this particular Heegner point (actually, tau is in the2749quadratic imagqinary field K associated to this Heegner point).27502751EXAMPLES::27522753sage: x = heegner_point(37,-7,5); tau = x.tau(); tau27545/74*sqrt_minus_7 - 11/742755sage: 37 * tau.minpoly()275637*x^2 + 11*x + 22757sage: x.quadratic_form()275837*x^2 + 11*x*y + 2*y^22759"""2760K = self.quadratic_field()2761c = self.conductor()2762d = K.gen()*c2763A,B,_ = self.__f2764return (-B + d)/(2*A)27652766def map_to_curve(self, E):2767"""2768Return the image of this Heegner point on the elliptic curve2769`E`, which must also have conductor `N`, where `N` is the2770level of ``self``.27712772EXAMPLES::27732774sage: x = heegner_point(389,-7,5); x2775Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)2776sage: y = x.map_to_curve(EllipticCurve('389a')); y2777Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3892778sage: y.curve().cremona_label()2779'389a1'2780sage: y.heegner_point_on_X0N()2781Heegner point 5/778*sqrt(-7) - 147/778 of discriminant -7 and conductor 5 on X_0(389)27822783You can also directly apply the modular parametrization of the elliptic curve::27842785sage: x = heegner_point(37,-7); x2786Heegner point 1/74*sqrt(-7) - 17/74 of discriminant -7 on X_0(37)2787sage: E = EllipticCurve('37a'); phi = E.modular_parametrization()2788sage: phi(x)2789Heegner point of discriminant -7 on elliptic curve of conductor 372790"""2791return HeegnerPointOnEllipticCurve(E, self)27922793@cached_method2794def galois_orbit_over_K(self):2795r"""2796Return the `Gal(K_c/K)`-orbit of this Heegner point.27972798EXAMPLES::27992800sage: x = heegner_point(389,-7,3); x2801Heegner point 3/778*sqrt(-7) - 223/778 of discriminant -7 and conductor 3 on X_0(389)2802sage: x.galois_orbit_over_K()2803[Heegner point 3/778*sqrt(-7) - 223/778 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/1556*sqrt(-7) - 223/1556 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/1556*sqrt(-7) - 1001/1556 of discriminant -7 and conductor 3 on X_0(389), Heegner point 3/3112*sqrt(-7) - 223/3112 of discriminant -7 and conductor 3 on X_0(389)]2804"""2805c = self.conductor()2806N = self.level()2807D = self.discriminant()2808b = self.__f[1] % (2*N) # B28092810disc = D*c*c28112812U = []2813R = []2814h = self.ring_class_field().degree_over_K()2815a = 12816while len(U) < h:2817if c.gcd(a) != 1:2818a += 12819continue2820# todo (optimize) -- replace for over all s with for over solution set2821y = ZZ((b*b - disc)/(4*N))2822for s in Integers(a):2823if N*s*s + b*s + y == 0:2824s = s.lift()2825f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - disc)/(4*a*N)) )2826g = BinaryQF(f).reduced_form()2827assert g.discriminant() == disc2828if g not in U:2829U.append(g)2830R.append(HeegnerPointOnX0N(N,D,c,f))2831a += 12832return R28332834def plot(self, **kwds):2835r"""2836Draw a point at `(x,y)` where this Heegner point is2837represented by the point `\tau = x + i y` in the upper half2838plane.28392840The ``kwds`` get passed onto the point plotting command.28412842EXAMPLES::28432844sage: heegner_point(389,-7,1).plot(pointsize=50)2845"""2846from sage.plot.all import point2847return point(CDF(self.tau()), **kwds)28482849class HeegnerPointOnEllipticCurve(HeegnerPoint):2850"""2851A Heegner point on a curve associated to an order in a quadratic2852imaginary field.28532854EXAMPLES::28552856sage: E = EllipticCurve('37a'); P = E.heegner_point(-7,5); P2857Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 372858sage: type(P)2859<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>2860"""2861def __init__(self, E, x, check=True):2862r"""2863INPUT:28642865- `E` -- an elliptic curve over the rational numbers28662867- `x` -- Heegner point on `X_0(N)`28682869- ``check`` -- bool (default: ``True``); if ``True``, ensure that `D`,2870`c` are of type Integer and define a Heegner point2871on `E`28722873EXAMPLES::28742875sage: x = heegner_point(389,-7,5)2876sage: E = EllipticCurve('389a')2877sage: sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve(E, x)2878Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3892879"""2880if check:2881if E.conductor() != x.level():2882raise ValueError("conductor of curve must equal level of Heegner point")2883self.__E = E2884self.__x = x2885HeegnerPoint.__init__(self, x.level(), x.discriminant(), x.conductor())28862887@cached_method2888def satisfies_kolyvagin_hypothesis(self, n=None):2889"""2890Return ``True`` if this Heegner point and `n` satisfy the2891Kolyvagin hypothesis, i.e., that each prime dividing the2892conductor `c` of ``self`` is inert in K and coprime to `ND`.2893Moreover, if `n` is not ``None``, also check that for each prime2894`p` dividing `c` we have that `n | \gcd(a_p(E), p+1)`.28952896INPUT:28972898`n` -- positive integer28992900EXAMPLES::29012902sage: EllipticCurve('389a').heegner_point(-7).satisfies_kolyvagin_hypothesis()2903True2904sage: EllipticCurve('389a').heegner_point(-7,5).satisfies_kolyvagin_hypothesis()2905True2906sage: EllipticCurve('389a').heegner_point(-7,11).satisfies_kolyvagin_hypothesis()2907False2908"""2909if n is not None:2910n = ZZ(n)2911if n <= 0: raise ValueError("n must be a positive integer")2912return is_kolyvagin_conductor(N=self.level(), E=self.__E, D=self.discriminant(),2913r=None, n=n, c=self.conductor())29142915def __hash__(self):2916"""2917EXAMPLES::29182919sage: hash(EllipticCurve('389a').heegner_point(-7,5))2920-756867903203770682 # 64-bit2921-274399546 # 32-bit2922"""2923return hash((self.__E, self.__x))29242925def __eq__(self, right):2926"""2927EXAMPLES::29282929sage: y1 = EllipticCurve('389a').heegner_point(-7)2930sage: y5 = EllipticCurve('389a').heegner_point(-7,5)2931sage: y1 == y12932True2933sage: y5 == y52934True2935sage: y1 == y52936False2937sage: y1 == 102938False2939"""2940return isinstance(right, HeegnerPointOnEllipticCurve) and \2941(self.__E, self.__x) == (right.__E, right.__x)29422943def _repr_(self):2944"""2945Return string representation of this Heegner point.29462947EXAMPLES::29482949sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 97)2950sage: P._repr_()2951'Heegner point of discriminant -7 and conductor 97 on elliptic curve of conductor 389'2952"""2953s = " and conductor %s"%self.conductor() if self.conductor() != 1 else ""2954N = self.__E.conductor()2955return "Heegner point of discriminant %s%s on elliptic curve of conductor %s"%(self.discriminant(), s, N)29562957def heegner_point_on_X0N(self):2958r"""2959Return Heegner point on `X_0(N)` that maps to this Heegner point on `E`.29602961EXAMPLES::29622963sage: E = EllipticCurve('37a'); P = E.heegner_point(-7,5); P2964Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 372965sage: P.heegner_point_on_X0N()2966Heegner point 5/74*sqrt(-7) - 11/74 of discriminant -7 and conductor 5 on X_0(37)2967"""2968return self.__x29692970def map_to_complex_numbers(self, prec=53):2971"""2972Return the point in the subfield `M` of the complex numbers2973(well defined only modulo the period lattice) corresponding to2974this Heegner point.29752976EXAMPLES:29772978We compute a nonzero Heegner point over a ring class field on2979a curve of rank 2::29802981sage: E = EllipticCurve('389a'); y = E.heegner_point(-7,5)2982sage: y.map_to_complex_numbers()29831.49979679635196 + 0.369156204821526*I2984sage: y.map_to_complex_numbers(100)29851.4997967963519640592142411892 + 0.36915620482152626830089145962*I2986sage: y.map_to_complex_numbers(10)29871.5 + 0.37*I29882989Here we see that the Heegner point is 0 since it lies in the2990lattice::29912992sage: E = EllipticCurve('389a'); y = E.heegner_point(-7)2993sage: y.map_to_complex_numbers(10)29940.0034 - 3.9*I2995sage: y.map_to_complex_numbers()29964.71844785465692e-15 - 3.94347540310330*I2997sage: E.period_lattice().basis()2998(2.49021256085505, 1.97173770155165*I)2999sage: 2*E.period_lattice().basis()[1]30003.94347540310330*I30013002You can also directly coerce to the complex field::30033004sage: E = EllipticCurve('389a'); y = E.heegner_point(-7)3005sage: z = ComplexField(100)(y); z # real part approx. 03006-... - 3.9434754031032964088448153963*I3007sage: E.period_lattice().elliptic_exponential(z)3008(0.00000000000000000000000000000 : 1.0000000000000000000000000000 : 0.00000000000000000000000000000)3009"""3010phi = self.__E.modular_parametrization()3011tau = self.heegner_point_on_X0N().tau()3012return phi.map_to_complex_numbers(tau, prec)30133014def _complex_mpfr_field_(self, C):3015"""3016Used internally for coercing Heegner point to a complex field.30173018EXAMPLES::30193020sage: E = EllipticCurve('37a'); y = E.heegner_point(-7)3021sage: CC(y) # indirect doctest30220.929592715285395 - 1.22569469099340*I3023sage: ComplexField(100)(y)30240.92959271528539567440519934446 - 1.2256946909933950304271124159*I3025"""3026phi = self.__E.modular_parametrization()3027tau = C(self.heegner_point_on_X0N().tau())3028return phi.map_to_complex_numbers(tau)30293030@cached_method3031def kolyvagin_point(self):3032"""3033Return the Kolyvagin point corresponding to this Heegner3034point. This is the point obtained by applying the Kolyvagin3035operator `J_c I_c` in the group ring of the Galois group to3036this Heegner point. It is a point that defines an element3037of `H^1(K, E[n])`, under certain hypotheses on `n`.30383039EXAMPLES::30403041sage: E = EllipticCurve('37a1'); y = E.heegner_point(-7); y3042Heegner point of discriminant -7 on elliptic curve of conductor 373043sage: P = y.kolyvagin_point(); P3044Kolyvagin point of discriminant -7 on elliptic curve of conductor 373045sage: PP = P.numerical_approx() # approximately (0 : 0 : 1)3046sage: all([c.abs() < 1e-15 for c in PP.xy()])3047True3048"""3049return KolyvaginPoint(self)30503051@cached_method3052def _trace_index(self, *args, **kwds):3053"""3054Return index of the trace of this Heegner point down to `K` in3055the group of `K`-rational points.30563057IMPORTANT: See the help for ``E=self.curve(); E.index?`` for3058the inputs to this function and more details about what is3059computed. In particular, the returned index can be off at 2.30603061OUTPUT:30623063- ``Integer`` -- returns an integer30643065EXAMPLES::30663067sage: E = EllipticCurve('77a1')3068sage: P = E.heegner_point(-19); y = P._trace_numerical_conductor_1(); [c.real() for c in y]3069[-1.2...e-16, -1.00000000000000, 1.00000000000000]3070sage: -2*E.gens()[0]3071(0 : -1 : 1)3072sage: P._trace_index()3073230743075sage: P = E.heegner_point(-68); P3076Heegner point of discriminant -68 on elliptic curve of conductor 773077sage: N(P)3078(0.219223593595584 - 1.87443160153148*I : -1.34232921921325 - 1.52356748877889*I : 1.00000000000000)3079sage: P._trace_index()308003081"""3082if self.conductor() != 1:3083raise ValueError("conductor of Heegner point must be 1")3084i = self.__E.heegner_index(self.discriminant(), *args, **kwds)3085lower = i.lower().round()3086upper = i.upper().round()3087if lower == upper:3088return lower3089# here we would say raise precision somehow.3090raise NotImplementedError("unable to compute index")30913092def curve(self):3093"""3094Return the elliptic curve on which this is a Heegner point.30953096EXAMPLES::30973098sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5)3099sage: P.curve()3100Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field3101sage: P.curve() is E3102True3103"""3104return self.__E31053106@cached_method3107def quadratic_form(self):3108"""3109Return the integral primitive positive definite binary3110quadratic form associated to this Heegner point.311131123113EXAMPLES::31143115sage: EllipticCurve('389a').heegner_point(-7, 5).quadratic_form()3116389*x^2 + 147*x*y + 14*y^231173118sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P3119Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3893120sage: P.quadratic_form()3121778*x^2 + 925*x*y + 275*y^23122"""3123return self.__x.quadratic_form()31243125@cached_method3126def numerical_approx(self, prec=53):3127"""3128Return a numerical approximation to this Heegner point3129computed using a working precision of prec bits.31303131.. WARNING::31323133The answer is *not* provably correct to prec bits! A3134priori, due to rounding and other errors, it is possible that3135not a single digit is correct.31363137INPUT:31383139- prec -- (default: ``None``) the working precision31403141EXAMPLES::31423143sage: E = EllipticCurve('37a'); P = E.heegner_point(-7); P3144Heegner point of discriminant -7 on elliptic curve of conductor 373145sage: all([c.abs()< 1e-15 for c in P.numerical_approx().xy()])3146True3147sage: P.numerical_approx(10) # expect random digits3148(0.0030 - 0.0028*I : -0.0030 + 0.0028*I : 1.0)3149sage: P.numerical_approx(100)[0] # expect random digits31508.4...e-31 + 6.0...e-31*I3151sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P3152Heegner point of discriminant -40 on elliptic curve of conductor 373153sage: P.numerical_approx()3154(-6.68...e-16 + 1.41421356237310*I : 1.00000000000000 - 1.41421356237309*I : 1.00000000000000)31553156A rank 2 curve, where all Heegner points of conductor 1 are 0::31573158sage: E = EllipticCurve('389a'); E.rank()315923160sage: P = E.heegner_point(-7); P3161Heegner point of discriminant -7 on elliptic curve of conductor 3893162sage: P.numerical_approx()3163(0.000000000000000 : 1.00000000000000 : 0.000000000000000)31643165However, Heegner points of bigger conductor are often nonzero::31663167sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P3168Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3893169sage: numerical_approx(P)3170(0.675507556926806 + 0.344749649302635*I : -0.377142931401887 + 0.843366227137146*I : 1.00000000000000)3171sage: P.numerical_approx()3172(0.6755075569268... + 0.3447496493026...*I : -0.3771429314018... + 0.8433662271371...*I : 1.00000000000000)3173sage: E.heegner_point(-7, 11).numerical_approx()3174(0.1795583794118... + 0.02035501750912...*I : -0.5573941377055... + 0.2738940831635...*I : 1.00000000000000)3175sage: E.heegner_point(-7, 13).numerical_approx()3176(1.034302915374... - 3.302744319777...*I : 1.323937875767... + 6.908264226850...*I : 1.00000000000000)31773178We find (probably) the definining polynomial of the3179`x`-coordinate of `P`, which defines a class field. The shape of3180the discriminant below is strong confirmation -- but not proof3181-- that this polynomial is correct::31823183sage: f = P.numerical_approx(70)[0].algdep(6); f31841225*x^6 + 1750*x^5 - 21675*x^4 - 380*x^3 + 110180*x^2 - 129720*x + 487713185sage: f.discriminant().factor()31862^6 * 3^2 * 5^11 * 7^4 * 13^2 * 19^6 * 199^2 * 719^2 * 26161^23187"""3188tau = ComplexField(prec)(self.tau())3189E = self.curve()3190return E.modular_parametrization()(tau)31913192#This line is added to resolve ticket 9032, because both top-level function3193#and method call _numerical_approx instead of numerical_approx3194_numerical_approx=numerical_approx31953196def tau(self):3197r"""3198Return `\tau` in the upper half plane that maps via the3199modular parametrization to this Heegner point.32003201EXAMPLES::32023203sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5)3204sage: P.tau()32055/778*sqrt_minus_7 - 147/7783206"""3207return self.heegner_point_on_X0N().tau()32083209@cached_method3210def x_poly_exact(self, prec=53, algorithm='lll'):3211"""3212Return irreducible polynomial over the rational numbers3213satisfied by the `x` coordinate of this Heegner point. A3214ValueError is raised if the precision is clearly insignificant3215to define a point on the curve.32163217.. WARNING::32183219It is in theory possible for this function to not raise a3220ValueError, find a polynomial, but via some very unlikely3221coincidence that point is not actually this Heegner point.32223223INPUT:32243225- ``prec`` -- integer (default: 53)32263227- ``algorithm`` -- 'conjugates' or 'lll' (default); if3228'conjugates', compute numerically all the3229conjugates ``y[i]`` of the Heegner point and construct3230the characteristic polynomial as the product3231`f(X)=(X-y[i])`. If 'lll', compute only one of the3232conjugates ``y[0]``, then uses the LLL algorithm to3233guess `f(X)`.323432353236EXAMPLES:32373238We compute some `x`-coordinate polynomials of some conductor 13239Heegner points::32403241sage: E = EllipticCurve('37a')3242sage: v = E.heegner_discriminants_list(10)3243sage: [E.heegner_point(D).x_poly_exact() for D in v]3244[x, x, x^2 + 2, x^5 - x^4 + x^3 + x^2 - 2*x + 1, x - 6, x^7 - 2*x^6 + 9*x^5 - 10*x^4 - x^3 + 8*x^2 - 5*x + 1, x^3 + 5*x^2 + 10*x + 4, x^4 - 10*x^3 + 10*x^2 + 12*x - 12, x^8 - 5*x^7 + 7*x^6 + 13*x^5 - 10*x^4 - 4*x^3 + x^2 - 5*x + 7, x^6 - 2*x^5 + 11*x^4 - 24*x^3 + 30*x^2 - 16*x + 4]324532463247We compute `x`-coordinate polynomials for some Heegner points3248of conductor bigger than 1 on a rank 2 curve::32493250sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P3251Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3893252sage: P.x_poly_exact()3253Traceback (most recent call last):3254...3255ValueError: insufficient precision to determine Heegner point (fails discriminant test)3256sage: P.x_poly_exact(75)3257x^6 + 10/7*x^5 - 867/49*x^4 - 76/245*x^3 + 3148/35*x^2 - 25944/245*x + 48771/12253258sage: E.heegner_point(-7,11).x_poly_exact(300)3259x^10 + 282527/52441*x^9 + 27049007420/2750058481*x^8 - 22058564794/2750058481*x^7 - 140054237301/2750058481*x^6 + 696429998952/30250643291*x^5 + 2791387923058/30250643291*x^4 - 3148473886134/30250643291*x^3 + 1359454055022/30250643291*x^2 - 250620385365/30250643291*x + 181599685425/33275707620132603261Here we compute a Heegner point of conductor 5 on a rank 3 curve::32623263sage: E = EllipticCurve('5077a'); P = E.heegner_point(-7,5); P3264Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 50773265sage: P.x_poly_exact(300)3266x^6 + 1108754853727159228/72351048803252547*x^5 + 88875505551184048168/1953478317687818769*x^4 - 2216200271166098662132/3255797196146364615*x^3 + 14941627504168839449851/9767391588439093845*x^2 - 3456417460183342963918/3255797196146364615*x + 1306572835857500500459/54263286602439410253267"""3268D, c = self.discriminant(), self.conductor()3269n = self.ring_class_field().degree_over_K()32703271if algorithm == 'lll':3272P = self.numerical_approx(prec)3273g = None3274for e in [1,2]: # is there a condition under which we should not bother trying e=1?3275f = P[0].algdep(e*n)32763277# If f is correct, then disc(f) = m^2 * (a product of primes dividing D*c).3278# To check this, we divide out the primes dividing D*c, then3279# check that the resulting cofactor is a perfect square.3280F = f.factor()3281if len(F) == 1:3282f = F[0][0]3283if self._check_poly_discriminant(f):3284g = f3285break32863287if g is None:3288raise ValueError("insufficient precision to determine Heegner point (fails discriminant test)")3289f = g3290f = f/f.leading_coefficient()32913292elif algorithm == 'conjugates':32933294raise NotImplementedError32953296return f32973298def _check_poly_discriminant(self, f):3299"""3300Return ``True`` if the prime to `Dc` part of the discriminant of3301each factor of the polynomial `f` is plus or minus a square.3302This is used for verifying that a polynomial is likely to3303define a subfield of a specific ring class field.33043305INPUT:33063307- `f` -- a polynomial33083309EXAMPLES::33103311sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P3312Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3893313sage: R.<x> = QQ[]3314sage: P._check_poly_discriminant(x^2 - 5)3315True3316sage: P._check_poly_discriminant(x^2 - 19)3317False3318sage: P._check_poly_discriminant((x^2 - 19)*(x^2-5))3319False3320"""3321if f.is_irreducible():3322disc = f.discriminant()3323(D, c) = (self.discriminant(), self.conductor())3324for p in D.prime_divisors() + c.prime_divisors():3325disc = disc // (p**disc.valuation(p))3326if disc < 0: disc = -disc3327return disc.is_square()3328else:3329for g,_ in f.factor():3330if not self._check_poly_discriminant(g):3331return False3332return True333333343335def point_exact(self, prec=53, algorithm='lll', var='a', optimize=False):3336"""3337Return exact point on the elliptic curve over a number field3338defined by computing this Heegner point to the given number of3339bits of precision. A ValueError is raised if the precision3340is clearly insignificant to define a point on the curve.33413342.. WARNING::33433344It is in theory possible for this function to not raise a3345ValueError, find a point on the curve, but via some very3346unlikely coincidence that point is not actually this Heegner3347point.33483349.. WARNING::33503351Currently we make an arbitrary choice of `y`-coordinate for3352the lift of the `x`-coordinate.33533354INPUT:33553356- ``prec`` -- integer (default: 53)33573358- ``algorithm`` -- see the description of the algorithm3359parameter for the ``x_poly_exact`` method.33603361- ``var`` -- string (default: 'a')33623363- ``optimize`` -- book (default; False) if ``True``, try to3364optimize defining polynomial for the number field that3365the point is defined over. Off by default, since this3366can be very expensive.33673368EXAMPLES::33693370sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P3371Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3893372sage: z = P.point_exact(100, optimize=True)3373sage: z[1].charpoly()3374x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/18382656253375sage: f = P.numerical_approx(500)[1].algdep(12); f / f.leading_coefficient()3376x^12 + 6*x^11 + 90089/1715*x^10 + 71224/343*x^9 + 52563964/588245*x^8 - 483814934/588245*x^7 - 156744579/16807*x^6 - 2041518032/84035*x^5 + 1259355443184/14706125*x^4 + 3094420220918/14706125*x^3 + 123060442043827/367653125*x^2 + 82963044474852/367653125*x + 211679465261391/183826562533773378sage: E = EllipticCurve('5077a')3379sage: P = E.heegner_point(-7)3380sage: P.point_exact(prec=100)3381(0 : 1 : 0)3382"""3383E = self.__E3384if self.numerical_approx(prec)[-1] == 0:3385return E(0)3386f = self.x_poly_exact(prec, algorithm=algorithm)3387if f.degree() == 1:3388v = E.lift_x(-f[0], all=True)3389if len(v) > 0:3390return v[0]33913392g, d = make_monic(f)3393K = rings.NumberField(g, var)3394x = K.gen() / d3395if optimize:3396KO, from_KO, to_KO = K.optimized_representation()3397K = KO3398x = to_KO(x)3399if K.degree() < 2 * self.ring_class_field().degree_over_K():3400M = rings.QuadraticField(self.discriminant(),'b')3401KD = K.composite_fields(M, names='a')[0]3402phi = K.embeddings(KD)[0]3403x = phi(x)3404K = KD.change_names(names=var)3405x = K.structure()[1](x)3406a1,a2,a3,a4,a6 = E.a_invariants()3407R = K['Y']; Y = R.gen()3408g = Y**2 + a1*x*Y + a3*Y - (x**3 + a2*x**2 + a4*x + a6)3409F = g.factor() # this takes a long time3410if len(F) == 1 and F[0][0] == 2:3411# reducible -- 1 factor squared3412y = F[0][0]3413L = K3414elif len(F) == 2:3415# reducible -- 2 factors3416y0 = -F[0][0][0]3417# y1 = -F[1][0][0]3418# Figure out which of y0 or y1 is right by3419# P = self.numerical_approx(prec)3420# TODO: finish this -- have to do some thing numerical3421y = y03422L = K3423else:3424# TODO -- is there an issue with choice of root?3425# irreducible3426gg, dd = make_monic(g)3427M = K.extension(gg, names='b')3428y = M.gen()/dd3429x = M(x)3430L = M.absolute_field(names = var)3431phi = L.structure()[1]3432x = phi(x)3433y = phi(y)34343435EL = E.change_ring(L)3436P = EL.point((x,y,1), check=False)3437return P34383439@cached_method3440def conjugates_over_K(self):3441r"""3442Return the `Gal(K_c/K)` conjugates of this Heegner point.34433444EXAMPLES::34453446sage: E = EllipticCurve('77a')3447sage: y = E.heegner_point(-52,5); y3448Heegner point of discriminant -52 and conductor 5 on elliptic curve of conductor 773449sage: print [z.quadratic_form() for z in y.conjugates_over_K()]3450[77*x^2 + 52*x*y + 13*y^2, 154*x^2 + 206*x*y + 71*y^2, 539*x^2 + 822*x*y + 314*y^2, 847*x^2 + 1284*x*y + 487*y^2, 1001*x^2 + 52*x*y + y^2, 1078*x^2 + 822*x*y + 157*y^2, 1309*x^2 + 360*x*y + 25*y^2, 1309*x^2 + 2054*x*y + 806*y^2, 1463*x^2 + 976*x*y + 163*y^2, 2233*x^2 + 2824*x*y + 893*y^2, 2387*x^2 + 2054*x*y + 442*y^2, 3619*x^2 + 3286*x*y + 746*y^2]3451sage: y.quadratic_form()345277*x^2 + 52*x*y + 13*y^23453"""3454H = heegner_points(self.level(), self.discriminant(), self.conductor())3455E = self.curve()3456beta = self.quadratic_form()[1]3457return tuple([z.map_to_curve(E) for z in H.points(beta)])34583459def _numerical_approx_conjugates_over_QQ(self, prec=53):3460"""3461Return a list v of the numerical approximations to precision3462prec of the conjugates of this Heegner point, and their3463complex conjugates.34643465INPUT:34663467- ``prec`` -- positive integer (default: 53)34683469EXAMPLES::34703471sage: E = EllipticCurve('37a')3472sage: y = E.heegner_point(-7,3); y3473Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 373474sage: y._numerical_approx_conjugates_over_QQ()3475[(-1.89564392373896 - 0.444771808762067*I : -1.50000000000000 + 2.13102976222246*I : 1.00000000000000), ...]3476sage: y._numerical_approx_conjugates_over_QQ(prec=10)3477[(-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0), ...3478(-1.9 - 0.44*I : -1.5 + 2.1*I : 1.0)]3479"""3480v = []3481for z in self.conjugates_over_K():3482m = z.numerical_approx(prec)3483v.append(m)3484v.append(m.curve().point([w.conjugate() for w in m], check=False))3485v.sort()3486return v34873488def _numerical_approx_xy_poly(self, prec=53):3489r"""3490Return polynomials with real floating point coefficients got3491by taking the real part of the product of `X - \alpha` over3492the numerical approximations `\alpha` to the conjugates of3493this Heegner point. The first polynomial runs through the3494`x`-coordinates and the second through the `y`-coordinates.34953496INPUT:34973498- ``prec`` -- positive integer (default: 53)34993500OUTPUT:35013502- 2-tuple of polynomials with floating point coefficients35033504EXAMPLES::3505sage: E = EllipticCurve('37a')3506sage: y = E.heegner_point(-7,3); y3507Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 373508sage: y._numerical_approx_xy_poly()3509(X^8 + 6.00000000000000*X^7 + 8.99999999999998*X^6 - 12.0000000000000*X^5 - 42.0000000000000*X^4 - 17.999999999999...*X^3 + 36.0000000000001*X^2 + 35.9999999999999*X + 8.999999999999..., X^8 + 12.0000000000000*X^7 + 72.0000000000000*X^6 + 270.000000000000*X^5 + 678.000000000001*X^4 + 1152.00000000000*X^3 + 1269.00000000000*X^2 + 810.00000000000...*X + 225.000000000001)3510"""3511v = self._numerical_approx_conjugates_over_QQ(prec)3512R = ComplexField(prec)['X']3513S = RealField(prec)['X']3514X = R.gen()3515fx = prod(X-a[0] for a in v)3516fx = S([b.real() for b in fx])3517fy = prod(X-c[1] for c in v)3518fy = S([d.real() for d in fy])3519return fx, fy35203521def _xy_poly_nearby(self, prec=53, max_error=10**(-10)):3522"""3523Return polynomials with rational coefficients that for sufficiently3524tight bounds are the characteristic polynomial of the x and y3525coordinate of this Heegner point.35263527INPUT:35283529- ``prec`` -- positive integer (default: 53)35303531- ``max_error`` -- very small floating point number35323533OUTPUT:35343535- 2-tuple of polynomials with rational coefficients35363537EXAMPLES::35383539sage: E = EllipticCurve('37a')3540sage: y = E.heegner_point(-7,3); y3541Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 373542sage: y._xy_poly_nearby()3543[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,3544X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]354535463547"""3548v = self._numerical_approx_xy_poly(prec)3549return [nearby_rational_poly(g, max_error=max_error) for g in v]35503551def _xy_poly_simplest(self, prec=53, prec2=None):3552"""3553Return polynomials with rational coefficients that for3554sufficiently tight bounds are the characteristic polynomial of3555the x and y coordinate of this Heegner point.35563557INPUT:35583559- ``prec`` -- positive integer (default: 53)35603561- ``prec2`` -- passed into simplest_rational_poly function35623563EXAMPLES::35643565sage: E = EllipticCurve('37a'); y = E.heegner_point(-7,3)3566sage: y._xy_poly_simplest()3567[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,3568X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]3569"""3570v = self._numerical_approx_xy_poly(prec)3571if prec2 is None: prec2 = max(2, prec - 20)3572return [simplest_rational_poly(g,prec2) for g in v]35733574@cached_method3575def _square_roots_mod_2N_of_D_mod_4N(self):3576"""3577Return the square roots of `D` modulo `4N` all reduced mod `2N`,3578without multiplicity.35793580EXAMPLES::35813582sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P3583Heegner point of discriminant -40 on elliptic curve of conductor 373584sage: P._square_roots_mod_2N_of_D_mod_4N()3585[16, 58]3586sage: parent(P._square_roots_mod_2N_of_D_mod_4N()[0])3587Ring of integers modulo 743588"""3589N = self.__E.conductor()3590R = Integers(4*N)3591m = 2*N3592return sorted( set([a%m for a in R(self.discriminant()).sqrt(all=True)]) )35933594def _trace_numerical_conductor_1(self, prec=53):3595"""3596Return numerical approximation using ``prec`` terms of working3597precision to the trace down to the quadratic imaginary field3598`K` of this Heegner point.35993600INPUT:36013602- `prec` -- bits precision (default: 53)36033604EXAMPLES::36053606sage: E = EllipticCurve('57a1')3607sage: P = E.heegner_point(-8); P3608Heegner point of discriminant -8 on elliptic curve of conductor 573609sage: P._trace_numerical_conductor_1() # approx. (1 : 0 : 1)3610(1.00000000000000 + ...e-16*I : ...e-16 - ...e-16*I : 1.00000000000000)3611sage: P = E(2,1) # a generator3612sage: E([1,0]).height()36130.1502983709472953614sage: P.height()36150.03757459273682383616sage: E.heegner_index(-8)36172.0000?3618sage: E.torsion_order()361913620sage: 2*P3621(1 : 0 : 1)3622"""3623if self.conductor() != 1:3624raise ValueError("conductor must be 1")3625R, U = self._good_tau_representatives()3626E = self.__E3627phi = E.modular_parametrization()3628C = rings.ComplexField(prec)3629F = E.change_ring(C)3630s = 03631for u, weight in U:3632P = phi(C(self._qf_to_tau(u)))3633z = F.point(list(P),check=False)3634if abs(weight) == 2:3635t = F.point(z,check=False) + F.point(tuple([x.conjugate() for x in z]), check=False)3636if weight < 0:3637s -= t3638else:3639s += t3640else:3641if weight < 0:3642s -= z3643else:3644s += z3645return s36463647@cached_method3648def _good_tau_representatives(self):3649"""3650Return good upper half plane representatives for Heegner points.36513652ALGORITHM: This is Algorithm 3.5 in Watkins's paper.36533654EXAMPLES::36553656sage: P = EllipticCurve('389a1').heegner_point(-7)3657sage: P._good_tau_representatives()3658([(1, 1, 2)], [((389, 185, 22), 1)])3659"""3660if self.conductor() != 1: raise NotImplementedError3661E = self.__E3662SDN = self._square_roots_mod_2N_of_D_mod_4N()3663beta = SDN[0]3664U = []3665R = []3666N = self.__E.conductor()3667D = self.discriminant()3668h = self.ring_class_field().degree_over_K()3669divs = D.gcd(N).divisors()3670a = 13671while True:3672for b in SDN:3673b = b.lift()3674# todo (optimize) -- replace for over all s with for over solution3675# set that can be found quickly.3676y = ZZ((b*b - D)/(4*N))3677for s in Integers(a):3678if N*s*s + b*s + y == 0:3679s = s.lift()3680f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - D)/(4*a*N)) )3681for d in divs:3682Q = d * prod(p**k for p,k in N.factor() if (b-beta)%(p**k)!=0)3683g = self._qf_atkin_lehner_act(Q, f)3684gbar = (ZZ(g[0]/N), -g[1], g[2]*N)3685g = self._qf_reduce(g)3686gbar = self._qf_reduce(gbar)3687if g in R or gbar in R:3688continue3689R.append(g)3690if g != gbar:3691R.append(gbar)3692epsilon_Q = prod([E.root_number(q) for q in Q.prime_divisors()])3693if g == gbar:3694# weight is epsilon_Q3695weight = epsilon_Q3696else:3697# weight is 2*epsilon_Q3698weight = 2*epsilon_Q3699U.append((f,weight))3700if len(R) == h:3701return R, U3702assert len(R) < h, "bug -- too many quadratic forms"3703a += 137043705def _qf_to_tau(self, f):3706r"""3707Function used internally that given a quadratic form3708`f=(A,B,C)`, return `\tau` in the upper half plane with3709`A\tau^2 + B \tau + C = 0`. Here `A>0` and `\gcd(A,B,C)=1`.3710Also, `\tau` has discriminant `D=B^2-4AC`. In fact, `\tau =3711(-B + \sqrt{D})/(2A)`.37123713INPUT:37143715- `f` -- binary quadratic form37163717EXAMPLES::37183719sage: P = EllipticCurve('57a1').heegner_point(-8)3720sage: R, U = P._good_tau_representatives()3721sage: f = U[0][0]; f3722(57, 26, 3)3723sage: P._qf_to_tau(f)37241/114*sqrt_minus_8 - 13/573725"""3726c = self.conductor()3727A,B,_ = f3728alpha = c * self.quadratic_field().gen() # this is sqrt(D) = sqrt(c^2*disc(K))3729return (-B + alpha)/(2*A)37303731def _qf_from_tau(self, tau):3732r"""3733Return quadratic form associated to a given `\tau` in the upper3734half plane.37353736INPUT:37373738- `\tau` -- quadratic element of the upper half plane37393740EXAMPLES::37413742sage: P = EllipticCurve('57a1').heegner_point(-8)3743sage: R, U = P._good_tau_representatives()3744sage: f = U[0][0]; f3745(57, 26, 3)3746sage: tau = P._qf_to_tau(f); tau37471/114*sqrt_minus_8 - 13/573748sage: P._qf_from_tau(tau)3749(57, 26, 3)3750"""3751g = tau.minpoly()3752g *= g.denominator()3753return (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))375437553756def _qf_atkin_lehner_act(self, Q, f):3757r"""3758Given a positive integer `Q` with `Q | N` and `\gcd(Q, N/Q) =37591`, we compute the quadratic form corresponding to the image3760of the `tau` corresponding to `f` under the Atkin-Lehner3761operator `W_Q`.37623763We do this by letting `u,v` be integers such that3764`u Q^2 - v N = Q`, and using that `W_Q` sends `\tau`3765to `( (u Q \tau + v) / (N \tau + Q) ) / Q`.37663767INPUT:37683769- `Q` -- integer that divides the level `N`37703771- `f` -- quadratic form37723773OUTPUT:37743775- quadratic form37763777EXAMPLES::37783779sage: P = EllipticCurve('57a1').heegner_point(-8)3780sage: R, U = P._good_tau_representatives()3781sage: f = U[0][0]; f3782(57, 26, 3)3783sage: P._qf_atkin_lehner_act(3, f)3784(1938, 1204, 187)3785sage: g = P._qf_atkin_lehner_act(19, f); g3786(114, -64, 9)3787sage: h = P._qf_atkin_lehner_act(19, g); h3788(7353, -4762, 771)3789sage: BinaryQF(f).reduced_form() == BinaryQF(h).reduced_form()3790True3791"""3792N = self.__E.conductor()3793g, u, v = arith.xgcd(Q*Q, -N)3794assert g == Q3795tau = self._qf_to_tau(f)3796tau2 = ((u*Q*tau + v) / (N*tau + Q))3797return self._qf_from_tau(tau2)379837993800def _qf_reduce(self, f):3801"""3802Given a binary quadratic form `f` represented as a 3-tuple3803(A,B,C), return the reduced binary quadratic form equivalent3804to `f`, represented in the same way.38053806EXAMPLES::38073808sage: P = EllipticCurve('57a1').heegner_point(-8)3809sage: R, U = P._good_tau_representatives()3810sage: f = U[0][0]; f3811(57, 26, 3)3812sage: P._qf_reduce(f)3813(1, 0, 2)3814"""3815return tuple(BinaryQF(f).reduced_form())38163817def kolyvagin_cohomology_class(self, n=None):3818"""3819Return the Kolyvagin class associated to this Heegner point.38203821INPUT:38223823- `n` -- positive integer that divides the gcd of `a_p`3824and `p+1` for all `p` dividing the conductor. If `n` is3825``None``, choose the largest valid `n`.38263827EXAMPLES::38283829sage: y = EllipticCurve('389a').heegner_point(-7,5)3830sage: y.kolyvagin_cohomology_class(3)3831Kolyvagin cohomology class c(5) in H^1(K,E[3])3832"""3833return KolyvaginCohomologyClassEn(self.kolyvagin_point(), n)38343835#########################################################################################3836# Kolyvagin Points P_c3837#########################################################################################3838class KolyvaginPoint(HeegnerPoint):3839"""3840A Kolyvagin point.38413842EXAMPLES:38433844We create a few Kolyvagin points::38453846sage: EllipticCurve('11a1').kolyvagin_point(-7)3847Kolyvagin point of discriminant -7 on elliptic curve of conductor 113848sage: EllipticCurve('37a1').kolyvagin_point(-7)3849Kolyvagin point of discriminant -7 on elliptic curve of conductor 373850sage: EllipticCurve('37a1').kolyvagin_point(-67)3851Kolyvagin point of discriminant -67 on elliptic curve of conductor 373852sage: EllipticCurve('389a1').kolyvagin_point(-7, 5)3853Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 38938543855One can also associated a Kolyvagin point to a Heegner point::38563857sage: y = EllipticCurve('37a1').heegner_point(-7); y3858Heegner point of discriminant -7 on elliptic curve of conductor 373859sage: y.kolyvagin_point()3860Kolyvagin point of discriminant -7 on elliptic curve of conductor 3738613862TESTS::38633864sage: y = EllipticCurve('37a1').heegner_point(-7)3865sage: type(y)3866<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>3867sage: loads(dumps(y)) == y3868True3869"""3870def __init__(self, heegner_point):3871"""3872Create a Kolyvagin point.38733874INPUT:38753876- ``heegner_point`` -- a Heegner point on some elliptic curve38773878EXAMPLES:38793880We directly construct a Kolyvagin point from the KolyvaginPoint class::38813882sage: y = EllipticCurve('37a1').heegner_point(-7)3883sage: sage.schemes.elliptic_curves.heegner.KolyvaginPoint(y)3884Kolyvagin point of discriminant -7 on elliptic curve of conductor 373885"""3886if not heegner_point.satisfies_kolyvagin_hypothesis():3887raise ValueError("Heegner point does not satisfy Kolyvagin hypothesis")3888self.__heegner_point = heegner_point3889HeegnerPoint.__init__(self, heegner_point.level(), heegner_point.discriminant(),3890heegner_point.conductor())38913892def satisfies_kolyvagin_hypothesis(self, n=None):3893r"""3894Return ``True`` if this Kolyvagin point satisfies the Heegner3895hypothesis for `n`, so that it defines a Galois equivariant3896element of `E(K_c)/n E(K_c)`.38973898EXAMPLES::38993900sage: y = EllipticCurve('389a').heegner_point(-7,5); P = y.kolyvagin_point()3901sage: P.kolyvagin_cohomology_class(3)3902Kolyvagin cohomology class c(5) in H^1(K,E[3])3903sage: P.satisfies_kolyvagin_hypothesis(3)3904True3905sage: P.satisfies_kolyvagin_hypothesis(5)3906False3907sage: P.satisfies_kolyvagin_hypothesis(7)3908False3909sage: P.satisfies_kolyvagin_hypothesis(11)3910False3911"""3912return self.__heegner_point.satisfies_kolyvagin_hypothesis(n)39133914def curve(self):3915r"""3916Return the elliptic curve over `\QQ` on which this Kolyvagin3917point sits.39183919EXAMPLES::39203921sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67, 3)3922sage: P.curve()3923Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field3924"""3925return self.__heegner_point.curve()39263927def heegner_point(self):3928"""3929This Kolyvagin point `P_c` is associated to some Heegner point3930`y_c` via Kolyvagin's construction. This function returns that3931point `y_c`.39323933EXAMPLES::39343935sage: E = EllipticCurve('37a1')3936sage: P = E.kolyvagin_point(-67); P3937Kolyvagin point of discriminant -67 on elliptic curve of conductor 373938sage: y = P.heegner_point(); y3939Heegner point of discriminant -67 on elliptic curve of conductor 373940sage: y.kolyvagin_point() is P3941True3942"""3943return self.__heegner_point39443945def _repr_(self):3946"""3947Return string representation of this Kolyvagin point.39483949EXAMPLES::39503951sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P._repr_()3952'Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37'3953"""3954s = repr(self.__heegner_point)3955return s.replace('Heegner','Kolyvagin')39563957def index(self, *args, **kwds):3958"""3959Return index of this Kolyvagin point in the full group of3960$K_c$ rational points on $E$.39613962When the conductor is 1, this is computed numerically using3963the Gross-Zagier formula and explicit point search, and it may3964be off by $2$. See the documentation for ``E.heegner_index``,3965where `E` is the curve attached to ``self``.39663967EXAMPLES::39683969sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67); P.index()397063971"""3972if self.conductor() == 1:3973return self.__heegner_point._trace_index(*args, **kwds)3974raise NotImplementedError39753976def numerical_approx(self, prec=53):3977"""3978Return a numerical approximation to this Kolyvagin point using3979prec bits of working precision.39803981INPUT:39823983- ``prec`` -- precision in bits (default: 53)39843985EXAMPLES::39863987sage: P = EllipticCurve('37a1').kolyvagin_point(-7); P3988Kolyvagin point of discriminant -7 on elliptic curve of conductor 373989sage: P.numerical_approx() # approx. (0 : 0 : 1)3990(...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000)3991sage: P.numerical_approx(100)[0].abs() < 2.0^-993992True39933994sage: P = EllipticCurve('389a1').kolyvagin_point(-7, 5); P3995Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 38939963997Numerical approximation is only implemented for points of conductor 1::39983999sage: P.numerical_approx()4000Traceback (most recent call last):4001...4002NotImplementedError4003"""4004if self.conductor() == 1:4005return self.__heegner_point._trace_numerical_conductor_1(prec)4006raise NotImplementedError40074008def point_exact(self, prec=53):4009"""4010INPUT:40114012- ``prec`` -- precision in bits (default: 53)40134014EXAMPLES:40154016A rank 1 curve::40174018sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4019sage: P.point_exact()4020(6 : -15 : 1)4021sage: P.point_exact(40)4022(6 : -15 : 1)4023sage: P.point_exact(20)4024Traceback (most recent call last):4025...4026RuntimeError: insufficient precision to find exact point40274028A rank 0 curve::40294030sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)4031sage: P.point_exact()4032(-1/2*sqrt_minus_7 + 1/2 : -2*sqrt_minus_7 - 2 : 1)40334034A rank 2 curve::40354036sage: E = EllipticCurve('389a1'); P = E.kolyvagin_point(-7)4037sage: P.point_exact()4038(0 : 1 : 0)40394040"""4041if self.conductor() == 1:4042# the result is a point defined over K in the conductor 1 case, which is easier.4043P = self.numerical_approx(prec)40444045E = self.curve()4046if P[2] == 0:4047return E(0)40484049if E.root_number() == -1:4050return self._recognize_point_over_QQ(P, 2*self.index())4051else:4052# root number +1. We use algdep to recognize the x4053# coordinate, stick it in the appropriate quadratic4054# field, then make sure that we got the right4055# embedding, and if not fix things so we do.4056x = P[0]4057C = x.parent()4058f = x.algdep(2)4059K = self.quadratic_field()4060roots = [r[0] for r in f.roots(K)]4061if len(roots) == 0:4062raise RuntimeError("insufficient precision to find exact point")4063if len(roots) == 1:4064X = roots[0]4065else:4066d = [abs(C(r) - x) for r in roots]4067if d[0] == d[1]:4068raise RuntimeError("insufficient precision to distinguish roots")4069if d[0] < d[1]:4070X = roots[0]4071else:4072X = roots[1]4073F = E.change_ring(K)4074Q = F.lift_x(X, all=True)4075if len(Q) == 1:4076return Q[0]4077if len(Q) == 0:4078raise RuntimeError("insufficient precision")4079y = P[1]4080d = [abs(C(r[1])-y) for r in Q]4081if d[0] == d[1]:4082raise RuntimeError("insufficient precision to distinguish roots")4083if d[0] < d[1]:4084return Q[0]4085else:4086return Q[1]40874088else:4089raise NotImplementedError40904091def plot(self, prec=53, *args, **kwds):4092r"""4093Plot a Kolyvagin point `P_1` if it is defined over the4094rational numbers.40954096EXAMPLES::40974098sage: E = EllipticCurve('37a'); P = E.heegner_point(-11).kolyvagin_point()4099sage: P.plot(prec=30, pointsize=50, rgbcolor='red') + E.plot()4100"""4101if self.conductor() != 1:4102raise NotImplementedError41034104E = self.curve()4105if E.root_number() == -1:4106P = self.numerical_approx(prec=prec)4107from sage.plot.all import point, Graphics4108if not P:4109# point at infinity4110return Graphics()4111return point((P[0].real(), P[1].real()),*args, **kwds)4112else:4113raise NotImplementedError411441154116@cached_method4117def trace_to_real_numerical(self, prec=53):4118"""4119Return the trace of this Kolyvagin point down to the real4120numbers, computed numerically using prec bits of working4121precision.41224123EXAMPLES::41244125sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4126sage: PP = P.numerical_approx(); PP4127(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)4128sage: [c.real() for c in PP]4129[6.00000000000000, -15.0000000000000, 1.00000000000000]4130sage: all([c.imag().abs() < 1e-14 for c in PP])4131True4132sage: P.trace_to_real_numerical()4133(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4134sage: P.trace_to_real_numerical(prec=80)4135(1.6135552913198573127230 : -2.1844684078888023289187 : 1.0000000000000000000000)41364137"""4138# Compute numerical approximation of P in E(K).4139P = self.numerical_approx(prec=prec)4140# Trace this numerical approximation down to E(Q) (numerically).4141E = P.curve()4142if self.curve().root_number() == -1:4143R = 2*P4144else:4145R = P + E.point([x.conjugate() for x in P],check=False)4146F = self.curve().change_ring(rings.RealField(prec))4147return F.point([x.real() for x in R], check=False)41484149@cached_method4150def _trace_exact_conductor_1(self, prec=53):4151r"""4152Return the trace from `K` to `\QQ` of this Kolyvagin point in4153the case of conductor 1, computed using prec bits of4154precision, then approximated using some algorithm (e.g.,4155continued fractions). If the precision is not enough to4156determine a point on the curve, then a RuntimeError is raised.4157Even if the precision determines a point, there is no guarantee4158that it is correct.41594160EXAMPLES:41614162A Kolyvagin point on a rank 1 curve::41634164sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4165sage: P.trace_to_real_numerical()4166(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4167sage: P._trace_exact_conductor_1() # the actual point we're reducing4168(1357/841 : -53277/24389 : 1)4169sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()417012.00000000000004171"""4172if not self.conductor() == 1:4173raise ValueError("the conductor must be 1")41744175P = self.trace_to_real_numerical(prec)4176return self._recognize_point_over_QQ(P, 2*self.index())41774178def _recognize_point_over_QQ(self, P, n):4179"""4180Used internally when computing an exact point on an elliptic curve.41814182INPUT:41834184- `P` -- numerical approximation for a point on `E`41854186- `n` -- upper bound on divisibility index of `P` in group `E(\QQ)`41874188EXAMPLES::41894190sage: E = EllipticCurve('43a'); P = E.heegner_point(-20).kolyvagin_point()4191sage: PP = P.numerical_approx(); PP4192(...e-16 : -1.00000000000000 : 1.00000000000000)4193sage: P._recognize_point_over_QQ(PP, 4)4194(0 : -1 : 1)4195"""4196# Here is where we *should* implement the "method of Cremona4197# etc" mentioned in Watkins' article... which involves local4198# heights.4199E = self.curve() # over Q4200v = sum([list(n*w) for w in E.gens()] + [list(w) for w in E.torsion_points()], [])4201# note -- we do not claim to prove anything, so making up a factor of 100 is fine.4202max_denominator = 100*max([z.denominator() for z in v])4203try:4204# the coercion below also checks if point is on elliptic curve4205return E([x.real().nearby_rational(max_denominator=max_denominator) for x in P])4206except TypeError:4207raise RuntimeError("insufficient precision to find exact point")42084209def mod(self, p, prec=53):4210r"""4211Return the trace of the reduction `Q` modulo a prime over `p` of this4212Kolyvagin point as an element of `E(\GF{p})`, where4213`p` is any prime that is inert in `K` that is coprime to `NDc`.42144215The point `Q` is only well defined up to an element of4216`(p+1) E(\GF{p})`, i.e., it gives a well defined element4217of the abelian group `E(\GF{p}) / (p+1) E(\GF{p})`.42184219See [SteinToward]_, Proposition 5.4 for a proof of the above4220well-definedness assertion.42214222EXAMPLES:42234224A Kolyvagin point on a rank 1 curve::42254226sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4227sage: P.mod(2)4228(1 : 1 : 1)4229sage: P.mod(3)4230(1 : 0 : 1)4231sage: P.mod(5)4232(2 : 2 : 1)4233sage: P.mod(7)4234(6 : 0 : 1)4235sage: P.trace_to_real_numerical()4236(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4237sage: P._trace_exact_conductor_1() # the actual point we're reducing4238(1357/841 : -53277/24389 : 1)4239sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()424012.000000000000042414242Here the Kolyvagin point is a torsion point (since `E` has4243rank 1), and we reduce it modulo several primes.::42444245sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)4246sage: P.mod(3,70) # long time (4s on sage.math, 2013)4247(1 : 2 : 1)4248sage: P.mod(5,70)4249(1 : 4 : 1)4250sage: P.mod(7,70)4251Traceback (most recent call last):4252...4253ValueError: p must be coprime to conductors and discriminant4254sage: P.mod(11,70)4255Traceback (most recent call last):4256...4257ValueError: p must be coprime to conductors and discriminant4258sage: P.mod(13,70)4259(3 : 4 : 1)42604261REFERENCES:42624263.. [SteinToward] Stein, "Toward a Generalization of the Gross-Zagier Conjecture", Int Math Res Notices (2011), :doi:`10.1093/imrn/rnq075`4264"""4265# check preconditions4266p = ZZ(p)4267if not p.is_prime():4268raise ValueError("p must be prime")4269E = self.curve()4270D = self.discriminant()4271if (E.conductor() * D * self.conductor()) % p == 0:4272raise ValueError("p must be coprime to conductors and discriminant")4273K = self.heegner_point().quadratic_field()4274if len(K.factor(p)) != 1:4275raise ValueError("p must be inert")42764277# do actual calculation4278if self.conductor() == 1:42794280P = self._trace_exact_conductor_1(prec = prec)4281return E.change_ring(GF(p))(P)42824283else:42844285raise NotImplementedError42864287## def congruent_rational_point(self, n, prec=53):4288## r"""4289## Let `P` be this Kolyvagin point. Determine whether there is a4290## point `z` in `E(\QQ)` such that `z - P \in n E(K_c)`, where `K_c`4291## is the ring class field over which this Kolyvagin point is defined.4292## If `z` exists return `z`. Otherwise return None.4293##4294## INPUT:4295##4296## - `n` -- positive integer4297##4298## - ``prec`` -- positive integer (default: 53)4299##4300##4301## EXAMPLES::4302##4303## """4304## raise NotImplementedError430543064307def kolyvagin_cohomology_class(self, n=None):4308"""4309INPUT:43104311- `n` -- positive integer that divides the gcd of `a_p`4312and `p+1` for all `p` dividing the conductor. If `n` is4313``None``, choose the largest valid `n`.43144315EXAMPLES::43164317sage: y = EllipticCurve('389a').heegner_point(-7,5)4318sage: P = y.kolyvagin_point()4319sage: P.kolyvagin_cohomology_class(3)4320Kolyvagin cohomology class c(5) in H^1(K,E[3])43214322sage: y = EllipticCurve('37a').heegner_point(-7,5).kolyvagin_point()4323sage: y.kolyvagin_cohomology_class()4324Kolyvagin cohomology class c(5) in H^1(K,E[2])4325"""4326return KolyvaginCohomologyClassEn(self, n)432743284329class KolyvaginCohomologyClass(SageObject):4330"""4331A Kolyvagin cohomology class in `H^1(K,E[n])` or `H^1(K,E)[n]`4332attached to a Heegner point.43334334EXAMPLES::43354336sage: y = EllipticCurve('37a').heegner_point(-7)4337sage: c = y.kolyvagin_cohomology_class(3); c4338Kolyvagin cohomology class c(1) in H^1(K,E[3])4339sage: type(c)4340<class 'sage.schemes.elliptic_curves.heegner.KolyvaginCohomologyClassEn'>4341sage: loads(dumps(c)) == c4342True4343sage: y.kolyvagin_cohomology_class(5)4344Kolyvagin cohomology class c(1) in H^1(K,E[5])4345"""4346def __init__(self, kolyvagin_point, n):4347"""43484349EXAMPLES::43504351sage: y = EllipticCurve('389a').heegner_point(-7,5)4352sage: y.kolyvagin_cohomology_class(3)4353Kolyvagin cohomology class c(5) in H^1(K,E[3])4354"""4355if n is None:4356c = kolyvagin_point.conductor()4357E = kolyvagin_point.curve()4358n = arith.GCD([(p+1).gcd(E.ap(p)) for p in c.prime_divisors()])43594360if not kolyvagin_point.satisfies_kolyvagin_hypothesis(n):4361raise ValueError("Kolyvagin point does not satisfy Kolyvagin hypothesis for %s"%n)4362self.__kolyvagin_point = kolyvagin_point4363self.__n = n43644365def __eq__(self, other):4366"""4367EXAMPLES:4368sage: y = EllipticCurve('37a').heegner_point(-7)4369sage: c = y.kolyvagin_cohomology_class(3)4370sage: c == y.kolyvagin_cohomology_class(3)4371True4372sage: c == y.kolyvagin_cohomology_class(5)4373False43744375This does not mean that c is nonzero (!) -- it just means c is not the number 0::43764377sage: c == 04378False4379"""4380return isinstance(other, KolyvaginCohomologyClass) and \4381self.__kolyvagin_point == other.__kolyvagin_point and \4382self.__n == other.__n438343844385def n(self):4386"""4387Return the integer `n` so that this is a cohomology class in4388`H^1(K,E[n])` or `H^1(K,E)[n]`.43894390EXAMPLES::43914392sage: y = EllipticCurve('37a').heegner_point(-7)4393sage: t = y.kolyvagin_cohomology_class(3); t4394Kolyvagin cohomology class c(1) in H^1(K,E[3])4395sage: t.n()439634397"""4398return self.__n43994400def conductor(self):4401r"""4402Return the integer `c` such that this cohomology class is associated4403to the Heegner point `y_c`.44044405EXAMPLES::44064407sage: y = EllipticCurve('37a').heegner_point(-7,5)4408sage: t = y.kolyvagin_cohomology_class()4409sage: t.conductor()441054411"""4412return self.__kolyvagin_point.conductor()44134414def kolyvagin_point(self):4415"""4416Return the Kolyvagin point `P_c` to which this cohomology4417class is associated.44184419EXAMPLES::44204421sage: y = EllipticCurve('37a').heegner_point(-7,5)4422sage: t = y.kolyvagin_cohomology_class()4423sage: t.kolyvagin_point()4424Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 374425"""4426return self.__kolyvagin_point44274428def heegner_point(self):4429"""4430Return the Heegner point `y_c` to which this cohomology class4431is associated.44324433EXAMPLES::44344435sage: y = EllipticCurve('37a').heegner_point(-7,5)4436sage: t = y.kolyvagin_cohomology_class()4437sage: t.heegner_point()4438Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 374439"""4440return self.__kolyvagin_point.heegner_point()44414442class KolyvaginCohomologyClassEn(KolyvaginCohomologyClass):4443"""44444445EXAMPLES:44464447"""4448def _repr_(self):4449"""44504451EXAMPLES::44524453sage: y = EllipticCurve('37a').heegner_point(-7,5)4454sage: t = y.kolyvagin_cohomology_class()4455sage: t._repr_()4456'Kolyvagin cohomology class c(5) in H^1(K,E[2])'4457"""4458return "Kolyvagin cohomology class c(%s) in H^1(K,E[%s])"%(4459self.conductor(), self.n())446044614462#############################################################################4463# Reduction of Heegner points using Quaternion Algebras4464#4465# This section contains implementations of algorithms for computing4466# information about reduction modulo primes of Heegner points using4467# quaternion algebras. Some of this code could later be moved to the4468# quaternion algebras code, but it is too immature and not general4469# enough at present for that.4470#############################################################################44714472class HeegnerQuatAlg(SageObject):4473r"""4474Heegner points viewed as supersingular points on the modular curve4475`X_0(N)/\mathbf{F}_{\ell}`.44764477EXAMPLES::44784479sage: H = heegner_points(11).reduce_mod(13); H4480Heegner points on X_0(11) over F_134481sage: type(H)4482<class 'sage.schemes.elliptic_curves.heegner.HeegnerQuatAlg'>4483sage: loads(dumps(H)) == H4484True4485"""4486def __init__(self, level, ell):4487r"""4488INPUT:44894490- ``level`` -- the level (a positive integer)44914492- `\ell` -- the characteristic, a prime coprime to the level44934494EXAMPLES::44954496sage: sage.schemes.elliptic_curves.heegner.HeegnerQuatAlg(11, 13)4497Heegner points on X_0(11) over F_134498"""4499level = ZZ(level); ell = ZZ(ell)4500if not ell.is_prime():4501raise ValueError("ell must be prime")4502if level.gcd(ell) != 1:4503raise ValueError("level and ell must be coprime")4504self.__level = level4505self.__ell = ell45064507def __eq__(self, other):4508"""4509EXAMPLES::45104511sage: H = heegner_points(11).reduce_mod(3)4512sage: H == heegner_points(11).reduce_mod(3)4513True4514sage: H == heegner_points(11).reduce_mod(5)4515False4516sage: H == 04517False4518"""4519return isinstance(other, HeegnerQuatAlg) and self.__level == other.__level \4520and self.__ell == other.__ell45214522def _repr_(self):4523"""4524Return string representation.45254526EXAMPLES::45274528sage: heegner_points(11).reduce_mod(13)._repr_()4529'Heegner points on X_0(11) over F_13'4530"""4531return "Heegner points on X_0(%s) over F_%s"%(4532self.__level, self.__ell)45334534def level(self):4535"""4536Return the level.45374538EXAMPLES::45394540sage: heegner_points(11).reduce_mod(3).level()4541114542"""4543return self.__level45444545def ell(self):4546r"""4547Return the prime `\ell` modulo which we are working.45484549EXAMPLES::45504551sage: heegner_points(11).reduce_mod(3).ell()455234553"""4554return self.__ell45554556def satisfies_heegner_hypothesis(self, D, c=ZZ(1)):4557r"""4558The fundamental discriminant `D` must be coprime to `N\ell`,4559and must define a quadratic imaginary field `K` in which `\ell`4560is inert. Also, all primes dividing `N` must split in `K`,4561and `c` must be squarefree and coprime to `ND\ell`.45624563INPUT:45644565- `D` -- negative integer45664567- `c` -- positive integer (default: 1)45684569OUTPUT:45704571- bool45724573EXAMPLES::45744575sage: H = heegner_points(11).reduce_mod(7)4576sage: H.satisfies_heegner_hypothesis(-5)4577False4578sage: H.satisfies_heegner_hypothesis(-7)4579False4580sage: H.satisfies_heegner_hypothesis(-8)4581True4582sage: [D for D in [-1,-2..-100] if H.satisfies_heegner_hypothesis(D)]4583[-8, -39, -43, -51, -79, -95]4584"""4585D = ZZ(D); c = ZZ(c)4586if arith.gcd(c*D, self.__level*self.__ell) != 1 or arith.gcd(c,D) != 1:4587return False4588if not satisfies_weak_heegner_hypothesis(self.__level, D):4589return False4590if not is_inert(D, self.__ell):4591return False4592return True45934594def heegner_discriminants(self, n=5):4595r"""4596Return the first `n` negative fundamental discriminants4597coprime to `N\ell` such that `\ell` is inert in the4598corresponding quadratic imaginary field and that field4599satisfies the Heegner hypothesis, and `N` is the level.46004601INPUT:46024603- `n` -- positive integer (default: 5)46044605OUTPUT:46064607- list46084609EXAMPLES::46104611sage: H = heegner_points(11).reduce_mod(3)4612sage: H.heegner_discriminants()4613[-7, -19, -40, -43, -52]4614sage: H.heegner_discriminants(10)4615[-7, -19, -40, -43, -52, -79, -127, -139, -151, -184]4616"""4617v = []4618D = ZZ(-5)4619while len(v) < n:4620if self.satisfies_heegner_hypothesis(D):4621v.append(D)4622D -= 14623return v46244625def heegner_conductors(self, D, n=5):4626r"""4627Return the first `n` negative fundamental discriminants4628coprime to `N\ell` such that `\ell` is inert in the4629corresponding quadratic imaginary field and that field4630satisfies the Heegner hypothesis.46314632INPUT:46334634- `D` -- negative integer; a fundamental Heegner4635discriminant46364637- `n` -- positive integer (default: 5)46384639OUTPUT:46404641- list46424643EXAMPLES::46444645sage: H = heegner_points(11).reduce_mod(3)4646sage: H.heegner_conductors(-7)4647[1, 2, 4, 5, 8]4648sage: H.heegner_conductors(-7, 10)4649[1, 2, 4, 5, 8, 10, 13, 16, 17, 19]4650"""4651v = [ZZ(1)]4652c = ZZ(2)4653while len(v) < n:4654if self.satisfies_heegner_hypothesis(D, c):4655v.append(c)4656c += 14657return v465846594660def optimal_embeddings(self, D, c, R):4661"""4662INPUT:46634664- `D` -- negative fundamental disriminant46654666- `c` -- integer coprime46674668- `R` -- Eichler order46694670EXAMPLES::46714672sage: H = heegner_points(11).reduce_mod(3)4673sage: R = H.left_orders()[0]4674sage: H.optimal_embeddings(-7, 1, R)4675[Embedding sending sqrt(-7) to -i + j + k,4676Embedding sending sqrt(-7) to i - j - k]4677sage: H.optimal_embeddings(-7, 2, R)4678[Embedding sending 2*sqrt(-7) to -5*i + k,4679Embedding sending 2*sqrt(-7) to 5*i - k,4680Embedding sending 2*sqrt(-7) to -2*i + 2*j + 2*k,4681Embedding sending 2*sqrt(-7) to 2*i - 2*j - 2*k]4682"""4683Q, G = R.ternary_quadratic_form(include_basis=True)4684n = -D*c*c4685reps = Q.representation_vector_list(n+1)[-1]46864687# The representatives give elements in terms of the4688# subspace's basis such that the embedding is given by4689# phi(c*sqrt(D)) = beta4690E = []4691for r in reps:4692beta = sum(G[i]*r[i] for i in range(len(G)))4693phi = HeegnerQuatAlgEmbedding(D, c, R, beta)4694E.append(phi)4695return E46964697@cached_method4698def brandt_module(self):4699"""4700Return the Brandt module of right ideal classes that we4701used to represent the set of supersingular points on4702the modular curve.47034704EXAMPLES::47054706sage: heegner_points(11).reduce_mod(3).brandt_module()4707Brandt module of dimension 2 of level 3*11 of weight 2 over Rational Field4708"""4709from sage.modular.quatalg.all import BrandtModule4710return BrandtModule(self.__ell, self.__level)47114712@cached_method4713def quaternion_algebra(self):4714"""4715Return the rational quaternion algebra used to implement self.47164717EXAMPLES::47184719sage: heegner_points(389).reduce_mod(7).quaternion_algebra()4720Quaternion Algebra (-1, -7) with base ring Rational Field4721"""4722return self.brandt_module().quaternion_algebra()47234724def right_ideals(self):4725"""4726Return representative right ideals in the Brandt module.47274728EXAMPLES::47294730sage: heegner_points(11).reduce_mod(3).right_ideals()4731(Fractional ideal (2 + 2*j + 28*k, 2*i + 26*k, 4*j + 12*k, 44*k),4732Fractional ideal (2 + 2*j + 28*k, 2*i + 4*j + 38*k, 8*j + 24*k, 88*k))4733"""4734return self.brandt_module().right_ideals()47354736@cached_method4737def left_orders(self):4738"""4739Return the left orders associated to the representative right4740ideals in the Brandt module.47414742EXAMPLES::47434744sage: heegner_points(11).reduce_mod(3).left_orders()4745[Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/2*i + 13/2*k, j + 3*k, 11*k),4746Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/4*i + 1/2*j + 63/4*k, j + 14*k, 22*k)]4747"""4748return [I.left_order() for I in self.right_ideals()]47494750@cached_method4751def heegner_divisor(self, D, c=ZZ(1)):4752r"""4753Return Heegner divisor as an element of the Brandt module4754corresponding to the discriminant `D` and conductor `c`, which4755both must be coprime to `N\ell`.47564757More precisely, we compute the sum of the reductions of the4758`\textrm{Gal}(K_1/K)`-conjugates of each choice of `y_1`,4759where the choice comes from choosing the ideal `\mathcal{N}`.4760Then we apply the Hecke operator `T_c` to this sum.47614762INPUT:47634764- `D` -- discriminant (negative integer)47654766- `c` -- conductor (positive integer)47674768OUTPUT:47694770- Brandt module element47714772EXAMPLES::47734774sage: H = heegner_points(11).reduce_mod(7)4775sage: H.heegner_discriminants()4776[-8, -39, -43, -51, -79]4777sage: H.heegner_divisor(-8)4778(1, 0, 0, 1, 0, 0)4779sage: H.heegner_divisor(-39)4780(1, 2, 2, 1, 2, 0)4781sage: H.heegner_divisor(-43)4782(1, 0, 0, 1, 0, 0)4783sage: H.heegner_divisor(-51)4784(1, 0, 0, 1, 0, 2)4785sage: H.heegner_divisor(-79)4786(3, 2, 2, 3, 0, 0)47874788sage: sum(H.heegner_divisor(-39).element())478984790sage: QuadraticField(-39,'a').class_number()479144792"""4793if not self.satisfies_heegner_hypothesis(D, c):4794raise ValueError("D and c must be coprime to N and ell")47954796B = self.brandt_module()47974798if c > 1:4799# Just apply T_c to divisor for c=14800z = self.heegner_divisor(D)4801return B.hecke_operator(c)(z)48024803n = -D4804v = [0]*B.degree()4805for i, R in enumerate(self.left_orders()):4806Q = R.ternary_quadratic_form()4807a = Q.theta_series(n+1)[n]4808if a > 0:4809reps = Q.representation_vector_list(n+1)[-1]4810k = len([r for r in reps if arith.gcd(r) == 1])4811assert k%2 == 04812v[i] += k/24813return B(v)48144815@cached_method4816def modp_splitting_data(self, p):4817r"""4818Return mod `p` splitting data for the quaternion algebra at the4819unramified prime `p`. This is a pair of `2\times 2` matrices4820`A`, `B` over the finite field `\GF{p}` such that if the4821quaternion algebra has generators `i, j, k`, then the4822homomorphism sending `i` to `A` and `j` to `B` maps any4823maximal order homomorphically onto the ring of `2\times 2` matrices.48244825Because of how the homomorphism is defined, we must assume that the4826prime `p` is odd.48274828INPUT:48294830- `p` -- unramified odd prime48314832OUTPUT:48334834- 2-tuple of matrices over finite field48354836EXAMPLES::48374838sage: H = heegner_points(11).reduce_mod(7)4839sage: H.quaternion_algebra()4840Quaternion Algebra (-1, -7) with base ring Rational Field4841sage: I, J = H.modp_splitting_data(13)4842sage: I4843[ 0 12]4844[ 1 0]4845sage: J4846[7 3]4847[3 6]4848sage: I^24849[12 0]4850[ 0 12]4851sage: J^24852[6 0]4853[0 6]4854sage: I*J == -J*I4855True48564857The following is a good test because of the asserts in the code::48584859sage: v = [H.modp_splitting_data(p) for p in primes(13,200)]48604861Some edge cases::48624863sage: H.modp_splitting_data(11)4864(4865[ 0 10] [6 1]4866[ 1 0], [1 5]4867)48684869Proper error handling::48704871sage: H.modp_splitting_data(7)4872Traceback (most recent call last):4873...4874ValueError: p (=7) must be an unramified prime48754876sage: H.modp_splitting_data(2)4877Traceback (most recent call last):4878...4879ValueError: p must be odd4880"""4881p = ZZ(p)4882if not p.is_prime():4883raise ValueError("p (=%s) must be prime"%p)4884if p == 2:4885raise ValueError("p must be odd")4886Q = self.quaternion_algebra()4887if Q.discriminant() % p == 0:4888raise ValueError("p (=%s) must be an unramified prime"%p)4889i, j, k = Q.gens()4890F = GF(p)4891i2 = F(i*i)4892j2 = F(j*j)4893M = MatrixSpace(F, 2)4894I = M([0,i2,1,0])4895i2inv = 1/i24896a = None4897#for b in reversed(list(F)):4898for b in list(F):4899if not b: continue4900c = j2 + i2inv * b*b4901if c.is_square():4902a = -c.sqrt()4903break4904assert a is not None, "bug in that no splitting solution found"4905J = M([a,b,(j2-a*a)/b, -a])4906assert I*J == -J*I, "bug in that I,J do not skew commute"4907return I, J49084909def modp_splitting_map(self, p):4910r"""4911Return (algebra) map from the (`p`-integral) quaternion algebra to4912the set of `2\times 2` matrices over `\GF{p}`.49134914INPUT:49154916- `p` -- prime number49174918EXAMPLES::49194920sage: H = heegner_points(11).reduce_mod(7)4921sage: f = H.modp_splitting_map(13)4922sage: B = H.quaternion_algebra(); B4923Quaternion Algebra (-1, -7) with base ring Rational Field4924sage: i,j,k = H.quaternion_algebra().gens()4925sage: a = 2+i-j+3*k; b = 7+2*i-4*j+k4926sage: f(a*b)4927[12 3]4928[10 5]4929sage: f(a)*f(b)4930[12 3]4931[10 5]4932"""4933I, J = self.modp_splitting_data(p)4934K = I*J4935F = I.base_ring()4936def phi(q):4937v = [F(a) for a in q.coefficient_tuple()]4938return v[0] + I*v[1] + J*v[2] + K*v[3]4939return phi49404941def cyclic_subideal_p1(self, I, c):4942r"""4943Compute dictionary mapping 2-tuples that defined normalized4944elements of `P^1(\ZZ/c\ZZ)`49454946INPUT:49474948- `I` -- right ideal of Eichler order or in quaternion algebra49494950- `c` -- square free integer (currently must be odd prime4951and coprime to level, discriminant, characteristic,4952etc.49534954OUTPUT:49554956- dictionary mapping 2-tuples (u,v) to ideals49574958EXAMPLES::49594960sage: H = heegner_points(11).reduce_mod(7)4961sage: I = H.brandt_module().right_ideals()[0]4962sage: sorted(H.cyclic_subideal_p1(I,3).iteritems())4963[((0, 1),4964Fractional ideal (2 + 2*j + 32*k, 2*i + 8*j + 82*k, 12*j + 60*k, 132*k)),4965((1, 0),4966Fractional ideal (2 + 10*j + 28*k, 2*i + 4*j + 62*k, 12*j + 60*k, 132*k)),4967((1, 1),4968Fractional ideal (2 + 2*j + 76*k, 2*i + 4*j + 106*k, 12*j + 60*k, 132*k)),4969((1, 2),4970Fractional ideal (2 + 10*j + 116*k, 2*i + 8*j + 38*k, 12*j + 60*k, 132*k))]4971sage: len(H.cyclic_subideal_p1(I,17))4972184973"""4974c = ZZ(c)4975if not c.is_prime():4976raise NotImplementedError("currently c must be prime")4977if c == 2:4978raise NotImplementedError("currently c must be odd")4979phi = self.modp_splitting_map(c)4980B = self.brandt_module()4981P1 = P1List(c)4982ans = {}4983# Actually they are submodules despite the name below.4984for J in B.cyclic_submodules(I, c):4985B = J.basis()4986V = phi(B[0]).kernel()4987for i in [1,2,3]:4988V = V.intersection(phi(B[i]).kernel())4989b = V.basis()4990assert len(b) == 1, "common kernel must have dimension 1"4991uv = P1.normalize(ZZ(b[0][0])%c, ZZ(b[0][1])%c)4992ans[uv] = J4993assert len(ans) == c+14994return ans49954996@cached_method4997def galois_group_over_hilbert_class_field(self, D, c):4998"""4999Return the Galois group of the extension of ring class fields5000`K_c` over the Hilbert class field `K_{1}` of the quadratic5001imaginary field of discriminant `D`.50025003INPUT:50045005- `D` -- fundamental discriminant50065007- `c` -- conductor (square-free integer)50085009EXAMPLES::50105011sage: N = 37; D = -7; ell = 17; c = 41; p = 35012sage: H = heegner_points(N).reduce_mod(ell)5013sage: H.galois_group_over_hilbert_class_field(D, c)5014Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 41 over Hilbert class field of QQ[sqrt(-7)]5015"""5016Kc = heegner_points(self.level(), D, c).ring_class_field()5017K1 = heegner_points(self.level(), D, 1).ring_class_field()5018return Kc.galois_group(K1)50195020@cached_method5021def galois_group_over_quadratic_field(self, D, c):5022"""5023Return the Galois group of the extension of ring class fields5024`K_c` over the quadratic imaginary field `K` of discriminant `D`.50255026INPUT:50275028- `D` -- fundamental discriminant50295030- `c` -- conductor (square-free integer)50315032EXAMPLES::50335034sage: N = 37; D = -7; ell = 17; c = 41; p = 35035sage: H = heegner_points(N).reduce_mod(ell)5036sage: H.galois_group_over_quadratic_field(D, c)5037Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 41 over Number Field in sqrt_minus_7 with defining polynomial x^2 + 750385039"""5040Kc = heegner_points(self.level(), D, c).ring_class_field()5041return Kc.galois_group(Kc.quadratic_field())50425043@cached_method5044def quadratic_field(self, D):5045"""5046Return our fixed choice of quadratic imaginary field of5047discriminant `D`.50485049INPUT:50505051- `D` -- fundamental discriminant50525053OUTPUT:50545055- a quadratic number field50565057EXAMPLES::50585059sage: H = heegner_points(389).reduce_mod(5)5060sage: H.quadratic_field(-7)5061Number Field in sqrt_minus_7 with defining polynomial x^2 + 75062"""5063Kc = heegner_points(self.level(), D, 1).ring_class_field()5064return Kc.quadratic_field()50655066@cached_method5067def kolyvagin_cyclic_subideals(self, I, p, alpha_quaternion):5068r"""5069Return list of pairs `(J, n)` where `J` runs through the5070cyclic subideals of `I` of index `(\ZZ/p\ZZ)^2`, and `J \sim5071\alpha^n(J_0)` for some fixed choice of cyclic subideal `J_0`.50725073INPUT:50745075- `I` -- right ideal of the quaternion algebra50765077- `p` -- prime number50785079- ``alpha_quaternion`` -- image in the quaternion algebra5080of generator `\alpha` for5081`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`.50825083OUTPUT:50845085- list of 2-tuples50865087EXAMPLES::50885089sage: N = 37; D = -7; ell = 17; c=55090sage: H = heegner_points(N).reduce_mod(ell)5091sage: B = H.brandt_module(); I = B.right_ideals()[32]5092sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]5093sage: g = H.kolyvagin_generators(f.domain().number_field(), c)5094sage: alpha_quaternion = f(g[0]); alpha_quaternion50951 - 5/128*i - 77/192*j + 137/384*k5096sage: H.kolyvagin_cyclic_subideals(I, 5, alpha_quaternion)5097[(Fractional ideal (2 + 874/3*j + 128356/3*k, 2*i + 932/3*j + 198806/3*k, 2560/3*j + 33280/3*k, 94720*k), 0), (Fractional ideal (2 + 462*j + 82892*k, 2*i + 932/3*j + 141974/3*k, 2560/3*j + 33280/3*k, 94720*k), 1), (Fractional ideal (2 + 2410/3*j + 261988/3*k, 2*i + 652*j + 89650*k, 2560/3*j + 33280/3*k, 94720*k), 2), (Fractional ideal (2 + 2410/3*j + 91492/3*k, 2*i + 1444/3*j + 148630/3*k, 2560/3*j + 33280/3*k, 94720*k), 3), (Fractional ideal (2 + 874/3*j + 71524/3*k, 2*i + 2468/3*j + 275606/3*k, 2560/3*j + 33280/3*k, 94720*k), 4), (Fractional ideal (2 + 462*j + 63948*k, 2*i + 2468/3*j + 218774/3*k, 2560/3*j + 33280/3*k, 94720*k), 5)]5098"""5099X = I.cyclic_right_subideals(p, alpha_quaternion)5100return [(J, i) for i, J in enumerate(X)]51015102@cached_method5103def kolyvagin_generator(self, K, p):5104r"""5105Return element in `K` that maps to the multiplicative generator5106for the quotient group51075108`(\mathcal{O}_K / p \mathcal{O}_K)^* / (\ZZ/p\ZZ)^*`51095110of the form `\sqrt{D}+n` with `n\geq 1` minimal.51115112INPUT:51135114- `K` -- quadratic imaginary field51155116- `p` -- inert prime51175118EXAMPLES::51195120sage: N = 37; D = -7; ell = 17; p=55121sage: H = heegner_points(N).reduce_mod(ell)5122sage: B = H.brandt_module(); I = B.right_ideals()[32]5123sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]5124sage: H.kolyvagin_generator(f.domain().number_field(), 5)5125a + 151265127This function requires that p be prime, but kolyvagin_generators works in general::51285129sage: H.kolyvagin_generator(f.domain().number_field(), 5*17)5130Traceback (most recent call last):5131...5132NotImplementedError: p must be prime5133sage: H.kolyvagin_generators(f.domain().number_field(), 5*17)5134[-34*a + 1, 35*a + 106]5135"""5136p = ZZ(p)5137if not p.is_prime():5138raise NotImplementedError("p must be prime")5139if K.discriminant() % p == 0:5140raise ValueError("p must be unramified")5141if len(K.factor(p)) != 1:5142raise ValueError("p must be inert")51435144F = K.residue_field(p)5145a = F.gen()5146assert a*a == K.discriminant(), "bug: we assumed generator of finite field must be square root of discriminant, but for some reason this is not true"5147for n in range(1,p):5148if (a + n).multiplicative_order() % (p*p-1) == 0:5149return K.gen() + n5150raise RuntimeError("there is a bug in kolyvagin_generator")51515152@cached_method5153def kolyvagin_generators(self, K, c):5154r"""5155Return elements in `\mathcal{O}_K` that map to multiplicative generators5156for the factors of the quotient group51575158`(\mathcal{O}_K / c \mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`51595160corresponding to the prime divisors of c. Each generator is5161of the form `\sqrt{D}+n` with `n\geq 1` minimal.51625163INPUT:51645165- `K` -- quadratic imaginary field51665167- `c` -- square free product of inert prime51685169EXAMPLES::51705171sage: N = 37; D = -7; ell = 17; p=55172sage: H = heegner_points(N).reduce_mod(ell)5173sage: B = H.brandt_module(); I = B.right_ideals()[32]5174sage: f = H.optimal_embeddings(D, 1, I.left_order())[0]5175sage: H.kolyvagin_generators(f.domain().number_field(), 5*17)5176[-34*a + 1, 35*a + 106]5177"""5178v = []5179F = ZZ(c).factor()5180from sage.rings.integer_ring import crt_basis5181B = crt_basis([x[0] for x in F])5182for i, (p, e) in enumerate(F):5183if e > 1:5184raise ValueError("c must be square free")5185alpha = self.kolyvagin_generator(K, p)5186# Now we use the Chinese Remainder Theorem to make an element5187# of O_K that equals alpha modulo p and equals 1 modulo5188# all other prime divisors of c.5189Z = [1]*len(B)5190Z[i] = alpha[0]5191a0 = sum([Z[j]*B[j] for j in range(len(B))])5192Z = [0]*len(B)5193Z[i] = alpha[1]5194a1 = sum([Z[j]*B[j] for j in range(len(B))])5195v.append(alpha.parent()([a0,a1]))5196return v51975198@cached_method5199def kolyvagin_sigma_operator(self, D, c, r, bound=None):5200"""5201Return the action of the Kolyvagin sigma operator on the `r`-th5202basis vector.52035204INPUT:52055206- `D` -- fundamental discriminant52075208- `c` -- conductor (square-free integer, need not be prime)52095210- `r` -- nonnegative integer52115212- ``bound`` -- (default: ``None``), if given, controls5213precision of computation of theta series, which could5214impact performance, but does not impact correctness52155216EXAMPLES:52175218We first try to verify Kolyvagin's conjecture for a rank 25219curve by working modulo 5, but we are unlucky with `c=17`::52205221sage: N = 389; D = -7; ell = 5; c = 17; q = 35222sage: H = heegner_points(N).reduce_mod(ell)5223sage: E = EllipticCurve('389a')5224sage: V = H.modp_dual_elliptic_curve_factor(E, q, 5) # long time (4s on sage.math, 2012)5225sage: k118 = H.kolyvagin_sigma_operator(D, c, 118)5226sage: k104 = H.kolyvagin_sigma_operator(D, c, 104)5227sage: [b.dot_product(k104.element().change_ring(GF(3))) for b in V.basis()] # long time5228[0, 0]5229sage: [b.dot_product(k118.element().change_ring(GF(3))) for b in V.basis()] # long time5230[0, 0]52315232Next we try again with `c=41` and this does work, in that we5233get something nonzero, when dotting with V::52345235sage: c = 415236sage: k118 = H.kolyvagin_sigma_operator(D, c, 118)5237sage: k104 = H.kolyvagin_sigma_operator(D, c, 104)5238sage: [b.dot_product(k118.element().change_ring(GF(3))) for b in V.basis()] # long time5239[1, 0]5240sage: [b.dot_product(k104.element().change_ring(GF(3))) for b in V.basis()] # long time5241[2, 0]52425243By the way, the above is the first ever provable verification5244of Kolyvagin's conjecture for any curve of rank at least 2.52455246Another example, but where the curve has rank 1::52475248sage: N = 37; D = -7; ell = 17; c = 41; q = 35249sage: H = heegner_points(N).reduce_mod(ell)5250sage: H.heegner_divisor(D,1).element().nonzero_positions()5251[32, 51]5252sage: k32 = H.kolyvagin_sigma_operator(D, c, 32); k325253(63, 68, 47, 47, 31, 52, 37, 0, 0, 47, 3, 31, 47, 7, 21, 26, 19, 10, 0, 0, 11, 28, 41, 2, 47, 25, 0, 0, 36, 0, 33, 0, 0, 0, 40, 6, 14, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)5254sage: k51 = H.kolyvagin_sigma_operator(D, c, 51); k515255(5, 13, 0, 0, 14, 0, 21, 0, 0, 0, 29, 0, 0, 45, 0, 6, 0, 40, 0, 61, 0, 0, 40, 32, 0, 9, 0, 0, 0, 0, 17, 0, 0, 0, 77, 40, 2, 10, 18, 0, 0, 61, 19, 45, 26, 80, 61, 35, 35, 19, 1, 0)5256sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V5257Vector space of degree 52 and dimension 2 over Ring of integers modulo 35258Basis matrix:52592 x 52 dense matrix over Ring of integers modulo 35260sage: [b.dot_product(k32.element().change_ring(GF(q))) for b in V.basis()]5261[1, 1]5262sage: [b.dot_product(k51.element().change_ring(GF(q))) for b in V.basis()]5263[1, 1]52645265An example with `c` a product of two primes::52665267sage: N = 389; D = -7; ell = 5; q = 35268sage: H = heegner_points(N).reduce_mod(ell)5269sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('389a'), q, 5)5270sage: k = H.kolyvagin_sigma_operator(D, 17*41, 104) # long time5271sage: k # long time5272(494, 472, 1923, 1067, ..., 102, 926)5273sage: [b.dot_product(k.element().change_ring(GF(3))) for b in V.basis()] # long time (but only because depends on something slow)5274[0, 0]5275"""5276B = self.brandt_module()5277RI = B.right_ideals()52785279f = self.optimal_embeddings(D, 1, RI[r].left_order())[0]5280alphas = self.kolyvagin_generators(f.domain().number_field(), c)5281alpha_quaternions = [f(x) for x in alphas]52825283if bound is None:5284bound = B.dimension() // 2 + 55285theta_dict = B._theta_dict(bound)52865287c = ZZ(c)5288J_lists = []5289F = c.factor()5290I = RI[r]5291for i, (p, e) in enumerate(F):5292if e > 1: raise ValueError("c must be square free")5293X = I.cyclic_right_subideals(p, alpha_quaternions[i])5294J_lists.append(dict(enumerate(X)))52955296ans = [0]*B.dimension()5297from sage.misc.mrange import cartesian_product_iterator5298for v in cartesian_product_iterator([range(1,p+1) for p,_ in F]):5299J = J_lists[0][v[0]]5300for i in range(1,len(J_lists)):5301J = J.intersection(J_lists[i][v[i]])5302J_theta = tuple(J.theta_series_vector(bound))5303d = theta_dict[J_theta]5304j = None5305if len(d) == 1:5306j = d[0]5307else:5308for z in d:5309if RI[z].is_equivalent(J, 0):5310j = z5311# we found the right j5312break5313if j is None:5314raise RuntimeError("bug finding equivalent ideal")5315ans[j] += prod(v)5316return B(ans)53175318@cached_method5319def modp_dual_elliptic_curve_factor(self, E, p, bound=10):5320"""5321Return the factor of the Brandt module space modulo `p`5322corresponding to the elliptic curve `E`, cut out using5323Hecke operators up to ``bound``.53245325INPUT:53265327- `E` -- elliptic curve of conductor equal to the level of self53285329- `p` -- prime number53305331- `bound` -- positive integer (default: 10)53325333EXAMPLES::53345335sage: N = 37; D = -7; ell = 17; c = 41; q = 35336sage: H = heegner_points(N).reduce_mod(ell)5337sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V5338Vector space of degree 52 and dimension 2 over Ring of integers modulo 35339Basis matrix:53402 x 52 dense matrix over Ring of integers modulo 35341"""5342if E.conductor() != self.level():5343raise ValueError("conductor of E must equal level of self")5344p = ZZ(p)5345if not p.is_prime():5346raise ValueError("p (=%s) must be prime"%p)5347bad = self.__level * self.__ell53485349V = None5350q = ZZ(2)5351B = self.brandt_module()5352F = GF(p)5353while q <= bound and (V is None or V.dimension() > 2):5354verbose("q = %s"%q)5355if bad % q != 0:5356T = B._compute_hecke_matrix_directly(q).change_ring(F).transpose()5357if V is None:5358V = (T - E.ap(q)).kernel()5359else:5360t = T.restrict(V)5361W = (t - E.ap(q)).kernel()5362V = (W.basis_matrix() * V.basis_matrix()).row_space()5363q = q.next_prime()5364return V53655366@cached_method5367def rational_kolyvagin_divisor(self, D, c):5368r"""5369Return the Kolyvagin divisor as an element of the Brandt module5370corresponding to the discriminant `D` and conductor `c`, which5371both must be coprime to `N\ell`.53725373INPUT:53745375- `D` -- discriminant (negative integer)53765377- `c` -- conductor (positive integer)537853795380OUTPUT:53815382- Brandt module element (or tuple of them)53835384EXAMPLES::53855386sage: N = 389; D = -7; ell = 5; c = 17; q = 35387sage: H = heegner_points(N).reduce_mod(ell)5388sage: k = H.rational_kolyvagin_divisor(D, c); k # long time (5s on sage.math, 2013)5389(14, 16, 0, 0, ... 0, 0, 0)5390sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('389a'), q, 2)5391sage: [b.dot_product(k.element().change_ring(GF(q))) for b in V.basis()] # long time5392[0, 0]5393sage: k = H.rational_kolyvagin_divisor(D, 59)5394sage: [b.dot_product(k.element().change_ring(GF(q))) for b in V.basis()]5395[1, 0]5396"""5397if not self.satisfies_heegner_hypothesis(D, c):5398raise ValueError("D and c must be coprime to N and ell")53995400hd = self.heegner_divisor(D)5401v = hd.element()5402if class_number(D) != 1:5403raise NotImplementedError("class number greater than 1 not implemented")5404i = min(v.nonzero_positions())5405return self.kolyvagin_sigma_operator(D, c, i)54065407#w = 05408#for i, a in v.dict().iteritems():5409# w += a * self.kolyvagin_sigma_operator(D, c, i)5410# return w54115412@cached_method5413def kolyvagin_point_on_curve(self, D, c, E, p, bound=10):5414r"""5415Compute image of the Kolyvagin divisor `P_c` in5416`E(\GF{\ell^2}) / p E(\GF{\ell^2})`. Note that5417this image is by definition only well defined up to5418scalars. However, doing multiple computations5419will always yield the same result, and working5420modulo different `\ell` is compatible (since we5421always chose the same generator for `\textrm{Gal}(K_c/K_1)`).54225423INPUT:54245425- `D` -- fundamental negative discriminant54265427- `c` -- conductor54285429- `E` -- elliptic curve of conductor the level of self54305431- `p` -- odd prime number such that we consider image in5432`E(\GF{\ell^2}) / p E(\GF{\ell^2})`54335434- ``bound`` -- integer (default: 10)54355436EXAMPLES::54375438sage: N = 37; D = -7; ell = 17; c = 41; p = 35439sage: H = heegner_points(N).reduce_mod(ell)5440sage: H.kolyvagin_point_on_curve(D, c, EllipticCurve('37a'), p)5441[1, 1]5442"""5443k = self.rational_kolyvagin_divisor(D, c)5444V = self.modp_dual_elliptic_curve_factor(E, p, bound)5445return [b.dot_product(k.element().change_ring(GF(p))) for b in V.basis()]54465447def kolyvagin_reduction_data(E, q, first_only=True):5448r"""5449Given an elliptic curve of positive rank and a prime `q`, this5450function returns data about how to use Kolyvagin's `q`-torsion5451Heegner point Euler system to do computations with this curve.5452See the precise description of the output below.54535454INPUT:54555456- `E` -- elliptic curve over `\QQ` of rank 1 or 254575458- `q` -- an odd prime that does not divide the order of the5459rational torsion subgroup of `E`54605461- ``first_only`` -- bool (default: ``True``) whether two only return5462the first prime that one can work modulo to get data about5463the Euler system54645465OUTPUT in the rank 1 case or when the default flag ``first_only=True``:54665467- `\ell` -- first good odd prime satisfying the Kolyvagin5468condition that `q` divides \gcd(a_{\ell},\ell+1)` and the5469reduction map is surjective to `E(\GF{\ell}) / q5470E(\GF{\ell})`54715472- `D` -- discriminant of the first quadratic imaginary field5473`K` that satisfies the Heegner hypothesis for `E` such that5474both `\ell` is inert in `K`, and the twist `E^D` has analytic5475rank `\leq 1`54765477- `h_D` -- the class number of `K`54785479- the dimension of the Brandt module `B(\ell,N)`, where `N` is5480the conductor of `E`54815482OUTPUT in the rank 2 case:54835484- `\ell_1` -- first prime (as above in the rank 1 case) where5485reduction map is surjective54865487- `\ell_2` -- second prime (as above) where reduction map is5488surjective54895490- `D` -- discriminant of the first quadratic imaginary field5491`K` that satisfies the Heegner hypothesis for `E` such that5492both `\ell_1` and `\ell_2` are simultaneously inert in `K`,5493and the twist `E^D` has analytic rank `\leq 1`54945495- `h_D` -- the class number of `K`54965497- the dimension of the Brandt module `B(\ell_1,N)`, where `N` is5498the conductor of `E`54995500- the dimension of the Brandt module `B(\ell_2,N)`550155025503EXAMPLES:55045505Import this function::55065507sage: from sage.schemes.elliptic_curves.heegner import kolyvagin_reduction_data55085509A rank 1 example::55105511sage: kolyvagin_reduction_data(EllipticCurve('37a1'),3)5512(17, -7, 1, 52)55135514A rank 3 example::55155516sage: kolyvagin_reduction_data(EllipticCurve('5077a1'),3)5517(11, -47, 5, 4234)5518sage: H = heegner_points(5077, -47)5519sage: [c for c in H.kolyvagin_conductors(2,10,EllipticCurve('5077a1'),3) if c%11]5520[667, 943, 1189, 2461]5521sage: factor(667)552223 * 29552355245525A rank 4 example (the first Kolyvagin class that we could try to5526compute would be `P_{23\cdot 29\cdot 41}`, and would require5527working in a space of dimension 293060 (so prohibitive at5528present)::55295530sage: E = elliptic_curves.rank(4)[0]5531sage: kolyvagin_reduction_data(E,3) # long time5532(11, -71, 7, 293060)5533sage: H = heegner_points(293060, -71)5534sage: H.kolyvagin_conductors(1,4,E,3)5535[11, 17, 23, 41]55365537The first rank 2 example::55385539sage: kolyvagin_reduction_data(EllipticCurve('389a'),3)5540(5, -7, 1, 130)5541sage: kolyvagin_reduction_data(EllipticCurve('389a'),3, first_only=False)5542(5, 17, -7, 1, 130, 520)55435544A large `q = 7`::55455546sage: kolyvagin_reduction_data(EllipticCurve('1143c1'),7, first_only=False)5547(13, 83, -59, 3, 1536, 10496)55485549Additive reduction::55505551sage: kolyvagin_reduction_data(EllipticCurve('2350g1'),5, first_only=False)5552(19, 239, -311, 19, 6480, 85680)5553"""5554from ell_generic import is_EllipticCurve5555if not is_EllipticCurve(E):5556raise TypeError("E must be an elliptic curve")55575558q = ZZ(q)5559if not q.is_prime():5560raise ValueError("q must be a prime")55615562if q.gcd(E.torsion_order()) != 1:5563raise NotImplementedError("q must be coprime to torsion")55645565N = E.conductor()5566r = E.rank()55675568if r == 0:5569raise ValueError("E must have positive rank")55705571if E.rank() == 1:5572first_only = True55735574from sage.modular.quatalg.all import BrandtModule55755576def twist_is_minimal(D):5577# return True if the quadratic twist E^D has analytic rank <= 15578return E.quadratic_twist(D).analytic_rank() <= 155795580def red(P, ell):5581# reduce the point P on the elliptic curve modulo ell5582w = list(P)5583d = lcm([a.denominator() for a in w])5584return E.change_ring(GF(ell))([d*a for a in w])558555865587def best_heegner_D(ell_1, ell_2):5588# return the first Heegner D satisfy all hypothesis such that5589# both ell_1 and ell_2 are inert5590D = -55591while True:5592if is_fundamental_discriminant(D) and \5593D%ell_1 and D%ell_2 and \5594E.satisfies_heegner_hypothesis(D) and \5595is_inert(D, ell_1) and is_inert(D, ell_2) and \5596twist_is_minimal(D):5597return D5598D -= 155995600if first_only:5601# find first prime ell with various conditions5602# such that reduction is surjective to E(F_ell)/q.5603ell = ZZ(3)5604while True:5605while N % ell == 0 or gcd(ell+1,E.ap(ell)) % q != 0:5606ell = ell.next_prime()5607# determine if mod ell reduction is surjective, using5608# partly that it is a lemma that E(F_ell)/q is cyclic.5609m = ZZ(E.Np(ell) / q)5610for P in E.gens():5611if red(P,ell) * m != 0:5612# bingo, is surjective5613D = best_heegner_D(ell,ell)5614return (ell, D, class_number(D), BrandtModule(ell,N).dimension())5615# end for5616ell = ell.next_prime()56175618if E.rank() != 2:5619raise ValueError("if first_only is not True, then the curve E must have rank 1 or 2")56205621P, Q = E.gens()5622def kernel_of_reduction(ell):5623# return list of reps for the kernel as a subgroup of the map5624# E(Q) / q E(Q) ----> E(F_ell) / q E(F_ell)5625m = ZZ(E.Np(ell) / q)5626A = [a*P + b*Q for a in range(q) for b in range(q)]5627return [z for z in A if red(z,ell) * m == 0]56285629# compute first good odd prime5630ell_1 = ZZ(3)5631while True:5632while N % ell_1 == 0 or gcd(ell_1+1,E.ap(ell_1)) % q != 0:5633ell_1 = ell_1.next_prime()5634# compute kernel of reduction modulo ell_15635G1 = set(kernel_of_reduction(ell_1))5636if len(G1) == q: break5637ell_1 = ell_1.next_prime()56385639# compute next good odd prime with distinct kernel of order q5640ell_2 = ell_1.next_prime()5641while True:5642while N % ell_2 == 0 or gcd(ell_2+1,E.ap(ell_2)) % q != 0:5643ell_2 = ell_2.next_prime()5644G2 = set(kernel_of_reduction(ell_2))5645if G1 != G2 and len(G2) == q:5646break5647ell_2 = ell_2.next_prime()56485649# Find smallest D where both ell_1 and ell_2 are inert5650D = best_heegner_D(ell_1, ell_2)5651return (ell_1, ell_2, D, class_number(D),5652BrandtModule(ell_1,N).dimension(),5653BrandtModule(ell_2,N).dimension())56545655class HeegnerQuatAlgEmbedding(SageObject):5656r"""5657The homomorphism `\mathcal{O} \to R`, where `\mathcal{O}` is the5658order of conductor `c` in the quadratic field of discriminant `D`,5659and `R` is an Eichler order in a quaternion algebra.56605661EXAMPLES::56625663sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5664sage: f = H.optimal_embeddings(-7, 2, R)[0]; f5665Embedding sending 2*sqrt(-7) to -5*i + k5666sage: type(f)5667<class 'sage.schemes.elliptic_curves.heegner.HeegnerQuatAlgEmbedding'>5668sage: loads(dumps(f)) == f5669True5670"""5671def __init__(self, D, c, R, beta):5672r"""5673INPUT:56745675- `D` -- negative fundamental discriminant56765677- `c` -- positive integer coprime to `D`56785679- `R` -- Eichler order in a rational quaternion algebra56805681- `\beta` -- element of `R` such that the homomorphism5682sends `c\sqrt{D}` to `\beta`56835684EXAMPLES::56855686sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5687sage: i,j,k = H.quaternion_algebra().gens()5688sage: import sage.schemes.elliptic_curves.heegner as heegner5689sage: heegner.HeegnerQuatAlgEmbedding(-7, 2, R, -5*i+k)5690Embedding sending 2*sqrt(-7) to -5*i + k5691"""5692self.__D = D5693self.__c = c5694self.__R = R5695self.__beta = beta56965697def __eq__(self, other):5698"""5699EXAMPLES::57005701sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5702sage: f = H.optimal_embeddings(-7, 2, R)[0]5703sage: f == H.optimal_embeddings(-7, 2, R)[0]5704True5705sage: f == H.optimal_embeddings(-7, 2, R)[1]5706False5707sage: f == 05708False5709"""5710return isinstance(other, HeegnerQuatAlgEmbedding) and \5711self.__D == other.__D and \5712self.__c == other.__c and \5713self.__R == other.__R and \5714self.__beta == other.__beta57155716def __call__(self, x):5717"""5718Return image of `x` under this embedding.57195720INPUT:57215722- `x` -- element of the quadratic order57235724EXAMPLES::57255726sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5727sage: f = H.optimal_embeddings(-7, 1, R)[0]; f5728Embedding sending sqrt(-7) to -i + j + k5729sage: a = f.domain_gen(); a^25730-75731sage: f(2 + 3*a)57322 - 3*i + 3*j + 3*k5733sage: 2 + 3*f(a)57342 - 3*i + 3*j + 3*k5735sage: f(a)^25736-75737"""5738v = self.domain().number_field()(x).vector()5739w = v * self.matrix()5740z = self.codomain().quaternion_algebra()(w)5741# There is no notion of an "element of an order" implemented5742# for quaternion algebras right now. All elements are5743# elements of the ambient rational quaternion algebra.5744return z57455746@cached_method5747def matrix(self):5748r"""5749Return matrix over `\QQ` of this morphism, with respect to the5750basis 1, `c\sqrt{D}` of the domain and the basis `1,i,j,k` of5751the ambient rational quaternion algebra (which contains the5752domain).57535754EXAMPLES::57555756sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5757sage: f = H.optimal_embeddings(-7, 1, R)[0]; f5758Embedding sending sqrt(-7) to -i + j + k5759sage: f.matrix()5760[ 1 0 0 0]5761[ 0 -1 1 1]5762sage: f.conjugate().matrix()5763[ 1 0 0 0]5764[ 0 1 -1 -1]5765"""5766return matrix(QQ,2,4,[[1,0,0,0], self.__beta.coefficient_tuple()])57675768@cached_method5769def domain(self):5770"""5771Return the domain of this embedding.57725773EXAMPLES::57745775sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5776sage: H.optimal_embeddings(-7, 2, R)[0].domain()5777Order in Number Field in a with defining polynomial x^2 + 75778"""5779R, a = quadratic_order(self.__D, self.__c)57805781# The following assumption is used, e.g., in the __call__5782# method. I know that it is satisfied by the current5783# implementation. But somebody might someday annoying change5784# the implementation, and we want to catch that if it were to5785# ever happen.57865787assert R.basis() == [1, a], "an assumption about construction of orders is violated"5788self.__domain_gen = a5789return R57905791def domain_gen(self):5792r"""5793Return the specific generator `c \sqrt{D}` for the domain5794order.57955796EXAMPLES::57975798sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5799sage: f = H.optimal_embeddings(-7, 2, R)[0]5800sage: f.domain_gen()58012*a5802sage: f.domain_gen()^25803-285804"""5805self.domain()5806return self.__domain_gen58075808def domain_conductor(self):5809"""5810Return the conductor of the domain.58115812EXAMPLES::58135814sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5815sage: H.optimal_embeddings(-7, 2, R)[0].domain_conductor()581625817"""5818return self.__c58195820def beta(self):5821r"""5822Return the element `\beta` in the quaternion algebra order5823that `c\sqrt{D}` maps to.58245825EXAMPLES::58265827sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5828sage: H.optimal_embeddings(-7, 2, R)[0].beta()5829-5*i + k5830"""5831return self.__beta58325833def codomain(self):5834"""5835Return the codomain of this embedding.58365837EXAMPLES::58385839sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5840sage: H.optimal_embeddings(-7, 2, R)[0].codomain()5841Order of Quaternion Algebra (-1, -3) with base ring Rational Field with basis (1/2 + 1/2*j + 7*k, 1/2*i + 13/2*k, j + 3*k, 11*k)5842"""5843return self.__R58445845@cached_method5846def _repr_(self):5847"""5848Return string representation of this embedding.58495850EXAMPLES::58515852sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5853sage: f = H.optimal_embeddings(-7, 2, R)[0]; f._repr_()5854'Embedding sending 2*sqrt(-7) to -5*i + k'5855"""5856a = '%ssqrt(%s)'%('%s*'%self.__c if self.__c > 1 else '', self.__D)5857return "Embedding sending %s to %s"%(a, self.__beta)58585859def conjugate(self):5860"""5861Return the conjugate of this embedding, which is also an5862embedding.58635864EXAMPLES::58655866sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5867sage: f = H.optimal_embeddings(-7, 2, R)[0]5868sage: f.conjugate()5869Embedding sending 2*sqrt(-7) to 5*i - k5870sage: f5871Embedding sending 2*sqrt(-7) to -5*i + k5872"""5873return HeegnerQuatAlgEmbedding(self.__D, self.__c,5874self.__R, self.__beta.conjugate())587558765877#############################################################################5878# Utility Functions5879#############################################################################58805881def quadratic_order(D, c, names='a'):5882"""5883Return order of conductor `c` in quadratic field with fundamental5884discriminant `D`.58855886INPUT:58875888- `D` -- fundamental discriminant58895890- `c` -- conductor58915892- ``names`` -- string (default: 'a')58935894OUTPUT:58955896- order `R` of conductor `c` in an imaginary quadratic field58975898- the element `c\sqrt{D}` as an element of `R`58995900The generator for the field is named 'a' by default.59015902EXAMPLES::59035904sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3)5905(Order in Number Field in a with defining polynomial x^2 + 7, 3*a)5906sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3,'alpha')5907(Order in Number Field in alpha with defining polynomial x^2 + 7, 3*alpha)5908"""5909K = QuadraticField(D, names)5910sqrtD = K.gen(0)5911t = sqrtD * c5912R = K.order([t])5913return R, R(t)59145915def class_number(D):5916"""5917Return the class number of the quadratic field with fundamental5918discriminant `D`.59195920INPUT:59215922- `D` -- integer59235924EXAMPLES::59255926sage: sage.schemes.elliptic_curves.heegner.class_number(-20)592725928sage: sage.schemes.elliptic_curves.heegner.class_number(-23)592935930sage: sage.schemes.elliptic_curves.heegner.class_number(-163)5931159325933A ValueError is raised when `D` is not a fundamental5934discriminant::59355936sage: sage.schemes.elliptic_curves.heegner.class_number(-5)5937Traceback (most recent call last):5938...5939ValueError: D (=-5) must be a fundamental discriminant5940"""5941if not number_field.is_fundamental_discriminant(D):5942raise ValueError("D (=%s) must be a fundamental discriminant"%D)5943return QuadraticField(D,'a').class_number()59445945def is_inert(D, p):5946r"""5947Return ``True`` if p is an inert prime in the field `\QQ(\sqrt{D})`.59485949INPUT:59505951- `D` -- fundamental discriminant59525953- `p` -- prime integer59545955EXAMPLES::59565957sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,3)5958True5959sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,7)5960False5961sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,11)5962False5963"""5964K = QuadraticField(D,'a')5965F = K.factor(p)5966return len(F) == 1 and F[0][1] == 159675968def is_split(D, p):5969r"""5970Return ``True`` if p is a split prime in the field `\QQ(\sqrt{D})`.59715972INPUT:59735974- `D` -- fundamental discriminant59755976- `p` -- prime integer59775978EXAMPLES::59795980sage: sage.schemes.elliptic_curves.heegner.is_split(-7,3)5981False5982sage: sage.schemes.elliptic_curves.heegner.is_split(-7,7)5983False5984sage: sage.schemes.elliptic_curves.heegner.is_split(-7,11)5985True5986"""5987K = QuadraticField(D,'a')5988F = K.factor(p)5989return len(F) == 259905991def is_ramified(D, p):5992r"""5993Return ``True`` if p is a ramified prime in the field `\QQ(\sqrt{D})`.59945995INPUT:59965997- `D` -- fundamental discriminant59985999- `p` -- prime integer60006001EXAMPLES::60026003sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,2)6004False6005sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,7)6006True6007sage: sage.schemes.elliptic_curves.heegner.is_ramified(-1,2)6008True6009"""6010return QuadraticField(D,'a').discriminant() % p == 060116012def nearby_rational_poly(f, **kwds):6013r"""6014Return a polynomial whose coefficients are rational numbers close6015to the coefficients of `f`.60166017INPUT:60186019- `f` -- polynomial with real floating point entries60206021- ``**kwds`` -- passed on to ``nearby_rational`` method60226023EXAMPLES::60246025sage: R.<x> = RR[]6026sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-16)602721/10*X^2 + 7/2*X - 6/56028sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-17)60294728779608739021/2251799813685248*X^2 + 7/2*X - 5404319552844595/45035996273704966030sage: RR(4728779608739021/2251799813685248 - 21/10)60318.88178419700125e-176032"""6033R = QQ['X']6034return R([a.nearby_rational(**kwds) for a in f])60356036def simplest_rational_poly(f, prec):6037"""6038Return a polynomial whose coefficients are as simple as possible6039rationals that are also close to the coefficients of f.60406041INPUT:60426043- `f` -- polynomial with real floating point entries60446045- ``prec`` -- positive integer60466047EXAMPLES::60486049sage: R.<x> = RR[]6050sage: sage.schemes.elliptic_curves.heegner.simplest_rational_poly(2.1*x^2 + 3.5*x - 1.2, 53)605121/10*X^2 + 7/2*X - 6/56052"""6053R = QQ['X']6054Z = RealField(prec)6055return R([Z(a).simplest_rational() for a in f])60566057def satisfies_weak_heegner_hypothesis(N, D):6058r"""6059Check that `D` satisfies the weak Heegner hypothesis relative to `N`.6060This is all that is needed to define Heegner points.60616062The condition is that `D<0` is a fundamental discriminant and that6063each unramified prime dividing `N` splits in `K=\QQ(\sqrt{D})` and6064each ramified prime exactly divides `N`. We also do not require6065that `D<-4`.60666067INPUT:60686069- `N` -- positive integer60706071- `D` -- negative integer60726073EXAMPLES::60746075sage: s = sage.schemes.elliptic_curves.heegner.satisfies_weak_heegner_hypothesis6076sage: s(37,-7)6077True6078sage: s(37,-37)6079False6080sage: s(37,-37*4)6081True6082sage: s(100,-4)6083False6084sage: [D for D in [-1,-2,..,-40] if s(37,D)]6085[-3, -4, -7, -11, -40]6086sage: [D for D in [-1,-2,..,-100] if s(37,D)]6087[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]6088sage: EllipticCurve('37a').heegner_discriminants_list(10)6089[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]6090"""6091if not number_field.is_fundamental_discriminant(D):6092return False6093if D >= 0: return False6094for p, e in N.factor():6095if D % p == 0:6096if e > 1:6097return False6098elif D.kronecker(p) != 1:6099return False6100return True61016102def make_monic(f):6103r"""6104``make_monic`` returns a monic integral polynomial `g` and an6105integer `d` such that if `\alpha` is a root of `g` then a root of6106`f` is `\alpha/d`.61076108INPUT:61096110- f -- polynomial over the rational numbers61116112EXAMPLES::61136114sage: R.<x> = QQ[]6115sage: sage.schemes.elliptic_curves.heegner.make_monic(3*x^3 + 14*x^2 - 7*x + 5)6116(x^3 + 14*x^2 - 21*x + 45, 3)61176118In this example we verify that make_monic does what we claim it does::61196120sage: K.<a> = NumberField(x^3 + 17*x - 3)6121sage: f = (a/7+2/3).minpoly(); f6122x^3 - 2*x^2 + 247/147*x - 4967/92616123sage: g, d = sage.schemes.elliptic_curves.heegner.make_monic(f)6124sage: g6125x^3 - 18522*x^2 + 144110421*x - 4260003230076126sage: d612792616128sage: K.<b> = NumberField(g)6129sage: (b/d).minpoly()6130x^3 - 2*x^2 + 247/147*x - 4967/92616131"""6132# make f monic6133n = f.degree()6134f = f / f.leading_coefficient()6135# find lcm of denominators6136d = arith.lcm([b.denominator() for b in f.list() if b])6137x = f.variables()[0]6138g = (d**n) * f(x/d)6139return g, d614061416142#####################################################################6143# Elliptic curve methods6144# Everywhere self below is an elliptic curve over QQ.6145#####################################################################61466147def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True):6148"""6149Returns the Heegner point on this curve associated to the6150quadratic imaginary field `K=\QQ(\sqrt{D})`.61516152If the optional parameter `c` is given, returns the higher Heegner6153point associated to the order of conductor `c`.61546155INPUT::61566157- `D` -- a Heegner discriminant61586159- `c` -- (default: 1) conductor, must be coprime to `DN`61606161- `f` -- binary quadratic form or 3-tuple `(A,B,C)` of coefficients6162of `AX^2 + BXY + CY^2`61636164- ``check`` -- bool (default: ``True``)616561666167OUTPUT::61686169The Heegner point `y_c`.617061716172EXAMPLES::61736174sage: E = EllipticCurve('37a')6175sage: E.heegner_discriminants_list(10)6176[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]6177sage: P = E.heegner_point(-7); P # indirect doctest6178Heegner point of discriminant -7 on elliptic curve of conductor 376179sage: P.point_exact()6180(0 : 0 : 1)6181sage: P.curve()6182Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field6183sage: P = E.heegner_point(-40).point_exact(); P6184(a : -a + 1 : 1)6185sage: P = E.heegner_point(-47).point_exact(); P6186(a : a^4 + a - 1 : 1)6187sage: P[0].parent()6188Number Field in a with defining polynomial x^5 - x^4 + x^3 + x^2 - 2*x + 161896190Working out the details manually::61916192sage: P = E.heegner_point(-47).numerical_approx(prec=200)6193sage: f = algdep(P[0], 5); f6194x^5 - x^4 + x^3 + x^2 - 2*x + 16195sage: f.discriminant().factor()619647^261976198The Heegner hypothesis is checked::61996200sage: E = EllipticCurve('389a'); P = E.heegner_point(-5,7);6201Traceback (most recent call last):6202...6203ValueError: N (=389) and D (=-5) must satisfy the Heegner hypothesis62046205We can specify the quadratic form::62066207sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P6208Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3896209sage: P.quadratic_form()6210778*x^2 + 925*x*y + 275*y^26211"""6212y = HeegnerPointOnX0N(self.conductor(), D, c, f, check=check)6213return y.map_to_curve(self)62146215def kolyvagin_point(self, D, c=ZZ(1), check=True):6216"""6217Returns the Kolyvagin point on this curve associated to the6218quadratic imaginary field `K=\QQ(\sqrt{D})` and conductor `c`.62196220INPUT:62216222- `D` -- a Heegner discriminant62236224- `c` -- (default: 1) conductor, must be coprime to `DN`62256226- ``check`` -- bool (default: ``True``)622762286229OUTPUT:62306231The Kolyvagin point `P` of conductor `c`.62326233EXAMPLES::62346235sage: E = EllipticCurve('37a1')6236sage: P = E.kolyvagin_point(-67); P6237Kolyvagin point of discriminant -67 on elliptic curve of conductor 376238sage: P.numerical_approx() # imaginary parts approx. 06239(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)6240sage: P.index()624166242sage: g = E((0,-1,1)) # a generator6243sage: E.regulator() == E.regulator_of_points([g])6244True6245sage: 6*g6246(6 : -15 : 1)62476248"""6249return self.heegner_point(D,c,check=check).kolyvagin_point()62506251def ell_heegner_discriminants(self, bound):6252"""6253Return the list of self's Heegner discriminants between -1 and6254-bound.62556256INPUT:625762586259- ``bound (int)`` - upper bound for -discriminant626062616262OUTPUT: The list of Heegner discriminants between -1 and -bound for6263the given elliptic curve.62646265EXAMPLES::62666267sage: E=EllipticCurve('11a')6268sage: E.heegner_discriminants(30) # indirect doctest6269[-7, -8, -19, -24]6270"""6271return [-D for D in xrange(1,bound) if self.satisfies_heegner_hypothesis(-D)]62726273def ell_heegner_discriminants_list(self, n):6274"""6275Return the list of self's first `n` Heegner discriminants smaller6276than -5.62776278INPUT:62796280- ``n (int)`` - the number of discriminants to6281compute628262836284OUTPUT: The list of the first n Heegner discriminants smaller than6285-5 for the given elliptic curve.62866287EXAMPLE::62886289sage: E=EllipticCurve('11a')6290sage: E.heegner_discriminants_list(4) # indirect doctest6291[-7, -8, -19, -24]6292"""6293v = []6294D = -56295while len(v) < n:6296while not self.satisfies_heegner_hypothesis(D):6297D -= 16298v.append(D)6299D -= 16300return v63016302def heegner_point_height(self, D, prec=2, check_rank=True):6303r"""6304Use the Gross-Zagier formula to compute the Neron-Tate canonical6305height over `K` of the Heegner point corresponding to `D`, as an6306interval (it is computed to some precision using `L`-functions).63076308If the curve has rank at least 2, then the returned height is the6309exact Sage integer 0.63106311INPUT:631263136314- ``D (int)`` - fundamental discriminant (=/= -3, -4)63156316- ``prec (int)`` - (default: 2), use `prec \cdot \sqrt(N) + 20`6317terms of `L`-series in computations, where `N` is the6318conductor.63196320- ``check_rank`` - whether to check if the rank is at least 2 by6321computing the Mordell-Weil rank directly.632263236324OUTPUT: Interval that contains the height of the Heegner point.63256326EXAMPLE::63276328sage: E = EllipticCurve('11a')6329sage: E.heegner_point_height(-7)63300.22227?63316332Some higher rank examples::63336334sage: E = EllipticCurve('389a')6335sage: E.heegner_point_height(-7)633606337sage: E = EllipticCurve('5077a')6338sage: E.heegner_point_height(-7)633906340sage: E.heegner_point_height(-7,check_rank=False)63410.0000?6342"""63436344if not self.satisfies_heegner_hypothesis(D):6345raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)63466347if check_rank and self.rank() >= 2:6348return ZZ(0)63496350if D == -3 or D == -4:6351raise ArithmeticError("Discriminant (=%s) must not be -3 or -4."%D)6352eps = self.root_number()6353L1_vanishes = self.lseries().L1_vanishes()63546355IR = rings.RealIntervalField(20) # TODO: why 20 bits here?63566357if eps == 1 and L1_vanishes:6358return IR(0) # rank even hence >= 2, so Heegner point is torsion.63596360RR = rings.RealField()6361from math import sqrt63626363alpha = RR(sqrt(abs(D)))/(2*self.period_lattice().complex_area())6364F = self.quadratic_twist(D)6365E = self6366k_E = prec*sqrt(E.conductor()) + 206367k_F = prec*sqrt(F.conductor()) + 2063686369MIN_ERR = RR('1e-6') # we assume that regulator and6370# discriminant, etc., computed to this accuracy (which is easily the case).6371# this should be made more intelligent / rigorous relative6372# to the rest of the system.63736374if eps == 1: # E has even rank6375LF1, err_F = F.lseries().deriv_at1(k_F)6376LE1, err_E = E.lseries().at1(k_E)6377err_F = max(err_F, MIN_ERR)6378err_E = max(err_E, MIN_ERR)6379return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)63806381else: # E has odd rank6382LE1, err_E = E.lseries().deriv_at1(k_E)6383LF1, err_F = F.lseries().at1(k_F)6384err_F = max(err_F, MIN_ERR)6385err_E = max(err_E, MIN_ERR)6386return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)638763886389def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, verbose_mwrank=False, check_rank=True):6390r"""6391Return an interval that contains the index of the Heegner6392point `y_K` in the group of `K`-rational points modulo torsion6393on this elliptic curve, computed using the Gross-Zagier6394formula and/or a point search, or possibly half the index6395if the rank is greater than one.63966397If the curve has rank > 1, then the returned index is infinity.63986399.. NOTE::64006401If ``min_p`` is bigger than 2 then the index can be off by6402any prime less than ``min_p``. This function returns the6403index divided by `2` exactly when the rank of `E(K)` is6404greater than 1 and `E(\QQ)_{/tor} \oplus E^D(\QQ)_{/tor}`6405has index `2` in `E(K)_{/tor}`, where the second factor6406undergoes a twist.64076408INPUT:64096410- ``D (int)`` - Heegner discriminant64116412- ``min_p (int)`` - (default: 2) only rule out primes6413= min_p dividing the index.64146415- ``verbose_mwrank (bool)`` - (default: ``False``); print lots of6416mwrank search status information when computing regulator64176418- ``prec (int)`` - (default: 5), use prec\*sqrt(N) +641920 terms of L-series in computations, where N is the conductor.64206421- ``descent_second_limit`` - (default: 12)- used in 2-descent6422when computing regulator of the twist64236424- ``check_rank`` - whether to check if the rank is at least 2 by6425computing the Mordell-Weil rank directly.642664276428OUTPUT: an interval that contains the index, or half the index64296430EXAMPLES::64316432sage: E = EllipticCurve('11a')6433sage: E.heegner_discriminants(50)6434[-7, -8, -19, -24, -35, -39, -40, -43]6435sage: E.heegner_index(-7)64361.00000?64376438::64396440sage: E = EllipticCurve('37b')6441sage: E.heegner_discriminants(100)6442[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]6443sage: E.heegner_index(-95) # long time (1 second)64442.00000?64456446This tests doing direct computation of the Mordell-Weil group.64476448::64496450sage: EllipticCurve('675b').heegner_index(-11)64513.0000?64526453Currently discriminants -3 and -4 are not supported::64546455sage: E.heegner_index(-3)6456Traceback (most recent call last):6457...6458ArithmeticError: Discriminant (=-3) must not be -3 or -4.64596460The curve 681b returns the true index, which is `3`::64616462sage: E = EllipticCurve('681b')6463sage: I = E.heegner_index(-8); I64643.0000?64656466In fact, whenever the returned index has a denominator of6467`2`, the true index is got by multiplying the returned6468index by `2`. Unfortunately, this is not an if and only if6469condition, i.e., sometimes the index must be multiplied by6470`2` even though the denominator is not `2`.64716472This example demonstrates the ``descent_second_limit`` option,6473which can be used to fine tune the 2-descent used to compute6474the regulator of the twist::64756476sage: E = EllipticCurve([0, 0, 1, -34874, -2506691])6477sage: E.heegner_index(-8)6478Traceback (most recent call last):6479...6480RuntimeError: ...64816482However when we search higher, we find the points we need::64836484sage: E.heegner_index(-8, descent_second_limit=16, check_rank=False)64851.00000?648664876488Two higher rank examples (of ranks 2 and 3)::64896490sage: E = EllipticCurve('389a')6491sage: E.heegner_index(-7)6492+Infinity6493sage: E = EllipticCurve('5077a')6494sage: E.heegner_index(-7)6495+Infinity6496sage: E.heegner_index(-7, check_rank=False)64970.001?6498sage: E.heegner_index(-7, check_rank=False).lower() == 06499True6500"""6501if not self.satisfies_heegner_hypothesis(D):6502raise ArithmeticError("Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D)65036504if check_rank and self.rank() >= 2:6505return rings.infinity65066507# First compute upper bound on height of Heegner point.6508tm = verbose("computing heegner point height...")6509h0 = self.heegner_point_height(D, prec=prec, check_rank=check_rank)6510if h0 == 0:6511return rings.infinity65126513# We divide by 2 to get the height **over Q** of the6514# Heegner point on the twist.65156516ht = h0/26517verbose('Height of heegner point = %s'%ht, tm)65186519if self.root_number() == 1:6520F = self.quadratic_twist(D)6521else:6522F = self6523# Now rank(F) > 06524h = ht.upper()6525verbose("Heegner height bound = %s"%h)6526B = F.CPS_height_bound()6527verbose("CPS bound = %s"%B)6528c = h/(min_p**2) + B6529verbose("Search would have to be up to height = %s"%c)65306531from ell_rational_field import _MAX_HEIGHT65326533IR = rings.RealIntervalField(20) # todo: 20?65346535a = 16536if c > _MAX_HEIGHT or F is self:6537verbose("Doing direct computation of MW group.")6538reg = F.regulator(descent_second_limit=descent_second_limit, verbose=verbose_mwrank)6539if F.rank(use_database=True) == 1:6540z = F.gens()[0]6541FK = F.base_extend(QuadraticField(D,'a'))6542z = FK(z)6543if z.is_divisible_by(2):6544a = 26545else:6546FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]6547if len(FK_even_tor_pts) == 2:6548FK_even_tor_pts.append(sum(FK_even_tor_pts))6549for T in FK_even_tor_pts:6550if (z + T).is_divisible_by(2):6551a = 2; break6552return a*self._adjust_heegner_index(ht/IR(reg))65536554# Do naive search to eliminate possibility that Heegner point6555# is divisible by p<min_p, without finding Heegner point.6556verbose("doing point search")6557P = F.point_search(c)6558verbose("done with point search")6559P = [x for x in P if x.order() == rings.infinity]6560a = 16561if len(P) == 0:6562return IR(1)6563elif len(P) == 1:6564z = P[0]6565FK = F.base_extend(QuadraticField(D,'a'))6566z = FK(z)6567if z.is_divisible_by(2):6568a = 26569else:6570FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]6571if len(FK_even_tor_pts) == 2:6572FK_even_tor_pts.append(sum(FK_even_tor_pts))6573for T in FK_even_tor_pts:6574if (z + T).is_divisible_by(2):6575a = 2; break65766577verbose("saturating")6578S, I, reg = F.saturation(P)6579verbose("done saturating")6580return a*self._adjust_heegner_index(ht/IR(reg))65816582def _adjust_heegner_index(self, a):6583r"""6584Take the square root of the interval that contains the Heegner6585index.65866587EXAMPLES::65886589sage: E = EllipticCurve('11a1')6590sage: a = RIF(sqrt(2))-1.41421356237309516591sage: E._adjust_heegner_index(a)65921.?e-86593"""6594if a.lower() < 0:6595IR = rings.RealIntervalField(20) # todo: 20?6596a = IR((0, a.upper()))6597return a.sqrt()659865996600def heegner_index_bound(self, D=0, prec=5, max_height=None):6601r"""6602Assume ``self`` has rank 0.66036604Return a list `v` of primes such that if an odd prime `p` divides6605the index of the Heegner point in the group of rational points6606modulo torsion, then `p` is in `v`.66076608If 0 is in the interval of the height of the Heegner point6609computed to the given prec, then this function returns `v =66100`. This does not mean that the Heegner point is torsion, just6611that it is very likely torsion.66126613If we obtain no information from a search up to ``max_height``,6614e.g., if the Siksek et al. bound is bigger than ``max_height``,6615then we return `v = -1`.66166617INPUT:661866196620- ``D (int)`` - (default: 0) Heegner discriminant; if66210, use the first discriminant -4 that satisfies the Heegner6622hypothesis66236624- ``verbose (bool)`` - (default: ``True``)66256626- ``prec (int)`` - (default: 5), use `prec \cdot \sqrt(N) + 20`6627terms of `L`-series in computations, where `N` is the conductor.66286629- ``max_height (float)`` - should be = 21; bound on6630logarithmic naive height used in point searches. Make smaller to6631make this function faster, at the expense of possibly obtaining a6632worse answer. A good range is between 13 and 21.663366346635OUTPUT:663666376638- ``v`` - list or int (bad primes or 0 or -1)66396640- ``D`` - the discriminant that was used (this is6641useful if `D` was automatically selected).66426643- ``exact`` - either False, or the exact Heegner index6644(up to factors of 2)66456646EXAMPLES::66476648sage: E = EllipticCurve('11a1')6649sage: E.heegner_index_bound()6650([2], -7, 2)6651"""6652from ell_rational_field import _MAX_HEIGHT6653if max_height is None:6654max_height = _MAX_HEIGHT6655else:6656max_height = min(float(max_height), _MAX_HEIGHT)6657if self.root_number() != 1:6658raise RuntimeError("The rank must be 0.")66596660if D == 0:6661D = -56662while not self.satisfies_heegner_hypothesis(D):6663D -= 166646665# First compute upper bound on Height of Heegner point.6666ht = self.heegner_point_height(D, prec=prec)6667if 0 in ht:6668return 0, D, False6669F = self.quadratic_twist(D)6670h = ht.upper()6671verbose("Heegner height bound = %s"%h)6672B = F.CPS_height_bound()6673verbose("CPS bound = %s"%B)6674if self.two_torsion_rank() == 0:6675H = h6676else:6677H = 4*h6678p = 36679from sage.all import next_prime6680while True:6681c = H/(2*p**2) + B6682if c < max_height:6683break6684if p > 100:6685break6686p = next_prime(p)6687verbose("Using p = %s"%p)66886689if c > max_height:6690verbose("No information by searching only up to max_height (=%s)."%c)6691return -1, D, False66926693verbose("Searching up to height = %s"%c)6694eps = 10e-566956696def _bound(P):6697"""6698We will use this function below in two places. It bounds the index6699using a nontrivial point.6700"""6701assert len(P) == 167026703S, I, reg = F.saturation(P)67046705IR = rings.RealIntervalField(20) # todo: 20?6706h = IR(reg-eps,reg+eps)6707ind2 = ht/(h/2)6708verbose("index squared = %s"%ind2)6709ind = ind2.sqrt()6710verbose("index = %s"%ind)6711# Compute upper bound on square root of index.6712if ind.absolute_diameter() < 1:6713t, i = ind.is_int()6714if t: # unique integer in interval, so we've found exact index squared.6715return arith.prime_divisors(i), D, i6716raise RuntimeError("Unable to compute bound for e=%s, D=%s (try increasing precision)"%(self, D))67176718# First try a quick search, in case we get lucky and find6719# a generator.6720P = F.point_search(13, rank_bound=1)6721P = [x for x in P if x.order() == rings.infinity]6722if len(P) > 0:6723return _bound(P)67246725# Do search to eliminate possibility that Heegner point is6726# divisible by primes up to p, without finding Heegner point.6727P = F.point_search(c, rank_bound=1)6728P = [x for x in P if x.order() == rings.infinity]6729if len(P) == 0:6730# We've eliminated the possibility of a divisor up to p.6731return rings.prime_range(3, p), D, False6732else:6733return _bound(P)673467356736#################################################################################6737def _heegner_index_in_EK(self, D):6738"""6739Return the index of the sum of `E(\QQ)/tor + E^D(\QQ)/tor` in `E(K)/tor`.67406741INPUT:6742- `D` -- negative integer; the Heegner discriminant67436744OUTPUT:6745a power of 2 -- the given index67466747EXAMPLES:6748We compute the index for a rank 2 curve and found that it is 2::67496750sage: E = EllipticCurve('389a')6751sage: E._heegner_index_in_EK(-7)6752267536754We explicitly verify in the above example that indeed that6755index is divisibly by 2 by writing down a generator of6756`E(\QQ)/tor + E^D(\QQ)/tor` that is divisible by 2 in `E(K)`::67576758sage: F = E.quadratic_twist(-7)6759sage: K = QuadraticField(-7,'a')6760sage: G = E.change_ring(K)6761sage: phi = F.change_ring(K).isomorphism_to(G)6762sage: P = G(E(-1,1)) + G((0,-1)) + G(phi(F(14,25))); P6763(-867/3872*a - 3615/3872 : -18003/170368*a - 374575/170368 : 1)6764sage: P.division_points(2)6765[(1/8*a + 5/8 : -5/16*a - 9/16 : 1)]67666767"""6768# check conditions, then use cache if possible.6769if not self.satisfies_heegner_hypothesis(D):6770raise ValueError("D (=%s) must satisfy the Heegner hypothesis"%D)6771try:6772return self.__heegner_index_in_EK[D]6773except AttributeError:6774self.__heegner_index_in_EK = {}6775except KeyError:6776pass67776778#####################################################################6779# THE ALGORITHM:6780#6781# For an element P of an abelian group A, let [P] denote the6782# equivalence class of P in the quotient A/A_tor of A by6783# its torsion subgroup. Then for P in E(Q) + E^D(QQ), we6784# have that [P] is divisible by 2 in E(K)/tor if and only6785# there is R in E(K) such that 2*[R] = [P], and this is6786# only if there is R in E(K) and t in E(K)_tor such that6787# 2*R = P + t.6788#6789# Using complex conjugation, one sees that the quotient6790# group E(K)/tor / ( E(Q)/tor + E^D(Q)/tor ) is killed by 2.6791# So to compute the order of this group we run through6792# representatives P for A/(2A) where A = E(Q)/tor + E^D(Q)/tor,6793# and for each we see whether there is a torsion point t in E(K)6794# such that P + t is divisible by 2. Also, we have6795# 2 | P+t <==> 2 | P+n*t for any odd integer n,6796# so we may assume t is of 2-power order.6797#####################################################################67986799E = self # nice shortcut6800F = E.quadratic_twist(D).minimal_model()6801K = rings.QuadraticField(D, 'a')68026803# Define a map phi that we'll use to put the points of E^D(QQ)6804# into E(K):6805G = E.change_ring(K)6806G2 = F.change_ring(K)6807phi = G2.isomorphism_to(G)68086809# Basis for E(Q)/tor oplus E^D(QQ)/tor in E(K):6810basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]6811# Make a list of the 2-power order torsion points in E(K), including 0.6812T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or6813((z.order() % 2 == 0 and len(z.order().factor()) == 1))]68146815r = len(basis) # rank6816V = rings.QQ**r6817B = []68186819# Iterate through reps for A/(2*A) creating vectors in (1/2)*ZZ^r6820for v in rings.GF(2)**r:6821if not v: continue6822P = sum([basis[i] for i in range(r) if v[i]])6823for t in T:6824if (P+t).is_divisible_by(2):6825B.append(V(v)/2)68266827A = rings.ZZ**r6828# Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r. This is E(K)/tor.6829W = V.span(B, rings.ZZ) + A68306831# Compute the index in E(K)/tor of A = E(Q)/tor + E^D(Q)/tor, cache, and return.6832index = A.index_in(W)6833self.__heegner_index_in_EK[D] = index6834return index68356836def heegner_sha_an(self, D, prec=53):6837r"""6838Return the conjectural (analytic) order of Sha for E over the field `K=\QQ(\sqrt{D})`.68396840INPUT:68416842- `D` -- negative integer; the Heegner discriminant68436844- prec -- integer (default: 53); bits of precision to6845compute analytic order of Sha68466847OUTPUT:68486849(floating point number) an approximation to the conjectural order of Sha.68506851.. NOTE::68526853Often you'll want to do ``proof.elliptic_curve(False)`` when6854using this function, since often the twisted elliptic6855curves that come up have enormous conductor, and Sha is6856nontrivial, which makes provably finding the Mordell-Weil6857group using 2-descent difficult.685868596860EXAMPLES:68616862An example where E has conductor 11::68636864sage: E = EllipticCurve('11a')6865sage: E.heegner_sha_an(-7) # long time68661.0000000000000068676868The cache works::68696870sage: E.heegner_sha_an(-7) is E.heegner_sha_an(-7) # long time6871True68726873Lower precision::68746875sage: E.heegner_sha_an(-7,10) # long time68761.068776878Checking that the cache works for any precision::68796880sage: E.heegner_sha_an(-7,10) is E.heegner_sha_an(-7,10) # long time6881True68826883Next we consider a rank 1 curve with nontrivial Sha over the6884quadratic imaginary field `K`; however, there is no Sha for `E`6885over `\QQ` or for the quadratic twist of `E`::68866887sage: E = EllipticCurve('37a')6888sage: E.heegner_sha_an(-40) # long time68894.000000000000006890sage: E.quadratic_twist(-40).sha().an() # long time689116892sage: E.sha().an() # long time6893168946895A rank 2 curve::68966897sage: E = EllipticCurve('389a') # long time6898sage: E.heegner_sha_an(-7) # long time68991.0000000000000069006901If we remove the hypothesis that `E(K)` has rank 1 in Conjecture69022.3 in [Gross-Zagier, 1986, page 311], then that conjecture is6903false, as the following example shows::69046905sage: E = EllipticCurve('65a') # long time6906sage: E.heegner_sha_an(-56) # long time69071.000000000000006908sage: E.torsion_order() # long time690926910sage: E.tamagawa_product() # long time691116912sage: E.quadratic_twist(-56).rank() # long time691326914"""6915# check conditions, then return from cache if possible.6916if not self.satisfies_heegner_hypothesis(D):6917raise ValueError("D (=%s) must satisfy the Heegner hypothesis"%D)6918try:6919return self.__heegner_sha_an[(D, prec)]6920except AttributeError:6921self.__heegner_sha_an = {}6922except KeyError:6923pass69246925# Use the BSD conjecture over the quadratic imaginary K --6926# see page 311 of [Gross-Zagier, 1986] for the formula.6927E = self # notational convenience6928F = E.quadratic_twist(D).minimal_model()6929K = rings.QuadraticField(D, 'a')69306931# Compute each of the quantities in BSD6932# - The torsion subgroup over K.6933T = E.change_ring(K).torsion_order()69346935# - The product of the Tamagawa numbers, which because D is6936# coprime to N is just the square of the product of the6937# Tamagawa numbers over QQ for E. (we square below in the6938# BSD formula)6939cqprod = E.tamagawa_product()69406941# - The leading term of the L-series, as a product of two6942# other L-series.6943rE = E.rank()6944rF = F.rank()6945L_E = E.lseries().dokchitser(prec).derivative(1, rE)6946L_F = F.lseries().dokchitser(prec).derivative(1, rF)6947# NOTE: The binomial coefficient in the following formula6948# for the leading term in terms of the other two leading6949# terms comes from the product rule for the derivative.6950# You can think this through or just type something like6951# f = function('f',x); g = function('g',x); diff(f*g,6)6952# into Sage to be convinced.6953L = rings.binomial(rE + rF, rE) * (L_E * L_F / (rings.factorial(rE+rF)) )69546955# - ||omega||^2 -- the period. It is twice the volume of the6956# period lattice. See the following paper for a derivation:6957# "Verification of the Birch and Swinnerton-Dyer Conjecture6958# for Specific Elliptic Curves", G. Grigorov, A. Jorza, S. Patrikis,6959# C. Patrascu, W. Stein6960omega = 2 * abs(E.period_lattice().basis_matrix().det())69616962# - The regulator.6963# First we compute the regualtor of the subgroup E(QQ) + E^D(QQ)6964# of E(K). The factor of 2 in the regulator6965# accounts for the fact that the height over K is twice the6966# height over QQ, i.e., for P in E(QQ) we have h_K(P,P) =6967# 2*h_Q(P,P). See, e.g., equation (6.4) on page 230 of6968# [Gross-Zagier, 1986].6969Reg_prod = 2**(rE + rF) * E.regulator(precision=prec) * F.regulator(precision=prec)6970# Next we call off to the _heegner_index_in_EK function, which6971# saturates the group E(QQ) + E^D(QQ) in E(K), given us the index,6972# which must be a power of 2, since E(QQ) is the +1 eigenspace for6973# complex conjugation, and E^D(QQ) is the -1 eigenspace.6974ind = self._heegner_index_in_EK(D)6975# Finally, we know the regulator of E(K).6976Reg = Reg_prod / ind**269776978# - Square root of the absolute value of the discriminant. This is6979# easy; we just make sure the D passed in is an integer, so we6980# can call sqrt with the chosen precision.6981sqrtD = ZZ(abs(D)).sqrt(prec=prec)69826983# - Done: Finally, we plug everything into the BSD formula to get the6984# analytic order of Sha.6985sha_an = (L * T**2 * sqrtD) / (omega * Reg * cqprod**2)69866987# - We cache and return the answer.6988self.__heegner_sha_an[(D, prec)] = sha_an6989return sha_an69906991def _heegner_forms_list(self, D, beta=None, expected_count=None):6992"""6993Returns a list of quadratic forms corresponding to Heegner points6994with discriminant `D` and a choice of `\beta` a square root of6995`D` mod `4N`. Specifically, given a quadratic form6996`f = Ax^2 + Bxy + Cy^2` we let `\tau_f` be a root of `Ax^2 + Bx + C`6997and the discriminant `\Delta(\tau_f) = \Delta(f) = D` must be6998invariant under multiplication by `N`, the conductor of ``self``.69997000`\Delta(N\tau_f) = \Delta(\tau_f) = \Delta(f) = D`70017002EXAMPLES::70037004sage: E = EllipticCurve('37a')7005sage: E._heegner_forms_list(-7)7006[37*x^2 + 17*x*y + 2*y^2]7007sage: E._heegner_forms_list(-195)7008[37*x^2 + 29*x*y + 7*y^2, 259*x^2 + 29*x*y + y^2, 111*x^2 + 177*x*y + 71*y^2, 2627*x^2 + 177*x*y + 3*y^2]7009sage: E._heegner_forms_list(-195)[-1].discriminant()7010-1957011sage: len(E._heegner_forms_list(-195))701247013sage: QQ[sqrt(-195)].class_number()7014470157016sage: E = EllipticCurve('389a')7017sage: E._heegner_forms_list(-7)7018[389*x^2 + 185*x*y + 22*y^2]7019sage: E._heegner_forms_list(-59)7020[389*x^2 + 313*x*y + 63*y^2, 1167*x^2 + 313*x*y + 21*y^2, 3501*x^2 + 313*x*y + 7*y^2]7021"""7022if expected_count is None:7023expected_count = number_field.QuadraticField(D, 'a').class_number()7024N = self.conductor()7025if beta is None:7026beta = Integers(4*N)(D).sqrt(extend=False)7027else:7028assert beta**2 == Integers(4*N)(D)7029from sage.quadratic_forms.all import BinaryQF7030b = ZZ(beta) % (2*N)7031all = []7032seen = []7033# Note: This may give a sub-optimal list of forms.7034while True:7035R = (b**2-D)//(4*N)7036for d in R.divisors():7037f = BinaryQF([d*N, b, R//d])7038fr = f.reduced_form()7039if fr not in seen:7040seen.append(fr)7041all.append(f)7042if len(all) == expected_count:7043return all7044b += 2*N70457046def _heegner_best_tau(self, D, prec=None):7047r"""7048Given a discrimanent `D`, find the Heegner point `\tau` in the7049upper half plane with largest imaginary part (which is optimal7050for evaluating the modular parametrization). If the optional7051parameter ``prec`` is given, return `\tau` to ``prec`` bits of7052precision, otherwise return it exactly as a symbolic object.70537054EXAMPLES::70557056sage: E = EllipticCurve('37a')7057sage: E._heegner_best_tau(-7)70581/74*sqrt(-7) - 17/747059sage: EllipticCurve('389a')._heegner_best_tau(-11)70601/778*sqrt(-11) - 355/7787061sage: EllipticCurve('389a')._heegner_best_tau(-11, prec=100)7062-0.45629820051413881748071979434 + 0.0042630138693514136878083968338*I7063"""7064# We know that N|A, so A = N is optimal.7065N = self.conductor()7066b = ZZ(Integers(4*N)(D).sqrt(extend=False) % (2*N))7067# TODO: make sure a different choice of b is not better?7068return (-b + ZZ(D).sqrt(prec=prec)) / (2*N)70697070def satisfies_heegner_hypothesis(self, D):7071"""7072Returns ``True`` precisely when `D` is a fundamental discriminant that7073satisfies the Heegner hypothesis for this elliptic curve.70747075EXAMPLES::70767077sage: E = EllipticCurve('11a1')7078sage: E.satisfies_heegner_hypothesis(-7)7079True7080sage: E.satisfies_heegner_hypothesis(-11)7081False7082"""7083if not number_field.is_fundamental_discriminant(D):7084return False7085D = ZZ(D)7086if D >= 0: return False7087if D.gcd(self.conductor()) != 1:7088return False7089for p, _ in self.conductor().factor():7090if D.kronecker(p) != 1:7091return False7092return True709370947095#####################################################################7096# End of elliptic curve methods.7097#####################################################################709870997100