def velu_codomain(kernel, domain=None): E = kernel[0].curve() R = [] v = 0 w = 0 for P in kernel: g_x = 3 * P[0]**2 + E.a4() g_y = -2 * P[1] u_P = g_y**2 if 2 * P == E(0, 1, 0): R.append(P) v_P = g_x else: if not -P in R: R.append(P) v_P = 2 * g_x else: continue v += v_P w += (u_P + P[0] * v_P) a = E.a4() - 5 * v b = E.a6() - 7 * w if domain != None: try: a = domain.base_field()(a) b = domain.base_field()(b) codomain = EllipticCurve(domain.base_field(), [a, b]) except: codomain = EllipticCurve(E.base_field(), [a, b]) else: codomain = EllipticCurve(E.base_field(), [a, b]) return codomain
def isomorphism_isogeny(E, iso_E): field = E.base_field() iso_E = EllipticCurve(field, [field(iso_E.a4()), field(iso_E.a6())]) isomorphism = E.isomorphism_to(iso_E) x, y = PolynomialRing(E.base_ring(), ['x', 'y']).gens() u, r, s, t = isomorphism.tuple() return u**2 * x + r, u**3 * y + s * u**2 * x + t
def velu(kernel, domain=None): E = kernel[0].curve() Q = PolynomialRing(E.base_field(), ['x', 'y']) x, y = Q.gens() X = x Y = y R = [] v = 0 w = 0 for P in kernel: g_x = 3 * P[0]**2 + E.a4() g_y = -2 * P[1] u_P = g_y**2 if 2 * P == E(0, 1, 0): R.append(P) v_P = g_x else: if not -P in R: R.append(P) v_P = 2 * g_x else: continue v += v_P w += (u_P + P[0] * v_P) X += (v_P / (x - P[0]) + u_P / (x - P[0])**2) Y -= (2 * u_P * y / (x - P[0])**3 + v_P * (y - P[1]) / (x - P[0])**2 - g_x * g_y / (x - P[0])**2) a = E.a4() - 5 * v b = E.a6() - 7 * w if domain != None: try: a = domain.base_field()(a) b = domain.base_field()(b) codomain = EllipticCurve(domain.base_field(), [a, b]) R = PolynomialRing(E.base_field(), ['x', 'y']) Rf = R.fraction_field() X = Rf(X) Y = Rf(Y) f = standard_form((X, Y), domain) except: codomain = EllipticCurve(E.base_field(), [a, b]) f = standard_form((X, Y), E) else: codomain = EllipticCurve(E.base_field(), [a, b]) f = standard_form((X, Y), E) return codomain, f
def c4c6_model(c4, c6, assume_nonsingular=False): r""" Return the elliptic curve [0,0,0,-c4/48,-c6/864] with given c-invariants. INPUT: - ``c4``, ``c6`` -- elements of a number field - ``assume_nonsingular`` (boolean, default False) -- if True, check for integrality and nosingularity. OUTPUT: The elliptic curve with a-invariants [0,0,0,-c4/48,-c6/864], whose c-invariants are the given c4, c6. If the supplied invariants are singular, returns None when ``assume_nonsingular`` is False and raises an ArithmeticError otherwise. EXAMPLES:: sage: from sage.schemes.elliptic_curves.kraus import c4c6_model sage: K.<a> = NumberField(x^3-10) sage: c4c6_model(-217728*a - 679104, 141460992*a + 409826304) Elliptic Curve defined by y^2 = x^3 + (4536*a+14148)*x + (-163728*a-474336) over Number Field in a with defining polynomial x^3 - 10 sage: c4, c6 = EllipticCurve('389a1').c_invariants() sage: c4c6_model(c4,c6) Elliptic Curve defined by y^2 = x^3 - 7/3*x + 107/108 over Rational Field """ if not assume_nonsingular: if not c4c6_nonsingular(c4,c6): return None return EllipticCurve([0,0,0,-c4/48,-c6/864])
def elkies_mod_poly(E, j2, l): j = E.j_invariant() Phi = ClassicalModularPolynomialDatabase()[l] x = PolynomialRing(E.base_field(), 'x').gen() f = Phi(j2, x) F1 = f.derivative()(j) g = Phi(j, x) F2 = g.derivative()(j2) try: lam = E.a6() / E.a4() * F1 / F2 * j * (-18) / l aa = -lam**2 / (j2 * (j2 - 1728) * l**4 * 48) bb = -lam**3 / (j2**2 * (j2 - 1728) * l**6 * 864) return EllipticCurve(E.base_field(), [aa, bb]) except: return None
def c4c6_model(c4, c6, assume_nonsingular=False): r""" Return the elliptic curve [0,0,0,-c4/48,-c6/864] with given c-invariants. INPUT: - ``c4``, ``c6`` -- elements of a number field - ``assume_nonsingular`` (boolean, default False) -- if True, check for integrality and nosingularity. OUTPUT: The elliptic curve with a-invariants [0,0,0,-c4/48,-c6/864], whose c-invariants are the given c4, c6. If the supplied invariants are singular, returns None when ``assume_nonsingular`` is False and raises an ArithmeticError otherwise. """ if not assume_nonsingular: if not check_c4c6_nonsingular(c4, c6): return None return EllipticCurve([0, 0, 0, -c4 / 48, -c6 / 864])
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError( "is_cm_j_invariant() is only implemented for number field elements" ) # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj, (d, f)) for d, f, jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ, 'x')([-j, 1 ]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method == 'old': if h > 100: raise NotImplementedError( "CM data only available for class numbers up to 100") for d, f in cm_orders(h): if jpol == hilbert_class_polynomial(d * f**2): return True, (d, f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminants to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *= 2 if D.valuation(P) > 0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4 * P.norm() dP = DP.squarefree_part() fP = ZZ(DP // dP).isqrt() if cmd == 0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd == 0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd % 4 != 1: cmd = cmd * 4 cmf = cmf // 2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd * f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j) == 0: return True, (cmd, f) return False, None
def make_E(self): coeffs = self.ainvs # list of 5 lists of d strings self.ainvs = [self.field.parse_NFelt(x) for x in coeffs] self.latex_ainvs = web_latex(self.ainvs) from sage.schemes.elliptic_curves.all import EllipticCurve self.E = E = EllipticCurve(self.ainvs) self.equn = web_latex(E) self.numb = str(self.number) # Conductor, discriminant, j-invariant N = E.conductor() self.cond = web_latex(N) self.cond_norm = web_latex(N.norm()) if N.norm() == 1: # since the factorization of (1) displays as "1" self.fact_cond = self.cond else: self.fact_cond = web_latex_ideal_fact(N.factor()) self.fact_cond_norm = web_latex(N.norm().factor()) D = self.field.K().ideal(E.discriminant()) self.disc = web_latex(D) self.disc_norm = web_latex(D.norm()) if D.norm() == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc else: self.fact_disc = web_latex_ideal_fact(D.factor()) self.fact_disc_norm = web_latex(D.norm().factor()) # Minimal model? # # All curves in the database should be given # by models which are globally minimal if possible, else # minimal at all but one prime. But we do not rely on this # here, and the display should be correct if either (1) there # exists a global minimal model but this model is not; or (2) # this model is non-minimal at more than one prime. # self.non_min_primes = non_minimal_primes(E) self.is_minimal = (len(self.non_min_primes) == 0) self.has_minimal_model = True if not self.is_minimal: self.non_min_prime = ','.join( [web_latex(P) for P in self.non_min_primes]) self.has_minimal_model = has_global_minimal_model(E) if not self.is_minimal: Dmin = minimal_discriminant_ideal(E) self.mindisc = web_latex(Dmin) self.mindisc_norm = web_latex(Dmin.norm()) if Dmin.norm( ) == 1: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc else: self.fact_mindisc = web_latex_ideal_fact(Dmin.factor()) self.fact_mindisc_norm = web_latex(Dmin.norm().factor()) j = E.j_invariant() if j: d = j.denominator() n = d * j # numerator exists for quadratic fields only! g = GCD(list(n)) n1 = n / g self.j = web_latex(n1) if d != 1: if n1 > 1: #self.j = "("+self.j+")\(/\)"+web_latex(d) self.j = web_latex(r"\frac{%s}{%s}" % (self.j, d)) else: self.j = web_latex(d) if g > 1: if n1 > 1: self.j = web_latex(g) + self.j else: self.j = web_latex(g) self.j = web_latex(j) self.fact_j = None if j.is_zero(): self.fact_j = web_latex(j) else: try: self.fact_j = web_latex(j.factor()) except (ArithmeticError, ValueError): # if not all prime ideal factors principal pass # CM and End(E) self.cm_bool = "no" self.End = "\(\Z\)" if self.cm: self.cm_bool = "yes (\(%s\))" % self.cm if self.cm % 4 == 0: d4 = ZZ(self.cm) // 4 self.End = "\(\Z[\sqrt{%s}]\)" % (d4) else: self.End = "\(\Z[(1+\sqrt{%s})/2]\)" % self.cm # Q-curve / Base change self.qc = "no" try: if self.q_curve: self.qc = "yes" except AttributeError: # in case the db entry does not have this field set pass # Torsion self.ntors = web_latex(self.torsion_order) self.tr = len(self.torsion_structure) if self.tr == 0: self.tor_struct_pretty = "Trivial" if self.tr == 1: self.tor_struct_pretty = "\(\Z/%s\Z\)" % self.torsion_structure[0] if self.tr == 2: self.tor_struct_pretty = r"\(\Z/%s\Z\times\Z/%s\Z\)" % tuple( self.torsion_structure) torsion_gens = [ E([self.field.parse_NFelt(x) for x in P]) for P in self.torsion_gens ] self.torsion_gens = ",".join([web_latex(P) for P in torsion_gens]) # Rank etc try: self.rk = web_latex(self.rank) except AttributeError: self.rk = "not recorded" # if rank in self: # self.r = web_latex(self.rank) # Local data self.local_data = [] for p in N.prime_factors(): self.local_info = E.local_data(p, algorithm="generic") self.local_data.append({ 'p': web_latex(p), 'norm': web_latex(p.norm().factor()), 'tamagawa_number': self.local_info.tamagawa_number(), 'kodaira_symbol': web_latex(self.local_info.kodaira_symbol()).replace('$', ''), 'reduction_type': self.local_info.bad_reduction_type(), 'ord_den_j': max(0, E.j_invariant().valuation(p)), 'ord_mindisc': self.local_info.discriminant_valuation() }) # URLs of self and related objects: self.urls = {} self.urls['curve'] = url_for(".show_ecnf", nf=self.field_label, conductor_label=self.conductor_label, class_label=self.iso_label, number=self.number) self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=self.conductor_label, class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=self.conductor_label) self.urls['field'] = url_for(".show_ecnf1", nf=self.field_label) if self.field.is_real_quadratic(): self.hmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.urls['hmf'] = url_for('hmf.render_hmf_webpage', field_label=self.field.label, label=self.hmf_label) if self.field.is_imag_quadratic(): self.bmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.friends = [] self.friends += [('Isogeny class ' + self.short_class_label, self.urls['class'])] if self.field.is_real_quadratic(): self.friends += [('Hilbert Modular Form ' + self.hmf_label, self.urls['hmf'])] if self.field.is_imag_quadratic(): self.friends += [ ('Bianchi Modular Form %s not yet available' % self.bmf_label, '') ] self.properties = [('Base field', self.field.field_pretty()), ('Label', self.label)] # Plot n1 = len(E.base_field().embeddings(RR)) if (n1): self.plot = encode_plot(EC_nf_plot(E, self.field.generator_name())) self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot self.properties += [(None, self.plot_link)] self.properties += [('Conductor', self.cond), ('Conductor norm', self.cond_norm), ('j-invariant', self.j), ('CM', self.cm_bool)] if self.base_change: self.properties += [ ('base-change', 'yes: %s' % ','.join([str(lab) for lab in self.base_change])) ] else: self.base_change = [] # in case it was False instead of [] self.properties += [('Q-curve', self.qc)] self.properties += [ ('Torsion order', self.ntors), ('Rank', self.rk), ] for E0 in self.base_change: self.friends += [('Base-change of %s /\(\Q\)' % E0, url_for("ec.by_ec_label", label=E0))]
def make_E(self): coeffs = self.ainvs # list of 5 lists of d strings self.ainvs = [self.field.parse_NFelt(x) for x in coeffs] self.latex_ainvs = web_latex(self.ainvs) from sage.schemes.elliptic_curves.all import EllipticCurve self.E = E = EllipticCurve(self.ainvs) self.equn = web_latex(E) self.numb = str(self.number) # Conductor, discriminant, j-invariant N = E.conductor() self.cond = web_latex(N) self.cond_norm = web_latex(N.norm()) if N.norm() == 1: # since the factorization of (1) displays as "1" self.fact_cond = self.cond else: self.fact_cond = web_latex_ideal_fact(N.factor()) self.fact_cond_norm = web_latex(N.norm().factor()) D = self.field.K().ideal(E.discriminant()) self.disc = web_latex(D) self.disc_norm = web_latex(D.norm()) if D.norm() == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc else: self.fact_disc = web_latex_ideal_fact(D.factor()) self.fact_disc_norm = web_latex(D.norm().factor()) # Minimal model? # # All curves in the database should be given # by models which are globally minimal if possible, else # minimal at all but one prime. But we do not rely on this # here, and the display should be correct if either (1) there # exists a global minimal model but this model is not; or (2) # this model is non-minimal at more than one prime. # self.non_min_primes = non_minimal_primes(E) self.is_minimal = (len(self.non_min_primes) == 0) self.has_minimal_model = True if not self.is_minimal: self.non_min_prime = ','.join( [web_latex(P) for P in self.non_min_primes]) self.has_minimal_model = has_global_minimal_model(E) if not self.is_minimal: Dmin = minimal_discriminant_ideal(E) self.mindisc = web_latex(Dmin) self.mindisc_norm = web_latex(Dmin.norm()) if Dmin.norm( ) == 1: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc else: self.fact_mindisc = web_latex_ideal_fact(Dmin.factor()) self.fact_mindisc_norm = web_latex(Dmin.norm().factor()) j = E.j_invariant() if j: d = j.denominator() n = d * j # numerator exists for quadratic fields only! g = GCD(list(n)) n1 = n / g self.j = web_latex(n1) if d != 1: if n1 > 1: # self.j = "("+self.j+")\(/\)"+web_latex(d) self.j = web_latex(r"\frac{%s}{%s}" % (self.j, d)) else: self.j = web_latex(d) if g > 1: if n1 > 1: self.j = web_latex(g) + self.j else: self.j = web_latex(g) self.j = web_latex(j) self.fact_j = None # See issue 1258: some j factorizations work bu take too long (e.g. EllipticCurve/6.6.371293.1/1.1/a/1) # If these are really wanted, they could be precomputed and stored in the db if j.is_zero(): self.fact_j = web_latex(j) else: if self.field.K().degree( ) < 3: #j.numerator_ideal().norm()<1000000000000: try: self.fact_j = web_latex(j.factor()) except (ArithmeticError, ValueError ): # if not all prime ideal factors principal pass # CM and End(E) self.cm_bool = "no" self.End = "\(\Z\)" if self.cm: self.cm_bool = "yes (\(%s\))" % self.cm if self.cm % 4 == 0: d4 = ZZ(self.cm) // 4 self.End = "\(\Z[\sqrt{%s}]\)" % (d4) else: self.End = "\(\Z[(1+\sqrt{%s})/2]\)" % self.cm # The line below will need to change once we have curves over non-quadratic fields # that contain the Hilbert class field of an imaginary quadratic field if self.signature == [0, 1] and ZZ( -self.abs_disc * self.cm).is_square(): self.ST = '<a href="%s">$%s$</a>' % (url_for( 'st.by_label', label='1.2.U(1)'), '\\mathrm{U}(1)') else: self.ST = '<a href="%s">$%s$</a>' % (url_for( 'st.by_label', label='1.2.N(U(1))'), 'N(\\mathrm{U}(1))') else: self.ST = '<a href="%s">$%s$</a>' % (url_for( 'st.by_label', label='1.2.SU(2)'), '\\mathrm{SU}(2)') # Q-curve / Base change self.qc = "no" try: if self.q_curve: self.qc = "yes" except AttributeError: # in case the db entry does not have this field set pass # Torsion self.ntors = web_latex(self.torsion_order) self.tr = len(self.torsion_structure) if self.tr == 0: self.tor_struct_pretty = "Trivial" if self.tr == 1: self.tor_struct_pretty = "\(\Z/%s\Z\)" % self.torsion_structure[0] if self.tr == 2: self.tor_struct_pretty = r"\(\Z/%s\Z\times\Z/%s\Z\)" % tuple( self.torsion_structure) torsion_gens = [ E([self.field.parse_NFelt(x) for x in P]) for P in self.torsion_gens ] self.torsion_gens = ",".join([web_latex(P) for P in torsion_gens]) # Rank or bounds try: self.rk = web_latex(self.rank) except AttributeError: self.rk = "?" try: self.rk_bnds = "%s...%s" % tuple(self.rank_bounds) except AttributeError: self.rank_bounds = [0, Infinity] self.rk_bnds = "not available" # Generators try: gens = [ E([self.field.parse_NFelt(x) for x in P]) for P in self.gens ] self.gens = ", ".join([web_latex(P) for P in gens]) if self.rk == "?": self.reg = "not available" else: if gens: self.reg = E.regulator_of_points(gens) else: self.reg = 1 # otherwise we only get 1.00000... except AttributeError: self.gens = "not available" self.reg = "not available" try: if self.rank == 0: self.reg = 1 except AttributeError: pass # Local data self.local_data = [] for p in N.prime_factors(): self.local_info = E.local_data(p, algorithm="generic") self.local_data.append({ 'p': web_latex(p), 'norm': web_latex(p.norm().factor()), 'tamagawa_number': self.local_info.tamagawa_number(), 'kodaira_symbol': web_latex(self.local_info.kodaira_symbol()).replace('$', ''), 'reduction_type': self.local_info.bad_reduction_type(), 'ord_den_j': max(0, -E.j_invariant().valuation(p)), 'ord_mindisc': self.local_info.discriminant_valuation(), 'ord_cond': self.local_info.conductor_valuation() }) # URLs of self and related objects: self.urls = {} # It's useful to be able to use this class out of context, when calling url_for will fail: try: self.urls['curve'] = url_for(".show_ecnf", nf=self.field_label, conductor_label=quote( self.conductor_label), class_label=self.iso_label, number=self.number) except RuntimeError: return self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=quote( self.conductor_label), class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=quote( self.conductor_label)) self.urls['field'] = url_for(".show_ecnf1", nf=self.field_label) sig = self.signature real_quadratic = sig == [2, 0] totally_real = sig[1] == 0 imag_quadratic = sig == [0, 1] if totally_real: self.hmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.urls['hmf'] = url_for('hmf.render_hmf_webpage', field_label=self.field.label, label=self.hmf_label) self.urls['Lfunction'] = url_for("l_functions.l_function_hmf_page", field=self.field_label, label=self.hmf_label, character='0', number='0') if imag_quadratic: self.bmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.friends = [] self.friends += [('Isogeny class ' + self.short_class_label, self.urls['class'])] self.friends += [('Twists', url_for('ecnf.index', field_label=self.field_label, jinv=self.jinv))] if totally_real: self.friends += [('Hilbert Modular Form ' + self.hmf_label, self.urls['hmf'])] self.friends += [('L-function', self.urls['Lfunction'])] if imag_quadratic: self.friends += [ ('Bianchi Modular Form %s not available' % self.bmf_label, '') ] self.properties = [('Base field', self.field.field_pretty()), ('Label', self.label)] # Plot if E.base_field().signature()[0]: self.plot = encode_plot(EC_nf_plot(E, self.field.generator_name())) self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot self.properties += [(None, self.plot_link)] self.properties += [ ('Conductor', self.cond), ('Conductor norm', self.cond_norm), # See issue #796 for why this is hidden # ('j-invariant', self.j), ('CM', self.cm_bool) ] if self.base_change: self.properties += [ ('base-change', 'yes: %s' % ','.join([str(lab) for lab in self.base_change])) ] else: self.base_change = [] # in case it was False instead of [] self.properties += [('Q-curve', self.qc)] r = self.rk if r == "?": r = self.rk_bnds self.properties += [ ('Torsion order', self.ntors), ('Rank', r), ] for E0 in self.base_change: self.friends += [('Base-change of %s /\(\Q\)' % E0, url_for("ec.by_ec_label", label=E0))] self._code = None # will be set if needed by get_code()
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method=='old': if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminats to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *=2 if D.valuation(P)>0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4*P.norm() dP = DP.squarefree_part() fP = ZZ(DP//dP).isqrt() if cmd==0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd==0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd%4!=1: cmd = cmd*4 cmf = cmf//2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd*f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j)==0: return True, (cmd,f) return False, None
def add_reductions(self, q): r"""Add reduction data at primes above q if not already there. INPUT: - ``q`` -- a prime number not dividing the defining polynomial of self.__field. OUTPUT: Returns nothing, but updates self._reductions dictionary for key ``q`` to a dict whose keys are the roots of the defining polynomial mod ``q`` and values tuples (``nq``, ``Eq``) where ``Eq`` is an elliptic curve over `GF(q)` and ``nq`` its cardinality. If ``q`` divides the conductor norm or order discriminant nothing is added. EXAMPLES: Over `\QQ`:: sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: E = EllipticCurve('11a1') sage: saturator = EllipticCurveSaturator(E) sage: saturator._reductions {} sage: saturator.add_reductions(19) sage: saturator._reductions {19: {0: (20, Elliptic Curve defined by y^2 + y = x^3 + 18*x^2 + 9*x + 18 over Finite Field of size 19)}} Over a number field:: sage: x = polygen(QQ); K.<a> = NumberField(x^2 + 2) sage: E = EllipticCurve(K, [0,1,0,a,a]) sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: saturator = EllipticCurveSaturator(E) sage: for q in primes(20): ....: saturator.add_reductions(q) ....: sage: saturator._reductions {2: {}, 3: {}, 5: {}, 7: {}, 11: {3: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 3 over Finite Field of size 11), 8: (8, Elliptic Curve defined by y^2 = x^3 + x^2 + 8*x + 8 over Finite Field of size 11)}, 13: {}, 17: {7: (20, Elliptic Curve defined by y^2 = x^3 + x^2 + 7*x + 7 over Finite Field of size 17), 10: (18, Elliptic Curve defined by y^2 = x^3 + x^2 + 10*x + 10 over Finite Field of size 17)}, 19: {6: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 6*x + 6 over Finite Field of size 19), 13: (12, Elliptic Curve defined by y^2 = x^3 + x^2 + 13*x + 13 over Finite Field of size 19)}} """ if q in self._reductions: return self._reductions[q] = redmodq = dict() if q.divides(self._N) or q.divides(self._D): return from sage.schemes.elliptic_curves.all import EllipticCurve for amodq in sorted(self._Kpol.roots(GF(q), multiplicities=False)): Eq = EllipticCurve( [reduce_mod_q(ai, amodq) for ai in self._curve.ainvs()]) nq = Eq.cardinality() redmodq[amodq] = (nq, Eq)
def frobenius_image_curve(E, r): p = E.base_field().characteristic() return EllipticCurve(E.base_field(), [E.a4()**(p**r), E.a6()**(p**r)])
def extend_field(E, r): q = E.base_field().order() field = GF(q**r) a = field(E.a4()) b = field(E.a6()) return EllipticCurve(field, [a, b])
def r_frobenius_morphism(E, r): x, y = PolynomialRing(E.base_ring(), ['x', 'y']).gens() p = E.base_field().characteristic() a = E.a4()**(p**r) b = E.a6()**(p**r) return (x**(p**r), y**(p**r)), EllipticCurve(E.base_field(), [a, b])