def tate_factor_Zp(cyc_factor): """ return the factorization of characteristic polynomial of Frobenius on Tate classes in H^2 over Zp """ T = cyc_factor.variables()[0] p = cyc_factor.leading_coefficient().prime_factors()[0] R = Zp(p) factorization = [] for fac, exp in cyc_factor(T / p).factor(): for fp, ep in fac.change_ring(R).factor(): factorization.append((fp(p * T).reverse().monic(), exp * ep)) res = Factorization(factorization) assert res.expand().change_ring(R) == cyc_factor.change_ring(R).reverse( ).monic(), "%s\n%s" % (res.expand(), cyc_factor.change_ring(R).reverse().monic()) return res
def polyquo_knowl(f, disc=None, unit=1, cutoff=None): quo = "x^{%s}" % (len(f) - 1) i = len(f) - 2 while i >= 0 and f[i] == 0: i -= 1 if i >= 0: # nonzero terms if f[i] > 0: quo += r" + \cdots" else: quo += r" - \cdots" short = r'\mathbb{Q}[x]/(%s)'%(quo) long = r'Defining polynomial: %s' % (web_latex(coeff_to_poly(f))) if cutoff: long = make_bigint(long, cutoff, max_width=70).replace('"',"'") if disc is not None: if isinstance(disc, list): long += '\n<br>\nDiscriminant: \\(%s\\)' % (factor_base_factorization_latex(disc)) else: long += '\n<br>\nDiscriminant: \\(%s\\)' % (Factorization(disc, unit=unit)._latex_()) return r'<a title="[poly]" knowl="dynamic_show" kwargs="%s">\(%s\)</a>'%(long, short)
def rank_fieldextension(frob_polynomial, shift=0): """ Return rank, degree of field extension, and the factorization of characteristic polynomial, into twisted cyclotomic factors, of the Frobenius action on Tate classes factorized Input:: - frob_polynomial, Frobenius polynomial for H^2 - shift, an integer, 0 by default, accounting for missing cycles, for example when frob_polynomial doesn't include the polarization """ p = frob_polynomial.list()[-1].prime_factors()[0] rank = shift k = 1 cyc_factorization = [] for fac, exp in frob_polynomial.factor(): ki = fac(fac.variables()[0] / p).is_cyclotomic(certificate=True) if ki: k = lcm(k, ki) cyc_factorization.append((fac, exp)) rank += fac.degree() * exp return rank, k, Factorization(cyc_factorization)
def make_curve(self): data = self.data = {} lmfdb_label = self.lmfdb_label # Some data fields of self are just those from the database. # These only need some reformatting. data['ainvs'] = self.ainvs data['conductor'] = N = self.conductor data['j_invariant'] = QQ(tuple(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_latex'] = web_latex(data['j_invariant']) # retrieve local reduction data from table ec_localdata: self.local_data = local_data = list( db.ec_localdata.search({"lmfdb_label": lmfdb_label})) for ld in local_data: if ld['kodaira_symbol'] <= -14: # Work around bug in Sage's latex ld['kod'] = 'I_{%s}^{*}' % (-ld['kodaira_symbol'] - 4) else: ld['kod'] = latex(KodairaSymbol(ld['kodaira_symbol'])) Nfac = Factorization([(ZZ(ld['prime']), ld['conductor_valuation']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['prime']), ld['discriminant_valuation']) for ld in local_data], unit=ZZ(self.signD)) data['disc_factor'] = latex(Dfac) data['disc'] = D = Dfac.value() data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) # retrieve data about MW rank, generators, heights and # torsion, leading term of L-function & other BSD data from # table ec_mwbsd: self.make_mwbsd() # latex equation: latexeqn = latex_equation(self.ainvs) data['equation'] = raw_typeset(unlatex(latexeqn), latexeqn) # minimal quadratic twist: data['minq_D'] = minqD = self.min_quad_twist_disc data['minq_label'] = db.ec_curvedata.lucky( {'ainvs': self.min_quad_twist_ainvs}, projection='lmfdb_label' if self.label_type == 'LMFDB' else 'Clabel') data['minq_info'] = '(itself)' if minqD == 1 else '(by {})'.format( minqD) # modular degree: try: data['degree'] = ZZ(self.degree) # convert None to 0 except AttributeError: # if not computed, db has Null and the attribute is missing data['degree'] = 0 # invalid, but will be displayed nicely # coefficients of modular form / L-series: classdata = db.ec_classdata.lookup(self.lmfdb_iso) data['an'] = classdata['anlist'] data['ap'] = classdata['aplist'] # mod-p Galois images: data['galois_data'] = list( db.ec_galrep.search({'lmfdb_label': lmfdb_label})) for gd in data[ 'galois_data']: # remove the prime prefix from each image code gd['image'] = trim_galois_image_code(gd['image']) # CM and Endo ring: data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = r"\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.nonmax_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join(str(p) for p in data['cm_ramp']) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = r"yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = r"\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = r"\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') # Isogeny degrees: cond, iso, num = split_lmfdb_label(lmfdb_label) self.class_deg = classdata['class_deg'] self.one_deg = ZZ(self.class_deg).is_prime() isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) self.make_twoadic_data() # Optimality # The optimal curve in the class is the curve whose Cremona # label ends in '1' except for '990h' which was labelled # wrongly long ago. This is proved for N up to # OPTIMALITY_BOUND (and when there is only one curve in an # isogeny class, obviously) and expected for all N. # Column 'optimality' is 1 for certainly optimal curves, 0 for # certainly non-optimal curves, and is n>1 if the curve is one # of n in the isogeny class which may be optimal given current # knowledge. # Column "manin_constant' is the correct Manin constant # assuming that the optimal curve in the class is known, or # otherwise if it is the curve with (Cremona) number 1. # The code here allows us to update the display correctly by # changing one line in this file (defining OPTIMALITY_BOUND) # without changing the data. data['optimality_bound'] = OPTIMALITY_BOUND self.cremona_bound = CREMONA_BOUND if N < CREMONA_BOUND: data[ 'manin_constant'] = self.manin_constant # (conditional on data['optimality_known']) else: data['manin_constant'] = 0 # (meaning not available) if N < OPTIMALITY_BOUND: data['optimality_code'] = int( self.Cnumber == (3 if self.Ciso == '990h' else 1)) data['optimality_known'] = True data['manin_known'] = True if self.label_type == 'Cremona': data[ 'optimal_label'] = '990h3' if self.Ciso == '990h' else self.Ciso + '1' else: data[ 'optimal_label'] = '990.i3' if self.lmfdb_iso == '990.i' else self.lmfdb_iso + '1' elif N < CREMONA_BOUND: data['optimality_code'] = self.optimality data['optimality_known'] = (self.optimality < 2) if self.optimality == 1: data['manin_known'] = True data[ 'optimal_label'] = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label else: if self.Cnumber == 1: data['manin_known'] = False data[ 'optimal_label'] = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label else: # find curve #1 in this class and its optimailty code: opt_curve = db.ec_curvedata.lucky( { 'Ciso': self.Ciso, 'Cnumber': 1 }, projection=['Clabel', 'lmfdb_label', 'optimality']) data['manin_known'] = (opt_curve['optimality'] == 1) data['optimal_label'] = opt_curve[ 'Clabel' if self.label_type == 'Cremona' else 'lmfdb_label'] else: data['optimality_code'] = None data['optimality_known'] = False data['manin_known'] = False data['optimal_label'] = '' # p-adic data: data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] data['p_adic_data_exists'] = False if data['optimality_code'] == 1: data['p_adic_data_exists'] = db.ec_padic.exists( {'lmfdb_iso': self.lmfdb_iso}) # Iwasawa data (where present) self.make_iwasawa() # Torsion growth data (where present) self.make_torsion_growth() # Newform rawnewform = str(PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform'] = raw_typeset( rawnewform, web_latex(PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True))) data['newform_label'] = self.newform_label = ".".join( [str(cond), str(2), 'a', iso]) self.newform_link = url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) self.newform_exists_in_db = db.mf_newforms.label_exists( self.newform_label) self._code = None if self.label_type == 'Cremona': self.class_url = url_for(".by_ec_label", label=self.Ciso) self.class_name = self.Ciso else: self.class_url = url_for(".by_ec_label", label=self.lmfdb_iso) self.class_name = self.lmfdb_iso data['class_name'] = self.class_name data['Cnumber'] = self.Cnumber if N < CREMONA_BOUND else None self.friends = [('Isogeny class ' + self.class_name, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_ec_label", label=data['minq_label'])), ('All twists ', url_for(".rational_elliptic_curves", jinv=data['j_invariant']))] lfun_url = url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso) origin_url = lfun_url.lstrip('/L/').rstrip('/') if db.lfunc_instances.exists({'url': origin_url}): self.friends += [('L-function', lfun_url)] else: self.friends += [('L-function not available', "")] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.lmfdb_label)), ('Code to Magma', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Code to SageMath', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Code to GP', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [ ('Label', self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label), (None, self.plot_link), ('Conductor', prop_int_pretty(data['conductor'])), ('Discriminant', prop_int_pretty(data['disc'])), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', 'unknown' if self.mwbsd['rank'] == '?' else prop_int_pretty(self.mwbsd['rank'])), ('Torsion structure', (r'\(%s\)' % self.mwbsd['tor_struct']) if self.mwbsd['tor_struct'] else 'trivial'), ] if self.label_type == 'Cremona': self.title = "Elliptic curve with Cremona label {} (LMFDB label {})".format( self.Clabel, self.lmfdb_label) elif N < CREMONA_BOUND: self.title = "Elliptic curve with LMFDB label {} (Cremona label {})".format( self.lmfdb_label, self.Clabel) else: self.title = "Elliptic curve with LMFDB label {}".format( self.lmfdb_label) self.bread = [('Elliptic curves', url_for("ecnf.index")), (r'$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} data['ainvs'] = [ZZ(ai) for ai in self.ainvs] data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) # extract data about MW rank, generators, heights and torsion: self.make_mw() # get more data from the database entry data['equation'] = self.equation local_data = self.local_data D = self.signD * prod([ld['p']**ld['ord_disc'] for ld in local_data]) for ld in local_data: ld['kod'] = ld['kod'].replace("\\\\", "\\") data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] data['minq_label'] = self.min_quad_twist[ 'lmfdb_label'] if self.label_type == 'LMFDB' else self.min_quad_twist[ 'label'] data['minq_info'] = '(itself)' if minqD == 1 else '(by {})'.format( minqD) if self.degree is None: data['degree'] = 0 # invalid, but will be displayed nicely else: data['degree'] = self.degree try: data['an'] = self.anlist data['ap'] = self.aplist except AttributeError: r = db.ec_curves.lucky({'lmfdb_iso': self.lmfdb_iso, 'number': 1}) data['an'] = r['anlist'] data['ap'] = r['aplist'] data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['galois_images'] = [ trim_galois_image_code(s) for s in self.mod_p_images ] data['non_maximal_primes'] = self.non_maximal_primes data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_maximal_primes'], data['galois_images'])] data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.non_maximal_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join([str(p) for p in data['cm_ramp']]) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.one_deg = ZZ(self.class_deg).is_prime() isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & other BSD data self.make_bsd() # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago): this is proved for N up to # OPTIMALITY_BOUND (and when there is only one curve in an # isogeny class, obviously) and expected for all N. # Column 'optimality' is 1 for certainly optimal curves, 0 for # certainly non-optimal curves, and is n>1 if the curve is one # of n in the isogeny class which may be optimal given current # knowledge. # Column "manin_constant' is the correct Manin constant # assuming that the optimal curve in the class is known, or # otherwise if it is the curve with (Cremona) number 1. # The code here allows us to update the display correctly by # changing one line in this file (defining OPTIMALITY_BOUND) # without changing the data. data['optimality_bound'] = OPTIMALITY_BOUND data[ 'manin_constant'] = self.manin_constant # (conditional on data['optimality_known']) if N < OPTIMALITY_BOUND: data['optimality_code'] = int( self.number == (3 if self.iso == '990h' else 1)) data['optimality_known'] = True data['manin_known'] = True if self.label_type == 'Cremona': data[ 'optimal_label'] = '990h3' if self.iso == '990h' else self.iso + '1' else: data[ 'optimal_label'] = '990.i3' if self.lmfdb_iso == '990.i' else self.lmfdb_iso + '1' else: data['optimality_code'] = self.optimality data['optimality_known'] = (self.optimality < 2) if self.optimality == 1: data['manin_known'] = True data[ 'optimal_label'] = self.label if self.label_type == 'Cremona' else self.lmfdb_label else: if self.number == 1: data['manin_known'] = False data[ 'optimal_label'] = self.label if self.label_type == 'Cremona' else self.lmfdb_label else: # find curve #1 in this class and its optimailty code: opt_curve = db.ec_curves.lucky( { 'iso': self.iso, 'number': 1 }, projection=['label', 'lmfdb_label', 'optimality']) data['manin_known'] = (opt_curve['optimality'] == 1) data['optimal_label'] = opt_curve[ 'label' if self.label_type == 'Cremona' else 'lmfdb_label'] data['p_adic_data_exists'] = False if data['optimality_code'] == 1: data['p_adic_data_exists'] = db.ec_padic.exists( {'lmfdb_iso': self.lmfdb_iso}) # Iwasawa data (where present) self.make_iwasawa() # Torsion growth data (where present) self.make_torsion_growth() data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = ".".join( [str(cond), str(2), 'a', iso]) self.newform_link = url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) self.newform_exists_in_db = db.mf_newforms.label_exists( self.newform_label) self._code = None if self.label_type == 'Cremona': self.class_url = url_for(".by_ec_label", label=self.iso) self.class_name = self.iso else: self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.class_name = self.lmfdb_iso data['class_name'] = self.class_name data['number'] = self.number self.friends = [('Isogeny class ' + self.class_name, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_ec_label", label=data['minq_label'])), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv))] lfun_url = url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso) origin_url = lfun_url.lstrip('/L/').rstrip('/') if db.lfunc_instances.exists({'url': origin_url}): self.friends += [('L-function', lfun_url)] else: self.friends += [('L-function not available', "")] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.lmfdb_label)), ('Code to Magma', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Code to SageMath', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Code to GP', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [ ('Label', self.label if self.label_type == 'Cremona' else self.lmfdb_label), (None, self.plot_link), ('Conductor', '%s' % data['conductor']), ('Discriminant', '%s' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '%s' % self.mw['rank']), ('Torsion Structure', '\(%s\)' % self.mw['tor_struct']) ] if self.label_type == 'Cremona': self.title = "Elliptic Curve with Cremona label {} (LMFDB label {})".format( self.label, self.lmfdb_label) else: self.title = "Elliptic Curve with LMFDB label {} (Cremona label {})".format( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def make_E(self): K = self.field.K() # a-invariants self.ainvs = parse_ainvs(K, self.ainvs) self.latex_ainvs = web_latex(self.ainvs) self.numb = str(self.number) # Conductor, discriminant, j-invariant N = ideal_from_string(K, self.conductor_ideal) self.cond = web_latex(N) self.cond_norm = web_latex(self.conductor_norm) local_data = self.local_data # NB badprimes is a list of primes which divide the # discriminant of this model. At most one of these might # actually be a prime of good reduction, if the curve has no # global minimal model. badprimes = [ideal_from_string(K, ld['p']) for ld in local_data] badnorms = [ZZ(ld['normp']) for ld in local_data] mindisc_ords = [ld['ord_disc'] for ld in local_data] # Assumption: the curve models stored in the database are # either global minimal models or minimal at all but one # prime, so the list here has length 0 or 1: self.non_min_primes = [ideal_from_string(K, P) for P in self.non_min_p] self.is_minimal = (len(self.non_min_primes) == 0) self.has_minimal_model = self.is_minimal disc_ords = [ld['ord_disc'] for ld in local_data] if not self.is_minimal: Pmin = self.non_min_primes[0] P_index = badprimes.index(Pmin) self.non_min_prime = web_latex(Pmin) disc_ords[P_index] += 12 if self.conductor_norm == 1: # since the factorization of (1) displays as "1" self.fact_cond = self.cond self.fact_cond_norm = self.cond else: Nfac = Factorization([(P, ld['ord_cond']) for P, ld in zip(badprimes, local_data)]) self.fact_cond = web_latex_ideal_fact(Nfac) Nnormfac = Factorization([(q, ld['ord_cond']) for q, ld in zip(badnorms, local_data)]) self.fact_cond_norm = web_latex(Nnormfac) # D is the discriminant ideal of the model D = prod([P**e for P, e in zip(badprimes, disc_ords)], K.ideal(1)) self.disc = web_latex(D) Dnorm = D.norm() self.disc_norm = web_latex(Dnorm) if Dnorm == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc self.fact_disc_norm = self.disc else: Dfac = Factorization([(P, e) for P, e in zip(badprimes, disc_ords)]) self.fact_disc = web_latex_ideal_fact(Dfac) Dnormfac = Factorization([(q, e) for q, e in zip(badnorms, disc_ords)]) self.fact_disc_norm = web_latex(Dnormfac) if not self.is_minimal: Dmin = ideal_from_string(K, self.minD) self.mindisc = web_latex(Dmin) Dmin_norm = Dmin.norm() self.mindisc_norm = web_latex(Dmin_norm) if Dmin_norm == 1: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc self.fact_mindisc_norm = self.mindisc else: Dminfac = Factorization([ (P, e) for P, edd in zip(badprimes, mindisc_ords) ]) self.fact_mindisc = web_latex_ideal_fact(Dminfac) Dminnormfac = Factorization([ (q, e) for q, e in zip(badnorms, mindisc_ords) ]) self.fact_mindisc_norm = web_latex(Dminnormfac) j = self.field.parse_NFelt(self.jinv) # 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 but take too long # (e.g. EllipticCurve/6.6.371293.1/1.1/a/1). Note that we do # store the factorization of the denominator of j and display # that, which is the most interesting part. # Images of Galois representations if not hasattr(self, 'galois_images'): #print "No Galois image data" self.galois_images = "?" self.non_surjective_primes = "?" self.galois_data = [] else: self.galois_data = [{ 'p': p, 'image': im } for p, im in zip(self.non_surjective_primes, self.galois_images)] # CM and End(E) self.cm_bool = "no" self.End = "\(\Z\)" if self.cm: self.rational_cm = K(self.cm).is_square() self.cm_sqf = ZZ(self.cm).squarefree_part() 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 # Galois images in CM case: if self.cm and self.galois_images != '?': self.cm_ramp = [ p for p in ZZ(self.cm).support() if not p in self.non_surjective_primes ] self.cm_nramp = len(self.cm_ramp) if self.cm_nramp == 1: self.cm_ramp = self.cm_ramp[0] else: self.cm_ramp = ", ".join([str(p) for p in self.cm_ramp]) # Sato-Tate: # The lines 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.cm: if self.signature == [0, 1] and ZZ( -self.abs_disc * self.cm).is_square(): self.ST = st_link_by_name(1, 2, 'U(1)') else: self.ST = st_link_by_name(1, 2, 'N(U(1))') else: self.ST = st_link_by_name(1, 2, '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 = [parse_point(K, P) for P in self.torsion_gens] self.torsion_gens = ",".join([web_point(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 = [parse_point(K, P) for P in self.gens] self.gens = ", ".join([web_point(P) for P in gens]) if self.rk == "?": self.reg = "not available" else: if gens: try: self.reg = self.reg except AttributeError: self.reg = "not available" pass # self.reg already set 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 for P, ld in zip(badprimes, local_data): ld['p'] = web_latex(P) ld['norm'] = P.norm() ld['kod'] = web_latex(ld['kod']).replace('$', '') # 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) # Isogeny information if self.number == 1: isogmat = self.isogeny_matrix else: isogmat = db_ecnf().find_one({ 'class_label': self.class_label, 'number': 1 })['isogeny_matrix'] self.class_deg = max([max(d) for d in isogmat]) self.one_deg = ZZ(self.class_deg).is_prime() self.ncurves = db_ecnf().count({'class_label': self.class_label}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: self.isogeny_degrees = " and ".join(isodegs) else: self.isogeny_degrees = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) sig = self.signature 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.bmf_url = url_for('bmf.render_bmf_webpage', field_label=self.field_label, level_label=self.conductor_label, label_suffix=self.iso_label) self.friends = [] self.friends += [('Isogeny class ' + self.short_class_label, self.urls['class'])] self.friends += [('Twists', url_for('ecnf.index', field=self.field_label, jinv=rename_j(j)))] 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, '')] if "CM" in self.label: self.friends += [('Bianchi Modular Form is not cuspidal', '')] else: self.friends += [('Bianchi Modular Form %s' % self.bmf_label, self.bmf_url)] self.properties = [('Base field', self.field.field_pretty()), ('Label', self.label)] # Plot if K.signature()[0]: self.plot = encode_plot( EC_nf_plot(K, self.ainvs, 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 (can be very large) # ('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 make_E(self): #print("Creating ECNF object for {}".format(self.label)) #sys.stdout.flush() K = self.field.K() # a-invariants self.ainvs = parse_ainvs(K, self.ainvs) self.latex_ainvs = web_latex(self.ainvs) self.numb = str(self.number) # Conductor, discriminant, j-invariant if self.conductor_norm == 1: N = K.ideal(1) else: N = ideal_from_string(K, self.conductor_ideal) # The following can trigger expensive computations! #self.cond = web_latex(N) self.cond = pretty_ideal(N) self.cond_norm = web_latex(self.conductor_norm) local_data = self.local_data # NB badprimes is a list of primes which divide the # discriminant of this model. At most one of these might # actually be a prime of good reduction, if the curve has no # global minimal model. badprimes = [ideal_from_string(K, ld['p']) for ld in local_data] badnorms = [ZZ(ld['normp']) for ld in local_data] mindisc_ords = [ld['ord_disc'] for ld in local_data] # Assumption: the curve models stored in the database are # either global minimal models or minimal at all but one # prime, so the list here has length 0 or 1: self.non_min_primes = [ideal_from_string(K, P) for P in self.non_min_p] self.is_minimal = (len(self.non_min_primes) == 0) self.has_minimal_model = self.is_minimal disc_ords = [ld['ord_disc'] for ld in local_data] if not self.is_minimal: Pmin = self.non_min_primes[0] P_index = badprimes.index(Pmin) self.non_min_prime = pretty_ideal(Pmin) disc_ords[P_index] += 12 if self.conductor_norm == 1: # since the factorization of (1) displays as "1" self.fact_cond = self.cond self.fact_cond_norm = '1' else: Nfac = Factorization([(P, ld['ord_cond']) for P, ld in zip(badprimes, local_data)]) self.fact_cond = web_latex_ideal_fact(Nfac) Nnormfac = Factorization([(q, ld['ord_cond']) for q, ld in zip(badnorms, local_data)]) self.fact_cond_norm = web_latex(Nnormfac) # D is the discriminant ideal of the model D = prod([P**e for P, e in zip(badprimes, disc_ords)], K.ideal(1)) self.disc = pretty_ideal(D) Dnorm = D.norm() self.disc_norm = web_latex(Dnorm) if Dnorm == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc self.fact_disc_norm = '1' else: Dfac = Factorization([(P, e) for P, e in zip(badprimes, disc_ords)]) self.fact_disc = web_latex_ideal_fact(Dfac) Dnormfac = Factorization([(q, e) for q, e in zip(badnorms, disc_ords)]) self.fact_disc_norm = web_latex(Dnormfac) if not self.is_minimal: Dmin = ideal_from_string(K, self.minD) self.mindisc = pretty_ideal(Dmin) Dmin_norm = Dmin.norm() self.mindisc_norm = web_latex(Dmin_norm) if Dmin_norm == 1: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc self.fact_mindisc_norm = self.mindisc_norm else: Dminfac = Factorization(list(zip(badprimes, mindisc_ords))) self.fact_mindisc = web_latex_ideal_fact(Dminfac) Dminnormfac = Factorization(list(zip(badnorms, mindisc_ords))) self.fact_mindisc_norm = web_latex(Dminnormfac) j = self.field.parse_NFelt(self.jinv) # 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 but take too long # (e.g. EllipticCurve/6.6.371293.1/1.1/a/1). Note that we do # store the factorization of the denominator of j and display # that, which is the most interesting part. # The equation is stored in the database as a latex string. # Some of these have extraneous double quotes at beginning and # end, shich we fix here. We also strip out initial \( and \) # (if present) which are added in the template. self.equation = self.equation.replace('"', '').replace('\\(', '').replace( '\\)', '') # Images of Galois representations if not hasattr(self, 'galois_images'): #print "No Galois image data" self.galois_images = "?" self.non_surjective_primes = "?" self.galois_data = [] else: self.galois_data = [{ 'p': p, 'image': im } for p, im in zip(self.non_surjective_primes, self.galois_images)] # CM and End(E) self.cm_bool = "no" self.End = r"\(\Z\)" if self.cm: # When we switch to storing rational cm by having |D| in # the column, change the following lines: if self.cm > 0: self.rational_cm = True self.cm = -self.cm else: self.rational_cm = K(self.cm).is_square() self.cm_sqf = ZZ(self.cm).squarefree_part() self.cm_bool = r"yes (\(%s\))" % self.cm if self.cm % 4 == 0: d4 = ZZ(self.cm) // 4 self.End = r"\(\Z[\sqrt{%s}]\)" % (d4) else: self.End = r"\(\Z[(1+\sqrt{%s})/2]\)" % self.cm # Galois images in CM case: if self.cm and self.galois_images != '?': self.cm_ramp = [ p for p in ZZ(self.cm).support() if not p in self.non_surjective_primes ] self.cm_nramp = len(self.cm_ramp) if self.cm_nramp == 1: self.cm_ramp = self.cm_ramp[0] else: self.cm_ramp = ", ".join([str(p) for p in self.cm_ramp]) # Sato-Tate: # The lines 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.cm: if self.signature == [0, 1] and ZZ( -self.abs_disc * self.cm).is_square(): self.ST = st_link_by_name(1, 2, 'U(1)') else: self.ST = st_link_by_name(1, 2, 'N(U(1))') else: self.ST = st_link_by_name(1, 2, 'SU(2)') # Q-curve / Base change try: qc = self.q_curve if qc is True: self.qc = "yes" elif qc is False: self.qc = "no" else: # just in case self.qc = "not determined" except AttributeError: self.qc = "not determined" # 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 = r"\(\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) self.torsion_gens = [ web_point(parse_point(K, P)) for P in self.torsion_gens ] # BSD data # # We divide into 3 cases, based on rank_bounds [lb,ub], # analytic_rank ar, (lb=ngens always). The flag # self.bsd_status is set to one of the following: # # "unconditional" # lb=ar=ub: we always have reg but in some cases over sextic fields we do not have omega, Lvalue, sha. # i.e. [lb,ar,ub] = [r,r,r] # # "conditional" # lb=ar<ub: we always have reg but in some cases over sextic fields we do not have omega, Lvalue, sha. # e.g. [lb,ar,ub] = [0,0,2], [1,1,3] # # "missing_gens" # lb<ar<=ub # e.g. [lb,ar,ub] = [0,1,1], [0,2,2], [1,2,2], [0,1,3] # # "incomplete" # ar not computed. (We can always set lb=0, ub=Infinity.) # Rank and bounds try: self.rk = web_latex(self.rank) except AttributeError: self.rank = None self.rk = "not available" try: self.rk_lb, self.rk_ub = self.rank_bounds except AttributeError: self.rk_lb = 0 self.rk_ub = Infinity self.rank_bounds = "not available" # Analytic rank try: self.ar = web_latex(self.analytic_rank) except AttributeError: self.analytic_rank = None self.ar = "not available" # for debugging: assert self.rk == "not available" or (self.rk_lb == self.rank and self.rank == self.rk_ub) assert self.ar == "not available" or (self.rk_lb <= self.analytic_rank and self.analytic_rank <= self.rk_ub) self.bsd_status = "incomplete" if self.analytic_rank != None: if self.rk_lb == self.rk_ub: self.bsd_status = "unconditional" elif self.rk_lb == self.analytic_rank: self.bsd_status = "conditional" else: self.bsd_status = "missing_gens" # Regulator only in conditional/unconditional cases, or when we know the rank: if self.bsd_status in ["conditional", "unconditional"]: if self.ar == 0: self.reg = web_latex(1) # otherwise we only get 1.00000... else: try: self.reg = web_latex(self.reg) except AttributeError: self.reg = "not available" elif self.rk != "not available": self.reg = web_latex(self.reg) if self.rank else web_latex(1) else: self.reg = "not available" # Generators try: self.gens = [web_point(parse_point(K, P)) for P in self.gens] except AttributeError: self.gens = [] # Global period try: self.omega = web_latex(self.omega) except AttributeError: self.omega = "not available" # L-value try: r = int(self.analytic_rank) # lhs = "L(E,1) = " if r==0 else "L'(E,1) = " if r==1 else "L^{{({})}}(E,1)/{}! = ".format(r,r) self.Lvalue = "\\(" + str(self.Lvalue) + "\\)" except (TypeError, AttributeError): self.Lvalue = "not available" # Tamagawa product tamagawa_numbers = [ZZ(_ld['cp']) for _ld in self.local_data] cp_fac = [cp.factor() for cp in tamagawa_numbers] cp_fac = [ latex(cp) if len(cp) < 2 else '(' + latex(cp) + ')' for cp in cp_fac ] if len(cp_fac) > 1: self.tamagawa_factors = r'\cdot'.join(cp_fac) else: self.tamagawa_factors = None self.tamagawa_product = web_latex(prod(tamagawa_numbers, 1)) # Analytic Sha try: self.sha = web_latex(self.sha) + " (rounded)" except AttributeError: self.sha = "not available" # Local data # Fix for Kodaira symbols, which in the database start and end # with \( and \) and may have multiple backslashes. Note that # to put a single backslash into a python string you have to # use '\\' which will display as '\\' but only counts as one # character in the string. which are added in the template. def tidy_kod(kod): while '\\\\' in kod: kod = kod.replace('\\\\', '\\') kod = kod.replace('\\(', '').replace('\\)', '') return kod for P, ld in zip(badprimes, local_data): ld['p'] = web_latex(P) ld['norm'] = P.norm() ld['kod'] = tidy_kod(ld['kod']) # 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) # Isogeny information self.one_deg = ZZ(self.class_deg).is_prime() isodegs = [str(d) for d in self.isodeg if d > 1] if len(isodegs) < 3: self.isodeg = " and ".join(isodegs) else: self.isodeg = " and ".join([", ".join(isodegs[:-1]), isodegs[-1]]) sig = self.signature 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) lfun_url = url_for("l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) origin_url = lfun_url.lstrip('/L/').rstrip('/') if sig[0] <= 2 and db.lfunc_instances.exists({'url': origin_url}): self.urls['Lfunction'] = lfun_url elif self.abs_disc**2 * self.conductor_norm < 70000: # we shouldn't trust the Lfun computed on the fly for large conductor 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.bmf_url = url_for('bmf.render_bmf_webpage', field_label=self.field_label, level_label=self.conductor_label, label_suffix=self.iso_label) lfun_url = url_for("l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) origin_url = lfun_url.lstrip('/L/').rstrip('/') if db.lfunc_instances.exists({'url': origin_url}): self.urls['Lfunction'] = lfun_url # most of this code is repeated in isog_class.py # and should be refactored self.friends = [] self.friends += [('Isogeny class ' + self.short_class_label, self.urls['class'])] self.friends += [('Twists', url_for('ecnf.index', field=self.field_label, jinv=rename_j(j)))] if totally_real and not 'Lfunction' in self.urls: self.friends += [('Hilbert modular form ' + self.hmf_label, self.urls['hmf'])] if imag_quadratic: if "CM" in self.label: self.friends += [('Bianchi modular form is not cuspidal', '')] elif not 'Lfunction' in self.urls: if db.bmf_forms.label_exists(self.bmf_label): self.friends += [ ('Bianchi modular form %s' % self.bmf_label, self.bmf_url) ] else: self.friends += [ ('(Bianchi modular form %s)' % self.bmf_label, '') ] self.properties = [('Label', self.label)] # Plot if K.signature()[0]: self.plot = encode_plot( EC_nf_plot(K, self.ainvs, self.field.generator_name())) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties += [(None, self.plot_link)] self.properties += [('Base field', self.field.field_pretty())] self.properties += [ ('Conductor', self.cond), ('Conductor norm', self.cond_norm), # See issue #796 for why this is hidden (can be very large) # ('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 += [('Base change', 'no')] 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 += [(r'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() self.downloads = [('All stored data to text', url_for(".download_ECNF_all", nf=self.field_label, conductor_label=quote(self.conductor_label), class_label=self.iso_label, number=self.number))] for lang in [["Magma", "magma"], ["SageMath", "sage"], ["GP", "gp"]]: self.downloads.append( ('Code to {}'.format(lang[0]), url_for(".ecnf_code_download", nf=self.field_label, conductor_label=quote(self.conductor_label), class_label=self.iso_label, number=self.number, download_type=lang[1]))) if 'Lfunction' in self.urls: Lfun = get_lfunction_by_url( self.urls['Lfunction'].lstrip('/L').rstrip('/'), projection=['degree', 'trace_hash', 'Lhash']) if Lfun is None: self.friends += [('L-function not available', "")] else: instances = get_instances_by_Lhash_and_trace_hash( Lfun['Lhash'], Lfun['degree'], Lfun.get('trace_hash')) exclude = { elt[1].rstrip('/').lstrip('/') for elt in self.friends if elt[1] } self.friends += names_and_urls(instances, exclude=exclude) self.friends += [('L-function', self.urls['Lfunction'])] else: self.friends += [('L-function not available', "")]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} try: data['ainvs'] = [int(c) for c in self.xainvs[1:-1].split(',')] except AttributeError: data['ainvs'] = [int(ai) for ai in self.ainvs] data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) mw = self.mw = {} mw['rank'] = self.rank mw['int_points'] = '' if self.xintcoords: a1, a2, a3, a4, a6 = [ZZ(a) for a in data['ainvs']] def lift_x(x): f = ((x + a2) * x + a4) * x + a6 b = (a1 * x + a3) d = (b * b + 4 * f).sqrt() return (x, (-b + d) / 2) mw['int_points'] = ', '.join( web_latex(lift_x(x)) for x in self.xintcoords) mw['generators'] = '' mw['heights'] = [] if self.gens: mw['generators'] = [ web_latex(tuple(P)) for P in parse_points(self.gens) ] mw['tor_order'] = self.torsion tor_struct = [int(c) for c in self.torsion_structure] if mw['tor_order'] == 1: mw['tor_struct'] = '\mathrm{Trivial}' mw['tor_gens'] = '' else: mw['tor_struct'] = ' \\times '.join( ['\Z/{%s}\Z' % n for n in tor_struct]) mw['tor_gens'] = ', '.join( web_latex(tuple(P)) for P in parse_points(self.torsion_generators)) # try to get all the data we need from the database entry (now in self) try: data['equation'] = self.equation local_data = self.local_data D = self.signD * prod( [ld['p']**ld['ord_disc'] for ld in local_data]) data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] minq_label = self.min_quad_twist['label'] data['minq_label'] = db_ec().find_one( {'label': minq_label}, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD try: data['degree'] = self.degree except AttributeError: data['degree'] = 0 # invalid, but will be displayed nicely mw['heights'] = self.heights if self.number == 1: data['an'] = self.anlist data['ap'] = self.aplist else: r = db_ec().find_one({ 'lmfdb_iso': self.lmfdb_iso, 'number': 1 }, ['anlist', 'aplist']) data['an'] = r['anlist'] data['ap'] = r['aplist'] # otherwise fall back to computing it from the curve except AttributeError: print("Falling back to constructing E") self.E = EllipticCurve(data['ainvs']) data['equation'] = web_latex(self.E) data['disc'] = D = self.E.discriminant() Nfac = N.factor() Dfac = D.factor() bad_primes = [p for p, e in Nfac] try: data['degree'] = self.degree except AttributeError: try: data['degree'] = self.E.modular_degree() except RuntimeError: data['degree'] = 0 # invalid, but will be displayed nicely minq, minqD = self.E.minimal_quadratic_twist() data['minq_D'] = minqD if minqD == 1: data['minq_label'] = self.lmfdb_label data['minq_info'] = '(itself)' else: # This relies on the minimal twist being in the # database, which is true when the database only # contains the Cremona database. It would be a good # idea if, when the database is extended, we ensured # that for any curve included, all twists of smaller # conductor are also included. minq_ainvs = [str(c) for c in minq.ainvs()] data['minq_label'] = db_ec().find_one( { 'jinv': str(self.E.j_invariant()), 'ainvs': minq_ainvs }, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(by %s)' % minqD if self.gens: self.generators = [self.E(g) for g in parse_points(self.gens)] mw['heights'] = [P.height() for P in self.generators] data['an'] = self.E.anlist(20, python_ints=True) data['ap'] = self.E.aplist(100, python_ints=True) self.local_data = local_data = [] for p in bad_primes: ld = self.E.local_data(p, algorithm="generic") local_data_p = {} local_data_p['p'] = p local_data_p['cp'] = ld.tamagawa_number() local_data_p['kod'] = web_latex(ld.kodaira_symbol()).replace( '$', '') local_data_p['red'] = ld.bad_reduction_type() rootno = -ld.bad_reduction_type() if rootno == 0: rootno = self.E.root_number(p) local_data_p['rootno'] = rootno local_data_p['ord_cond'] = ld.conductor_valuation() local_data_p['ord_disc'] = ld.discriminant_valuation() local_data_p['ord_den_j'] = max( 0, -self.E.j_invariant().valuation(p)) local_data.append(local_data_p) # If we got the data from the database, the root numbers may # not have been stored there, so we have to compute them. If # there are additive primes this means constructing the curve. for ld in self.local_data: if not 'rootno' in ld: rootno = -ld['red'] if rootno == 0: try: E = self.E except AttributeError: self.E = E = EllipticCurve(data['ainvs']) rootno = E.root_number(ld['p']) ld['rootno'] = rootno minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label']) data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] try: data['galois_images'] = [ trim_galois_image_code(s) for s in self.galois_images ] data['non_surjective_primes'] = self.non_surjective_primes except AttributeError: #print "No Galois image data" data['galois_images'] = [] data['non_surjective_primes'] = [] data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_surjective_primes'], data['galois_images'])] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.ncurves = db_ec().count({'lmfdb_iso': self.lmfdb_iso}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & BSD data bsd = self.bsd = {} r = self.rank if r >= 2: bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r, r) elif r: bsd['lder_name'] = "L'(E,1)" else: bsd['lder_name'] = "L(E,1)" bsd['reg'] = self.regulator bsd['omega'] = self.real_period bsd['sha'] = int(0.1 + self.sha_an) bsd['lder'] = self.special_value # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago) if self.iso == '990h': data['Gamma0optimal'] = bool(self.number == 3) else: data['Gamma0optimal'] = bool(self.number == 1) data['p_adic_data_exists'] = False if data['Gamma0optimal']: data['p_adic_data_exists'] = (padic_db().find({ 'lmfdb_iso': self.lmfdb_iso }).count()) > 0 tamagawa_numbers = [ZZ(ld['cp']) for ld in local_data] cp_fac = [cp.factor() for cp in tamagawa_numbers] cp_fac = [ latex(cp) if len(cp) < 2 else '(' + latex(cp) + ')' for cp in cp_fac ] bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac) bsd['tamagawa_product'] = prod(tamagawa_numbers) data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = newform_label( cond, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self._code = None self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)), ('L-function', url_for("l_functions.l_function_ec_page", label=self.lmfdb_label))] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', label=self.lmfdb_iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)), ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Download Sage code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot self.properties = [('Label', self.lmfdb_label), (None, self.plot_link), ('Conductor', '\(%s\)' % data['conductor']), ('Discriminant', '\(%s\)' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '\(%s\)' % mw['rank']), ('Torsion Structure', '\(%s\)' % mw['tor_struct'])] self.title = "Elliptic Curve %s (Cremona label %s)" % ( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} data['ainvs'] = self.ainvs data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) # extract data about MW rank, generators, heights and torsion: self.make_mw() # get more data from the database entry data['equation'] = self.equation local_data = self.local_data D = self.signD * prod([ld['p']**ld['ord_disc'] for ld in local_data]) data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] minq_label = self.min_quad_twist['label'] data['minq_label'] = db.ec_curves.lucky({'label': minq_label}, 'lmfdb_label') data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD if self.degree is None: data['degree'] = 0 # invalid, but will be displayed nicely else: data['degree'] = self.degree if self.number == 1: data['an'] = self.anlist data['ap'] = self.aplist else: r = db.ec_curves.lucky({'lmfdb_iso': self.lmfdb_iso, 'number': 1}) data['an'] = r['anlist'] data['ap'] = r['aplist'] minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label']) data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['galois_images'] = [ trim_galois_image_code(s) for s in self.mod_p_images ] data['non_maximal_primes'] = self.non_maximal_primes data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_maximal_primes'], data['galois_images'])] data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.non_maximal_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join([str(p) for p in data['cm_ramp']]) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.one_deg = ZZ(self.class_deg).is_prime() self.ncurves = db.ec_curves.count({'lmfdb_iso': self.lmfdb_iso}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & other BSD data self.make_bsd() # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago) if self.iso == '990h': data['Gamma0optimal'] = bool(self.number == 3) else: data['Gamma0optimal'] = bool(self.number == 1) data['p_adic_data_exists'] = False if data['Gamma0optimal']: data['p_adic_data_exists'] = db.ec_padic.exists( {'lmfdb_iso': self.lmfdb_iso}) # Iwasawa data (where present) self.make_iwasawa() # Torsion growth data (where present) self.make_torsion_growth() data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = newform_label( cond, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self._code = None self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)), ('L-function', url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso))] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)), ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Download SageMath code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [('Label', self.lmfdb_label), (None, self.plot_link), ('Conductor', '\(%s\)' % data['conductor']), ('Discriminant', '\(%s\)' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '\(%s\)' % self.mw['rank']), ('Torsion Structure', '\(%s\)' % self.mw['tor_struct'])] self.title = "Elliptic Curve %s (Cremona label %s)" % ( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} try: data['ainvs'] = [int(c) for c in self.xainvs[1:-1].split(',')] except AttributeError: data['ainvs'] = [int(ai) for ai in self.ainvs] data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) mw = self.mw = {} mw['rank'] = self.rank mw['int_points'] = '' if self.xintcoords: a1, a2, a3, a4, a6 = [ZZ(a) for a in data['ainvs']] def lift_x(x): f = ((x + a2) * x + a4) * x + a6 b = (a1 * x + a3) d = (b * b + 4 * f).sqrt() return (x, (-b + d) / 2) mw['int_points'] = ', '.join( web_latex(lift_x(x)) for x in self.xintcoords) mw['generators'] = '' mw['heights'] = [] if self.gens: mw['generators'] = [ web_latex(tuple(P)) for P in parse_points(self.gens) ] mw['tor_order'] = self.torsion tor_struct = [int(c) for c in self.torsion_structure] if mw['tor_order'] == 1: mw['tor_struct'] = '\mathrm{Trivial}' mw['tor_gens'] = '' else: mw['tor_struct'] = ' \\times '.join( ['\Z/{%s}\Z' % n for n in tor_struct]) mw['tor_gens'] = ', '.join( web_latex(tuple(P)) for P in parse_points(self.torsion_generators)) # try to get all the data we need from the database entry (now in self) try: data['equation'] = self.equation local_data = self.local_data D = self.signD * prod( [ld['p']**ld['ord_disc'] for ld in local_data]) data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] minq_label = self.min_quad_twist['label'] data['minq_label'] = db_ec().find_one( {'label': minq_label}, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD try: data['degree'] = self.degree except AttributeError: data['degree'] = 0 # invalid, but will be displayed nicely mw['heights'] = self.heights if self.number == 1: data['an'] = self.anlist data['ap'] = self.aplist else: r = db_ec().find_one({ 'lmfdb_iso': self.lmfdb_iso, 'number': 1 }, ['anlist', 'aplist']) data['an'] = r['anlist'] data['ap'] = r['aplist'] # otherwise fall back to computing it from the curve except AttributeError: self.E = EllipticCurve(data['ainvs']) data['equation'] = web_latex(self.E) data['disc'] = D = self.E.discriminant() Nfac = N.factor() Dfac = D.factor() bad_primes = [p for p, e in Nfac] try: data['degree'] = self.degree except AttributeError: try: data['degree'] = self.E.modular_degree() except RuntimeError: data['degree'] = 0 # invalid, but will be displayed nicely minq, minqD = self.E.minimal_quadratic_twist() data['minq_D'] = minqD if minqD == 1: data['minq_label'] = self.lmfdb_label data['minq_info'] = '(itself)' else: # This relies on the minimal twist being in the # database, which is true when the database only # contains the Cremona database. It would be a good # idea if, when the database is extended, we ensured # that for any curve included, all twists of smaller # conductor are also included. minq_ainvs = [str(c) for c in minq.ainvs()] data['minq_label'] = db_ec().find_one( { 'jinv': str(self.E.j_invariant()), 'ainvs': minq_ainvs }, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(by %s)' % minqD if self.gens: self.generators = [self.E(g) for g in parse_points(self.gens)] mw['heights'] = [P.height() for P in self.generators] data['an'] = self.E.anlist(20, python_ints=True) data['ap'] = self.E.aplist(100, python_ints=True) self.local_data = local_data = [] for p in bad_primes: ld = self.E.local_data(p, algorithm="generic") local_data_p = {} local_data_p['p'] = p local_data_p['cp'] = ld.tamagawa_number() local_data_p['kod'] = web_latex(ld.kodaira_symbol()).replace( '$', '') local_data_p['red'] = ld.bad_reduction_type() rootno = -ld.bad_reduction_type() if rootno == 0: rootno = self.E.root_number(p) local_data_p['rootno'] = rootno local_data_p['ord_cond'] = ld.conductor_valuation() local_data_p['ord_disc'] = ld.discriminant_valuation() local_data_p['ord_den_j'] = max( 0, -self.E.j_invariant().valuation(p)) local_data.append(local_data_p) # If we got the data from the database, the root numbers may # not have been stored there, so we have to compute them. If # there are additive primes this means constructing the curve. for ld in self.local_data: if not 'rootno' in ld: rootno = -ld['red'] if rootno == 0: try: E = self.E except AttributeError: self.E = E = EllipticCurve(data['ainvs']) rootno = E.root_number(ld['p']) ld['rootno'] = rootno minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label']) data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['galois_images'] = [ trim_galois_image_code(s) for s in self.mod_p_images ] data['non_maximal_primes'] = self.non_maximal_primes data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_maximal_primes'], data['galois_images'])] data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.non_surjective_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join([str(p) for p in data['cm_ramp']]) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.one_deg = ZZ(self.class_deg).is_prime() self.ncurves = db_ec().count({'lmfdb_iso': self.lmfdb_iso}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & BSD data bsd = self.bsd = {} r = self.rank if r >= 2: bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r, r) elif r: bsd['lder_name'] = "L'(E,1)" else: bsd['lder_name'] = "L(E,1)" bsd['reg'] = self.regulator bsd['omega'] = self.real_period bsd['sha'] = int(0.1 + self.sha_an) bsd['lder'] = self.special_value # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago) if self.iso == '990h': data['Gamma0optimal'] = bool(self.number == 3) else: data['Gamma0optimal'] = bool(self.number == 1) data['p_adic_data_exists'] = False if data['Gamma0optimal']: data['p_adic_data_exists'] = (padic_db().find({ 'lmfdb_iso': self.lmfdb_iso }).count()) > 0 data['iwdata'] = [] try: pp = [int(p) for p in self.iwdata] badp = [l['p'] for l in self.local_data] rtypes = [l['red'] for l in self.local_data] data[ 'iw_missing_flag'] = False # flags that there is at least one "?" in the table data[ 'additive_shown'] = False # flags that there is at least one additive prime in table for p in sorted(pp): rtype = "" if p in badp: red = rtypes[badp.index(p)] # Additive primes are excluded from the table # if red==0: # continue #rtype = ["nsmult","add", "smult"][1+red] rtype = ["nonsplit", "add", "split"][1 + red] p = str(p) pdata = self.iwdata[p] if isinstance(pdata, type(u'?')): if not rtype: rtype = "ordinary" if pdata == "o?" else "ss" if rtype == "add": data['iwdata'] += [[p, rtype, "-", "-"]] data['additive_shown'] = True else: data['iwdata'] += [[p, rtype, "?", "?"]] data['iw_missing_flag'] = True else: if len(pdata) == 2: if not rtype: rtype = "ordinary" lambdas = str(pdata[0]) mus = str(pdata[1]) else: rtype = "ss" lambdas = ",".join([str(pdata[0]), str(pdata[1])]) mus = str(pdata[2]) mus = ",".join([mus, mus]) data['iwdata'] += [[p, rtype, lambdas, mus]] except AttributeError: # For curves with no Iwasawa data pass tamagawa_numbers = [ZZ(ld['cp']) for ld in local_data] cp_fac = [cp.factor() for cp in tamagawa_numbers] cp_fac = [ latex(cp) if len(cp) < 2 else '(' + latex(cp) + ')' for cp in cp_fac ] bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac) bsd['tamagawa_product'] = prod(tamagawa_numbers) # Torsion growth data data['torsion_growth_data_exists'] = False try: tg = self.tor_gro data['torsion_growth_data_exists'] = True data['tgx'] = tgextra = [] # find all base-changes of this curve in the database, if any bcs = [ res['label'] for res in getDBConnection().elliptic_curves.nfcurves.find( {'base_change': self.lmfdb_label}, projection={ 'label': True, '_id': False }) ] bcfs = [lab.split("-")[0] for lab in bcs] for F, T in tg.items(): tg1 = {} tg1['bc'] = "Not in database" if ":" in F: F = F.replace(":", ".") field_data = nf_display_knowl(F, getDBConnection(), field_pretty(F)) deg = int(F.split(".")[0]) bcc = [x for x, y in zip(bcs, bcfs) if y == F] if bcc: from lmfdb.ecnf.main import split_full_label F, NN, I, C = split_full_label(bcc[0]) tg1['bc'] = bcc[0] tg1['bc_url'] = url_for('ecnf.show_ecnf', nf=F, conductor_label=NN, class_label=I, number=C) else: field_data = web_latex_split_on_pm( coeff_to_poly(string2list(F))) deg = F.count(",") tg1['d'] = deg tg1['f'] = field_data tg1['t'] = '\(' + ' \\times '.join( ['\Z/{}\Z'.format(n) for n in T.split(",")]) + '\)' tg1['m'] = 0 tgextra.append(tg1) tgextra.sort(key=lambda x: x['d']) data['ntgx'] = len(tgextra) lastd = 1 for tg in tgextra: d = tg['d'] if d != lastd: tg['m'] = len([x for x in tgextra if x['d'] == d]) lastd = d data['tg_maxd'] = max(db_ecstats().find_one( {'_id': 'torsion_growth'})['degrees']) except AttributeError: pass # we have no torsion growth data data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = newform_label( cond, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self._code = None self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)), ('L-function', url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso))] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)), ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Download Sage code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [('Label', self.lmfdb_label), (None, self.plot_link), ('Conductor', '\(%s\)' % data['conductor']), ('Discriminant', '\(%s\)' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '\(%s\)' % mw['rank']), ('Torsion Structure', '\(%s\)' % mw['tor_struct'])] self.title = "Elliptic Curve %s (Cremona label %s)" % ( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]