def field_pretty(label): d, r, D, i = label.split('.') if d == '1': # Q return r'\(\Q\)' if d == '2': # quadratic field D = ZZ(int(D)) if r == '0': D = -D # Don't prettify invalid quadratic field labels if not is_fundamental_discriminant(D): return label return r'\(\Q(\sqrt{' + str(D if D%4 else D/4) + r'}) \)' if label in cycloinfo: return r'\(\Q(\zeta_{%d})\)' % cycloinfo[label] if d == '4': wnf = WebNumberField(label) subs = wnf.subfields() if len(subs)==3: # only for V_4 fields subs = [wnf.from_coeffs(string2list(str(z[0]))) for z in subs] # Abort if we don't know one of these fields if not any(z._data is None for z in subs): labels = [str(z.get_label()) for z in subs] labels = [z.split('.') for z in labels] # extract abs disc and signature to be good for sorting labels = [[integer_squarefree_part(ZZ(z[2])), - int(z[1])] for z in labels] labels.sort() # put in +/- sign labels = [z[0]*(-1)**(1+z[1]/2) for z in labels] labels = ['i' if z == -1 else r'\sqrt{%d}'% z for z in labels] return r'\(\Q(%s, %s)\)'%(labels[0],labels[1]) if label in rcycloinfo: return r'\(\Q(\zeta_{%d})^+\)' % rcycloinfo[label] return label
def make_E(self): #print("Creating ECNF object for {}".format(self.label)) #sys.stdout.flush() K = self.field.K() Kgen = str(K.gen()) # a-invariants # NB Here we construct the ai as elements of K, which are used as follows: # (1) to compute the model discriminant (if not stored) # (2) to compute the latex equation (if not stored) # (3) to compute the plots under real embeddings of K # Of these, (2) is not needed and (1) will soon be obsolete; # for (3) it would be possible to rewrite the function EC_nf_plot() not to need this. # Then we might also be able to avoid constructing the field K also. self.ainvs = parse_ainvs(K, self.ainvs) self.numb = str(self.number) # Conductor, discriminant, j-invariant self.cond_norm = web_latex(self.conductor_norm) Dnorm = self.normdisc self.disc = pretty_ideal(Kgen, self.disc) local_data = self.local_data local_data.sort(key=lambda ld: ld['normp']) badprimes = [ pretty_ideal(Kgen, ld['p'], enclose=False) for ld in local_data ] badnorms = [ld['normp'] for ld in local_data] disc_ords = [ld['ord_disc'] for ld in local_data] mindisc_ords = [ld['ord_disc'] for ld in local_data] cond_ords = [ld['ord_cond'] for ld in local_data] if self.conductor_norm == 1: self.cond = r"\((1)\)" self.fact_cond = self.cond self.fact_cond_norm = '1' else: self.cond = pretty_ideal(Kgen, self.conductor_ideal) self.fact_cond = latex_factorization(badprimes, cond_ords) self.fact_cond_norm = latex_factorization(badnorms, cond_ords) # 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.is_minimal = (len(self.non_min_p) == 0) self.has_minimal_model = self.is_minimal if not self.is_minimal: non_min_p = self.non_min_p[0] self.non_min_prime = pretty_ideal(Kgen, non_min_p) ip = [ld['p'] for ld in local_data].index(non_min_p) disc_ords[ip] += 12 Dnorm_factor = local_data[ip]['normp']**12 self.disc_norm = web_latex(Dnorm) signDnorm = 1 if Dnorm > 0 else -1 if Dnorm in [1, -1]: # since the factorization of (1) displays as "1" self.fact_disc = self.disc self.fact_disc_norm = str(Dnorm) else: self.fact_disc = latex_factorization(badprimes, disc_ords) self.fact_disc_norm = latex_factorization(badnorms, disc_ords, sign=signDnorm) if self.is_minimal: Dmin_norm = Dnorm self.mindisc = self.disc else: Dmin_norm = Dnorm // Dnorm_factor self.mindisc = pretty_ideal(Kgen, self.minD) self.mindisc_norm = web_latex(Dmin_norm) if Dmin_norm in [1, -1]: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc self.fact_mindisc_norm = self.mindisc_norm else: self.fact_mindisc = latex_factorization(badprimes, mindisc_ords) self.fact_mindisc_norm = latex_factorization(badnorms, mindisc_ords, sign=signDnorm) j = self.field.parse_NFelt(self.jinv) 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. # When the equation is stored in the database as a latex string, # it may have extraneous double quotes at beginning and # end, which we fix here. We also strip out initial \( and \) # (if present) which are added in the template. try: self.equation = self.equation.replace('"', '').replace( r'\\(', '').replace(r'\\)', '') except AttributeError: self.equation = latex_equation(self.ainvs) # Images of Galois representations if not hasattr(self, 'galois_images'): #print "No Galois image data" self.galois_images = "?" self.nonmax_primes = "?" self.galois_data = [] else: self.galois_data = [{ 'p': p, 'image': im } for p, im in zip(self.nonmax_primes, self.galois_images)] # CM and End(E) self.cm_bool = "no" self.End = r"\(\Z\)" self.rational_cm = self.cm_type > 0 if self.cm: self.cm_sqf = integer_squarefree_part(ZZ(self.cm)) 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 p not in self.nonmax_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: self.ST = st_display_knowl('1.2.A.1.1a' if not self.cm_type else ( '1.2.B.2.1a' if self.cm_type < 0 else '1.2.B.1.1a')) # 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 is not 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 = web_latex(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 # The Kodaira symbol is stored as an int in pari encoding. The # conversion to latex must take into account the bug (in Sage # 9.2) for I_m^* when m has more than one digit. def latex_kod(kod): return latex( KodairaSymbol(kod)) if kod > -14 else 'I_{%s}^{*}' % (-kod - 4) for P, NP, ld in zip(badprimes, badnorms, local_data): ld['p'] = P ld['norm'] = NP ld['kod'] = latex_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 'Lfunction' not 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 'Lfunction' not 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.base_change = [ lab for lab in self.base_change if '?' not in lab ] 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"], ["GP", "gp"], ["SageMath", "sage"]]: 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]))) self.downloads.append( ('Underlying data', url_for(".ecnf_data", label=self.label))) 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): 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']) data['faltings_height'] = RR(self.faltings_height) data['stable_faltings_height'] = RR(self.stable_faltings_height) # 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})) # 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 p not 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'] = integer_squarefree_part(ZZ(self.cm)) 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'] = display_knowl('st_group.data', title=r"$N(\mathrm{U}(1))$", kwargs={'label':'1.2.B.2.1a'}) else: data['ST'] = display_knowl('st_group.data', title=r"$\mathrm{SU}(2)$", kwargs={'label':'1.2.A.1.1a'}) # 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]]) # 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')), ('Underlying data', url_for(".EC_data", label=self.lmfdb_label)), ] 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_class(self): # Extract the size of the isogeny class from the database classdata = db.ec_classdata.lucky({'lmfdb_iso': self.lmfdb_iso}) self.class_size = ncurves = classdata['class_size'] # Create a list of the curves in the class from the database number_key = 'Cnumber' if self.label_type == 'Cremona' else 'lmfdb_number' self.curves = [ db.ec_curvedata.lucky({ 'lmfdb_iso': self.lmfdb_iso, number_key: i + 1 }) for i in range(ncurves) ] # Set optimality flags. The optimal curve is conditionally # number 1 except in one case which is labeled differently in # the Cremona tables. We know which curve is optimal iff the # optimality code for curve #1 is 1 (except for class 990h). # Note that self is actually an elliptic curve, with 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. self.cremona_bound = CREMONA_BOUND self.optimality_bound = OPTIMALITY_BOUND self.optimality_known = (self.conductor < OPTIMALITY_BOUND) or ( (self.conductor < CREMONA_BOUND) and ((self.optimality == 1) or (self.Ciso == '990h'))) self.optimal_label = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label if self.conductor < OPTIMALITY_BOUND: for c in self.curves: c['optimal'] = (c['Cnumber'] == (3 if self.Ciso == '990h' else 1)) c['optimality_known'] = True elif self.conductor < CREMONA_BOUND: for c in self.curves: c['optimal'] = (c['optimality'] > 0 ) # this curve possibly optimal c['optimality_known'] = (c['optimality'] == 1 ) # this curve certainly optimal else: for c in self.curves: c['optimal'] = None c['optimality_known'] = False for c in self.curves: c['ai'] = c['ainvs'] c['curve_url_lmfdb'] = url_for(".by_ec_label", label=c['lmfdb_label']) c['curve_url_cremona'] = url_for( ".by_ec_label", label=c['Clabel']) if self.conductor < CREMONA_BOUND else "N/A" if self.label_type == 'Cremona': c['curve_label'] = c['Clabel'] _, c_iso, c_number = split_cremona_label(c['Clabel']) else: c['curve_label'] = c['lmfdb_label'] _, c_iso, c_number = split_lmfdb_label(c['lmfdb_label']) c['short_label'] = "{}{}".format(c_iso, c_number) c['FH'] = RealField(20)(c['faltings_height']) c['j_inv'] = QQ(tuple( c['jinv'])) # convert [num,den] to rational for display c['disc'] = c['signD'] * c['absD'] from sage.matrix.all import Matrix M = classdata['isogeny_matrix'] # permute rows/cols to match labelling: the rows/cols in the # ec_classdata table are with respect to LMFDB ordering. if self.label_type == 'Cremona': perm = lambda i: next(c for c in self.curves if c['Cnumber'] == i + 1)['lmfdb_number'] - 1 M = [[M[perm(i)][perm(j)] for i in range(ncurves)] for j in range(ncurves)] M = Matrix(M) self.isogeny_matrix_str = latex(M) # Create isogeny graph with appropriate vertex labels: self.graph = make_graph(M, [c['short_label'] for c in self.curves]) P = self.graph.plot(edge_labels=True, vertex_size=1000) self.graph_img = encode_plot(P) self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img self.newform = raw_typeset( PowerSeriesRing(QQ, 'q')(classdata['anlist'], 20, check=True)) self.newform_label = ".".join( [str(self.conductor), str(2), 'a', self.iso_label]) self.newform_exists_in_db = db.mf_newforms.label_exists( self.newform_label) if self.newform_exists_in_db: char_orbit, hecke_orbit = self.newform_label.split('.')[2:] self.newform_link = url_for("cmf.by_url_newform_label", level=self.conductor, weight=2, char_orbit_label=char_orbit, hecke_orbit=hecke_orbit) self.lfunction_link = url_for("l_functions.l_function_ec_page", conductor_label=self.conductor, isogeny_class_label=self.iso_label) self.friends = [('L-function', self.lfunction_link)] if self.cm: # set CM field for Properties box. D = integer_squarefree_part(ZZ(self.cm)) coeffs = [(1 - D) // 4, -1, 1] if D % 4 == 1 else [-D, 0, 1] lab = db.nf_fields.lucky({'coeffs': coeffs}, projection='label') self.CMfield = field_pretty(lab) else: self.CMfield = "no" if self.conductor <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=self.conductor, isogeny=self.iso_label))] if self.conductor <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=self.conductor, isogeny=self.iso_label))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] if self.label_type == 'Cremona': self.title = "Elliptic curve isogeny class with Cremona label {} (LMFDB label {})".format( self.Ciso, self.lmfdb_iso) elif self.conductor < CREMONA_BOUND: self.title = "Elliptic curve isogeny class with LMFDB label {} (Cremona label {})".format( self.lmfdb_iso, self.Ciso) else: self.title = "Elliptic curve isogeny class with LMFDB label {}".format( self.lmfdb_iso) self.properties = [ ('Label', self.Ciso if self.label_type == 'Cremona' else self.lmfdb_iso), ('Number of curves', prop_int_pretty(ncurves)), ('Conductor', prop_int_pretty(self.conductor)), ('CM', '%s' % self.CMfield), ('Rank', prop_int_pretty(self.rank)) ] if ncurves > 1: self.properties += [('Graph', ''), (None, self.graph_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.iso_label, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.iso_label))] self.bread = [('Elliptic curves', url_for("ecnf.index")), (r'$\Q$', url_for(".rational_elliptic_curves")), ('%s' % self.conductor, url_for(".by_conductor", conductor=self.conductor)), ('%s' % self.iso_label, ' ')] self.code = {} self.code['show'] = {'sage': ''} # use default show names self.code['class'] = { 'sage': 'E = EllipticCurve("%s1")\n' % (self.iso_label) + 'E.isogeny_class()\n' } self.code['curves'] = {'sage': 'E.isogeny_class().curves'} self.code['rank'] = {'sage': 'E.rank()'} self.code['q_eigenform'] = {'sage': 'E.q_eigenform(10)'} self.code['matrix'] = {'sage': 'E.isogeny_class().matrix()'} self.code['plot'] = { 'sage': 'E.isogeny_graph().plot(edge_labels=True)' }
LinkCol("lmfdb_iso", "ec.q.lmfdb_label", "Class", lambda label: url_for(".by_ec_label", label=label), default=True, align="center", short_title="LMFDB class label"), MultiProcessedCol("cremona_iso", "ec.q.cremona_label", "Cremona class", ["Ciso", "conductor"], lambda label, conductor: '<a href="%s">%s</a>' % (url_for(".by_ec_label", label=label), label) if conductor < CREMONA_BOUND else " - ", align="center", short_title="Cremona class label"), MathCol("class_size", "ec.isogeny_class", "Class size", align="center", default=lambda info: info.get("class_size") or info.get("optimal") == "on"), MathCol("class_deg", "ec.isogeny_class_degree", "Class degree", align="center", default=lambda info: info.get("class_deg")), ProcessedCol("conductor", "ec.q.conductor", "Conductor", lambda v: web_latex_factored_integer(ZZ(v)), default=True, align="center"), MultiProcessedCol("disc", "ec.discriminant", "Discriminant", ["signD", "absD"], lambda s, a: web_latex_factored_integer(s*ZZ(a)), default=lambda info: info.get("discriminant"), align="center"), MathCol("rank", "ec.rank", "Rank", default=True), ProcessedCol("torsion_structure", "ec.torsion_subgroup", "Torsion", lambda tors: r"\oplus".join([r"\Z/%s\Z"%n for n in tors]) if tors else r"\mathsf{trivial}", default=True, mathmode=True, align="center"), ProcessedCol("geom_end_alg", "ag.endomorphism_algebra", r"$\textrm{End}^0(E_{\overline\Q})$", lambda v: r"$\Q$" if not v else r"$\Q(\sqrt{%d})$"%(integer_squarefree_part(v)), short_title="Qbar-end algebra", align="center", orig="cm"), ProcessedCol("cm_discriminant", "ec.complex_multiplication", "CM", lambda v: "" if v == 0 else v, short_title="CM discriminant", mathmode=True, align="center", default=True, orig="cm"), ProcessedCol("sato_tate_group", "st_group.definition", "Sato-Tate", lambda v: st_display_knowl('1.2.A.1.1a' if v==0 else '1.2.B.2.1a'), short_title="Sato-Tate group", align="center", orig="cm"), CheckCol("semistable", "ec.reduction", "Semistable"), CheckCol("potential_good_reduction", "ec.reduction", "Potentially good"), ProcessedCol("nonmax_primes", "ec.maximal_elladic_galois_rep", r"Nonmax $\ell$", lambda primes: ", ".join([str(p) for p in primes]), default=lambda info: info.get("nonmax_primes"), short_title="nonmaximal primes", mathmode=True, align="center"), ProcessedCol("elladic_images", "ec.galois_rep_elladic_image", r"$\ell$-adic images", lambda v: ", ".join([display_knowl('gl2.subgroup_data', title=s, kwargs={'label':s}) for s in v]), short_title="ℓ-adic images", default=lambda info: info.get("nonmax_primes") or info.get("galois_image"), align="center"), ProcessedCol("modell_images", "ec.galois_rep_modell_image", r"mod-$\ell$ images", lambda v: ", ".join([display_knowl('gl2.subgroup_data', title=s, kwargs={'label':s}) for s in v]), short_title="mod-ℓ images", default=lambda info: info.get("nonmax_primes") or info.get("galois_image"), align="center"), ProcessedCol("regulator", "ec.regulator", "Regulator", lambda v: str(v)[:11], mathmode=True), MathCol("sha", "ec.analytic_sha_order", r"$Ш_{\textrm{an}}$", short_title="analytic Ш"),