Path: blob/master/sage/schemes/elliptic_curves/heegner.py
4128 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.204205NOTE: This is a *ring* class field, not a ray class field. In206general, the ring class field of given conductor is a subfield of207the ray class field of the same conductor.208209EXAMPLES::210211sage: heegner_point(37,-7).ring_class_field()212Hilbert class field of QQ[sqrt(-7)]213sage: heegner_point(37,-7,5).ring_class_field()214Ring class field extension of QQ[sqrt(-7)] of conductor 5215sage: heegner_point(37,-7,55).ring_class_field()216Ring class field extension of QQ[sqrt(-7)] of conductor 55217218TESTS::219220sage: K_c = heegner_point(37,-7).ring_class_field()221sage: type(K_c)222<class 'sage.schemes.elliptic_curves.heegner.RingClassField'>223sage: loads(dumps(K_c)) == K_c224True225"""226def __init__(self, D, c, check=True):227"""228INPUTS:229230- `D` -- discriminant of quadratic imaginary field231232- `c` -- conductor (positive integer coprime to `D`)233234- ``check`` -- bool (default: True); whether to check235validity of input236237EXAMPLES::238239sage: sage.schemes.elliptic_curves.heegner.RingClassField(-7,5, False)240Ring class field extension of QQ[sqrt(-7)] of conductor 5241242"""243if check:244D = ZZ(D); c = ZZ(c)245self.__D = D246self.__c = c247248def __eq__(self, other):249"""250Used for equality testing.251252EXAMPLES::253254sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()255sage: K11 = E.heegner_point(-7,11).ring_class_field()256sage: K5 == K11257False258sage: K5 == K5259True260sage: K11 == 11261False262"""263return isinstance(other, RingClassField) and self.__D == other.__D and self.__c == other.__c264265def __hash__(self):266"""267Used for computing hash of self.268269EXAMPLES::270271sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()272sage: hash(K5)273-3713088127102618519 # 64-bit2741817441385 # 32-bit275sage: hash((-7,5))276-3713088127102618519 # 64-bit2771817441385 # 32-bit278"""279return hash((self.__D, self.__c))280281def conductor(self):282"""283Return the conductor of this ring class field.284285EXAMPLES::286287sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()288sage: K5.conductor()2895290"""291return self.__c292293def discriminant_of_K(self):294"""295Return the discriminant of the quadratic imaginary field `K` contained in self.296297EXAMPLES::298299sage: E = EllipticCurve('389a'); K5 = E.heegner_point(-7,5).ring_class_field()300sage: K5.discriminant_of_K()301-7302"""303return self.__D304305@cached_method306def ramified_primes(self):307r"""308Return the primes of `\ZZ` that ramify in this ring class field.309310EXAMPLES::311312sage: E = EllipticCurve('389a'); K55 = E.heegner_point(-7,55).ring_class_field()313sage: K55.ramified_primes()314[5, 7, 11]315sage: E.heegner_point(-7).ring_class_field().ramified_primes()316[7]317"""318return arith.prime_divisors(self.__D * self.__c)319320def _repr_(self):321"""322EXAMPLES::323324sage: heegner_point(37,-7,55).ring_class_field()._repr_()325'Ring class field extension of QQ[sqrt(-7)] of conductor 55'326sage: heegner_point(37,-7).ring_class_field()._repr_()327'Hilbert class field of QQ[sqrt(-7)]'328"""329c = self.__c330if c == 1:331return "Hilbert class field of QQ[sqrt(%s)]"%self.__D332else:333return "Ring class field extension of QQ[sqrt(%s)] of conductor %s"%(self.__D, self.__c)334335@cached_method336def degree_over_K(self):337"""338Return the relative degree of this ring class field over the339quadratic imaginary field `K`.340341EXAMPLES::342343sage: E = EllipticCurve('389a'); P = E.heegner_point(-7,5)344sage: K5 = P.ring_class_field(); K5345Ring class field extension of QQ[sqrt(-7)] of conductor 5346sage: K5.degree_over_K()3476348sage: type(K5.degree_over_K())349<type 'sage.rings.integer.Integer'>350351sage: E = EllipticCurve('389a'); E.heegner_point(-20).ring_class_field().degree_over_K()3522353sage: E.heegner_point(-20,3).ring_class_field().degree_over_K()3544355sage: kronecker(-20,11)356-1357sage: E.heegner_point(-20,11).ring_class_field().degree_over_K()35824359"""360D, c = self.__D, self.__c361362K = self.quadratic_field()363364# Multiply class number by relative degree of the Hilbert class field H over K.365return K.class_number() * self.degree_over_H()366367@cached_method368def degree_over_H(self):369"""370Return the degree of this field over the Hilbert class field `H` of `K`.371372EXAMPLES::373374sage: E = EllipticCurve('389a')375sage: E.heegner_point(-59).ring_class_field().degree_over_H()3761377sage: E.heegner_point(-59).ring_class_field().degree_over_K()3783379sage: QuadraticField(-59,'a').class_number()3803381382Some examples in which prime dividing c is inert::383384sage: heegner_point(37,-7,3).ring_class_field().degree_over_H()3854386sage: heegner_point(37,-7,3^2).ring_class_field().degree_over_H()38712388sage: heegner_point(37,-7,3^3).ring_class_field().degree_over_H()38936390391The prime dividing c is split. For example, in the first case392`O_K/cO_K` is isomorphic to a direct sum of two copies of393``GF(2)``, so the units are trivial::394395sage: heegner_point(37,-7,2).ring_class_field().degree_over_H()3961397sage: heegner_point(37,-7,4).ring_class_field().degree_over_H()3982399sage: heegner_point(37,-7,8).ring_class_field().degree_over_H()4004401402Now c is ramified::403404sage: heegner_point(37,-7,7).ring_class_field().degree_over_H()4057406sage: heegner_point(37,-7,7^2).ring_class_field().degree_over_H()40749408"""409c = self.__c410if c == 1:411return ZZ(1)412413# Let K_c be the ring class field. We have by class field theory that414# Gal(K_c / H) = (O_K/c*O_K)^* / (Z/cZ)^*.415#416# To compute the cardinality of the above Galois group, we417# first reduce to the case that c = p^e is a prime power418# (since the expression is multiplicative in c).419# Of course, note also that #(Z/cZ)^* = phi(c)420#421# Case 1: p splits in O_K. Then422# #(O_K/p^e*O_K)^* = (#(Z/p^eZ)^*)^2 = phi(p^e)^2, so423# #(O_K/p^e*O_K)^*/(Z/p^eZ)^* = phi(p^e) = p^e - p^(e-1)424#425# Case 2: p is inert in O_K. Then426# #(O_K/p^e O_K)^* = p^(2*e)-p^(2*(e-1))427# so #(O_K/p^e*O_K)^*/(Z/p^eZ)^*428# = (p^(2*e)-p^(2*(e-1)))/(p^e-p^(e-1)) = p^e + p^(e-1).429#430# Case 3: p ramified in O_K. Then431# #(O_K/p^e O_K)^* = p^(2*e) - p^(2*e-1),432# so #(O_K/p^e O_K)^*/#(Z/p^eZ)^* = p^e.433#434# Section 4.2 of Cohen's "Advanced Computational Algebraic435# Number Theory" GTM is also relevant, though Cohen is working436# with *ray* class fields and here we want the cardinality437# of the *ring* class field, which is a subfield.438439K = self.quadratic_field()440441n = ZZ(1)442for p, e in c.factor():443F = K.factor(p)444if len(F) == 2:445# split case446n *= p**e - p**(e-1)447else:448if F[0][1] > 1:449# ramified case450n *= p**e451else:452# inert case453n *= p**e + p**(e-1)454return n455456@cached_method457def absolute_degree(self):458r"""459Return the absolute degree of this field over `\QQ`.460461EXAMPLES::462463sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()464sage: K.absolute_degree()46512466sage: K.degree_over_K()4676468"""469return 2*self.degree_over_K()470471degree_over_Q = absolute_degree472473@cached_method474def quadratic_field(self):475r"""476Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.477478EXAMPLES::479480sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()481sage: K.quadratic_field()482Number Field in sqrt_minus_7 with defining polynomial x^2 + 7483"""484D = self.__D485var = 'sqrt_minus_%s'%(-D)486return number_field.QuadraticField(D,var)487488@cached_method489def galois_group(self, base=QQ):490"""491Return the Galois group of self over base.492493INPUT:494495- ``base`` -- (default: `\QQ`) a subfield of self or `\QQ`496497EXAMPLES::498499sage: E = EllipticCurve('389a')500sage: A = E.heegner_point(-7,5).ring_class_field()501sage: A.galois_group()502Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5503sage: B = E.heegner_point(-7).ring_class_field()504sage: C = E.heegner_point(-7,15).ring_class_field()505sage: A.galois_group()506Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5507sage: A.galois_group(B)508Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]509sage: A.galois_group().cardinality()51012511sage: A.galois_group(B).cardinality()5126513sage: C.galois_group(A)514Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 15 over Ring class field extension of QQ[sqrt(-7)] of conductor 5515sage: C.galois_group(A).cardinality()5164517518"""519return GaloisGroup(self, base)520521def is_subfield(self, M):522"""523Return True if this ring class field is a subfield of the ring class field `M`.524If `M` is not a ring class field, then a TypeError is raised.525526EXAMPLES::527528sage: E = EllipticCurve('389a')529sage: A = E.heegner_point(-7,5).ring_class_field()530sage: B = E.heegner_point(-7).ring_class_field()531sage: C = E.heegner_point(-20).ring_class_field()532sage: D = E.heegner_point(-7,15).ring_class_field()533sage: B.is_subfield(A)534True535sage: B.is_subfield(B)536True537sage: B.is_subfield(D)538True539sage: B.is_subfield(C)540False541sage: A.is_subfield(B)542False543sage: A.is_subfield(D)544True545"""546if not isinstance(M, RingClassField):547raise TypeError, "M must be a ring class field"548return self.quadratic_field() == M.quadratic_field() and \549M.conductor() % self.conductor() == 0550551##################################################################################552#553# Galois groups of ring class fields554#555##################################################################################556557class GaloisGroup(SageObject):558"""559A Galois group of a ring class field.560561EXAMPLES::562563sage: E = EllipticCurve('389a')564sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G565Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5566sage: G.field()567Ring class field extension of QQ[sqrt(-7)] of conductor 5568sage: G.cardinality()56912570sage: G.complex_conjugation()571Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5572573TESTS::574575sage: G = heegner_point(37,-7).ring_class_field().galois_group()576sage: loads(dumps(G)) == G577True578sage: type(G)579<class 'sage.schemes.elliptic_curves.heegner.GaloisGroup'>580"""581def __init__(self, field, base=QQ):582r"""583INPUT:584585- ``field`` -- a ring class field586587- ``base`` -- subfield of field (default: `\QQ`)588589EXAMPLES::590591sage: K5 = heegner_points(389,-7,5).ring_class_field()592sage: K1 = heegner_points(389,-7,1).ring_class_field()593sage: sage.schemes.elliptic_curves.heegner.GaloisGroup(K5,K1)594Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]595sage: K5.galois_group(K1)596Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Hilbert class field of QQ[sqrt(-7)]597"""598if not isinstance(field, RingClassField):599raise TypeError, "field must be of type RingClassField"600if base != QQ and base != field.quadratic_field():601if not isinstance(base, RingClassField):602raise TypeError, "base must be of type RingClassField or QQ or quadratic field"603if not base.is_subfield(field):604raise TypeError, "base must be a subfield of field"605self.__field = field606self.__base = base607608def __eq__(self, G):609"""610EXAMPLES::611612sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()613sage: G == G614True615sage: G == 0616False617sage: H = EllipticCurve('389a').heegner_point(-7,11).ring_class_field().galois_group()618sage: G == H619False620"""621return isinstance(G, GaloisGroup) and (G.__field,G.__base) == (self.__field,self.__base)622623def __hash__(self):624"""625Return hash of this Galois group, which is the same as the626hash of the pair, the field and its base.627628EXAMPLES::629630sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()631sage: hash(G)632-6198252699510613726 # 64-bit6331905285410 # 32-bit634sage: hash((G.field(), G.base_field()))635-6198252699510613726 # 64-bit6361905285410 # 32-bit637"""638return hash((self.__field, self.__base))639640def __call__(self, x):641"""642Coerce `x` into self, where `x` is a Galois group element, or643in case self has base field the Hilbert class field, `x` can644also be an element of the ring of integers.645646INPUT:647648- `x` -- automorphism or quadratic field element649650OUTPUT:651652- automorphism (or TypeError)653654EXAMPLES::655656sage: K5 = heegner_points(389,-52,5).ring_class_field()657sage: K1 = heegner_points(389,-52,1).ring_class_field()658sage: G = K5.galois_group(K1)659sage: G(1)660Class field automorphism defined by x^2 + 325*y^2661sage: G(G[0])662Class field automorphism defined by x^2 + 325*y^2663sage: alpha = 2 + K1.quadratic_field().gen(); alpha664sqrt_minus_52 + 2665sage: G(alpha)666Class field automorphism defined by 14*x^2 - 10*x*y + 25*y^2667668A TypeError is raised when the coercion isn't possible::669670sage: G(0)671Traceback (most recent call last):672...673TypeError: x does not define element of (O_K/c*O_K)^*674675"""676if isinstance(x, GaloisAutomorphism) and x.parent() == self:677return x678try:679return self._alpha_to_automorphism(x)680except Exception, msg:681raise TypeError, "x does not define element of (O_K/c*O_K)^*"682683def _repr_(self):684"""685Return string representation of this Galois group.686687EXAMPLES::688689sage: E = EllipticCurve('389a')690sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()691sage: G._repr_()692'Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5'693"""694if self.base_field() != QQ:695s = " over %s"%self.base_field()696else:697s = ''698return "Galois group of %s%s"%(self.field(), s)699700def field(self):701"""702Return the ring class field that this Galois group acts on.703704EXAMPLES::705706sage: G = heegner_point(389,-7,5).ring_class_field().galois_group()707sage: G.field()708Ring class field extension of QQ[sqrt(-7)] of conductor 5709"""710return self.__field711712def base_field(self):713"""714Return the base field, which the field fixed by all the715automorphisms in this Galois group.716717EXAMPLES::718719sage: x = heegner_point(37,-7,5)720sage: Kc = x.ring_class_field(); Kc721Ring class field extension of QQ[sqrt(-7)] of conductor 5722sage: K = x.quadratic_field()723sage: G = Kc.galois_group(); G724Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5725sage: G.base_field()726Rational Field727sage: G.cardinality()72812729sage: Kc.absolute_degree()73012731sage: G = Kc.galois_group(K); G732Galois 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 + 7733sage: G.cardinality()7346735sage: G.base_field()736Number Field in sqrt_minus_7 with defining polynomial x^2 + 7737sage: G = Kc.galois_group(Kc); G738Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 5 over Ring class field extension of QQ[sqrt(-7)] of conductor 5739sage: G.cardinality()7401741sage: G.base_field()742Ring class field extension of QQ[sqrt(-7)] of conductor 5743"""744return self.__base745746@cached_method747def kolyvagin_generators(self):748r"""749Assuming this Galois group `G` is of the form750`G=\textrm{Gal}(K_c/K_1)`, with `c=p_1\dots p_n` satisfying the751Kolyvagin hypothesis, this function returns noncanonical752choices of lifts of generators for each of the cyclic factors753of `G` corresponding to the primes dividing `c`. Thus the754`i`-th returned valued is an element of `G` that maps to the755identity element of `\textrm{Gal}(K_p/K_1)` for all `p \neq p_i` and756to a choice of generator of `\textrm{Gal}(K_{p_i}/K_1)`.757758OUTPUT:759760- list of elements of self761762EXAMPLES::763764sage: K3 = heegner_points(389,-52,3).ring_class_field()765sage: K1 = heegner_points(389,-52,1).ring_class_field()766sage: G = K3.galois_group(K1)767sage: G.kolyvagin_generators()768(Class field automorphism defined by 9*x^2 - 6*x*y + 14*y^2,)769770sage: K5 = heegner_points(389,-52,5).ring_class_field()771sage: K1 = heegner_points(389,-52,1).ring_class_field()772sage: G = K5.galois_group(K1)773sage: G.kolyvagin_generators()774(Class field automorphism defined by 17*x^2 - 14*x*y + 22*y^2,)775"""776M = self.field()777c = M.conductor()778if not (self._base_is_hilbert_class_field() and self.is_kolyvagin()):779raise ValueError, "field must be of the form Gal(K_c/K_1)"780if not c.is_prime():781raise NotImplementedError, "only implemented when c is prime"782783# Since c satisfies Kolyvagin and is prime, the group is cyclic,784# so we just find a generator.785for sigma in self:786if sigma.order() == self.cardinality():787return tuple([sigma])788789raise NotImplementedError790791@cached_method792def lift_of_hilbert_class_field_galois_group(self):793r"""794Assuming this Galois group `G` is of the form `G=\textrm{Gal}(K_c/K)`,795this function returns noncanonical choices of lifts of the796elements of the quotient group `\textrm{Gal}(K_1/K)`.797798OUTPUT:799800- tuple of elements of self801802EXAMPLES::803804sage: K5 = heegner_points(389,-52,5).ring_class_field()805sage: G = K5.galois_group(K5.quadratic_field())806sage: G.lift_of_hilbert_class_field_galois_group()807(Class field automorphism defined by x^2 + 325*y^2, Class field automorphism defined by 2*x^2 + 2*x*y + 163*y^2)808sage: G.cardinality()80912810sage: K5.quadratic_field().class_number()8112812"""813if not self._base_is_quad_imag_field():814raise ValueError, "Galois group must be of the form Gal(K_c/K)"815K = self.base_field()816C = K.class_group()817v = []818lifts = []819for sigma in self:820I = sigma.ideal()821g = C(I)822if g not in v:823v.append(g)824lifts.append(sigma)825return tuple(lifts)826827@cached_method828def _list(self):829r"""830Enumerate the elements of self.831832EXAMPLES::833834Example with order 1 (a special case)::835836sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,1).ring_class_field()837sage: G = F.galois_group(F.quadratic_field())838sage: G._list()839(Class field automorphism defined by x^2 + x*y + 2*y^2,)840841Example over quadratic imaginary field::842843sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()844sage: G = F.galois_group(F.quadratic_field())845sage: G._list()846(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)847848Example over `\QQ` (it is not implemented yet)::849850sage: K3 = heegner_points(389,-52,3).ring_class_field()851sage: K3.galois_group()._list()852Traceback (most recent call last):853...854NotImplementedError: Galois group over QQ not yet implemented855856Example over Hilbert class field::857858sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()859sage: G = K3.galois_group(K1)860sage: G._list()861(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)862"""863if self._base_is_QQ():864raise NotImplementedError, "Galois group over QQ not yet implemented"865elif self._base_is_quad_imag_field():866# Over the quadratic imaginary field, so straightforward867# enumeration of all reduced primitive binary quadratic868# forms of discriminant D*c^2.869D = self.base_field().discriminant()870c = self.field().conductor()871Q = [f for f in BinaryQF_reduced_representatives(D*c*c) if f.is_primitive()]872v = [GaloisAutomorphismQuadraticForm(self, f) for f in Q]873874elif self._base_is_hilbert_class_field() and self.is_kolyvagin():875# Take only the automorphisms in the quad imag case that map to876# a principal ideal.877M = self.field()878K = M.quadratic_field()879A = K.maximal_order().free_module()880v = []881self.__p1_to_automorphism = {}882for sigma in M.galois_group(K)._list():883I = sigma.ideal()884if I.is_principal():885# sigma does define an element of our Galois subgroup.886alpha = sigma.ideal().gens_reduced()[0]887t = GaloisAutomorphismQuadraticForm(self, sigma.quadratic_form(), alpha=alpha)888self.__p1_to_automorphism[t.p1_element()] = t889v.append(t)890else:891raise NotImplementedError, "general Galois group not yet implemented"892893v.sort()894assert len(v) == self.cardinality(), "bug enumerating Galois group elements"895return tuple(v)896897def _quadratic_form_to_alpha(self, f):898"""899INPUT:900901- `f` -- a binary quadratic form with discriminant `c^2 D`902903OUTPUT:904905- an element of the ring of integers of the quadratic906imaginary field907908EXAMPLES::909910sage: K3 = heegner_points(389,-52,3).ring_class_field(); K1 = heegner_points(389,-52,1).ring_class_field()911sage: G = K3.galois_group(K1)912sage: [G._quadratic_form_to_alpha(s.quadratic_form()) for s in G]913[3/2*sqrt_minus_52, 1/6*sqrt_minus_52 + 1/3, 1/6*sqrt_minus_52, 1/6*sqrt_minus_52 - 1/3]914915What happens when we input a quadratic form that has nothing916to do with `G`::917918sage: G._quadratic_form_to_alpha(BinaryQF([1,2,3]))919Traceback (most recent call last):920...921ValueError: quadratic form has the wrong discriminant922"""923A,B,C = f924K = self.field().quadratic_field()925if f.discriminant() != self.field().conductor()**2 * K.discriminant():926raise ValueError, "quadratic form has the wrong discriminant"927928R = K['X']929S = K.maximal_order()930M = S.free_module()931v = R([C,B,A]).roots()[0][0]932return v933934def _alpha_to_automorphism(self, alpha):935r"""936Assuming self has base field the Hilbert class field, make an937automorphism from the element `\alpha` of the ring of integers938into self.939940INPUT:941942- `\alpha` -- element of quadratic imaginary field coprime to conductor943944EXAMPLES::945946sage: K3 = heegner_points(389,-52,3).ring_class_field()947sage: K1 = heegner_points(389,-52,1).ring_class_field()948sage: G = K3.galois_group(K1)949sage: G._alpha_to_automorphism(1)950Class field automorphism defined by x^2 + 117*y^2951sage: [G._alpha_to_automorphism(s.alpha()) for s in G] == list(G)952True953"""954if not self._base_is_hilbert_class_field() and self.is_kolyvagin():955raise TypeError, "base must be Hilbert class field with Kolyvagin condition on conductor"956R = self.field().quadratic_field().maximal_order()957uv = self._alpha_to_p1_element(R(alpha))958try:959d = self.__p1_to_automorphism960except AttributeError:961self._list() # computes attribute as side-effect962d = self.__p1_to_automorphism963return d[uv]964965966def _alpha_to_p1_element(self, alpha):967r"""968Given an element of the ring of integers that is nonzero969modulo c, return canonical (after our fixed choice of basis)970element of the project line corresponding to it.971972INPUT:973974- `\alpha` -- element of the ring of integers of the975quadratic imaginary field976977OUTPUT:978979- 2-tuple of integers980981EXAMPLES::982983sage: K3 = heegner_points(389,-52,3).ring_class_field()984sage: K1 = heegner_points(389,-52,1).ring_class_field()985sage: G = K3.galois_group(K1)986sage: G._alpha_to_p1_element(1)987(1, 0)988sage: sorted([G._alpha_to_p1_element(s.alpha()) for s in G])989[(0, 1), (1, 0), (1, 1), (1, 2)]990"""991try:992A, P1 = self.__alpha_to_p1_element993except AttributeError:994# todo (optimize) -- this whole function can be massively optimized:995M = self.field()996A = M.quadratic_field().maximal_order().free_module()997P1 = P1List(M.conductor())998self.__alpha_to_p1_element = A, P1999alpha = self.field().quadratic_field()(alpha)1000w = A.coordinate_vector(alpha.vector())1001w *= w.denominator()1002w = w.change_ring(ZZ)1003n = arith.gcd(w)1004w /= n1005c = P1.N()1006w = P1.normalize(ZZ(w[0])%c, ZZ(w[1])%c)1007if w == (0,0):1008w = (1,0)1009return w10101011def _p1_element_to_alpha(self, uv):1012"""1013Convert a normalized pair ``uv=(u,v)`` of integers to the1014corresponding element of the ring of integers got by taking `u1015b_0 + v b_1` where `b_0, b_1` are the basis for the ring of1016integers.10171018INPUT:10191020- ``uv`` -- pair of integers10211022OUTPUT:10231024- element of maximal order of quadratic field10251026EXAMPLES::10271028sage: K5 = heegner_points(389,-52,5).ring_class_field()1029sage: K1 = heegner_points(389,-52,1).ring_class_field()1030sage: G = K5.galois_group(K1)1031sage: v = [G._alpha_to_p1_element(s.alpha()) for s in G]1032sage: [G._p1_element_to_alpha(z) for z in v]1033[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]1034sage: [G(G._p1_element_to_alpha(z)) for z in v] == list(G)1035True1036"""1037B = self.field().quadratic_field().maximal_order().basis()1038return uv[0]*B[0] + uv[1]*B[1]103910401041def _base_is_QQ(self):1042r"""1043Return True if the base field of this ring class field is `\QQ`.10441045EXAMPLES::10461047sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1048sage: M.galois_group(H.quadratic_field())._base_is_QQ()1049False1050sage: M.galois_group(QQ)._base_is_QQ()1051True1052sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_QQ()1053False1054"""1055return self.__base == QQ10561057def _base_is_quad_imag_field(self):1058"""1059Return True if the base field of this ring class field is the1060quadratic imaginary field `K`.10611062EXAMPLES::10631064sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1065sage: M.galois_group(H.quadratic_field())._base_is_quad_imag_field()1066True1067sage: M.galois_group(QQ)._base_is_quad_imag_field()1068False1069sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_quad_imag_field()1070False1071"""1072return number_field.is_QuadraticField(self.__base)10731074def is_kolyvagin(self):1075"""1076Return True if conductor `c` is prime to the discriminant of the1077quadratic field, `c` is squarefree and each prime dividing `c`1078is inert.10791080EXAMPLES::10811082sage: K5 = heegner_points(389,-52,5).ring_class_field()1083sage: K1 = heegner_points(389,-52,1).ring_class_field()1084sage: K5.galois_group(K1).is_kolyvagin()1085True1086sage: K7 = heegner_points(389,-52,7).ring_class_field()1087sage: K7.galois_group(K1).is_kolyvagin()1088False1089sage: K25 = heegner_points(389,-52,25).ring_class_field()1090sage: K25.galois_group(K1).is_kolyvagin()1091False1092"""1093M = self.field()1094c = M.conductor()1095D = M.quadratic_field().discriminant()1096if c.gcd(D) != 1: return False1097if not c.is_squarefree(): return False1098for p in c.prime_divisors():1099if not is_inert(D,p):1100return False1101return True11021103def _base_is_hilbert_class_field(self):1104"""1105Return True if the base field of this ring class field is the1106Hilbert class field of `K` viewed as a ring class field (so1107not of data type QuadraticField).11081109EXAMPLES::11101111sage: H = heegner_points(389,-20,3); M = H.ring_class_field()1112sage: M.galois_group(H.quadratic_field())._base_is_hilbert_class_field()1113False1114sage: M.galois_group(QQ)._base_is_hilbert_class_field()1115False1116sage: M.galois_group(heegner_points(389,-20,1).ring_class_field())._base_is_hilbert_class_field()1117True1118"""1119M = self.__base1120return isinstance(M, RingClassField) and M.conductor() == 1112111221123def __getitem__(self, i):1124"""1125EXAMPLES::11261127sage: E = EllipticCurve('389a'); F= E.heegner_point(-7,5).ring_class_field()1128sage: G = F.galois_group(F.quadratic_field())1129sage: G[0]1130Class field automorphism defined by x^2 + x*y + 44*y^21131"""1132return self._list()[i]113311341135def __len__(self):1136"""1137EXAMPLES::11381139sage: K5 = heegner_points(389,-52,5).ring_class_field()1140sage: K1 = heegner_points(389,-52,1).ring_class_field()1141sage: G = K5.galois_group(K1)1142sage: G.cardinality()114361144sage: len(G)114561146"""1147return self.cardinality()11481149@cached_method1150def cardinality(self):1151"""1152Return the cardinality of this Galois group.11531154EXAMPLES::11551156sage: E = EllipticCurve('389a')1157sage: G = E.heegner_point(-7,5).ring_class_field().galois_group(); G1158Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51159sage: G.cardinality()1160121161sage: G = E.heegner_point(-7).ring_class_field().galois_group()1162sage: G.cardinality()116321164sage: G = E.heegner_point(-7,55).ring_class_field().galois_group()1165sage: G.cardinality()11661201167"""1168return self.__field.absolute_degree() // self.__base.absolute_degree()11691170@cached_method1171def complex_conjugation(self):1172"""1173Return the automorphism of self determined by complex1174conjugation. The base field must be the rational numbers.11751176EXAMPLES::11771178sage: E = EllipticCurve('389a')1179sage: G = E.heegner_point(-7,5).ring_class_field().galois_group()1180sage: G.complex_conjugation()1181Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51182"""1183if self.base_field() != QQ:1184raise ValueError, "the base field must be fixed by complex conjugation"1185return GaloisAutomorphismComplexConjugation(self)118611871188##################################################################################1189#1190# Elements of Galois groups1191#1192##################################################################################119311941195# TODO -- make GaloisAutomorphism derive from GroupElement so get powers for free, etc.11961197class GaloisAutomorphism(SageObject):1198"""1199An abstract automorphism of a ring class field.1200"""1201def __init__(self, parent):1202"""1203INPUT:12041205- ``parent`` -- a group of automorphisms of a ring class field12061207EXAMPLES::12081209sage: G = heegner_points(389,-7,5).ring_class_field().galois_group(); G1210Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51211sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphism(G)1212<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphism'>1213"""1214self.__parent = parent12151216def parent(self):1217"""1218Return the parent of this automorphism, which is a Galois1219group of a ring class field.12201221EXAMPLES::12221223sage: E = EllipticCurve('389a')1224sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()1225sage: s.parent()1226Galois group of Ring class field extension of QQ[sqrt(-7)] of conductor 51227"""1228return self.__parent12291230def domain(self):1231"""1232Return the domain of this automorphism.12331234EXAMPLES::12351236sage: E = EllipticCurve('389a')1237sage: s = E.heegner_point(-7,5).ring_class_field().galois_group().complex_conjugation()1238sage: s.domain()1239Ring class field extension of QQ[sqrt(-7)] of conductor 51240"""1241return self.parent().field()12421243class GaloisAutomorphismComplexConjugation(GaloisAutomorphism):1244"""1245The complex conjugation automorphism of a ring class field.12461247EXAMPLES::12481249sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1250sage: conj1251Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51252sage: conj.domain()1253Ring class field extension of QQ[sqrt(-7)] of conductor 512541255TESTS::12561257sage: type(conj)1258<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation'>1259sage: loads(dumps(conj)) == conj1260True1261"""1262def __init__(self, parent):1263"""1264INPUT:12651266- ``parent`` -- a group of automorphisms of a ring class field12671268EXAMPLES::12691270sage: G = heegner_point(37,-7,5).ring_class_field().galois_group()1271sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)1272Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51273"""1274GaloisAutomorphism.__init__(self, parent)12751276def __hash__(self):1277"""1278EXAMPLES::12791280sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()1281sage: conj = G.complex_conjugation(); hash(conj)12821347197483068745902 # 64-bit1283480045230 # 32-bit1284"""1285return hash((self.parent(), 1))12861287def __eq__(self, right):1288"""1289EXAMPLES::12901291sage: G = EllipticCurve('389a').heegner_point(-7,5).ring_class_field().galois_group()1292sage: conj = G.complex_conjugation()1293sage: conj2 = sage.schemes.elliptic_curves.heegner.GaloisAutomorphismComplexConjugation(G)1294sage: conj is conj21295False1296sage: conj == conj21297True1298"""1299return isinstance(right, GaloisAutomorphismComplexConjugation) and \1300self.parent() == right.parent()13011302def _repr_(self):1303"""1304Return print representation of the complex conjugation automorphism.13051306EXAMPLES::13071308sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1309sage: conj._repr_()1310'Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 5'1311"""1312return "Complex conjugation automorphism of %s"%self.domain()13131314## def __mul__(self, right):1315## """1316## Return the composition of two automorphisms.13171318## EXAMPLES::13191320## sage: ?1321## """1322## if self.parent() != right.__parent():1323## raise TypeError, "automorphisms must be of the same class field"1324## raise NotImplementedError13251326def __invert__(self):1327"""1328Return the inverse of self, which is just self again.13291330EXAMPLES::13311332sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1333sage: ~conj1334Complex conjugation automorphism of Ring class field extension of QQ[sqrt(-7)] of conductor 51335"""1336return self13371338def order(self):1339"""1340EXAMPLES::13411342sage: conj = heegner_point(37,-7,5).ring_class_field().galois_group().complex_conjugation()1343sage: conj.order()134421345"""1346return ZZ(2)13471348class GaloisAutomorphismQuadraticForm(GaloisAutomorphism):1349"""1350An automorphism of a ring class field defined by a quadratic form.13511352EXAMPLES::13531354sage: H = heegner_points(389,-20,3)1355sage: sigma = H.ring_class_field().galois_group(H.quadratic_field())[0]; sigma1356Class field automorphism defined by x^2 + 45*y^21357sage: type(sigma)1358<class 'sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm'>1359sage: loads(dumps(sigma)) == sigma1360True1361"""1362def __init__(self, parent, quadratic_form, alpha=None):1363r"""1364INPUT:13651366- ``parent`` -- a group of automorphisms of a ring class field13671368- ``quadratic_form`` -- a binary quadratic form that1369defines an element of the Galois group of `K_c` over `K`.13701371- ``\alpha`` -- (default: None) optional data that specified1372element corresponding element of `(\mathcal{O}_K /1373c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class field1374theory.13751376EXAMPLES::13771378sage: H = heegner_points(389,-20,3); G = H.ring_class_field().galois_group(H.quadratic_field())1379sage: f = BinaryQF_reduced_representatives(-20*9)[0]1380sage: sage.schemes.elliptic_curves.heegner.GaloisAutomorphismQuadraticForm(G, f)1381Class field automorphism defined by x^2 + 45*y^21382"""1383self.__quadratic_form = quadratic_form.reduced_form()1384self.__alpha = alpha1385GaloisAutomorphism.__init__(self, parent)13861387@cached_method1388def order(self):1389"""1390Return the multiplicative order of this Galois group automorphism.13911392EXAMPLES::13931394sage: K3 = heegner_points(389,-52,3).ring_class_field()1395sage: K1 = heegner_points(389,-52,1).ring_class_field()1396sage: G = K3.galois_group(K1)1397sage: sorted([g.order() for g in G])1398[1, 2, 4, 4]1399sage: K5 = heegner_points(389,-52,5).ring_class_field()1400sage: K1 = heegner_points(389,-52,1).ring_class_field()1401sage: G = K5.galois_group(K1)1402sage: sorted([g.order() for g in G])1403[1, 2, 3, 3, 6, 6]1404"""1405alpha = self.__alpha1406if alpha is None:1407raise NotImplementedError, "order only currently implemented when alpha given in construction"1408G = self.parent()1409one = G(1).p1_element()1410ans = ZZ(1)1411z = alpha1412for i in range(G.cardinality()):1413if G._alpha_to_p1_element(z) == one:1414return ans1415ans += 11416z *= alpha1417assert False, "bug in order"14181419def alpha(self):1420r"""1421Optional data that specified element corresponding element of1422`(\mathcal{O}_K / c\mathcal{O}_K)^* / (\ZZ/c\ZZ)^*`, via class1423field theory.14241425This is a generator of the ideal corresponding to this1426automorphism.14271428EXAMPLES::14291430sage: K3 = heegner_points(389,-52,3).ring_class_field()1431sage: K1 = heegner_points(389,-52,1).ring_class_field()1432sage: G = K3.galois_group(K1)1433sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)1434[1, 1/2*sqrt_minus_52 + 1, -1/2*sqrt_minus_52, 1/2*sqrt_minus_52 - 1]1435sage: [x^2 for x in orb] # this is just for testing1436[1, sqrt_minus_52 - 12, -13, -sqrt_minus_52 - 12]14371438sage: K5 = heegner_points(389,-52,5).ring_class_field()1439sage: K1 = heegner_points(389,-52,1).ring_class_field()1440sage: G = K5.galois_group(K1)1441sage: orb = sorted([g.alpha() for g in G]); orb # random (the sign depends on the database being installed or not)1442[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]1443sage: [x^2 for x in orb] # just for testing1444[1, -13, sqrt_minus_52 - 12, -sqrt_minus_52 - 12, -2*sqrt_minus_52 - 9, 2*sqrt_minus_52 - 9]14451446"""1447if self.__alpha is None:1448raise ValueError, "alpha data not defined"1449return self.__alpha14501451@cached_method1452def p1_element(self):1453r"""1454Return element of the projective line corresponding to this1455automorphism.14561457This only makes sense if this automorphism is in the Galois1458group `\textrm{Gal}(K_c/K_1)`.14591460EXAMPLES::14611462sage: K3 = heegner_points(389,-52,3).ring_class_field()1463sage: K1 = heegner_points(389,-52,1).ring_class_field()1464sage: G = K3.galois_group(K1)1465sage: sorted([g.p1_element() for g in G])1466[(0, 1), (1, 0), (1, 1), (1, 2)]14671468sage: K5 = heegner_points(389,-52,5).ring_class_field()1469sage: K1 = heegner_points(389,-52,1).ring_class_field()1470sage: G = K5.galois_group(K1)1471sage: sorted([g.p1_element() for g in G])1472[(0, 1), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4)]1473"""1474return self.parent()._alpha_to_p1_element(self.__alpha)14751476def __hash__(self):1477"""1478EXAMPLES::14791480sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1481sage: hash(s)14824262582128197601113 # 64-bit1483-1994029223 # 32-bit1484"""1485return hash((self.parent(), tuple(self.__quadratic_form)))14861487def __eq__(self, right):1488"""1489EXAMPLES::14901491sage: H = heegner_points(389,-7,5); s = H.ring_class_field().galois_group(H.quadratic_field())[1]1492sage: s == s1493True1494sage: s == s*s1495False1496sage: s == s*s*s*s*s1497False1498sage: s == s*s*s*s*s*s*s1499True1500"""1501return isinstance(right, GaloisAutomorphismQuadraticForm) and \1502self.parent() == right.parent() and \1503self.quadratic_form().is_equivalent(right.quadratic_form())15041505def __cmp__(self, right):1506"""1507Compare self and right. Used mainly so that lists of1508automorphisms are sorted consistently between runs.15091510EXAMPLES::15111512sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1513sage: s.__cmp__(s)151401515sage: s.__cmp__(0) != 01516True1517"""1518if not isinstance(right, GaloisAutomorphismQuadraticForm):1519return cmp(type(self), type(right))1520c = cmp(self.parent(), right.parent())1521if c: return c1522if self.quadratic_form().is_equivalent(right.quadratic_form()):1523return 01524return cmp(self.quadratic_form(), right.quadratic_form())15251526def _repr_(self):1527"""1528Return string representation of this automorphism.15291530EXAMPLES::15311532sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1533sage: s._repr_()1534'Class field automorphism defined by x^2 + 45*y^2'15351536"""1537return "Class field automorphism defined by %s"%self.__quadratic_form15381539def __mul__(self, right):1540"""1541Return the composition of two automorphisms.15421543EXAMPLES::15441545sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1546sage: s * s1547Class field automorphism defined by x^2 + 45*y^21548sage: G = s.parent(); list(G)1549[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]1550sage: G[0]*G[0]1551Class field automorphism defined by x^2 + 45*y^21552sage: G[1]*G[2] == G[3]1553True1554"""1555if self.parent() != right.parent():1556raise TypeError, "automorphisms must be of the same class field"1557if not isinstance(right, GaloisAutomorphismQuadraticForm):1558# TODO: special case when right is complex conjugation1559raise NotImplementedError1560Q = (self.__quadratic_form * right.__quadratic_form).reduced_form()1561if self.__alpha and right.__alpha:1562alpha = self.__alpha * right.__alpha1563else:1564alpha = None1565return GaloisAutomorphismQuadraticForm(self.parent(), Q, alpha=alpha)15661567def quadratic_form(self):1568"""1569Return reduced quadratic form corresponding to this Galois1570automorphism.157115721573EXAMPLES::15741575sage: H = heegner_points(389,-20,3); s = H.ring_class_field().galois_group(H.quadratic_field())[0]1576sage: s.quadratic_form()1577x^2 + 45*y^21578"""1579return self.__quadratic_form15801581@cached_method1582def ideal(self):1583r"""1584Return ideal of ring of integers of quadratic imaginary field1585corresponding to this quadratic form. This is the ideal15861587`I = \left(A, \frac{-B+ c\sqrt{D}}{2}\right) \mathcal{O}_K`.15881589EXAMPLES::15901591sage: E = EllipticCurve('389a'); F= E.heegner_point(-20,3).ring_class_field()1592sage: G = F.galois_group(F.quadratic_field())1593sage: G[1].ideal()1594Fractional ideal (2, 1/2*sqrt_minus_20 + 1)1595sage: [s.ideal().gens() for s in G]1596[(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)]1597"""1598M = self.parent().field()1599K = M.quadratic_field()1600f = self.quadratic_form()1601c = M.conductor()1602sqrtD = K.gen()1603(A,B,C) = f1604if A%c == 0:1605A, C = C, A1606return K.maximal_order().ideal([A, (-B+c*sqrtD)/2])16071608## def __call__(self, z):1609## """1610## Return image of the Heegner point `z` under this automorphism.1611##1612## INPUT:1613##1614## - `z` -- a Heegner point on `X_0(N)` or an elliptic curve1615##1616## OUTPUT:1617##1618## - a Heegner point1619##1620## EXAMPLES::1621##1622## sage: x = heegner_point(389,-20,3); F = x.ring_class_field()1623## sage: sigma = F.galois_group(F.quadratic_field())[1]; sigma1624## Class field automorphism defined by 2*x^2 + 2*x*y + 23*y^21625## sage: sigma(x)1626## Heegner point 3/1556*sqrt(-20) - 495/778 of discriminant -20 and conductor 3 on X_0(389)1627## """1628## if isinstance(z, HeegnerPointOnX0N):1629## if z.ring_class_field() != self.domain():1630## raise NotImplementedError, "class fields must be the same"1631## # TODO -- check more compatibilities?1632## # TODO -- this is surely backwards -- something must be inverted?1633## f = z.quadratic_form() * self.quadratic_form()1634## # TODO -- put f into the correct form with A divisible by N, etc.?1635## # That could be done by looking up reduced form of f in a canonical1636## # list of best reps.1637## N,D,c = z.level(),z.discriminant(),z.conductor()1638## return HeegnerPointOnX0N(N,D,c, f = f)1639## else:1640## raise NotImplementedError16411642##################################################################################1643#1644# Specific Heegner points1645#1646##################################################################################164716481649class HeegnerPoint(SageObject):1650r"""1651A Heegner point of level `N`, discriminant `D` and conductor `c`1652is any point on a modular curve or elliptic curve that is1653concocted in some way from a quadratic imaginary `\tau` in the upper1654half plane with `\Delta(\tau) = D c = \Delta(N \tau)`.16551656EXAMPLES::16571658sage: x = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,13); x1659Heegner point of level 389, discriminant -7, and conductor 131660sage: type(x)1661<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1662sage: loads(dumps(x)) == x1663True1664"""1665def __init__(self, N, D, c):1666"""1667INPUT:16681669- `N` -- (positive integer) the level16701671- `D` -- (negative integer) fundamental discriminant16721673- `c` -- (positive integer) conductor16741675Since this is an abstract base class, no type or compatibility1676checks are done, as those are all assumed to be done in the1677derived class.16781679EXAMPLES::16801681sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1682sage: type(H)1683<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1684"""1685self.__N = N1686self.__D = D1687self.__c = c16881689def __cmp__(self, x):1690"""1691Compare two Heegner points.16921693EXAMPLES::16941695sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1696sage: H.__cmp__(H)169701698"""1699if not isinstance(x, HeegnerPoint):1700raise NotImplementedError1701return cmp((self.__N, self.__D, self.__c), (x.__N, x.__D, x.__c))17021703def _repr_(self):1704"""1705EXAMPLES::17061707sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5)1708sage: type(H)1709<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1710sage: H._repr_()1711'Heegner point of level 389, discriminant -7, and conductor 5'1712"""1713return "Heegner point of level %s, discriminant %s, and conductor %s"%(1714self.__N, self.__D, self.__c)17151716def __hash__(self):1717"""1718EXAMPLES::17191720sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)1721<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1722sage: hash(H)17236187687223143458874 # 64-bit1724-458201030 # 32-bit1725"""1726return hash((self.__N, self.__D, self.__c))17271728def __eq__(self, right):1729"""1730EXAMPLES::17311732sage: H = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,5); type(H)1733<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoint'>1734sage: J = sage.schemes.elliptic_curves.heegner.HeegnerPoint(389,-7,11)1735sage: H == H1736True1737sage: H == J1738False1739sage: J == H1740False1741sage: H == 01742False1743"""1744if not isinstance(right, HeegnerPoint): return False1745return (self.__N, self.__D, self.__c) == (right.__N, right.__D, right.__c)17461747def level(self):1748"""1749Return the level of this Heegner point, which is the level of the1750modular curve `X_0(N)` on which this is a Heegner point.17511752EXAMPLES::17531754sage: heegner_point(389,-7,5).level()17553891756"""1757return self.__N17581759def conductor(self):1760"""1761Return the conductor of this Heegner point.17621763EXAMPLES::17641765sage: heegner_point(389,-7,5).conductor()176651767sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P1768Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 371769sage: P.conductor()177071771sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.conductor()177251773"""1774return self.__c17751776def discriminant(self):1777"""1778Return the discriminant of the quadratic imaginary field1779associated to this Heegner point.17801781EXAMPLES::17821783sage: heegner_point(389,-7,5).discriminant()1784-71785sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P1786Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 371787sage: P.discriminant()1788-671789sage: E = EllipticCurve('389a'); P = E.heegner_point(-7, 5); P.discriminant()1790-71791"""1792return self.__D17931794@cached_method1795def quadratic_field(self):1796"""1797Return the quadratic number field of discriminant `D`.17981799EXAMPLES::18001801sage: x = heegner_point(37,-7,5)1802sage: x.quadratic_field()1803Number Field in sqrt_minus_7 with defining polynomial x^2 + 7180418051806sage: E = EllipticCurve('37a'); P = E.heegner_point(-40)1807sage: P.quadratic_field()1808Number Field in sqrt_minus_40 with defining polynomial x^2 + 401809sage: P.quadratic_field() is P.quadratic_field()1810True1811sage: type(P.quadratic_field())1812<class 'sage.rings.number_field.number_field.NumberField_quadratic_with_category'>1813"""1814return self.ring_class_field().quadratic_field()18151816@cached_method1817def quadratic_order(self):1818"""1819Return the order in the quadratic imaginary field of conductor1820`c`, where `c` is the conductor of this Heegner point.18211822EXAMPLES::18231824sage: heegner_point(389,-7,5).quadratic_order()1825Order in Number Field in sqrt_minus_7 with defining polynomial x^2 + 71826sage: heegner_point(389,-7,5).quadratic_order().basis()1827[1, 5*sqrt_minus_7]18281829sage: E = EllipticCurve('37a'); P = E.heegner_point(-40,11)1830sage: P.quadratic_order()1831Order in Number Field in sqrt_minus_40 with defining polynomial x^2 + 401832sage: P.quadratic_order().basis()1833[1, 11*sqrt_minus_40]18341835"""1836K = self.quadratic_field()1837return K.order([1,self.conductor()*K.gen()])18381839@cached_method1840def ring_class_field(self):1841"""1842Return the ring class field associated to this Heegner point.1843This is an extension `K_c` over `K`, where `K` is the1844quadratic imaginary field and `c` is the conductor associated1845to this Heegner point. This Heegner point is defined over1846`K_c` and the Galois group `Gal(K_c/K)` acts transitively on1847the Galois conjugates of this Heegner point.18481849EXAMPLES::18501851sage: E = EllipticCurve('389a'); K.<a> = QuadraticField(-5)1852sage: len(K.factor(5))185311854sage: len(K.factor(23))185521856sage: E.heegner_point(-7, 5).ring_class_field().degree_over_K()185761858sage: E.heegner_point(-7, 23).ring_class_field().degree_over_K()1859221860sage: E.heegner_point(-7, 5*23).ring_class_field().degree_over_K()18611321862sage: E.heegner_point(-7, 5^2).ring_class_field().degree_over_K()1863301864sage: E.heegner_point(-7, 7).ring_class_field().degree_over_K()186571866"""1867return RingClassField(self.discriminant(), self.conductor())186818691870##################################################################################1871#1872# Sets of Heegner points1873#1874##################################################################################18751876class HeegnerPoints(SageObject):1877"""1878The set of Heegner points with given parameters.18791880EXAMPLES::18811882sage: H = heegner_points(389); H1883Set of all Heegner points on X_0(389)1884sage: type(H)1885<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>1886sage: isinstance(H, sage.schemes.elliptic_curves.heegner.HeegnerPoints)1887True1888"""1889def __init__(self, N):1890"""1891INPUT:18921893- `N` -- level, a positive integer18941895EXAMPLES::18961897sage: heegner_points(37)1898Set of all Heegner points on X_0(37)1899sage: heegner_points(0)1900Traceback (most recent call last):1901...1902ValueError: N must a positive integer1903"""1904self.__N = ZZ(N)1905if self.__N <= 0:1906raise ValueError, "N must a positive integer"19071908def level(self):1909"""1910Return the level `N` of the modular curve `X_0(N)`.19111912EXAMPLES::19131914sage: heegner_points(389).level()19153891916"""1917return self.__N191819191920class HeegnerPoints_level(HeegnerPoints):1921"""1922Return the infinite set of all Heegner points on `X_0(N)` for all1923quadratic imaginary fields.19241925EXAMPLES::19261927sage: H = heegner_points(11); H1928Set of all Heegner points on X_0(11)1929sage: type(H)1930<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level'>1931sage: loads(dumps(H)) == H1932True1933"""1934def __eq__(self, other):1935"""1936EXAMPLES::19371938sage: H = heegner_points(11)1939sage: H == heegner_points(13)1940False1941sage: H == heegner_points(11)1942True1943sage: H == 01944False1945"""1946return isinstance(other, HeegnerPoints_level) and self.level() == other.level()19471948def _repr_(self):1949"""1950Return string representation of the set of Heegner points.19511952EXAMPLES::19531954sage: heegner_points(389)._repr_()1955'Set of all Heegner points on X_0(389)'1956"""1957return "Set of all Heegner points on X_0(%s)"%self.level()19581959def reduce_mod(self, ell):1960r"""1961eturn object that allows for computation with Heegner points1962of level `N` modulo the prime `\ell`, represented using1963quaternion algebras.19641965INPUT:19661967- `\ell` -- prime19681969EXAMPLES::19701971sage: heegner_points(389).reduce_mod(7).quaternion_algebra()1972Quaternion Algebra (-1, -7) with base ring Rational Field1973"""1974return HeegnerQuatAlg(self.level(), ell)19751976def discriminants(self, n=10, weak=False):1977r"""1978Return the first `n` quadratic imaginary discriminants that1979satisfy the Heegner hypothesis for `N`.19801981INPUTS:19821983- `n` -- nonnegative integer19841985- ``weak`` -- bool (default: False); if True only require1986weak Heegner hypothesis, which is the same as usual but1987without the condition that `\gcd(D,N)=1`.19881989EXAMPLES::19901991sage: X = heegner_points(37)1992sage: X.discriminants(5)1993[-7, -11, -40, -47, -67]19941995The default is 10::19961997sage: X.discriminants()1998[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]1999sage: X.discriminants(15)2000[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -115, -120, -123, -127]20012002The discriminant -111 satisfies only the weak Heegner hypothesis, since it2003is divisible by 37::20042005sage: X.discriminants(15,weak=True)2006[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104, -107, -111, -115, -120, -123]2007"""2008N = self.level()2009n = ZZ(n)2010v = []2011D = ZZ(-4)2012while len(v) < n:2013D -= 12014if satisfies_weak_heegner_hypothesis(N,D):2015# if not weak, then also require gcd(D,N)=12016if not weak and D.gcd(N) != 1:2017continue2018v.append(D)2019return v20202021class HeegnerPoints_level_disc(HeegnerPoints):2022"""2023Set of Heegner points of given level and all conductors associated2024to a quadratic imaginary field.20252026EXAMPLES::20272028sage: H = heegner_points(389,-7); H2029Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]2030sage: type(H)2031<class 'sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc'>2032sage: H._repr_()2033'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'2034sage: H.discriminant()2035-72036sage: H.quadratic_field()2037Number Field in sqrt_minus_7 with defining polynomial x^2 + 72038sage: H.kolyvagin_conductors()2039[1, 3, 5, 13, 15, 17, 19, 31, 39, 41]20402041sage: loads(dumps(H)) == H2042True2043"""2044def __init__(self, N, D):2045"""2046INPUT:20472048- `N` -- positive integer20492050- `D` -- negative fundamental discriminant20512052EXAMPLES::20532054sage: sage.schemes.elliptic_curves.heegner.HeegnerPoints_level_disc(37,-7)2055Set of all Heegner points on X_0(37) associated to QQ[sqrt(-7)]2056"""2057HeegnerPoints.__init__(self, N)2058D = ZZ(D)2059if not satisfies_weak_heegner_hypothesis(N,D):2060raise ValueError, "D (=%s) must satisfy the weak Heegner hypothesis for N (=%s)"%(D,N)2061self.__D = D20622063def __eq__(self, other):2064"""2065EXAMPLES::20662067sage: H = heegner_points(389,-7)2068sage: H == heegner_points(389,-7)2069True2070sage: H == 02071False2072sage: H == heegner_points(389,-11)2073False2074"""2075return isinstance(other, HeegnerPoints_level_disc) and \2076self.level() == other.level() and self.__D == other.__D20772078def _repr_(self):2079"""2080Return string representation of the set of Heegner points for a given2081quadratic field.20822083EXAMPLES::20842085sage: heegner_points(389,-7)._repr_()2086'Set of all Heegner points on X_0(389) associated to QQ[sqrt(-7)]'2087"""2088return "Set of all Heegner points on X_0(%s) associated to QQ[sqrt(%s)]"%(2089self.level(), self.discriminant())209020912092def discriminant(self):2093r"""2094Return the discriminant of the quadratic imaginary extension `K`.20952096EXAMPLES::20972098sage: heegner_points(389,-7).discriminant()2099-72100"""2101return self.__D21022103@cached_method2104def quadratic_field(self):2105r"""2106Return the quadratic imaginary field `K = \QQ(\sqrt{D})`.21072108EXAMPLES::21092110sage: E = EllipticCurve('389a'); K = E.heegner_point(-7,5).ring_class_field()2111sage: K.quadratic_field()2112Number Field in sqrt_minus_7 with defining polynomial x^2 + 72113"""2114D = self.__D2115var = 'sqrt_minus_%s'%(-D)2116return number_field.QuadraticField(D,var)21172118def kolyvagin_conductors(self, r=None, n=10, E=None, m=None):2119r"""2120Return the first `n` conductors that are squarefree products2121of distinct primes inert in the quadratic imaginary field2122`K = \QQ(\sqrt{D})`. If `r` is specified, return only2123conductors that are a product of `r` distinct primes all inert2124in `K`. If `r = 0`, always return the list ``[1]``,2125no matter what.21262127If the optional elliptic curve `E` and integer `m` are given,2128then only include conductors `c` such that for each prime2129divisor `p` of `c` we have `m \mid \gcd(a_p(E), p+1)`.21302131INPUT:21322133- `r` -- (default: None) nonnegative integer or None21342135- `n` -- positive integer21362137- `E` -- an elliptic curve21382139- `m` -- a positive integer21402141EXAMPLES::21422143sage: H = heegner_points(389,-7)2144sage: H.kolyvagin_conductors(0)2145[1]2146sage: H.kolyvagin_conductors(1)2147[3, 5, 13, 17, 19, 31, 41, 47, 59, 61]2148sage: H.kolyvagin_conductors(1,15)2149[3, 5, 13, 17, 19, 31, 41, 47, 59, 61, 73, 83, 89, 97, 101]2150sage: H.kolyvagin_conductors(1,5)2151[3, 5, 13, 17, 19]2152sage: H.kolyvagin_conductors(1,5,EllipticCurve('389a'),3)2153[5, 17, 41, 59, 83]2154sage: H.kolyvagin_conductors(2,5,EllipticCurve('389a'),3)2155[85, 205, 295, 415, 697]2156"""2157D = self.__D2158if not satisfies_weak_heegner_hypothesis(self.level(),D):2159raise ValueError, "D must satisfy the weak Heegner hypothesis"2160n = ZZ(n)2161if n <= 0:2162raise ValueError, "n must be a positive integer"2163if r is not None:2164r = ZZ(r)2165if r < 0:2166raise ValueError, "n must be a nonnegative integer"2167if r == 0:2168return [ZZ(1)]21692170c = ZZ(1)2171v = []2172N = self.level()2173ND = N * D21742175if 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 None21992200- `D` -- negative fundamental discriminant22012202- `r` -- number of prime factors (nonnegative integer) or None22032204- `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 isn't::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's good/important that this return a copy, since2725# BinaryQF's stupidly are mutable and can't 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` isn't 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's 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 shouldn't 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]3417y1 = -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"""3480E = self.curve()3481v = []3482for z in self.conjugates_over_K():3483m = z.numerical_approx(prec)3484v.append(m)3485v.append(m.curve().point([w.conjugate() for w in m], check=False))3486v.sort()3487return v34883489def _numerical_approx_xy_poly(self, prec=53):3490r"""3491Return polynomials with real floating point coefficients got3492by taking the real part of the product of `X - \alpha` over3493the numerical approximations `\alpha` to the conjugates of3494this Heegner point. The first polynomial runs through the3495`x`-coordinates and the second through the `y`-coordinates.34963497INPUT:34983499- ``prec`` -- positive integer (default: 53)35003501OUTPUT:35023503- 2-tuple of polynomials with floating point coefficients35043505EXAMPLES::3506sage: E = EllipticCurve('37a')3507sage: y = E.heegner_point(-7,3); y3508Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 373509sage: y._numerical_approx_xy_poly()3510(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)3511"""3512v = self._numerical_approx_conjugates_over_QQ(prec)3513R = ComplexField(prec)['X']3514S = RealField(prec)['X']3515X = R.gen()3516fx = prod(X-a[0] for a in v)3517fx = S([a.real() for a in fx])3518fy = prod(X-a[1] for a in v)3519fy = S([a.real() for a in fy])3520return fx, fy35213522def _xy_poly_nearby(self, prec=53, max_error=10**(-10)):3523"""3524Return polynomials with rational coefficients that for sufficiently3525tight bounds are the characteristic polynomial of the x and y3526coordinate of this Heegner point.35273528INPUT:35293530- ``prec`` -- positive integer (default: 53)35313532- ``max_error`` -- very small floating point number35333534OUTPUT:35353536- 2-tuple of polynomials with rational coefficients35373538EXAMPLES::35393540sage: E = EllipticCurve('37a')3541sage: y = E.heegner_point(-7,3); y3542Heegner point of discriminant -7 and conductor 3 on elliptic curve of conductor 373543sage: y._xy_poly_nearby()3544[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,3545X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]354635473548"""3549v = self._numerical_approx_xy_poly(prec)3550return [nearby_rational_poly(g, max_error=max_error) for g in v]35513552def _xy_poly_simplest(self, prec=53, prec2=None):3553"""3554Return polynomials with rational coefficients that for3555sufficiently tight bounds are the characteristic polynomial of3556the x and y coordinate of this Heegner point.35573558INPUT:35593560- ``prec`` -- positive integer (default: 53)35613562- ``prec2`` -- passed into simplest_rational_poly function35633564EXAMPLES::35653566sage: E = EllipticCurve('37a'); y = E.heegner_point(-7,3)3567sage: y._xy_poly_simplest()3568[X^8 + 6*X^7 + 9*X^6 - 12*X^5 - 42*X^4 - 18*X^3 + 36*X^2 + 36*X + 9,3569X^8 + 12*X^7 + 72*X^6 + 270*X^5 + 678*X^4 + 1152*X^3 + 1269*X^2 + 810*X + 225]3570"""3571v = self._numerical_approx_xy_poly(prec)3572if prec2 is None: prec2 = max(2, prec - 20)3573return [simplest_rational_poly(g,prec2) for g in v]35743575@cached_method3576def _square_roots_mod_2N_of_D_mod_4N(self):3577"""3578Return the square roots of `D` modulo `4N` all reduced mod `2N`,3579without multiplicity.35803581EXAMPLES::35823583sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P3584Heegner point of discriminant -40 on elliptic curve of conductor 373585sage: P._square_roots_mod_2N_of_D_mod_4N()3586[16, 58]3587sage: parent(P._square_roots_mod_2N_of_D_mod_4N()[0])3588Ring of integers modulo 743589"""3590D = self.discriminant()3591N = self.__E.conductor()3592R = Integers(4*N)3593m = 2*N3594return sorted( set([a%m for a in R(self.discriminant()).sqrt(all=True)]) )35953596def _trace_numerical_conductor_1(self, prec=53):3597"""3598Return numerical approximation using ``prec`` terms of working3599precision to the trace down to the quadratic imaginary field3600`K` of this Heegner point.36013602INPUT:36033604- `prec` -- bits precision (default: 53)36053606EXAMPLES::36073608sage: E = EllipticCurve('57a1')3609sage: P = E.heegner_point(-8); P3610Heegner point of discriminant -8 on elliptic curve of conductor 573611sage: P._trace_numerical_conductor_1() # approx. (1 : 0 : 1)3612(1.00000000000000 + ...e-16*I : ...e-16 - ...e-16*I : 1.00000000000000)3613sage: P = E(2,1) # a generator3614sage: E([1,0]).height()36150.1502983709472953616sage: P.height()36170.03757459273682383618sage: E.heegner_index(-8)36192.0000?3620sage: E.torsion_order()362113622sage: 2*P3623(1 : 0 : 1)3624"""3625if self.conductor() != 1:3626raise ValueError, "conductor must be 1"3627R, U = self._good_tau_representatives()3628E = self.__E3629phi = E.modular_parametrization()3630C = rings.ComplexField(prec)3631F = E.change_ring(C)3632s = 03633for u, weight in U:3634P = phi(C(self._qf_to_tau(u)))3635z = F.point(list(P),check=False)3636if abs(weight) == 2:3637t = F.point(z,check=False) + F.point(tuple([x.conjugate() for x in z]), check=False)3638if weight < 0:3639s -= t3640else:3641s += t3642else:3643if weight < 0:3644s -= z3645else:3646s += z3647return s36483649@cached_method3650def _good_tau_representatives(self):3651"""3652Return good upper half plane representatives for Heegner points.36533654ALGORITHM: This is Algorithm 3.5 in Watkins's paper.36553656EXAMPLES::36573658sage: P = EllipticCurve('389a1').heegner_point(-7)3659sage: P._good_tau_representatives()3660([(1, 1, 2)], [((389, 185, 22), 1)])3661"""3662if self.conductor() != 1: raise NotImplementedError3663E = self.__E3664SDN = self._square_roots_mod_2N_of_D_mod_4N()3665beta = SDN[0]3666U = []3667R = []3668N = self.__E.conductor()3669D = self.discriminant()3670h = self.ring_class_field().degree_over_K()3671divs = D.gcd(N).divisors()3672a = 13673while True:3674for b in SDN:3675b = b.lift()3676# todo (optimize) -- replace for over all s with for over solution3677# set that can be found quickly.3678y = ZZ((b*b - D)/(4*N))3679for s in Integers(a):3680if N*s*s + b*s + y == 0:3681s = s.lift()3682f = (a*N, b+2*N*s, ZZ( ((b + 2*N*s)**2 - D)/(4*a*N)) )3683for d in divs:3684Q = d * prod(p**k for p,k in N.factor() if (b-beta)%(p**k)!=0)3685g = self._qf_atkin_lehner_act(Q, f)3686gbar = (ZZ(g[0]/N), -g[1], g[2]*N)3687g = self._qf_reduce(g)3688gbar = self._qf_reduce(gbar)3689if g in R or gbar in R:3690continue3691R.append(g)3692if g != gbar:3693R.append(gbar)3694epsilon_Q = prod([E.root_number(q) for q in Q.prime_divisors()])3695if g == gbar:3696# weight is epsilon_Q3697weight = epsilon_Q3698else:3699# weight is 2*epsilon_Q3700weight = 2*epsilon_Q3701U.append((f,weight))3702if len(R) == h:3703return R, U3704assert len(R) < h, "bug -- too many quadratic forms"3705a += 137063707def _qf_to_tau(self, f):3708r"""3709Function used internally that given a quadratic form3710`f=(A,B,C)`, return `\tau` in the upper half plane with3711`A\tau^2 + B \tau + C = 0`. Here `A>0` and `\gcd(A,B,C)=1`.3712Also, `\tau` has discriminant `D=B^2-4AC`. In fact, `\tau =3713(-B + \sqrt{D})/(2A)`.37143715INPUT:37163717- `f` -- binary quadratic form37183719EXAMPLES::37203721sage: P = EllipticCurve('57a1').heegner_point(-8)3722sage: R, U = P._good_tau_representatives()3723sage: f = U[0][0]; f3724(57, 26, 3)3725sage: P._qf_to_tau(f)37261/114*sqrt_minus_8 - 13/573727"""3728c = self.conductor()3729A,B,_ = f3730alpha = c * self.quadratic_field().gen() # this is sqrt(D) = sqrt(c^2*disc(K))3731return (-B + alpha)/(2*A)37323733def _qf_from_tau(self, tau):3734r"""3735Return quadratic form associated to a given `\tau` in the upper3736half plane.37373738INPUT:37393740- `\tau` -- quadratic element of the upper half plane37413742EXAMPLES::37433744sage: P = EllipticCurve('57a1').heegner_point(-8)3745sage: R, U = P._good_tau_representatives()3746sage: f = U[0][0]; f3747(57, 26, 3)3748sage: tau = P._qf_to_tau(f); tau37491/114*sqrt_minus_8 - 13/573750sage: P._qf_from_tau(tau)3751(57, 26, 3)3752"""3753g = tau.minpoly()3754g *= g.denominator()3755return (ZZ(g[2]), ZZ(g[1]), ZZ(g[0]))375637573758def _qf_atkin_lehner_act(self, Q, f):3759r"""3760Given a positive integer `Q` with `Q | N` and `\gcd(Q, N/Q) =37611`, we compute the quadratic form corresponding to the image3762of the `tau` corresponding to `f` under the Atkin-Lehner3763operator `W_Q`.37643765We do this by letting `u,v` be integers such that3766`u Q^2 - v N = Q`, and using that `W_Q` sends `\tau`3767to `( (u Q \tau + v) / (N \tau + Q) ) / Q`.37683769INPUT:37703771- `Q` -- integer that divides the level `N`37723773- `f` -- quadratic form37743775OUTPUT:37763777- quadratic form37783779EXAMPLES::37803781sage: P = EllipticCurve('57a1').heegner_point(-8)3782sage: R, U = P._good_tau_representatives()3783sage: f = U[0][0]; f3784(57, 26, 3)3785sage: P._qf_atkin_lehner_act(3, f)3786(1938, 1204, 187)3787sage: g = P._qf_atkin_lehner_act(19, f); g3788(114, -64, 9)3789sage: h = P._qf_atkin_lehner_act(19, g); h3790(7353, -4762, 771)3791sage: BinaryQF(f).reduced_form() == BinaryQF(h).reduced_form()3792True3793"""3794N = self.__E.conductor()3795g, u, v = arith.xgcd(Q*Q, -N)3796assert g == Q3797tau = self._qf_to_tau(f)3798tau2 = ((u*Q*tau + v) / (N*tau + Q))3799return self._qf_from_tau(tau2)380038013802def _qf_reduce(self, f):3803"""3804Given a binary quadratic form `f` represented as a 3-tuple3805(A,B,C), return the reduced binary quadratic form equivalent3806to `f`, represented in the same way.38073808EXAMPLES::38093810sage: P = EllipticCurve('57a1').heegner_point(-8)3811sage: R, U = P._good_tau_representatives()3812sage: f = U[0][0]; f3813(57, 26, 3)3814sage: P._qf_reduce(f)3815(1, 0, 2)3816"""3817return tuple(BinaryQF(f).reduced_form())38183819def kolyvagin_cohomology_class(self, n=None):3820"""3821Return the Kolyvagin class associated to this Heegner point.38223823INPUT:38243825- `n` -- positive integer that divides the gcd of `a_p`3826and `p+1` for all `p` dividing the conductor. If `n` is3827None, choose the largest valid `n`.38283829EXAMPLES::38303831sage: y = EllipticCurve('389a').heegner_point(-7,5)3832sage: y.kolyvagin_cohomology_class(3)3833Kolyvagin cohomology class c(5) in H^1(K,E[3])3834"""3835return KolyvaginCohomologyClassEn(self.kolyvagin_point(), n)38363837#########################################################################################3838# Kolyvagin Points P_c3839#########################################################################################3840class KolyvaginPoint(HeegnerPoint):3841"""3842A Kolyvagin point.38433844EXAMPLES:38453846We create a few Kolyvagin points::38473848sage: EllipticCurve('11a1').kolyvagin_point(-7)3849Kolyvagin point of discriminant -7 on elliptic curve of conductor 113850sage: EllipticCurve('37a1').kolyvagin_point(-7)3851Kolyvagin point of discriminant -7 on elliptic curve of conductor 373852sage: EllipticCurve('37a1').kolyvagin_point(-67)3853Kolyvagin point of discriminant -67 on elliptic curve of conductor 373854sage: EllipticCurve('389a1').kolyvagin_point(-7, 5)3855Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 38938563857One can also associated a Kolyvagin point to a Heegner point::38583859sage: y = EllipticCurve('37a1').heegner_point(-7); y3860Heegner point of discriminant -7 on elliptic curve of conductor 373861sage: y.kolyvagin_point()3862Kolyvagin point of discriminant -7 on elliptic curve of conductor 3738633864TESTS::38653866sage: y = EllipticCurve('37a1').heegner_point(-7)3867sage: type(y)3868<class 'sage.schemes.elliptic_curves.heegner.HeegnerPointOnEllipticCurve'>3869sage: loads(dumps(y)) == y3870True3871"""3872def __init__(self, heegner_point):3873"""3874Create a Kolyvagin point.38753876INPUT:38773878- ``heegner_point`` -- a Heegner point on some elliptic curve38793880EXAMPLES:38813882We directly construct a Kolyvagin point from the KolyvaginPoint class::38833884sage: y = EllipticCurve('37a1').heegner_point(-7)3885sage: sage.schemes.elliptic_curves.heegner.KolyvaginPoint(y)3886Kolyvagin point of discriminant -7 on elliptic curve of conductor 373887"""3888if not heegner_point.satisfies_kolyvagin_hypothesis():3889raise ValueError, "Heegner point doesn't satisfy Kolyvagin hypothesis"3890self.__heegner_point = heegner_point3891HeegnerPoint.__init__(self, heegner_point.level(), heegner_point.discriminant(),3892heegner_point.conductor())38933894def satisfies_kolyvagin_hypothesis(self, n=None):3895r"""3896Return True if this Kolyvagin point satisfies the Heegner3897hypothesis for `n`, so that it defines a Galois equivariant3898element of `E(K_c)/n E(K_c)`.38993900EXAMPLES::39013902sage: y = EllipticCurve('389a').heegner_point(-7,5); P = y.kolyvagin_point()3903sage: P.kolyvagin_cohomology_class(3)3904Kolyvagin cohomology class c(5) in H^1(K,E[3])3905sage: P.satisfies_kolyvagin_hypothesis(3)3906True3907sage: P.satisfies_kolyvagin_hypothesis(5)3908False3909sage: P.satisfies_kolyvagin_hypothesis(7)3910False3911sage: P.satisfies_kolyvagin_hypothesis(11)3912False3913"""3914return self.__heegner_point.satisfies_kolyvagin_hypothesis(n)39153916def curve(self):3917r"""3918Return the elliptic curve over `\QQ` on which this Kolyvagin3919point sits.39203921EXAMPLES::39223923sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67, 3)3924sage: P.curve()3925Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field3926"""3927return self.__heegner_point.curve()39283929def heegner_point(self):3930"""3931This Kolyvagin point `P_c` is associated to some Heegner point3932`y_c` via Kolyvagin's construction. This function returns that3933point `y_c`.39343935EXAMPLES::39363937sage: E = EllipticCurve('37a1')3938sage: P = E.kolyvagin_point(-67); P3939Kolyvagin point of discriminant -67 on elliptic curve of conductor 373940sage: y = P.heegner_point(); y3941Heegner point of discriminant -67 on elliptic curve of conductor 373942sage: y.kolyvagin_point() is P3943True3944"""3945return self.__heegner_point39463947def _repr_(self):3948"""3949Return string representation of this Kolyvagin point.39503951EXAMPLES::39523953sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67,7); P._repr_()3954'Kolyvagin point of discriminant -67 and conductor 7 on elliptic curve of conductor 37'3955"""3956s = repr(self.__heegner_point)3957return s.replace('Heegner','Kolyvagin')39583959def index(self, *args, **kwds):3960"""3961Return index of this Kolyvagin point in the full group of3962$K_c$ rational points on $E$.39633964When the conductor is 1, this is computed numerically using3965the Gross-Zagier formula and explicit point search, and it may3966be off by $2$. See the documentation for ``E.heegner_index``,3967where `E` is the curve attached to self.39683969EXAMPLES::39703971sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67); P.index()397263973"""3974if self.conductor() == 1:3975return self.__heegner_point._trace_index(*args, **kwds)3976raise NotImplementedError39773978def numerical_approx(self, prec=53):3979"""3980Return a numerical approximation to this Kolyvagin point using3981prec bits of working precision.39823983INPUT:39843985- ``prec`` -- precision in bits (default: 53)39863987EXAMPLES::39883989sage: P = EllipticCurve('37a1').kolyvagin_point(-7); P3990Kolyvagin point of discriminant -7 on elliptic curve of conductor 373991sage: P.numerical_approx() # approx. (0 : 0 : 1)3992(...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000)3993sage: P.numerical_approx(100)[0].abs() < 2.0^-993994True39953996sage: P = EllipticCurve('389a1').kolyvagin_point(-7, 5); P3997Kolyvagin point of discriminant -7 and conductor 5 on elliptic curve of conductor 38939983999Numerical approximation is only implemented for points of conductor 1::40004001sage: P.numerical_approx()4002Traceback (most recent call last):4003...4004NotImplementedError4005"""4006if self.conductor() == 1:4007return self.__heegner_point._trace_numerical_conductor_1(prec)4008raise NotImplementedError40094010def point_exact(self, prec=53):4011"""4012INPUT:40134014- ``prec`` -- precision in bits (default: 53)40154016EXAMPLES:40174018A rank 1 curve::40194020sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4021sage: P.point_exact()4022(6 : -15 : 1)4023sage: P.point_exact(40)4024(6 : -15 : 1)4025sage: P.point_exact(20)4026Traceback (most recent call last):4027...4028RuntimeError: insufficient precision to find exact point40294030A rank 0 curve::40314032sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)4033sage: P.point_exact()4034(-1/2*sqrt_minus_7 + 1/2 : -2*sqrt_minus_7 - 2 : 1)40354036A rank 2 curve::40374038sage: E = EllipticCurve('389a1'); P = E.kolyvagin_point(-7)4039sage: P.point_exact()4040(0 : 1 : 0)40414042"""4043if self.conductor() == 1:4044# the result is a point defined over K in the conductor 1 case, which is easier.4045P = self.numerical_approx(prec)40464047E = self.curve()4048if P[2] == 0:4049return E(0)40504051if E.root_number() == -1:4052return self._recognize_point_over_QQ(P, 2*self.index())4053else:4054# root number +1. We use algdep to recognize the x4055# coordinate, stick it in the appropriate quadratic4056# field, then make sure that we got the right4057# embedding, and if not fix things so we do.4058x = P[0]4059C = x.parent()4060f = x.algdep(2)4061K = self.quadratic_field()4062roots = [r[0] for r in f.roots(K)]4063if len(roots) == 0:4064raise RuntimeError, "insufficient precision to find exact point"4065if len(roots) == 1:4066X = roots[0]4067else:4068d = [abs(C(r) - x) for r in roots]4069if d[0] == d[1]:4070raise RuntimeError, "insufficient precision to distinguish roots"4071if d[0] < d[1]:4072X = roots[0]4073else:4074X = roots[1]4075F = E.change_ring(K)4076Q = F.lift_x(X, all=True)4077if len(Q) == 1:4078return Q[0]4079if len(Q) == 0:4080raise RuntimeError, "insufficient precision"4081y = P[1]4082d = [abs(C(r[1])-y) for r in Q]4083if d[0] == d[1]:4084raise RuntimeError, "insufficient precision to distinguish roots"4085if d[0] < d[1]:4086return Q[0]4087else:4088return Q[1]40894090else:4091raise NotImplementedError40924093def plot(self, prec=53, *args, **kwds):4094r"""4095Plot a Kolyvagin point `P_1` if it is defined over the4096rational numbers.40974098EXAMPLES::40994100sage: E = EllipticCurve('37a'); P = E.heegner_point(-11).kolyvagin_point()4101sage: P.plot(prec=30, pointsize=50, rgbcolor='red') + E.plot()4102"""4103if self.conductor() != 1:4104raise NotImplementedError41054106E = self.curve()4107if E.root_number() == -1:4108P = self.numerical_approx(prec=prec)4109from sage.plot.all import point, Graphics4110if not P:4111# point at infinity4112return Graphics()4113return point((P[0].real(), P[1].real()),*args, **kwds)4114else:4115raise NotImplementedError411641174118@cached_method4119def trace_to_real_numerical(self, prec=53):4120"""4121Return the trace of this Kolyvagin point down to the real4122numbers, computed numerically using prec bits of working4123precision.41244125EXAMPLES::41264127sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4128sage: PP = P.numerical_approx(); PP4129(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)4130sage: [c.real() for c in PP]4131[6.00000000000000, -15.0000000000000, 1.00000000000000]4132sage: all([c.imag().abs() < 1e-14 for c in PP])4133True4134sage: P.trace_to_real_numerical()4135(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4136sage: P.trace_to_real_numerical(prec=80)4137(1.6135552913198573127230 : -2.1844684078888023289187 : 1.0000000000000000000000)41384139"""4140# Compute numerical approximation of P in E(K).4141P = self.numerical_approx(prec=prec)4142# Trace this numerical approximation down to E(Q) (numerically).4143E = P.curve()4144if self.curve().root_number() == -1:4145R = 2*P4146else:4147R = P + E.point([x.conjugate() for x in P],check=False)4148F = self.curve().change_ring(rings.RealField(prec))4149return F.point([x.real() for x in R], check=False)41504151@cached_method4152def _trace_exact_conductor_1(self, prec=53):4153r"""4154Return the trace from `K` to `\QQ` of this Kolyvagin point in4155the case of conductor 1, computed using prec bits of4156precision, then approximated using some algorithm (e.g.,4157continued fractions). If the precision is not enough to4158determine a point on the curve, then a RuntimeError is raised.4159Even if the precision determines a point, there is no guarantee4160that it is correct.41614162EXAMPLES:41634164A Kolyvagin point on a rank 1 curve::41654166sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4167sage: P.trace_to_real_numerical()4168(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4169sage: P._trace_exact_conductor_1() # the actual point we're reducing4170(1357/841 : -53277/24389 : 1)4171sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()417212.00000000000004173"""4174if not self.conductor() == 1:4175raise ValueError, "the conductor must be 1"41764177P = self.trace_to_real_numerical(prec)4178return self._recognize_point_over_QQ(P, 2*self.index())41794180def _recognize_point_over_QQ(self, P, n):4181"""4182Used internally when computing an exact point on an elliptic curve.41834184INPUT:41854186- `P` -- numerical approximation for a point on `E`41874188- `n` -- upper bound on divisibility index of `P` in group `E(\QQ)`41894190EXAMPLES::41914192sage: E = EllipticCurve('43a'); P = E.heegner_point(-20).kolyvagin_point()4193sage: PP = P.numerical_approx(); PP4194(...e-16 : -1.00000000000000 : 1.00000000000000)4195sage: P._recognize_point_over_QQ(PP, 4)4196(0 : -1 : 1)4197"""4198# Here is where we *should* implement the "method of Cremona4199# etc" mentioned in Watkins' article... which involves local4200# heights.4201E = self.curve() # over Q4202v = sum([list(n*w) for w in E.gens()] + [list(w) for w in E.torsion_points()], [])4203# note -- we do not claim to prove anything, so making up a factor of 100 is fine.4204max_denominator = 100*max([z.denominator() for z in v])4205try:4206# the coercion below also checks if point is on elliptic curve4207return E([x.real().nearby_rational(max_denominator=max_denominator) for x in P])4208except TypeError:4209raise RuntimeError, "insufficient precision to find exact point"42104211def mod(self, p, prec=53):4212r"""4213Return the trace of the reduction `Q` modulo a prime over `p` of this4214Kolyvagin point as an element of `E(\GF{p})`, where4215`p` is any prime that is inert in `K` that is coprime to `NDc`.4216The point `Q` is only well defined up to an element of4217`(p+1) E(\GF{p})`, i.e., it gives a well defined element4218of the abelian group `E(\GF{p}) / (p+1) E(\GF{p})`.42194220See Stein, "Toward a Generalization of the Gross-Zagier Conjecture",4221Proposition 5.4 for a proof of the above well-definedness assertion.42224223EXAMPLES:42244225A Kolyvagin point on a rank 1 curve::42264227sage: E = EllipticCurve('37a1'); P = E.kolyvagin_point(-67)4228sage: P.mod(2)4229(1 : 1 : 1)4230sage: P.mod(3)4231(1 : 0 : 1)4232sage: P.mod(5)4233(2 : 2 : 1)4234sage: P.mod(7)4235(6 : 0 : 1)4236sage: P.trace_to_real_numerical()4237(1.61355529131986 : -2.18446840788880 : 1.00000000000000)4238sage: P._trace_exact_conductor_1() # the actual point we're reducing4239(1357/841 : -53277/24389 : 1)4240sage: (P._trace_exact_conductor_1().height() / E.regulator()).sqrt()424112.000000000000042424243Here the Kolyvagin point is a torsion point (since `E` has4244rank 1), and we reduce it modulo several primes.::42454246sage: E = EllipticCurve('11a1'); P = E.kolyvagin_point(-7)4247sage: P.mod(3,70)4248(1 : 2 : 1)4249sage: P.mod(5,70)4250(1 : 4 : 1)4251sage: P.mod(7,70)4252Traceback (most recent call last):4253...4254ValueError: p must be coprime to conductors and discriminant4255sage: P.mod(11,70)4256Traceback (most recent call last):4257...4258ValueError: p must be coprime to conductors and discriminant4259sage: P.mod(13,70)4260(3 : 4 : 1)4261"""4262# check preconditions4263p = ZZ(p)4264if not p.is_prime():4265raise ValueError, "p must be prime"4266E = self.curve()4267D = self.discriminant()4268if (E.conductor() * D * self.conductor()) % p == 0:4269raise ValueError, "p must be coprime to conductors and discriminant"4270K = self.heegner_point().quadratic_field()4271if len(K.factor(p)) != 1:4272raise ValueError, "p must be inert"42734274# do actual calculation4275if self.conductor() == 1:42764277P = self._trace_exact_conductor_1(prec = prec)4278return E.change_ring(GF(p))(P)42794280else:42814282raise NotImplementedError42834284## def congruent_rational_point(self, n, prec=53):4285## r"""4286## Let `P` be this Kolyvagin point. Determine whether there is a4287## point `z` in `E(\QQ)` such that `z - P \in n E(K_c)`, where `K_c`4288## is the ring class field over which this Kolyvagin point is defined.4289## If `z` exists return `z`. Otherwise return None.4290##4291## INPUT:4292##4293## - `n` -- positive integer4294##4295## - ``prec`` -- positive integer (default: 53)4296##4297##4298## EXAMPLES::4299##4300## """4301## raise NotImplementedError430243034304def kolyvagin_cohomology_class(self, n=None):4305"""4306INPUT:43074308- `n` -- positive integer that divides the gcd of `a_p`4309and `p+1` for all `p` dividing the conductor. If `n` is4310None, choose the largest valid `n`.43114312EXAMPLES::43134314sage: y = EllipticCurve('389a').heegner_point(-7,5)4315sage: P = y.kolyvagin_point()4316sage: P.kolyvagin_cohomology_class(3)4317Kolyvagin cohomology class c(5) in H^1(K,E[3])43184319sage: y = EllipticCurve('37a').heegner_point(-7,5).kolyvagin_point()4320sage: y.kolyvagin_cohomology_class()4321Kolyvagin cohomology class c(5) in H^1(K,E[2])4322"""4323D = self.discriminant()4324c = self.conductor()43254326return 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 doesn't satisfy Kolyvagin hypothesis for %s"%n4362self.__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 isn't 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([z for z in reps if arith.gcd(z) == 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"%p4884if 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"%p4889i, 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 don't 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 isn't 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"%p5347bad = 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); k5389(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()]5392[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 doesn't 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's 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"""5766from sage.matrix.all import matrix5767return matrix(QQ,2,4,[[1,0,0,0], self.__beta.coefficient_tuple()])57685769@cached_method5770def domain(self):5771"""5772Return the domain of this embedding.57735774EXAMPLES::57755776sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5777sage: H.optimal_embeddings(-7, 2, R)[0].domain()5778Order in Number Field in a with defining polynomial x^2 + 75779"""5780R, a = quadratic_order(self.__D, self.__c)57815782# The following assumption is used, e.g., in the __call__5783# method. I know that it is satisfied by the current5784# implementation. But somebody might someday annoying change5785# the implementation, and we want to catch that if it were to5786# ever happen.57875788assert R.basis() == [1, a], "an assumption about construction of orders is violated"5789self.__domain_gen = a5790return R57915792def domain_gen(self):5793r"""5794Return the specific generator `c \sqrt{D}` for the domain5795order.57965797EXAMPLES::57985799sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5800sage: f = H.optimal_embeddings(-7, 2, R)[0]5801sage: f.domain_gen()58022*a5803sage: f.domain_gen()^25804-285805"""5806self.domain()5807return self.__domain_gen58085809def domain_conductor(self):5810"""5811Return the conductor of the domain.58125813EXAMPLES::58145815sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5816sage: H.optimal_embeddings(-7, 2, R)[0].domain_conductor()581725818"""5819return self.__c58205821def beta(self):5822r"""5823Return the element `\beta` in the quaternion algebra order5824that `c\sqrt{D}` maps to.58255826EXAMPLES::58275828sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5829sage: H.optimal_embeddings(-7, 2, R)[0].beta()5830-5*i + k5831"""5832return self.__beta58335834def codomain(self):5835"""5836Return the codomain of this embedding.58375838EXAMPLES::58395840sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5841sage: H.optimal_embeddings(-7, 2, R)[0].codomain()5842Order 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)5843"""5844return self.__R58455846@cached_method5847def _repr_(self):5848"""5849Return string representation of this embedding.58505851EXAMPLES::58525853sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5854sage: f = H.optimal_embeddings(-7, 2, R)[0]; f._repr_()5855'Embedding sending 2*sqrt(-7) to -5*i + k'5856"""5857R = self.domain()5858a = '%ssqrt(%s)'%('%s*'%self.__c if self.__c > 1 else '', self.__D)5859return "Embedding sending %s to %s"%(a, self.__beta)58605861def conjugate(self):5862"""5863Return the conjugate of this embedding, which is also an5864embedding.58655866EXAMPLES::58675868sage: H = heegner_points(11).reduce_mod(3); R = H.left_orders()[0]5869sage: f = H.optimal_embeddings(-7, 2, R)[0]5870sage: f.conjugate()5871Embedding sending 2*sqrt(-7) to 5*i - k5872sage: f5873Embedding sending 2*sqrt(-7) to -5*i + k5874"""5875return HeegnerQuatAlgEmbedding(self.__D, self.__c,5876self.__R, self.__beta.conjugate())587758785879#############################################################################5880# Utility Functions5881#############################################################################58825883def quadratic_order(D, c, names='a'):5884"""5885Return order of conductor `c` in quadratic field with fundamental5886discriminant `D`.58875888INPUT:58895890- `D` -- fundamental discriminant58915892- `c` -- conductor58935894- ``names`` -- string (default: 'a')58955896OUTPUT:58975898- order `R` of conductor `c` in an imaginary quadratic field58995900- the element `c\sqrt{D}` as an element of `R`59015902The generator for the field is named 'a' by default.59035904EXAMPLES::59055906sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3)5907(Order in Number Field in a with defining polynomial x^2 + 7, 3*a)5908sage: sage.schemes.elliptic_curves.heegner.quadratic_order(-7,3,'alpha')5909(Order in Number Field in alpha with defining polynomial x^2 + 7, 3*alpha)5910"""5911K = QuadraticField(D, names)5912sqrtD = K.gen(0)5913t = sqrtD * c5914R = K.order([t])5915return R, R(t)59165917def class_number(D):5918"""5919Return the class number of the quadratic field with fundamental5920discriminant `D`.59215922INPUT:59235924- `D` -- integer59255926EXAMPLES::59275928sage: sage.schemes.elliptic_curves.heegner.class_number(-20)592925930sage: sage.schemes.elliptic_curves.heegner.class_number(-23)593135932sage: sage.schemes.elliptic_curves.heegner.class_number(-163)5933159345935A ValueError is raised when `D` is not a fundamental5936discriminant::59375938sage: sage.schemes.elliptic_curves.heegner.class_number(-5)5939Traceback (most recent call last):5940...5941ValueError: D (=-5) must be a fundamental discriminant5942"""5943if not number_field.is_fundamental_discriminant(D):5944raise ValueError, "D (=%s) must be a fundamental discriminant"%D5945return QuadraticField(D,'a').class_number()59465947def is_inert(D, p):5948r"""5949Return True if p is an inert prime in the field `\QQ(\sqrt{D})`.59505951INPUT:59525953- `D` -- fundamental discriminant59545955- `p` -- prime integer59565957EXAMPLES::59585959sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,3)5960True5961sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,7)5962False5963sage: sage.schemes.elliptic_curves.heegner.is_inert(-7,11)5964False5965"""5966K = QuadraticField(D,'a')5967F = K.factor(p)5968return len(F) == 1 and F[0][1] == 159695970def is_split(D, p):5971r"""5972Return True if p is a split prime in the field `\QQ(\sqrt{D})`.59735974INPUT:59755976- `D` -- fundamental discriminant59775978- `p` -- prime integer59795980EXAMPLES::59815982sage: sage.schemes.elliptic_curves.heegner.is_split(-7,3)5983False5984sage: sage.schemes.elliptic_curves.heegner.is_split(-7,7)5985False5986sage: sage.schemes.elliptic_curves.heegner.is_split(-7,11)5987True5988"""5989K = QuadraticField(D,'a')5990F = K.factor(p)5991return len(F) == 259925993def is_ramified(D, p):5994r"""5995Return True if p is a ramified prime in the field `\QQ(\sqrt{D})`.59965997INPUT:59985999- `D` -- fundamental discriminant60006001- `p` -- prime integer60026003EXAMPLES::60046005sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,2)6006False6007sage: sage.schemes.elliptic_curves.heegner.is_ramified(-7,7)6008True6009sage: sage.schemes.elliptic_curves.heegner.is_ramified(-1,2)6010True6011"""6012return QuadraticField(D,'a').discriminant() % p == 060136014def nearby_rational_poly(f, **kwds):6015r"""6016Return a polynomial whose coefficients are rational numbers close6017to the coefficients of `f`.60186019INPUT:60206021- `f` -- polynomial with real floating point entries60226023- ``**kwds`` -- passed on to ``nearby_rational`` method60246025EXAMPLES::60266027sage: R.<x> = RR[]6028sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-16)602921/10*X^2 + 7/2*X - 6/56030sage: sage.schemes.elliptic_curves.heegner.nearby_rational_poly(2.1*x^2 + 3.5*x - 1.2, max_error=10e-17)60314728779608739021/2251799813685248*X^2 + 7/2*X - 5404319552844595/45035996273704966032sage: RR(4728779608739021/2251799813685248 - 21/10)60338.88178419700125e-176034"""6035R = QQ['X']6036return R([a.nearby_rational(**kwds) for a in f])60376038def simplest_rational_poly(f, prec):6039"""6040Return a polynomial whose coefficients are as simple as possible6041rationals that are also close to the coefficients of f.60426043INPUT:60446045- `f` -- polynomial with real floating point entries60466047- ``prec`` -- positive integer60486049EXAMPLES::60506051sage: R.<x> = RR[]6052sage: sage.schemes.elliptic_curves.heegner.simplest_rational_poly(2.1*x^2 + 3.5*x - 1.2, 53)605321/10*X^2 + 7/2*X - 6/56054"""6055R = QQ['X']6056Z = RealField(prec)6057return R([Z(a).simplest_rational() for a in f])60586059def satisfies_weak_heegner_hypothesis(N, D):6060r"""6061Check that `D` satisfies the weak Heegner hypothesis relative to `N`.6062This is all that is needed to define Heegner points.60636064The condition is that `D<0` is a fundamental discriminant and that6065each unramified prime dividing `N` splits in `K=\QQ(\sqrt{D})` and6066each ramified prime exactly divides `N`. We also do not require6067that `D<-4`.60686069INPUT:60706071- `N` -- positive integer60726073- `D` -- negative integer60746075EXAMPLES::60766077sage: s = sage.schemes.elliptic_curves.heegner.satisfies_weak_heegner_hypothesis6078sage: s(37,-7)6079True6080sage: s(37,-37)6081False6082sage: s(37,-37*4)6083True6084sage: s(100,-4)6085False6086sage: [D for D in [-1,-2,..,-40] if s(37,D)]6087[-3, -4, -7, -11, -40]6088sage: [D for D in [-1,-2,..,-100] if s(37,D)]6089[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]6090sage: EllipticCurve('37a').heegner_discriminants_list(10)6091[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]6092"""6093if not number_field.is_fundamental_discriminant(D):6094return False6095if D >= 0: return False6096K = QuadraticField(D,'a')6097for p, e in N.factor():6098if D % p == 0:6099if e > 1:6100return False6101elif D.kronecker(p) != 1:6102return False6103return True61046105def make_monic(f):6106r"""6107``make_monic`` returns a monic integral polynomial `g` and an6108integer `d` such that if `\alpha` is a root of `g` then a root of6109`f` is `\alpha/d`.61106111INPUT:61126113- f -- polynomial over the rational numbers61146115EXAMPLES::61166117sage: R.<x> = QQ[]6118sage: sage.schemes.elliptic_curves.heegner.make_monic(3*x^3 + 14*x^2 - 7*x + 5)6119(x^3 + 14*x^2 - 21*x + 45, 3)61206121In this example we verify that make_monic does what we claim it does::61226123sage: K.<a> = NumberField(x^3 + 17*x - 3)6124sage: f = (a/7+2/3).minpoly(); f6125x^3 - 2*x^2 + 247/147*x - 4967/92616126sage: g, d = sage.schemes.elliptic_curves.heegner.make_monic(f)6127sage: g6128x^3 - 18522*x^2 + 144110421*x - 4260003230076129sage: d613092616131sage: K.<b> = NumberField(g)6132sage: (b/d).minpoly()6133x^3 - 2*x^2 + 247/147*x - 4967/92616134"""6135# make f monic6136n = f.degree()6137f = f / f.leading_coefficient()6138# find lcm of denominators6139d = arith.lcm([b.denominator() for b in f.list() if b])6140x = f.variables()[0]6141g = (d**n) * f(x/d)6142return g, d614361446145#####################################################################6146# Elliptic curve methods6147# Everywhere self below is an elliptic curve over QQ.6148#####################################################################61496150def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True):6151"""6152Returns the Heegner point on this curve associated to the6153quadratic imaginary field `K=\QQ(\sqrt{D})`.61546155If the optional parameter `c` is given, returns the higher Heegner6156point associated to the order of conductor `c`.61576158INPUT::61596160- `D` -- a Heegner discriminant61616162- `c` -- (default: 1) conductor, must be coprime to `DN`61636164- `f` -- binary quadratic form or 3-tuple `(A,B,C)` of coefficients6165of `AX^2 + BXY + CY^2`61666167- ``check`` -- bool (default: True)616861696170OUTPUT::61716172The Heegner point `y_c`.617361746175EXAMPLES::61766177sage: E = EllipticCurve('37a')6178sage: E.heegner_discriminants_list(10)6179[-7, -11, -40, -47, -67, -71, -83, -84, -95, -104]6180sage: P = E.heegner_point(-7); P # indirect doctest6181Heegner point of discriminant -7 on elliptic curve of conductor 376182sage: P.point_exact()6183(0 : 0 : 1)6184sage: P.curve()6185Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field6186sage: P = E.heegner_point(-40).point_exact(); P6187(a : a - 2 : 1)6188sage: P = E.heegner_point(-47).point_exact(); P6189(a : a^4 + a - 1 : 1)6190sage: P[0].parent()6191Number Field in a with defining polynomial x^5 - x^4 + x^3 + x^2 - 2*x + 161926193Working out the details manually::61946195sage: P = E.heegner_point(-47).numerical_approx(prec=200)6196sage: f = algdep(P[0], 5); f6197x^5 - x^4 + x^3 + x^2 - 2*x + 16198sage: f.discriminant().factor()619947^262006201The Heegner hypothesis is checked::62026203sage: E = EllipticCurve('389a'); P = E.heegner_point(-5,7);6204Traceback (most recent call last):6205...6206ValueError: N (=389) and D (=-5) must satisfy the Heegner hypothesis62076208We can specify the quadratic form::62096210sage: P = EllipticCurve('389a').heegner_point(-7, 5, (778,925,275)); P6211Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 3896212sage: P.quadratic_form()6213778*x^2 + 925*x*y + 275*y^26214"""6215y = HeegnerPointOnX0N(self.conductor(), D, c, f, check=check)6216return y.map_to_curve(self)62176218def kolyvagin_point(self, D, c=ZZ(1), check=True):6219"""6220Returns the Kolyvagin point on this curve associated to the6221quadratic imaginary field `K=\QQ(\sqrt{D})` and conductor `c`.62226223INPUT:62246225- `D` -- a Heegner discriminant62266227- `c` -- (default: 1) conductor, must be coprime to `DN`62286229- ``check`` -- bool (default: True)623062316232OUTPUT:62336234The Kolyvagin point `P` of conductor `c`.62356236EXAMPLES::62376238sage: E = EllipticCurve('37a1')6239sage: P = E.kolyvagin_point(-67); P6240Kolyvagin point of discriminant -67 on elliptic curve of conductor 376241sage: P.numerical_approx() # imaginary parts approx. 06242(6.00000000000000 ... : -15.0000000000000 ... : 1.00000000000000)6243sage: P.index()624466245sage: g = E((0,-1,1)) # a generator6246sage: E.regulator() == E.regulator_of_points([g])6247True6248sage: 6*g6249(6 : -15 : 1)62506251"""6252return self.heegner_point(D,c,check=check).kolyvagin_point()62536254def ell_heegner_discriminants(self, bound):6255"""6256Return the list of self's Heegner discriminants between -1 and6257-bound.62586259INPUT:626062616262- ``bound (int)`` - upper bound for -discriminant626362646265OUTPUT: The list of Heegner discriminants between -1 and -bound for6266the given elliptic curve.62676268EXAMPLES::62696270sage: E=EllipticCurve('11a')6271sage: E.heegner_discriminants(30) # indirect doctest6272[-7, -8, -19, -24]6273"""6274return [-D for D in xrange(1,bound) if self.satisfies_heegner_hypothesis(-D)]62756276def ell_heegner_discriminants_list(self, n):6277"""6278Return the list of self's first `n` Heegner discriminants smaller6279than -5.62806281INPUT:62826283- ``n (int)`` - the number of discriminants to6284compute628562866287OUTPUT: The list of the first n Heegner discriminants smaller than6288-5 for the given elliptic curve.62896290EXAMPLE::62916292sage: E=EllipticCurve('11a')6293sage: E.heegner_discriminants_list(4) # indirect doctest6294[-7, -8, -19, -24]6295"""6296v = []6297D = -56298while len(v) < n:6299while not self.satisfies_heegner_hypothesis(D):6300D -= 16301v.append(D)6302D -= 16303return v63046305def heegner_point_height(self, D, prec=2):6306r"""6307Use the Gross-Zagier formula to compute the Neron-Tate canonical6308height over `K` of the Heegner point corresponding to `D`, as an6309interval (it's computed to some precision using `L`-functions).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 the conductor.631863196320OUTPUT: Interval that contains the height of the Heegner point.63216322EXAMPLE::63236324sage: E = EllipticCurve('11a')6325sage: E.heegner_point_height(-7)63260.22227?6327"""63286329if not self.satisfies_heegner_hypothesis(D):6330raise ArithmeticError, "Discriminant (=%s) must be a fundamental discriminant that satisfies the Heegner hypothesis."%D6331if D == -3 or D == -4:6332raise ArithmeticError, "Discriminant (=%s) must not be -3 or -4."%D6333eps = self.root_number()6334L1_vanishes = self.lseries().L1_vanishes()63356336IR = rings.RealIntervalField(20) # TODO: why 20 bits here?63376338if eps == 1 and L1_vanishes:6339return IR(0) # rank even hence >= 2, so Heegner point is torsion.63406341RR = rings.RealField()6342from math import sqrt63436344alpha = RR(sqrt(abs(D)))/(2*self.period_lattice().complex_area())6345F = self.quadratic_twist(D)6346E = self6347k_E = prec*sqrt(E.conductor()) + 206348k_F = prec*sqrt(F.conductor()) + 2063496350MIN_ERR = RR('1e-6') # we assume that regulator and6351# discriminant, etc., computed to this accuracy (which is easily the case).6352# this should be made more intelligent / rigorous relative6353# to the rest of the system.63546355if eps == 1: # E has even rank6356LF1, err_F = F.lseries().deriv_at1(k_F)6357LE1, err_E = E.lseries().at1(k_E)6358err_F = max(err_F, MIN_ERR)6359err_E = max(err_E, MIN_ERR)6360return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)63616362else: # E has odd rank6363LE1, err_E = E.lseries().deriv_at1(k_E)6364LF1, err_F = F.lseries().at1(k_F)6365err_F = max(err_F, MIN_ERR)6366err_E = max(err_E, MIN_ERR)6367return IR(alpha-MIN_ERR,alpha+MIN_ERR) * IR(LE1-err_E,LE1+err_E) * IR(LF1-err_F,LF1+err_F)636863696370def heegner_index(self, D, min_p=2, prec=5, descent_second_limit=12, verbose_mwrank=False):6371r"""6372Return an interval that contains the index of the Heegner6373point `y_K` in the group of `K`-rational points modulo torsion6374on this elliptic curve, computed using the Gross-Zagier6375formula and/or a point search, or possibly half the index6376if the rank is greater than one.63776378.. note::63796380If ``min_p`` is bigger than 2 then the index can be off by6381any prime less than ``min_p``. This function returns the6382index divided by `2` exactly when the rank of `E(K)` is6383greater than 1 and `E(\QQ)_{/tor} \oplus E^D(\QQ)_{/tor}`6384has index `2` in `E(K)_{/tor}`, where the second factor6385undergoes a twist.63866387INPUT:638863896390- ``D (int)`` - Heegner discriminant63916392- ``min_p (int)`` - (default: 2) only rule out primes6393= min_p dividing the index.63946395- ``verbose_mwrank (bool)`` - (default: False); print lots of6396mwrank search status information when computing regulator63976398- ``prec (int)`` - (default: 5), use prec\*sqrt(N) +639920 terms of L-series in computations, where N is the conductor.64006401- ``descent_second_limit`` - (default: 12)- used in 2-descent6402when computing regulator of the twist64036404OUTPUT: an interval that contains the index, or half the index64056406EXAMPLES::64076408sage: E = EllipticCurve('11a')6409sage: E.heegner_discriminants(50)6410[-7, -8, -19, -24, -35, -39, -40, -43]6411sage: E.heegner_index(-7)64121.00000?64136414::64156416sage: E = EllipticCurve('37b')6417sage: E.heegner_discriminants(100)6418[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]6419sage: E.heegner_index(-95) # long time (1 second)64202.00000?64216422This tests doing direct computation of the Mordell-Weil group.64236424::64256426sage: EllipticCurve('675b').heegner_index(-11)64273.0000?64286429Currently discriminants -3 and -4 are not supported::64306431sage: E.heegner_index(-3)6432Traceback (most recent call last):6433...6434ArithmeticError: Discriminant (=-3) must not be -3 or -4.64356436The curve 681b returns the true index, which is `3`::64376438sage: E = EllipticCurve('681b')6439sage: I = E.heegner_index(-8); I64403.0000?64416442In fact, whenever the returned index has a denominator of6443`2`, the true index is got by multiplying the returned6444index by `2`. Unfortunately, this is not an if and only if6445condition, i.e., sometimes the index must be multiplied by6446`2` even though the denominator is not `2`.64476448This example demonstrates the `descent_second_limit` option,6449which can be used to fine tune the 2-descent used to compute6450the regulator of the twist::64516452sage: E = EllipticCurve([0, 0, 1, -34874, -2506691])6453sage: E.heegner_index(-8)6454Traceback (most recent call last):6455...6456RuntimeError: ...64576458However when we search higher, we find the points we need::64596460sage: E.heegner_index(-8, descent_second_limit=16)64611.00000?64626463"""6464# First compute upper bound on height of Heegner point.6465tm = verbose("computing heegner point height...")6466h0 = self.heegner_point_height(D, prec=prec)64676468# We divide by 2 to get the height **over Q** of the6469# Heegner point on the twist.64706471ht = h0/26472verbose('Height of heegner point = %s'%ht, tm)64736474if self.root_number() == 1:6475F = self.quadratic_twist(D)6476else:6477F = self6478# Now rank(F) > 06479h = ht.upper()6480verbose("Heegner height bound = %s"%h)6481B = F.CPS_height_bound()6482verbose("CPS bound = %s"%B)6483c = h/(min_p**2) + B6484verbose("Search would have to be up to height = %s"%c)64856486from ell_rational_field import _MAX_HEIGHT64876488IR = rings.RealIntervalField(20) # todo: 20?64896490a = 16491if c > _MAX_HEIGHT or F is self:6492verbose("Doing direct computation of MW group.")6493reg = F.regulator(descent_second_limit=descent_second_limit, verbose=verbose_mwrank)6494if F.rank(use_database=True) == 1:6495z = F.gens()[0]6496FK = F.base_extend(QuadraticField(D,'a'))6497z = FK(z)6498if z.is_divisible_by(2):6499a = 26500else:6501FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]6502if len(FK_even_tor_pts) == 2:6503FK_even_tor_pts.append(sum(FK_even_tor_pts))6504for T in FK_even_tor_pts:6505if (z + T).is_divisible_by(2):6506a = 2; break6507return a*self._adjust_heegner_index(ht/IR(reg))65086509# Do naive search to eliminate possibility that Heegner point6510# is divisible by p<min_p, without finding Heegner point.6511verbose("doing point search")6512P = F.point_search(c)6513verbose("done with point search")6514P = [x for x in P if x.order() == rings.infinity]6515a = 16516if len(P) == 0:6517return IR(1)6518elif len(P) == 1:6519z = P[0]6520FK = F.base_extend(QuadraticField(D,'a'))6521z = FK(z)6522if z.is_divisible_by(2):6523a = 26524else:6525FK_even_tor_pts = [T for T in FK.torsion_subgroup().gens() if T.order()%2==0]6526if len(FK_even_tor_pts) == 2:6527FK_even_tor_pts.append(sum(FK_even_tor_pts))6528for T in FK_even_tor_pts:6529if (z + T).is_divisible_by(2):6530a = 2; break65316532verbose("saturating")6533S, I, reg = F.saturation(P)6534verbose("done saturating")6535return a*self._adjust_heegner_index(ht/IR(reg))65366537def _adjust_heegner_index(self, a):6538r"""6539Take the square root of the interval that contains the Heegner6540index.65416542EXAMPLES::65436544sage: E = EllipticCurve('11a1')6545sage: a = RIF(sqrt(2))-1.41421356237309516546sage: E._adjust_heegner_index(a)65471.?e-86548"""6549if a.lower() < 0:6550IR = rings.RealIntervalField(20) # todo: 20?6551a = IR((0, a.upper()))6552return a.sqrt()655365546555def heegner_index_bound(self, D=0, prec=5, max_height=None):6556r"""6557Assume self has rank 0.65586559Return a list `v` of primes such that if an odd prime `p` divides6560the index of the Heegner point in the group of rational points6561modulo torsion, then `p` is in `v`.65626563If 0 is in the interval of the height of the Heegner point6564computed to the given prec, then this function returns `v =65650`. This does not mean that the Heegner point is torsion, just6566that it is very likely torsion.65676568If we obtain no information from a search up to ``max_height``,6569e.g., if the Siksek et al. bound is bigger than ``max_height``,6570then we return `v = -1`.65716572INPUT:657365746575- ``D (int)`` - (default: 0) Heegner discriminant; if65760, use the first discriminant -4 that satisfies the Heegner6577hypothesis65786579- ``verbose (bool)`` - (default: True)65806581- ``prec (int)`` - (default: 5), use `prec \cdot \sqrt(N) + 20`6582terms of `L`-series in computations, where `N` is the conductor.65836584- ``max_height (float)`` - should be = 21; bound on6585logarithmic naive height used in point searches. Make smaller to6586make this function faster, at the expense of possibly obtaining a6587worse answer. A good range is between 13 and 21.658865896590OUTPUT:659165926593- ``v`` - list or int (bad primes or 0 or -1)65946595- ``D`` - the discriminant that was used (this is6596useful if `D` was automatically selected).65976598- ``exact`` - either False, or the exact Heegner index6599(up to factors of 2)66006601EXAMPLES::66026603sage: E = EllipticCurve('11a1')6604sage: E.heegner_index_bound()6605([2], -7, 2)6606"""6607from ell_rational_field import _MAX_HEIGHT6608if max_height is None:6609max_height = _MAX_HEIGHT6610else:6611max_height = min(float(max_height), _MAX_HEIGHT)6612if self.root_number() != 1:6613raise RuntimeError, "The rank must be 0."66146615if D == 0:6616D = -56617while not self.satisfies_heegner_hypothesis(D):6618D -= 166196620# First compute upper bound on Height of Heegner point.6621ht = self.heegner_point_height(D, prec=prec)6622if 0 in ht:6623return 0, D, False6624F = self.quadratic_twist(D)6625h = ht.upper()6626verbose("Heegner height bound = %s"%h)6627B = F.CPS_height_bound()6628verbose("CPS bound = %s"%B)6629if self.two_torsion_rank() == 0:6630H = h6631else:6632H = 4*h6633p = 36634from sage.all import next_prime6635while True:6636c = H/(2*p**2) + B6637if c < max_height:6638break6639if p > 100:6640break6641p = next_prime(p)6642verbose("Using p = %s"%p)66436644if c > max_height:6645verbose("No information by searching only up to max_height (=%s)."%c)6646return -1, D, False66476648verbose("Searching up to height = %s"%c)6649eps = 10e-566506651def _bound(P):6652"""6653We will use this function below in two places. It bounds the index6654using a nontrivial point.6655"""6656assert len(P) == 166576658S, I, reg = F.saturation(P)66596660IR = rings.RealIntervalField(20) # todo: 20?6661h = IR(reg-eps,reg+eps)6662ind2 = ht/(h/2)6663verbose("index squared = %s"%ind2)6664ind = ind2.sqrt()6665verbose("index = %s"%ind)6666# Compute upper bound on square root of index.6667if ind.absolute_diameter() < 1:6668t, i = ind.is_int()6669if t: # unique integer in interval, so we've found exact index squared.6670return arith.prime_divisors(i), D, i6671raise RuntimeError, "Unable to compute bound for e=%s, D=%s (try increasing precision)"%(self,D)66726673# First try a quick search, in case we get lucky and find6674# a generator.6675P = F.point_search(13, rank_bound=1)6676P = [x for x in P if x.order() == rings.infinity]6677if len(P) > 0:6678return _bound(P)66796680# Do search to eliminate possibility that Heegner point is6681# divisible by primes up to p, without finding Heegner point.6682P = F.point_search(c, rank_bound=1)6683P = [x for x in P if x.order() == rings.infinity]6684if len(P) == 0:6685# We've eliminated the possibility of a divisor up to p.6686return rings.prime_range(3,p), D, False6687else:6688return _bound(P)668966906691#################################################################################6692def _heegner_index_in_EK(self, D):6693"""6694Return the index of the sum of `E(\QQ)/tor + E^D(\QQ)/tor` in `E(K)/tor`.66956696INPUT:6697- `D` -- negative integer; the Heegner discriminant66986699OUTPUT:6700a power of 2 -- the given index67016702EXAMPLES:6703We compute the index for a rank 2 curve and found that it is 2::67046705sage: E = EllipticCurve('389a')6706sage: E._heegner_index_in_EK(-7)6707267086709We explicitly verify in the above example that indeed that6710index is divisibly by 2 by writing down a generator of6711`E(\QQ)/tor + E^D(\QQ)/tor` that is divisible by 2 in `E(K)`::67126713sage: F = E.quadratic_twist(-7)6714sage: K = QuadraticField(-7,'a')6715sage: G = E.change_ring(K)6716sage: phi = F.change_ring(K).isomorphism_to(G)6717sage: P = G(E(-1,1)) + G((0,-1)) + G(phi(F(14,25))); P6718(-867/3872*a - 3615/3872 : -18003/170368*a - 374575/170368 : 1)6719sage: P.division_points(2)6720[(1/8*a + 5/8 : -5/16*a - 9/16 : 1)]67216722"""6723# check conditions, then use cache if possible.6724if not self.satisfies_heegner_hypothesis(D):6725raise ValueError, "D (=%s) must satisfy the Heegner hypothesis"%D6726try:6727return self.__heegner_index_in_EK[D]6728except AttributeError:6729self.__heegner_index_in_EK = {}6730except KeyError:6731pass67326733#####################################################################6734# THE ALGORITHM:6735#6736# For an element P of an abelian group A, let [P] denote the6737# equivalence class of P in the quotient A/A_tor of A by6738# its torsion subgroup. Then for P in E(Q) + E^D(QQ), we6739# have that [P] is divisible by 2 in E(K)/tor if and only6740# there is R in E(K) such that 2*[R] = [P], and this is6741# only if there is R in E(K) and t in E(K)_tor such that6742# 2*R = P + t.6743#6744# Using complex conjugation, one sees that the quotient6745# group E(K)/tor / ( E(Q)/tor + E^D(Q)/tor ) is killed by 2.6746# So to compute the order of this group we run through6747# representatives P for A/(2A) where A = E(Q)/tor + E^D(Q)/tor,6748# and for each we see whether there is a torsion point t in E(K)6749# such that P + t is divisible by 2. Also, we have6750# 2 | P+t <==> 2 | P+n*t for any odd integer n,6751# so we may assume t is of 2-power order.6752#####################################################################67536754E = self # nice shortcut6755F = E.quadratic_twist(D).minimal_model()6756K = rings.QuadraticField(D,'a')67576758# Define a map phi that we'll use to put the points of E^D(QQ)6759# into E(K):6760G = E.change_ring(K)6761G2 = F.change_ring(K)6762phi = G2.isomorphism_to(G)67636764# Basis for E(Q)/tor oplus E^D(QQ)/tor in E(K):6765basis = [G(z) for z in E.gens()] + [G(phi(z)) for z in F.gens()]6766# Make a list of the 2-power order torsion points in E(K), including 0.6767T = [G(z) for z in G.torsion_subgroup().list() if z.order() == 1 or6768((z.order() % 2 == 0 and len(z.order().factor()) == 1))]67696770r = len(basis) # rank6771V = rings.QQ**r6772B = []67736774# Iterate through reps for A/(2*A) creating vectors in (1/2)*ZZ^r6775for v in rings.GF(2)**r:6776if not v: continue6777P = sum([basis[i] for i in range(r) if v[i]])6778for t in T:6779if (P+t).is_divisible_by(2):6780B.append(V(v)/2)67816782A = rings.ZZ**r6783# Take span of our vectors in (1/2)*ZZ^r, along with ZZ^r. This is E(K)/tor.6784W = V.span(B,rings.ZZ) + A67856786# Compute the index in E(K)/tor of A = E(Q)/tor + E^D(Q)/tor, cache, and return.6787index = A.index_in(W)6788self.__heegner_index_in_EK[D] = index6789return index67906791def heegner_sha_an(self, D, prec=53):6792r"""6793Return the conjectural (analytic) order of Sha for E over the field `K=\QQ(\sqrt{D})`.67946795INPUT:67966797- `D` -- negative integer; the Heegner discriminant67986799- prec -- integer (default: 53); bits of precision to6800compute analytic order of Sha68016802OUTPUT:68036804(floating point number) an approximation to the conjectural order of Sha.68056806.. note::68076808Often you'll want to do ``proof.elliptic_curve(False)`` when6809using this function, since often the twisted elliptic6810curves that come up have enormous conductor, and Sha is6811nontrivial, which makes provably finding the Mordell-Weil6812group using 2-descent difficult.681368146815EXAMPLES:68166817An example where E has conductor 11::68186819sage: E = EllipticCurve('11a')6820sage: E.heegner_sha_an(-7) # long time68211.0000000000000068226823The cache works::68246825sage: E.heegner_sha_an(-7) is E.heegner_sha_an(-7) # long time6826True68276828Lower precision::68296830sage: E.heegner_sha_an(-7,10) # long time68311.068326833Checking that the cache works for any precision::68346835sage: E.heegner_sha_an(-7,10) is E.heegner_sha_an(-7,10) # long time6836True68376838Next we consider a rank 1 curve with nontrivial Sha over the6839quadratic imaginary field `K`; however, there is no Sha for `E`6840over `\QQ` or for the quadratic twist of `E`::68416842sage: E = EllipticCurve('37a')6843sage: E.heegner_sha_an(-40) # long time68444.000000000000006845sage: E.quadratic_twist(-40).sha().an() # long time684616847sage: E.sha().an() # long time6848168496850A rank 2 curve::68516852sage: E = EllipticCurve('389a') # long time6853sage: E.heegner_sha_an(-7) # long time68541.0000000000000068556856If we remove the hypothesis that `E(K)` has rank 1 in Conjecture68572.3 in [Gross-Zagier, 1986, page 311], then that conjecture is6858false, as the following example shows::68596860sage: E = EllipticCurve('65a') # long time6861sage: E.heegner_sha_an(-56) # long time68621.000000000000006863sage: E.torsion_order() # long time686426865sage: E.tamagawa_product() # long time686616867sage: E.quadratic_twist(-56).rank() # long time686826869"""6870# check conditions, then return from cache if possible.6871if not self.satisfies_heegner_hypothesis(D):6872raise ValueError, "D (=%s) must satisfy the Heegner hypothesis"%D6873try:6874return self.__heegner_sha_an[(D,prec)]6875except AttributeError:6876self.__heegner_sha_an = {}6877except KeyError:6878pass68796880# Use the BSD conjecture over the quadratic imaginary K --6881# see page 311 of [Gross-Zagier, 1986] for the formula.6882E = self # notational convenience6883F = E.quadratic_twist(D).minimal_model()6884K = rings.QuadraticField(D,'a')68856886# Compute each of the quantities in BSD6887# - The torsion subgroup over K.6888T = E.change_ring(K).torsion_order()68896890# - The product of the Tamagawa numbers, which because D is6891# coprime to N is just the square of the product of the6892# Tamagawa numbers over QQ for E. (we square below in the6893# BSD formula)6894cqprod = E.tamagawa_product()68956896# - The leading term of the L-series, as a product of two6897# other L-series.6898rE = E.rank()6899rF = F.rank()6900L_E = E.lseries().dokchitser(prec).derivative(1, rE)6901L_F = F.lseries().dokchitser(prec).derivative(1, rF)6902# NOTE: The binomial coefficient in the following formula6903# for the leading term in terms of the other two leading6904# terms comes from the product rule for the derivative.6905# You can think this through or just type something like6906# f = function('f',x); g = function('g',x); diff(f*g,6)6907# into Sage to be convinced.6908L = rings.binomial(rE + rF, rE) * (L_E * L_F / (rings.factorial(rE+rF)) )69096910# - ||omega||^2 -- the period. It's twice the volume of the6911# period lattice. See the following paper for a derivation:6912# "Verification of the Birch and Swinnerton-Dyer Conjecture6913# for Specific Elliptic Curves", G. Grigorov, A. Jorza, S. Patrikis,6914# C. Patrascu, W. Stein6915omega = 2 * abs(E.period_lattice().basis_matrix().det())69166917# - The regulator.6918# First we compute the regualtor of the subgroup E(QQ) + E^D(QQ)6919# of E(K). The factor of 2 in the regulator6920# accounts for the fact that the height over K is twice the6921# height over QQ, i.e., for P in E(QQ) we have h_K(P,P) =6922# 2*h_Q(P,P). See, e.g., equation (6.4) on page 230 of6923# [Gross-Zagier, 1986].6924Reg_prod = 2**(rE + rF) * E.regulator(precision=prec) * F.regulator(precision=prec)6925# Next we call off to the _heegner_index_in_EK function, which6926# saturates the group E(QQ) + E^D(QQ) in E(K), given us the index,6927# which must be a power of 2, since E(QQ) is the +1 eigenspace for6928# complex conjugation, and E^D(QQ) is the -1 eigenspace.6929ind = self._heegner_index_in_EK(D)6930# Finally, we know the regulator of E(K).6931Reg = Reg_prod / ind**269326933# - Square root of the absolute value of the discriminant. This is6934# easy; we just make sure the D passed in is an integer, so we6935# can call sqrt with the chosen precision.6936sqrtD = ZZ(abs(D)).sqrt(prec=prec)69376938# - Done: Finally, we plug everything into the BSD formula to get the6939# analytic order of Sha.6940sha_an = (L * T**2 * sqrtD) / (omega * Reg * cqprod**2)69416942# - We cache and return the answer.6943self.__heegner_sha_an[(D,prec)] = sha_an6944return sha_an69456946def _heegner_forms_list(self, D, beta=None, expected_count=None):6947"""6948Returns a list of quadratic forms corresponding to Heegner points6949with discriminant `D` and a choice of `\beta` a square root of6950`D` mod `4N`. Specifically, given a quadratic form6951`f = Ax^2 + Bxy + Cy^2` we let `\tau_f` be a root of `Ax^2 + Bx + C`6952and the discriminant `\Delta(\tau_f) = \Delta(f) = D` must be6953invariant under multiplication by `N`, the conductor of self.69546955`\Delta(N\tau_f) = \Delta(\tau_f) = \Delta(f) = D`69566957EXAMPLES::69586959sage: E = EllipticCurve('37a')6960sage: E._heegner_forms_list(-7)6961[37*x^2 + 17*x*y + 2*y^2]6962sage: E._heegner_forms_list(-195)6963[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]6964sage: E._heegner_forms_list(-195)[-1].discriminant()6965-1956966sage: len(E._heegner_forms_list(-195))696746968sage: QQ[sqrt(-195)].class_number()6969469706971sage: E = EllipticCurve('389a')6972sage: E._heegner_forms_list(-7)6973[389*x^2 + 185*x*y + 22*y^2]6974sage: E._heegner_forms_list(-59)6975[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]6976"""6977if expected_count is None:6978expected_count = number_field.QuadraticField(D, 'a').class_number()6979N = self.conductor()6980if beta is None:6981beta = Integers(4*N)(D).sqrt(extend=False)6982else:6983assert beta**2 == Integers(4*N)(D)6984from sage.quadratic_forms.all import BinaryQF6985b = ZZ(beta) % (2*N)6986all = []6987seen = []6988# Note: This may give a sub-optimal list of forms.6989while True:6990R = (b**2-D)//(4*N)6991for d in R.divisors():6992f = BinaryQF([d*N, b, R//d])6993fr = f.reduced_form()6994if fr not in seen:6995seen.append(fr)6996all.append(f)6997if len(all) == expected_count:6998return all6999b += 2*N70007001def _heegner_best_tau(self, D, prec=None):7002r"""7003Given a discrimanent `D`, find the Heegner point `\tau` in the7004upper half plane with largest imaginary part (which is optimal7005for evaluating the modular parametrization). If the optional7006parameter ``prec`` is given, return `\tau` to ``prec`` bits of7007precision, otherwise return it exactly as a symbolic object.70087009EXAMPLES::70107011sage: E = EllipticCurve('37a')7012sage: E._heegner_best_tau(-7)70131/74*sqrt(-7) - 17/747014sage: EllipticCurve('389a')._heegner_best_tau(-11)70151/778*sqrt(-11) - 355/7787016sage: EllipticCurve('389a')._heegner_best_tau(-11, prec=100)7017-0.45629820051413881748071979434 + 0.0042630138693514136878083968338*I7018"""7019# We know that N|A, so A = N is optimal.7020N = self.conductor()7021b = ZZ(Integers(4*N)(D).sqrt(extend=False) % (2*N))7022# TODO: make sure a different choice of b isn't better?7023return (-b + ZZ(D).sqrt(prec=prec)) / (2*N)70247025def satisfies_heegner_hypothesis(self, D):7026"""7027Returns True precisely when `D` is a fundamental discriminant that7028satisfies the Heegner hypothesis for this elliptic curve.70297030EXAMPLES::70317032sage: E = EllipticCurve('11a1')7033sage: E.satisfies_heegner_hypothesis(-7)7034True7035sage: E.satisfies_heegner_hypothesis(-11)7036False7037"""7038if not number_field.is_fundamental_discriminant(D):7039return False7040D = ZZ(D)7041if D >= 0: return False7042if D.gcd(self.conductor()) != 1:7043return False7044for p, _ in self.conductor().factor():7045if D.kronecker(p) != 1:7046return False7047return True704870497050#####################################################################7051# End of elliptic curve methods.7052#####################################################################705370547055