def check_related_objects(self, rec, verbose=False): """ check that URLS in related_objects are valid and identify objects present in the LMFDB """ names = names_and_urls(rec['related_objects']) if len(names) != len(rec['related_objects']): if verbose: print("Length failure", len(names), len(rec['related_objects'])) return False # if related_objects contains an Artin rep, check that k=1 and that conductor of artin rep matches level N for name, url in names: if name.startswith('Artin representation '): if rec['weight'] != 1: if verbose: print("Artin weight failure", name, rec['weight']) return False artin_label = name.split()[-1] conductor_string = artin_label.split('.')[1] conductor = 1 for elt in conductor_string.split('_'): pe = [int(s) for s in elt.split('e')] if len(pe) == 1: conductor *= pe[0] elif len(pe) == 2: conductor *= pe[0]**pe[1] else: raise ValueError(str(pe)) if conductor != rec['level']: if verbose: print("Conductor failure", name, conductor, rec['level']) return False # if k=2, char_orbit_index=1 and dim=1 check that elliptic curve isogeny class of conductor N is present in related_objects if url.startswith('/EllipticCurve/Q/'): if rec['weight'] != 2: if verbose: print("EC weight failure", url, rec['weight']) return False if rec['dim'] == 1: # Curve over Q if rec['level'] != int(name.split()[-1].split('.')[0]): if verbose: print("EC level failure", url, rec['level'], int(name.split()[-1].split('.')[0])) return False if (rec['weight'] == 2 and rec['char_orbit_index'] == 1 and rec['dim'] == 1 and not any( url.startswith('/EllipticCurve/Q/') for name, url in names)): if verbose: print("Modularity failure") return False return True
def check_related_objects(self, rec, verbose=False): """ check that URLS in related_objects are valid and identify objects present in the LMFDB """ names = names_and_urls(rec['related_objects']) if len(names) != len(rec['related_objects']): if verbose: print "Length failure", len(names), len(rec['related_objects']) return False # if related_objects contains an Artin rep, check that k=1 and that conductor of artin rep matches level N for name, url in names: if name.startswith('Artin representation '): if rec['weight'] != 1: if verbose: print "Artin weight failure", name, rec['weight'] return False artin_label = name.split()[-1] conductor_string = artin_label.split('.')[1] conductor = 1 for elt in conductor_string.split('_'): pe = map(int, elt.split('e')) if len(pe) == 1: conductor *= pe[0] elif len(pe) == 2: conductor *= pe[0]**pe[1] else: raise ValueError(str(pe)) if conductor != rec['level']: if verbose: print "Conductor failure", name, conductor, rec['level'] return False # if k=2, char_orbit_index=1 and dim=1 check that elliptic curve isogeny class of conductor N is present in related_objects if url.startswith('/EllipticCurve/Q/'): if rec['weight'] != 2: if verbose: print "EC weight failure", url, rec['weight'] return False if rec['dim'] == 1: # Curve over Q if rec['level'] != int(name.split()[-1].split('.')[0]): if verbose: print "EC level failure", url, rec['level'], int(name.split()[-1].split('.')[0]) return False if (rec['weight'] == 2 and rec['char_orbit_index'] == 1 and rec['dim'] == 1 and not any(url.startswith('/EllipticCurve/Q/') for name, url in names)): if verbose: print "Modularity failure" return False return True
def friends(self): # first newspaces res = [] base_label = map(str, [self.level, self.weight]) cmf_base = '/ModularForm/GL2/Q/holomorphic/' ns1_label = '.'.join(base_label) ns1_url = cmf_base + '/'.join(base_label) res.append(('Newspace ' + ns1_label, ns1_url)) char_letter = self.char_orbit_label ns_label = '.'.join(base_label + [char_letter]) ns_url = cmf_base + '/'.join(base_label + [char_letter]) res.append(('Newspace ' + ns_label, ns_url)) nf_url = ns_url + '/' + self.hecke_orbit_label if self.embedding_label is not None: res.append(('Newform ' + self.label, nf_url)) if self.dual_label is not None and self.dual_label != self.embedding_label: dlabel = self.label + '.' + self.dual_label d_url = nf_url + '/' + self.dual_label.replace('.','/') + '/' res.append(('Dual Form ' + dlabel, d_url)) # then related objects res += names_and_urls(self.related_objects) # finally L-functions if self.weight <= 200: if db.lfunc_instances.exists({'url': nf_url[1:]}): res.append(('L-function ' + self.label, '/L' + nf_url)) if self.embedding_label is None and len(self.conrey_indexes)*self.rel_dim > 50: res = map(lambda elt : list(map(str, elt)), res) # properties_lfun(initialFriends, label, nf_url, conrey_indexes, rel_dim) return '<script id="properties_script">$( document ).ready(function() {properties_lfun(%r, %r, %r, %r, %r)}); </script>' % (res, str(self.label), str(nf_url), self.conrey_indexes, self.rel_dim) if self.dim > 1: for lfun_label in self.embedding_labels: lfun_url = '/L' + cmf_base + lfun_label.replace('.','/') res.append(('L-function ' + lfun_label, lfun_url)) return res
def render_hmf_webpage(**args): if 'data' in args: data = args['data'] label = data['label'] else: label = str(args['label']) data = get_hmf(label) if data is None: flash_error( "%s is not a valid Hilbert modular form label. It must be of the form (number field label) - (level label) - (orbit label) separated by dashes, such as 2.2.5.1-31.1-a", args['label']) return search_input_error() info = {} try: info['count'] = args['count'] except KeyError: info['count'] = 50 hmf_field = get_hmf_field(data['field_label']) gen_name = findvar(hmf_field['ideals']) nf = WebNumberField(data['field_label'], gen_name=gen_name) info['hmf_field'] = hmf_field info['field'] = nf info['base_galois_group'] = nf.galois_string() info['field_degree'] = nf.degree() info['field_disc'] = str(nf.disc()) info['field_poly'] = teXify_pol(str(nf.poly())) info.update(data) info['downloads'] = [('Modular form to Magma', url_for(".render_hmf_webpage_download", field_label=info['field_label'], label=info['label'], download_type='magma')), ('Eigenvalues to Sage', url_for(".render_hmf_webpage_download", field_label=info['field_label'], label=info['label'], download_type='sage'))] # figure out friends # first try to see if there is an instance of this HMF on Lfun db url = 'ModularForm/GL2/TotallyReal/{}/holomorphic/{}'.format( info['field_label'], info['label']) Lfun = get_lfunction_by_url(url) if Lfun: instances = get_instances_by_Lhash_and_trace_hash( Lfun['Lhash'], Lfun['degree'], Lfun['trace_hash']) # This will also add the EC/G2C, as this how the Lfun was computed info['friends'] = names_and_urls(instances, exclude={url}) info['friends'] += [('L-function', url_for("l_functions.l_function_hmf_page", field=info['field_label'], label=info['label'], character='0', number='0'))] else: # if there is no instance # old code if hmf_field['narrow_class_no'] == 1 and nf.disc( )**2 * data['level_norm'] < 40000: info['friends'] = [('L-function', url_for("l_functions.l_function_hmf_page", field=info['field_label'], label=info['label'], character='0', number='0'))] else: info['friends'] = [('L-function not available', "")] if data['dimension'] == 1: # Try to attach associated elliptic curve lab = split_class_label(info['label']) ec_from_hmf = db.ec_nfcurves.lookup(label + '1') if ec_from_hmf is None: info['friends'] += [('Elliptic curve not available', "")] else: info['friends'] += [('Isogeny class ' + info['label'], url_for("ecnf.show_ecnf_isoclass", nf=lab[0], conductor_label=lab[1], class_label=lab[2]))] bread = [("Modular Forms", url_for('modular_forms')), ('Hilbert Modular Forms', url_for(".hilbert_modular_form_render_webpage")), ('%s' % data['label'], ' ')] t = "Hilbert Cusp Form %s" % info['label'] forms_dims = db.hmf_forms.search( { 'field_label': data['field_label'], 'level_ideal': data['level_ideal'] }, projection='dimension') info['newspace_dimension'] = sum(forms_dims) # Get hecke_polynomial, hecke_eigenvalues and AL_eigenvalues try: numeigs = request.args['numeigs'] numeigs = int(numeigs) except: numeigs = 20 info['numeigs'] = numeigs hecke_pol = data['hecke_polynomial'] eigs = map(str, data['hecke_eigenvalues']) eigs = eigs[:min(len(eigs), numeigs)] AL_eigs = data['AL_eigenvalues'] primes = hmf_field['primes'] n = min(len(eigs), len(primes)) info['eigs'] = [{ 'eigenvalue': add_space_if_positive(teXify_pol(eigs[i])), 'prime_ideal': teXify_pol(primes[i]), 'prime_norm': primes[i][1:primes[i].index(',')] } for i in range(n)] try: display_eigs = request.args['display_eigs'] if display_eigs in ['True', 'true', '1', 'yes']: display_eigs = True else: display_eigs = False except KeyError: display_eigs = False if 'numeigs' in request.args: display_eigs = True info['hecke_polynomial'] = "\(" + teXify_pol(hecke_pol) + "\)" if not AL_eigs: # empty list if data['level_norm'] == 1: # OK, no bad primes info['AL_eigs'] = 'none' else: # not OK, AL eigs are missing info['AL_eigs'] = 'missing' else: info['AL_eigs'] = [{ 'eigenvalue': teXify_pol(al[1]), 'prime_ideal': teXify_pol(al[0]), 'prime_norm': al[0][1:al[0].index(',')] } for al in data['AL_eigenvalues']] max_eig_len = max([len(eig['eigenvalue']) for eig in info['eigs']]) display_eigs = display_eigs or (max_eig_len <= 300) info['display_eigs'] = display_eigs if not display_eigs: for eig in info['eigs']: if len(eig['eigenvalue']) > 300: eig['eigenvalue'] = '...' info['level_ideal'] = teXify_pol(info['level_ideal']) if 'is_CM' in data: is_CM = data['is_CM'] else: is_CM = '?' info['is_CM'] = is_CM if 'is_base_change' in data: is_base_change = data['is_base_change'] else: is_base_change = '?' info['is_base_change'] = is_base_change if 'q_expansions' in data: info['q_expansions'] = data['q_expansions'] properties = [('Base field', '%s' % info['field'].field_pretty()), ('Weight', '%s' % data['weight']), ('Level norm', '%s' % data['level_norm']), ('Level', '$' + teXify_pol(data['level_ideal']) + '$'), ('Label', '%s' % data['label']), ('Dimension', '%s' % data['dimension']), ('CM', is_CM), ('Base change', is_base_change)] return render_template("hilbert_modular_form.html", downloads=info["downloads"], info=info, properties=properties, credit=hmf_credit, title=t, bread=bread, friends=info['friends'], learnmore=learnmore_list())
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_object(self, curve, endo, tama, ratpts, is_curve): from lmfdb.genus2_curves.main import url_for_curve_label # all information about the curve, its Jacobian, isogeny class, and endomorphisms goes in the data dictionary # most of the data from the database gets polished/formatted before we put it in the data dictionary data = self.data = {} data['label'] = curve['label'] if is_curve else curve['class'] data['slabel'] = data['label'].split('.') # set attributes common to curves and isogeny classes here data['Lhash'] = str(curve['Lhash']) data['cond'] = ZZ(curve['cond']) data['cond_factor_latex'] = web_latex(factor(int( data['cond']))).replace(r"-1 \cdot", "-") data['analytic_rank'] = ZZ(curve['analytic_rank']) data['mw_rank'] = ZZ(0) if curve.get('mw_rank') is None else ZZ( curve['mw_rank']) # 0 will be marked as a lower bound data['mw_rank_proved'] = curve['mw_rank_proved'] data['analytic_rank_proved'] = curve['analytic_rank_proved'] data['hasse_weil_proved'] = curve['hasse_weil_proved'] data['st_group'] = curve['st_group'] data['st_group_link'] = st_link_by_name(1, 4, data['st_group']) data['st0_group_name'] = st0_group_name(curve['real_geom_end_alg']) data['is_gl2_type'] = curve['is_gl2_type'] data['root_number'] = ZZ(curve['root_number']) data['lfunc_url'] = url_for("l_functions.l_function_genus2_page", cond=data['slabel'][0], x=data['slabel'][1]) data['bad_lfactors'] = literal_eval(curve['bad_lfactors']) data['bad_lfactors_pretty'] = [(c[0], list_to_factored_poly_otherorder(c[1])) for c in data['bad_lfactors']] if is_curve: # invariants specific to curve data['class'] = curve['class'] data['abs_disc'] = ZZ(curve['abs_disc']) data['disc'] = curve['disc_sign'] * data['abs_disc'] data['min_eqn'] = literal_eval(curve['eqn']) data['min_eqn_display'] = min_eqns_pretty(data['min_eqn']) data['disc_factor_latex'] = web_latex(factor( data['disc'])).replace(r"-1 \cdot", "-") data['igusa_clebsch'] = [ ZZ(a) for a in literal_eval(curve['igusa_clebsch_inv']) ] data['igusa'] = [ZZ(a) for a in literal_eval(curve['igusa_inv'])] data['g2'] = [QQ(a) for a in literal_eval(curve['g2_inv'])] data['igusa_clebsch_factor_latex'] = [ web_latex(zfactor(i)).replace(r"-1 \cdot", "-") for i in data['igusa_clebsch'] ] data['igusa_factor_latex'] = [ web_latex(zfactor(j)).replace(r"-1 \cdot", "-") for j in data['igusa'] ] data['aut_grp'] = small_group_label_display_knowl( '%d.%d' % tuple(literal_eval(curve['aut_grp_id']))) data['geom_aut_grp'] = small_group_label_display_knowl( '%d.%d' % tuple(literal_eval(curve['geom_aut_grp_id']))) data['num_rat_wpts'] = ZZ(curve['num_rat_wpts']) data['has_square_sha'] = "square" if curve[ 'has_square_sha'] else "twice a square" P = curve['non_solvable_places'] if len(P): sz = "except over " sz += ", ".join([QpName(p) for p in P]) last = " and" if len(P) > 2: last = ", and" sz = last.join(sz.rsplit(",", 1)) else: sz = "everywhere" data['non_solvable_places'] = sz data['two_selmer_rank'] = ZZ(curve['two_selmer_rank']) data['torsion_order'] = curve['torsion_order'] data['end_ring_base'] = endo['ring_base'] data['end_ring_geom'] = endo['ring_geom'] data['real_period'] = decimal_pretty(str(curve['real_period'])) data['regulator'] = decimal_pretty( str(curve['regulator'] )) if curve['regulator'] > -0.5 else 'unknown' if data['mw_rank'] == 0 and data['mw_rank_proved']: data['regulator'] = '1' # display an exact 1 when we know this data['tamagawa_product'] = ZZ( curve['tamagawa_product']) if curve.get( 'tamagawa_product') else 0 data['analytic_sha'] = ZZ( curve['analytic_sha']) if curve.get('analytic_sha') else 0 data['leading_coeff'] = decimal_pretty( str(curve['leading_coeff'] )) if curve['leading_coeff'] else 'unknown' data['rat_pts'] = ratpts['rat_pts'] data['rat_pts_v'] = ratpts['rat_pts_v'] data['rat_pts_table'] = ratpts_table(ratpts['rat_pts'], ratpts['rat_pts_v']) data['mw_gens_v'] = ratpts['mw_gens_v'] lower = len([n for n in ratpts['mw_invs'] if n == 0]) upper = data['analytic_rank'] invs = ratpts[ 'mw_invs'] if data['mw_gens_v'] or lower >= upper else [ 0 for n in range(upper - lower) ] + ratpts['mw_invs'] if len(invs) == 0: data['mw_group'] = 'trivial' else: data['mw_group'] = r'\(' + r' \times '.join([ (r'\Z' if n == 0 else r'\Z/{%s}\Z' % n) for n in invs ]) + r'\)' if lower >= upper: data['mw_gens_table'] = mw_gens_table(ratpts['mw_invs'], ratpts['mw_gens'], ratpts['mw_heights'], ratpts['rat_pts']) if curve['two_torsion_field'][0]: data['two_torsion_field_knowl'] = nf_display_knowl( curve['two_torsion_field'][0], field_pretty(curve['two_torsion_field'][0])) else: t = curve['two_torsion_field'] data[ 'two_torsion_field_knowl'] = r"splitting field of \(%s\) with Galois group %s" % ( intlist_to_poly( t[1]), group_display_knowl(t[2][0], t[2][1])) tamalist = [[item['p'], item['tamagawa_number']] for item in tama] data['local_table'] = local_table(data['abs_disc'], data['cond'], tamalist, data['bad_lfactors_pretty']) else: # invariants specific to isogeny class curves_data = list( db.g2c_curves.search({"class": curve['class']}, ['label', 'eqn'])) if not curves_data: raise KeyError( "No curves found in database for isogeny class %s of genus 2 curve %s." % (curve['class'], curve['label'])) data['curves'] = [{ "label": c['label'], "equation_formatted": min_eqn_pretty(literal_eval(c['eqn'])), "url": url_for_curve_label(c['label']) } for c in curves_data] lfunc_data = db.lfunc_lfunctions.lucky( {'Lhash': str(curve['Lhash'])}) if not lfunc_data: raise KeyError( "No Lfunction found in database for isogeny class of genus 2 curve %s." % curve['label']) if lfunc_data and lfunc_data.get('euler_factors'): data['good_lfactors'] = [ [nth_prime(n + 1), lfunc_data['euler_factors'][n]] for n in range(len(lfunc_data['euler_factors'])) if nth_prime(n + 1) < 30 and (data['cond'] % nth_prime(n + 1)) ] data['good_lfactors_pretty'] = [ (c[0], list_to_factored_poly_otherorder(c[1])) for c in data['good_lfactors'] ] # Endomorphism data over QQ: data['gl2_statement_base'] = gl2_statement_base( endo['factorsRR_base'], r'\(\Q\)') data['factorsQQ_base'] = endo['factorsQQ_base'] data['factorsRR_base'] = endo['factorsRR_base'] data['end_statement_base'] = ( r"Endomorphism %s over \(\Q\):<br>" % ("ring" if is_curve else "algebra") + end_statement(data['factorsQQ_base'], endo['factorsRR_base'], ring=data['end_ring_base'] if is_curve else None)) # Field over which all endomorphisms are defined data['end_field_label'] = endo['fod_label'] data['end_field_poly'] = intlist_to_poly(endo['fod_coeffs']) data['end_field_statement'] = end_field_statement( data['end_field_label'], data['end_field_poly']) # Endomorphism data over QQbar: data['factorsQQ_geom'] = endo['factorsQQ_geom'] data['factorsRR_geom'] = endo['factorsRR_geom'] if data['end_field_label'] != '1.1.1.1': data['gl2_statement_geom'] = gl2_statement_base( data['factorsRR_geom'], r'\(\overline{\Q}\)') data['end_statement_geom'] = ( r"Endomorphism %s over \(\overline{\Q}\):" % ("ring" if is_curve else "algebra") + end_statement( data['factorsQQ_geom'], data['factorsRR_geom'], field=r'\overline{\Q}', ring=data['end_ring_geom'] if is_curve else None)) data['real_geom_end_alg_name'] = real_geom_end_alg_name( curve['real_geom_end_alg']) data['geom_end_alg_name'] = geom_end_alg_name(curve['geom_end_alg']) # Endomorphism data over intermediate fields not already treated (only for curves, not necessarily isogeny invariant): if is_curve: data['end_lattice'] = (endo['lattice'])[1:-1] if data['end_lattice']: data['end_lattice_statement'] = end_lattice_statement( data['end_lattice']) # Field over which the Jacobian decomposes (base field if Jacobian is geometrically simple) data['is_simple_geom'] = endo['is_simple_geom'] data['split_field_label'] = endo['spl_fod_label'] data['split_field_poly'] = intlist_to_poly(endo['spl_fod_coeffs']) data['split_field_statement'] = split_field_statement( data['is_simple_geom'], data['split_field_label'], data['split_field_poly']) # Elliptic curve factors for non-simple Jacobians if not data['is_simple_geom']: data['split_coeffs'] = endo['spl_facs_coeffs'] if 'spl_facs_labels' in endo and len( endo['spl_facs_labels']) == len(endo['spl_facs_coeffs']): data['split_labels'] = endo['spl_facs_labels'] data['split_condnorms'] = endo['spl_facs_condnorms'] data['split_statement'] = split_statement(data['split_coeffs'], data.get('split_labels'), data['split_condnorms']) # Properties self.properties = properties = [('Label', data['label'])] if is_curve: plot_from_db = db.g2c_plots.lucky({"label": curve['label']}) if (plot_from_db is None): self.plot = encode_plot( eqn_list_to_curve_plot( data['min_eqn'], ratpts['rat_pts'] if ratpts else [])) else: self.plot = plot_from_db['plot'] plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) properties += [ (None, plot_link), ('Conductor', str(data['cond'])), ('Discriminant', str(data['disc'])), ] if data['mw_rank_proved']: properties += [('Mordell-Weil group', data['mw_group'])] properties += [ ('Sato-Tate group', data['st_group_link']), (r'\(\End(J_{\overline{\Q}}) \otimes \R\)', r'\(%s\)' % data['real_geom_end_alg_name']), (r'\(\End(J_{\overline{\Q}}) \otimes \Q\)', r'\(%s\)' % data['geom_end_alg_name']), (r'\(\overline{\Q}\)-simple', bool_pretty(data['is_simple_geom'])), (r'\(\mathrm{GL}_2\)-type', bool_pretty(data['is_gl2_type'])), ] # Friends self.friends = friends = [] if is_curve: friends.append(('Isogeny class %s.%s' % (data['slabel'][0], data['slabel'][1]), url_for(".by_url_isogeny_class_label", cond=data['slabel'][0], alpha=data['slabel'][1]))) # first deal with EC ecs = [] if 'split_labels' in data: for friend_label in data['split_labels']: if is_curve: ecs.append(("Elliptic curve " + friend_label, url_for_ec(friend_label))) else: ecs.append( ("Isogeny class " + ec_label_class(friend_label), url_for_ec_class(friend_label))) ecs.sort(key=lambda x: key_for_numerically_sort(x[0])) # then again EC from lfun instances = [] for elt in db.lfunc_instances.search( { 'Lhash': data['Lhash'], 'type': 'ECQP' }, 'url'): instances.extend(elt.split('|')) # and then the other isogeny friends instances.extend([ elt['url'] for elt in get_instances_by_Lhash_and_trace_hash( data["Lhash"], 4, int(data["Lhash"])) ]) exclude = { elt[1].rstrip('/').lstrip('/') for elt in self.friends if elt[1] } exclude.add(data['lfunc_url'].lstrip('/L/').rstrip('/')) for elt in ecs + names_and_urls(instances, exclude=exclude): # because of the splitting we must use G2C specific code add_friend(friends, elt) if is_curve: friends.append(('Twists', url_for(".index_Q", g20=str(data['g2'][0]), g21=str(data['g2'][1]), g22=str(data['g2'][2])))) friends.append(('L-function', data['lfunc_url'])) # Breadcrumbs self.bread = bread = [('Genus 2 Curves', url_for(".index")), (r'$\Q$', url_for(".index_Q")), ('%s' % data['slabel'][0], url_for(".by_conductor", cond=data['slabel'][0])), ('%s' % data['slabel'][1], url_for(".by_url_isogeny_class_label", cond=data['slabel'][0], alpha=data['slabel'][1]))] if is_curve: bread += [('%s' % data['slabel'][2], url_for(".by_url_isogeny_class_discriminant", cond=data['slabel'][0], alpha=data['slabel'][1], disc=data['slabel'][2])), ('%s' % data['slabel'][3], url_for(".by_url_curve_label", cond=data['slabel'][0], alpha=data['slabel'][1], disc=data['slabel'][2], num=data['slabel'][3]))] # Title self.title = "Genus 2 " + ("Curve " if is_curve else "Isogeny Class ") + data['label'] # Code snippets (only for curves) if not is_curve: return self.code = code = {} code['show'] = {'sage': '', 'magma': ''} # use default show names f, h = fh = data['min_eqn'] g = simplify_hyperelliptic(fh) code['curve'] = { 'sage': 'R.<x> = PolynomialRing(QQ); C = HyperellipticCurve(R(%s), R(%s));' % (f, h), 'magma': 'R<x> := PolynomialRing(Rationals()); C := HyperellipticCurve(R!%s, R!%s);' % (f, h) } code['simple_curve'] = { 'sage': 'X = HyperellipticCurve(R(%s))' % (g), 'magma': 'X,pi:= SimplifiedModel(C);' } if data['abs_disc'] % 4096 == 0: ind2 = [a[0] for a in data['bad_lfactors']].index(2) bad2 = data['bad_lfactors'][ind2][1] magma_cond_option = ': ExcFactors:=[*<2,Valuation(' + str( data['cond']) + ',2),R!' + str(bad2) + '>*]' else: magma_cond_option = '' code['cond'] = { 'magma': 'Conductor(LSeries(C%s)); Factorization($1);' % magma_cond_option } code['disc'] = { 'magma': 'Discriminant(C); Factorization(Integers()!$1);' } code['geom_inv'] = { 'sage': 'C.igusa_clebsch_invariants(); [factor(a) for a in _]', 'magma': 'IgusaClebschInvariants(C); IgusaInvariants(C); G2Invariants(C);' } code['aut'] = {'magma': 'AutomorphismGroup(C); IdentifyGroup($1);'} code['autQbar'] = { 'magma': 'AutomorphismGroup(ChangeRing(C,AlgebraicClosure(Rationals()))); IdentifyGroup($1);' } code['num_rat_wpts'] = { 'magma': '#Roots(HyperellipticPolynomials(SimplifiedModel(C)));' } if ratpts: code['rat_pts'] = { 'magma': '[' + ','.join([ "C![%s,%s,%s]" % (p[0], p[1], p[2]) for p in ratpts['rat_pts'] ]) + '];' } code['mw_group'] = {'magma': 'MordellWeilGroupGenus2(Jacobian(C));'} code['two_selmer'] = { 'magma': 'TwoSelmerGroup(Jacobian(C)); NumberOfGenerators($1);' } code['has_square_sha'] = {'magma': 'HasSquareSha(Jacobian(C));'} code['locally_solvable'] = { 'magma': 'f,h:=HyperellipticPolynomials(C); g:=4*f+h^2; HasPointsEverywhereLocally(g,2) and (#Roots(ChangeRing(g,RealField())) gt 0 or LeadingCoefficient(g) gt 0);' } code['torsion_subgroup'] = { 'magma': 'TorsionSubgroup(Jacobian(SimplifiedModel(C))); AbelianInvariants($1);' }
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_form(self): # To start with the data fields of self are just those from # the database. We need to reformat these and compute some # further (easy) data about it. # from lmfdb.ecnf.WebEllipticCurve import FIELD self.field = FIELD(self.field_label) pretty_field = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, pretty_field) try: dims = db.bmf_dims.lucky({'field_label':self.field_label, 'level_label':self.level_label}, projection='gl2_dims') self.newspace_dimension = dims[str(self.weight)]['new_dim'] except TypeError: self.newspace_dimension = 'not available' self.newspace_label = "-".join([self.field_label,self.level_label]) self.newspace_url = url_for(".render_bmf_space_webpage", field_label=self.field_label, level_label=self.level_label) K = self.field.K() if self.dimension>1: Qx = PolynomialRing(QQ,'x') self.hecke_poly = Qx(str(self.hecke_poly)) F = NumberField(self.hecke_poly,'z') self.hecke_poly = web_latex(self.hecke_poly) def conv(ap): if '?' in ap: return 'not known' else: return F(str(ap)) self.hecke_eigs = [conv(str(ap)) for ap in self.hecke_eigs] self.nap = len(self.hecke_eigs) self.nap0 = min(50, self.nap) self.hecke_table = [[web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap)] for p,ap in zip(primes_iter(K), self.hecke_eigs[:self.nap0])] level = ideal_from_label(K,self.level_label) self.level_ideal2 = web_latex(level) badp = level.prime_factors() self.have_AL = self.AL_eigs[0]!='?' if self.have_AL: self.AL_table = [[web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap)] for p,ap in zip(badp, self.AL_eigs)] self.sign = 'not determined' try: if self.sfe == 1: self.sign = "+1" elif self.sfe == -1: self.sign = "-1" except AttributeError: self.sfe = '?' if self.Lratio == '?': self.Lratio = "not determined" self.anrank = "not determined" else: self.Lratio = QQ(self.Lratio) self.anrank = "\(0\)" if self.Lratio!=0 else "odd" if self.sfe==-1 else "\(\ge2\), even" self.properties2 = [('Base field', pretty_field), ('Weight', str(self.weight)), ('Level norm', str(self.level_norm)), ('Level', self.level_ideal2), ('Label', self.label), ('Dimension', str(self.dimension)) ] try: if self.CM == '?': self.CM = 'not determined' elif self.CM == 0: self.CM = 'no' else: if self.CM%4 in [2,3]: self.CM = 4*self.CM except AttributeError: self.CM = 'not determined' self.properties2.append(('CM', str(self.CM))) self.bc_extra = '' self.bcd = 0 self.bct = self.bc!='?' and self.bc!=0 if self.bc == '?': self.bc = 'not determined' elif self.bc == 0: self.bc = 'no' elif self.bc == 1: self.bcd = self.bc self.bc = 'yes' elif self.bc >1: self.bcd = self.bc self.bc = 'yes' self.bc_extra = ', of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{'+str(self.bcd)+'})\)' elif self.bc == -1: self.bc = 'no' self.bc_extra = ', but is a twist of the base-change of a form over \(\mathbb{Q}\)' elif self.bc < -1: self.bcd = -self.bc self.bc = 'no' self.bc_extra = ', but is a twist of the base-change of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{'+str(self.bcd)+'})\)' self.properties2.append(('Base-change', str(self.bc))) curve_bc = db.ec_nfcurves.lucky({'class_label':self.label}, projection="base_change") if curve_bc is not None: self.ec_status = 'exists' self.ec_url = url_for("ecnf.show_ecnf_isoclass", nf=self.field_label, conductor_label=self.level_label, class_label=self.label_suffix) curve_bc_parts = [split_lmfdb_label(lab) for lab in curve_bc] bc_urls = [url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) for cond, iso, num in curve_bc_parts] bc_labels = [".".join( [str(cond), str(2), 'a', iso] ) for cond,iso,_ in curve_bc_parts] bc_exists = [db.mf_newforms.label_exists(lab) for lab in bc_labels] self.bc_forms = [{'exists':ex, 'label':lab, 'url':url} for ex,lab,url in zip(bc_exists, bc_labels, bc_urls)] else: self.bc_forms = [] if self.bct: self.ec_status = 'none' else: self.ec_status = 'missing' self.properties2.append(('Sign', self.sign)) self.properties2.append(('Analytic rank', self.anrank)) self.friends = [] self.friends += [('Newspace {}'.format(self.newspace_label),self.newspace_url)] url = 'ModularForm/GL2/ImaginaryQuadratic/{}'.format( self.label.replace('-', '/')) Lfun = get_lfunction_by_url(url) if Lfun: # first by Lhash instances = get_instances_by_Lhash(Lfun['Lhash']) # then by trace_hash instances += get_instances_by_trace_hash(Lfun['degree'], Lfun['trace_hash']) # This will also add the EC/G2C, as this how the Lfun was computed self.friends = names_and_urls(instances) # remove itself self.friends.remove( ('Bianchi modular form {}'.format(self.label), '/' + url)) self.friends.append(('L-function', '/L/'+url)) else: # old code if self.dimension == 1: if self.ec_status == 'exists': self.friends += [('Isogeny class {}'.format(self.label), self.ec_url)] elif self.ec_status == 'missing': self.friends += [('Isogeny class {} missing'.format(self.label), "")] else: self.friends += [('No elliptic curve', "")] self.friends += [ ('L-function not available','')]
def make_class(self): # Create a list of the curves in the class from the database self.db_curves = list(db.ec_nfcurves.search( {'field_label': self.field_label, 'conductor_norm': self.conductor_norm, 'conductor_label': self.conductor_label, 'iso_nlabel': self.iso_nlabel})) # Rank or bounds try: self.rk = web_latex(self.db_curves[0]['rank']) except KeyError: self.rk = "?" try: self.rk_bnds = "%s...%s" % tuple(self.db_curves[0]['rank_bounds']) except KeyError: self.rank_bounds = [0, Infinity] self.rk_bnds = "not recorded" # Extract the isogeny degree matrix from the database if not hasattr(self, 'isogeny_matrix'): # this would happen if the class is initiated with a curve # which is not #1 in its class: self.isogeny_matrix = self.db_curves[0].isogeny_matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) self.one_deg = ZZ(self.class_deg).is_prime() # Create isogeny graph: self.graph = make_graph(self.isogeny_matrix) P = self.graph.plot(edge_labels=True) self.graph_img = encode_plot(P) self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img self.isogeny_matrix_str = latex(Matrix(self.isogeny_matrix)) self.field = FIELD(self.field_label) self.field_name = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, self.field_name) def curve_url(c): return url_for(".show_ecnf", nf=c['field_label'], conductor_label=c['conductor_label'], class_label=c['iso_label'], number=c['number']) self.curves = [[c['short_label'], curve_url(c), web_ainvs(self.field_label,c['ainvs'])] for c in self.db_curves] self.urls = {} self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=self.conductor_label, class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=self.conductor_label) self.urls['field'] = url_for('.show_ecnf1', nf=self.field_label) 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 < 40000: # 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 WebEllipticCurve.py # and should be refactored self.friends = [] 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, '')] if 'Lfunction' in self.urls: Lfun = get_lfunction_by_url(self.urls['Lfunction'].lstrip('/L').rstrip('/'), projection=['degree', 'trace_hash', 'Lhash']) 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]} exclude.add(lfun_url.lstrip('/L/').rstrip('/')) self.friends += names_and_urls(instances, exclude=exclude) self.friends += [('L-function', self.urls['Lfunction'])] else: self.friends += [('L-function not available', "")] self.properties = [('Base field', self.field_name), ('Label', self.class_label), (None, self.graph_link), ('Conductor', '%s' % self.conductor_label) ] if self.rk != '?': self.properties += [('Rank', '%s' % self.rk)] else: if self.rk_bnds == 'not recorded': self.properties += [('Rank', '%s' % self.rk_bnds)] else: self.properties += [('Rank bounds', '%s' % self.rk_bnds)] self.bread = [('Elliptic curves ', url_for(".index")), (self.field_label, self.urls['field']), (self.conductor_label, self.urls['conductor']), ('isogeny class %s' % self.short_label, self.urls['class'])]
def make_form(self, nap0=50): # To start with the data fields of self are just those from # the database. We need to reformat these and compute some # further (easy) data about it. # from lmfdb.ecnf.WebEllipticCurve import FIELD self.field = FIELD(self.field_label) pretty_field = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, pretty_field) try: dims = db.bmf_dims.lucky( { 'field_label': self.field_label, 'level_label': self.level_label }, projection='gl2_dims') self.newspace_dimension = dims[str(self.weight)]['new_dim'] except TypeError: self.newspace_dimension = 'not available' self.newspace_label = "-".join([self.field_label, self.level_label]) self.newspace_url = url_for(".render_bmf_space_webpage", field_label=self.field_label, level_label=self.level_label) K = self.field.K() # 'hecke_poly_obj' is the non-LaTeX version of hecke_poly self.hecke_poly_obj = self.hecke_poly if self.dimension > 1: Qx = PolynomialRing(QQ, 'x') self.hecke_poly = Qx(str(self.hecke_poly)) F = NumberField(self.hecke_poly, 'z') self.hecke_poly = web_latex(self.hecke_poly) def conv(ap): if '?' in ap: return 'not known' else: return F(str(ap)) self.hecke_eigs = [conv(str(ap)) for ap in self.hecke_eigs] self.level = ideal_from_label(K, self.level_label) self.level_ideal2 = web_latex(self.level) badp = self.level.prime_factors() self.nap = len(self.hecke_eigs) self.nap0 = min(nap0, self.nap) self.neigs = self.nap0 + len(badp) self.hecke_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(primes_iter(K), self.hecke_eigs[:self.neigs]) if not p in badp] self.have_AL = self.AL_eigs[0] != '?' if self.have_AL: self.AL_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(badp, self.AL_eigs)] # The following helps to create Sage download data self.AL_table_data = [[p.gens_reduced(), ap] for p, ap in zip(badp, self.AL_eigs)] self.sign = 'not determined' try: if self.sfe == 1: self.sign = "$+1$" elif self.sfe == -1: self.sign = "$-1$" except AttributeError: self.sfe = '?' if self.Lratio == '?': self.Lratio = "not determined" self.anrank = "not determined" else: self.Lratio = QQ(self.Lratio) self.anrank = r"\(0\)" if self.Lratio != 0 else "odd" if self.sfe == -1 else r"\(\ge2\), even" self.properties = [('Label', self.label), ('Base field', pretty_field), ('Weight', prop_int_pretty(self.weight)), ('Level norm', prop_int_pretty(self.level_norm)), ('Level', self.level_ideal2), ('Dimension', prop_int_pretty(self.dimension))] try: if self.CM == '?': self.CM = 'not determined' elif self.CM == 0: self.CM = 'no' else: if int(self.CM) % 4 in [2, 3]: self.CM = 4 * int(self.CM) self.CM = "$%s$" % self.CM except AttributeError: self.CM = 'not determined' self.properties.append(('CM', str(self.CM))) self.bc_extra = '' self.bcd = 0 self.bct = self.bc != '?' and self.bc != 0 if self.bc == '?': self.bc = 'not determined' elif self.bc == 0: self.bc = 'no' elif self.bc == 1: self.bcd = self.bc self.bc = 'yes' elif self.bc > 1: self.bcd = self.bc self.bc = 'yes' self.bc_extra = r', of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + r'})\)' elif self.bc == -1: self.bc = 'no' self.bc_extra = r', but is a twist of the base change of a form over \(\mathbb{Q}\)' elif self.bc < -1: self.bcd = -self.bc self.bc = 'no' self.bc_extra = r', but is a twist of the base change of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + r'})\)' self.properties.append(('Base change', str(self.bc))) curve_bc = db.ec_nfcurves.lucky({'class_label': self.label}, projection="base_change") if curve_bc is not None: if curve_bc and "." not in curve_bc[0]: curve_bc = [ cremona_label_to_lmfdb_label(lab) for lab in curve_bc ] self.ec_status = 'exists' self.ec_url = url_for("ecnf.show_ecnf_isoclass", nf=self.field_label, conductor_label=self.level_label, class_label=self.label_suffix) curve_bc_parts = [split_lmfdb_label(lab) for lab in curve_bc] bc_urls = [ url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) for cond, iso, num in curve_bc_parts ] bc_labels = [ ".".join([str(cond), str(2), 'a', iso]) for cond, iso, _ in curve_bc_parts ] bc_exists = [db.mf_newforms.label_exists(lab) for lab in bc_labels] self.bc_forms = [{ 'exists': ex, 'label': lab, 'url': url } for ex, lab, url in zip(bc_exists, bc_labels, bc_urls)] else: self.bc_forms = [] if self.bct or self.label in bmfs_with_no_curve: self.ec_status = 'none' else: self.ec_status = 'missing' self.properties.append(('Sign', self.sign)) self.properties.append(('Analytic rank', self.anrank)) self.friends = [] self.friends += [('Newspace {}'.format(self.newspace_label), self.newspace_url)] url = 'ModularForm/GL2/ImaginaryQuadratic/{}'.format( self.label.replace('-', '/')) Lfun = get_lfunction_by_url(url) if Lfun: instances = get_instances_by_Lhash_and_trace_hash( Lfun['Lhash'], Lfun['degree'], Lfun['trace_hash']) # This will also add the EC/G2C, as this how the Lfun was computed # and not add itself self.friends = names_and_urls(instances, exclude={url}) self.friends.append(('L-function', '/L/' + url)) else: # old code if self.dimension == 1: if self.ec_status == 'exists': self.friends += [('Isogeny class {}'.format(self.label), self.ec_url)] elif self.ec_status == 'missing': self.friends += [ ('Isogeny class {} missing'.format(self.label), "") ] else: self.friends += [('No elliptic curve', "")] self.friends += [('L-function not available', '')]
def render_hmf_webpage(**args): if 'data' in args: data = args['data'] label = data['label'] else: label = str(args['label']) data = get_hmf(label) if data is None: flash(Markup("Error: <span style='color:black'>%s</span> is not a valid Hilbert modular form label. It must be of the form (number field label) - (level label) - (orbit label) separated by dashes, such as 2.2.5.1-31.1-a" % args['label']), "error") return search_input_error() info = {} try: info['count'] = args['count'] except KeyError: info['count'] = 50 hmf_field = get_hmf_field(data['field_label']) gen_name = findvar(hmf_field['ideals']) nf = WebNumberField(data['field_label'], gen_name=gen_name) info['hmf_field'] = hmf_field info['field'] = nf info['base_galois_group'] = nf.galois_string() info['field_degree'] = nf.degree() info['field_disc'] = str(nf.disc()) info['field_poly'] = teXify_pol(str(nf.poly())) info.update(data) info['downloads'] = [ ('Modular form to Magma', url_for(".render_hmf_webpage_download", field_label=info['field_label'], label=info['label'], download_type='magma')), ('Eigenvalues to Sage', url_for(".render_hmf_webpage_download", field_label=info['field_label'], label=info['label'], download_type='sage')) ] # figure out friends # first try to see if there is an instance of this HMF on Lfun db url = 'ModularForm/GL2/TotallyReal/{}/holomorphic/{}'.format( info['field_label'], info['label']) Lfun = get_lfunction_by_url(url) if Lfun: instances = get_instances_by_Lhash_and_trace_hash(Lfun['Lhash'], Lfun['degree'], Lfun['trace_hash']) # This will also add the EC/G2C, as this how the Lfun was computed info['friends'] = names_and_urls(instances, exclude={url}) info['friends'] += [('L-function', url_for("l_functions.l_function_hmf_page", field=info['field_label'], label=info['label'], character='0', number='0'))] else: # if there is no instance # old code if hmf_field['narrow_class_no'] == 1 and nf.disc()**2 * data['level_norm'] < 40000: info['friends'] = [('L-function', url_for("l_functions.l_function_hmf_page", field=info['field_label'], label=info['label'], character='0', number='0'))] else: info['friends'] = [('L-function not available', "")] if data['dimension'] == 1: # Try to attach associated elliptic curve lab = split_class_label(info['label']) ec_from_hmf = db.ec_nfcurves.lookup(label + '1') if ec_from_hmf is None: info['friends'] += [('Elliptic curve not available', "")] else: info['friends'] += [('Isogeny class ' + info['label'], url_for("ecnf.show_ecnf_isoclass", nf=lab[0], conductor_label=lab[1], class_label=lab[2]))] bread = [("Modular Forms", url_for('modular_forms')), ('Hilbert Modular Forms', url_for(".hilbert_modular_form_render_webpage")), ('%s' % data['label'], ' ')] t = "Hilbert Cusp Form %s" % info['label'] forms_dims = db.hmf_forms.search({'field_label': data['field_label'], 'level_ideal': data['level_ideal']}, projection='dimension') info['newspace_dimension'] = sum(forms_dims) # Get hecke_polynomial, hecke_eigenvalues and AL_eigenvalues try: numeigs = request.args['numeigs'] numeigs = int(numeigs) except: numeigs = 20 info['numeigs'] = numeigs hecke_pol = data['hecke_polynomial'] eigs = map(str, data['hecke_eigenvalues']) eigs = eigs[:min(len(eigs), numeigs)] AL_eigs = data['AL_eigenvalues'] primes = hmf_field['primes'] n = min(len(eigs), len(primes)) info['eigs'] = [{'eigenvalue': add_space_if_positive(teXify_pol(eigs[i])), 'prime_ideal': teXify_pol(primes[i]), 'prime_norm': primes[i][1:primes[i].index(',')]} for i in range(n)] try: display_eigs = request.args['display_eigs'] if display_eigs in ['True', 'true', '1', 'yes']: display_eigs = True else: display_eigs = False except KeyError: display_eigs = False if 'numeigs' in request.args: display_eigs = True info['hecke_polynomial'] = web_latex_split_on_pm(teXify_pol(hecke_pol)) if not AL_eigs: # empty list if data['level_norm']==1: # OK, no bad primes info['AL_eigs'] = 'none' else: # not OK, AL eigs are missing info['AL_eigs'] = 'missing' else: info['AL_eigs'] = [{'eigenvalue': teXify_pol(al[1]), 'prime_ideal': teXify_pol(al[0]), 'prime_norm': al[0][1:al[0].index(',')]} for al in data['AL_eigenvalues']] max_eig_len = max([len(eig['eigenvalue']) for eig in info['eigs']]) display_eigs = display_eigs or (max_eig_len<=300) info['display_eigs'] = display_eigs if not display_eigs: for eig in info['eigs']: if len(eig['eigenvalue']) > 300: eig['eigenvalue'] = '...' info['level_ideal'] = teXify_pol(info['level_ideal']) if 'is_CM' in data: is_CM = data['is_CM'] else: is_CM = '?' info['is_CM'] = is_CM if 'is_base_change' in data: is_base_change = data['is_base_change'] else: is_base_change = '?' info['is_base_change'] = is_base_change if 'q_expansions' in data: info['q_expansions'] = data['q_expansions'] properties2 = [('Base field', '%s' % info['field'].field_pretty()), ('Weight', '%s' % data['weight']), ('Level norm', '%s' % data['level_norm']), ('Level', '$' + teXify_pol(data['level_ideal']) + '$'), ('Label', '%s' % data['label']), ('Dimension', '%s' % data['dimension']), ('CM', is_CM), ('Base change', is_base_change) ] return render_template("hilbert_modular_form.html", downloads=info["downloads"], info=info, properties2=properties2, credit=hmf_credit, title=t, bread=bread, friends=info['friends'], learnmore=learnmore_list())
def __init__(self, galmap, triple=None): from lmfdb.belyi.main import url_for_belyi_passport_label, url_for_belyi_galmap_label # all information about the map goes in the data dictionary # most of the data from the database gets polished/formatted before we put it in the data dictionary data = self.data = {} # the stuff that does not need to be polished for elt in ("label", "plabel", "triples_cyc", "orbit_size", "g", "abc", "deg", "primitivization", "is_primitive"): data[elt] = galmap[elt] if triple: data["label"] += '-' + (triple).replace(' ', '') data["triple"] = triple data["group"] = transitive_group_display_knowl(galmap["group"]) data["geomtype"] = geomtypelet_to_geomtypename_dict[galmap["geomtype"]] data["lambdas"] = [str(c)[1:-1] for c in galmap["lambdas"]] data["primitivization_url"] = url_for_belyi_galmap_label( data['primitivization']) data["isQQ"] = False data["in_LMFDB"] = False F = belyi_base_field(galmap) if F._data is None: fld_coeffs = galmap["base_field"] pol = PolynomialRing(QQ, "t")(fld_coeffs) data["base_field"] = latex(pol) else: data["in_LMFDB"] = True if F.poly().degree() == 1: data["isQQ"] = True F.latex_poly = web_latex(F.poly(var="t")) data["base_field"] = F data['embeddings'] = galmap['embeddings'] # change pairs of floats to complex numbers embed_strs = [] for el in galmap["embeddings"]: if el[1] < 0: el_str = str(el[0]) + str(el[1]) + r"\sqrt{-1}" else: el_str = str(el[0]) + "+" + str(el[1]) + r"\sqrt{-1}" embed_strs.append(el_str) data["embeddings_and_triples"] = [] self.triple = None self.embedding = None for i in range(0, len(data["triples_cyc"])): my_dict = {} triple_str = ', '.join(data['triples_cyc'][i]) triple_link = triple_str.replace(' ', '') if triple_link == triple: self.triple = data['triples_cyc'][i] self.embedding = CC(data['embeddings'][i]) my_dict['triple'] = triple_str my_dict['triple_link'] = triple_link if data["isQQ"]: my_dict[ 'embedding'] = r"\text{not applicable (over $\mathbb{Q}$)}" else: my_dict['embedding'] = embed_strs[i] data['embeddings_and_triples'].append(my_dict) crv_str = galmap["curve"] if crv_str == "PP1": data["curve"] = r"\mathbb{P}^1" else: data["curve"] = make_curve_latex(crv_str, nu=self.embedding) data["map"] = make_map_latex(galmap["map"], nu=self.embedding) data["lambdas"] = [str(c)[1:-1] for c in galmap["lambdas"]] # Properties self.plot = db.belyi_galmap_portraits.lucky({"label": galmap['label']}, projection="portrait") plot_link = '<a href="{0}"><img src="{0}" width="200" height="200" style="background-color: white;"/></a>'.format( self.plot) properties = [("Label", galmap["label"])] if triple: properties += [("Triple", "$%s$" % triple)] if self.plot: properties += [(None, plot_link)] properties += [ ("Group", str(galmap["group"])), ("Orders", "$%s$" % (data["abc"])), ("Genus", prop_int_pretty(data["g"])), ("Size", prop_int_pretty(data["orbit_size"])), ] self.properties = properties # Friends self.friends = [("Passport", url_for_belyi_passport_label(galmap["plabel"]))] if galmap['label'] != galmap['primitivization']: self.friends.append( ("Primitivization", url_for_belyi_galmap_label(galmap["primitivization"]))) self.friends.extend(names_and_urls(galmap['friends'])) #add curve link, if in LMFDB if 'curve_label' in galmap.keys(): data['curve_label'] = galmap['curve_label'] for name, url in self.friends: if "curve" in name.lower() and data['curve_label'] in name: data["curve_url"] = url # Downloads if galmap["g"] <= 2: data_label = data["label"] if triple: spl = data_label.split("-") data_label = "-".join(spl[0:-1]) self.downloads = [ ( "Code to Magma", url_for(".belyi_galmap_magma_download", label=data_label), ), ( "Code to SageMath", url_for(".belyi_galmap_sage_download", label=data_label), ), ( "All data to text", url_for(".belyi_galmap_text_download", label=data_label), ), ] else: self.downloads = [] # Breadcrumbs label_spl = data["label"].split("-") groupstr = label_spl[0] letnum = label_spl[2] gstr = str(data['g']) sigmas = label_spl[1] sigma0, sigma1, sigmaoo = sigmas.split("_") abcstr = str(data['abc']).replace(' ', '') # does lambdasstr need to be updated? lambdasstr = "%s-%s-%s" % (sigma0, sigma1, sigmaoo) lambdasgstr = lambdasstr + "-" + gstr self.bread = [ ("Belyi Maps", url_for(".index")), (groupstr, url_for(".by_url_belyi_search_group", group=groupstr)), ( abcstr, url_for(".by_url_belyi_search_group_triple", group=groupstr, abc=abcstr), ), ( lambdasgstr, url_for( ".by_url_belyi_passport_label", group=groupstr, abc=abcstr, sigma0=sigma0, sigma1=sigma1, sigmaoo=sigmaoo, g=gstr, ), ), ( letnum, url_for( ".by_url_belyi_galmap_label", group=groupstr, abc=abcstr, sigma0=sigma0, sigma1=sigma1, sigmaoo=sigmaoo, g=gstr, letnum=letnum, ), ), ] # Title if self.triple: self.title = "Embedded Belyi map " + data["label"] else: self.title = "Belyi map orbit " + data["label"] # Code snippets (only for curves) self.code = {} self.__dict__.update(data) return
def make_object(self, curve, endo, tama, ratpts, is_curve): from lmfdb.genus2_curves.main import url_for_curve_label # all information about the curve, its Jacobian, isogeny class, and endomorphisms goes in the data dictionary # most of the data from the database gets polished/formatted before we put it in the data dictionary data = self.data = {} data['label'] = curve['label'] if is_curve else curve['class'] data['slabel'] = data['label'].split('.') # set attributes common to curves and isogeny classes here data['Lhash'] = str(curve['Lhash']) data['cond'] = ZZ(curve['cond']) data['cond_factor_latex'] = web_latex(factor(int(data['cond']))) data['analytic_rank'] = ZZ(curve['analytic_rank']) data['st_group'] = curve['st_group'] data['st_group_link'] = st_link_by_name(1,4,data['st_group']) data['st0_group_name'] = st0_group_name(curve['real_geom_end_alg']) data['is_gl2_type'] = curve['is_gl2_type'] data['root_number'] = ZZ(curve['root_number']) data['lfunc_url'] = url_for("l_functions.l_function_genus2_page", cond=data['slabel'][0], x=data['slabel'][1]) data['bad_lfactors'] = literal_eval(curve['bad_lfactors']) data['bad_lfactors_pretty'] = [ (c[0], list_to_factored_poly_otherorder(c[1])) for c in data['bad_lfactors']] if is_curve: # invariants specific to curve data['class'] = curve['class'] data['abs_disc'] = ZZ(curve['abs_disc']) data['disc'] = curve['disc_sign'] * data['abs_disc'] data['min_eqn'] = literal_eval(curve['eqn']) data['min_eqn_display'] = list_to_min_eqn(data['min_eqn']) data['disc_factor_latex'] = web_latex(factor(data['disc'])) data['igusa_clebsch'] = [ZZ(a) for a in literal_eval(curve['igusa_clebsch_inv'])] data['igusa'] = [ZZ(a) for a in literal_eval(curve['igusa_inv'])] data['g2'] = [QQ(a) for a in literal_eval(curve['g2_inv'])] data['igusa_clebsch_factor_latex'] = [web_latex(zfactor(i)) for i in data['igusa_clebsch']] data['igusa_factor_latex'] = [ web_latex(zfactor(j)) for j in data['igusa'] ] data['aut_grp_id'] = curve['aut_grp_id'] data['geom_aut_grp_id'] = curve['geom_aut_grp_id'] data['num_rat_wpts'] = ZZ(curve['num_rat_wpts']) data['two_selmer_rank'] = ZZ(curve['two_selmer_rank']) data['has_square_sha'] = "square" if curve['has_square_sha'] else "twice a square" P = curve['non_solvable_places'] if len(P): sz = "except over " sz += ", ".join([QpName(p) for p in P]) last = " and" if len(P) > 2: last = ", and" sz = last.join(sz.rsplit(",",1)) else: sz = "everywhere" data['non_solvable_places'] = sz data['torsion_order'] = curve['torsion_order'] data['torsion_factors'] = [ZZ(a) for a in literal_eval(curve['torsion_subgroup'])] if len(data['torsion_factors']) == 0: data['torsion_subgroup'] = '\mathrm{trivial}' else: data['torsion_subgroup'] = ' \\times '.join([ '\Z/{%s}\Z' % n for n in data['torsion_factors'] ]) data['end_ring_base'] = endo['ring_base'] data['end_ring_geom'] = endo['ring_geom'] data['tama'] = '' for item in tama: if item['tamagawa_number'] > 0: tamgwnr = str(item['tamagawa_number']) else: tamgwnr = 'N/A' data['tama'] += tamgwnr + ' (p = ' + str(item['p']) + '), ' data['tama'] = data['tama'][:-2] # trim last ", " if ratpts: if len(ratpts['rat_pts']): data['rat_pts'] = ', '.join(web_latex('(' +' : '.join(map(str, P)) + ')') for P in ratpts['rat_pts']) data['rat_pts_v'] = 2 if ratpts['rat_pts_v'] else 1 # data['mw_rank'] = ratpts['mw_rank'] # data['mw_rank_v'] = ratpts['mw_rank_v'] else: data['rat_pts_v'] = 0 if curve['two_torsion_field'][0]: data['two_torsion_field_knowl'] = nf_display_knowl (curve['two_torsion_field'][0], field_pretty(curve['two_torsion_field'][0])) else: t = curve['two_torsion_field'] data['two_torsion_field_knowl'] = """splitting field of \(%s\) with Galois group %s"""%(intlist_to_poly(t[1]),group_display_knowl(t[2][0],t[2][1])) else: # invariants specific to isogeny class curves_data = list(db.g2c_curves.search({"class" : curve['class']}, ['label','eqn'])) if not curves_data: raise KeyError("No curves found in database for isogeny class %s of genus 2 curve %s." %(curve['class'],curve['label'])) data['curves'] = [ {"label" : c['label'], "equation_formatted" : list_to_min_eqn(literal_eval(c['eqn'])), "url": url_for_curve_label(c['label'])} for c in curves_data ] lfunc_data = db.lfunc_lfunctions.lucky({'Lhash':str(curve['Lhash'])}) if not lfunc_data: raise KeyError("No Lfunction found in database for isogeny class of genus 2 curve %s." %curve['label']) if lfunc_data and lfunc_data.get('euler_factors'): data['good_lfactors'] = [[nth_prime(n+1),lfunc_data['euler_factors'][n]] for n in range(len(lfunc_data['euler_factors'])) if nth_prime(n+1) < 30 and (data['cond'] % nth_prime(n+1))] data['good_lfactors_pretty'] = [ (c[0], list_to_factored_poly_otherorder(c[1])) for c in data['good_lfactors']] # Endomorphism data over QQ: data['gl2_statement_base'] = gl2_statement_base(endo['factorsRR_base'], r'\(\Q\)') data['factorsQQ_base'] = endo['factorsQQ_base'] data['factorsRR_base'] = endo['factorsRR_base'] data['end_statement_base'] = """Endomorphism %s over \(\Q\):<br>""" %("ring" if is_curve else "algebra") + \ end_statement(data['factorsQQ_base'], endo['factorsRR_base'], ring=data['end_ring_base'] if is_curve else None) # Field over which all endomorphisms are defined data['end_field_label'] = endo['fod_label'] data['end_field_poly'] = intlist_to_poly(endo['fod_coeffs']) data['end_field_statement'] = end_field_statement(data['end_field_label'], data['end_field_poly']) # Endomorphism data over QQbar: data['factorsQQ_geom'] = endo['factorsQQ_geom'] data['factorsRR_geom'] = endo['factorsRR_geom'] if data['end_field_label'] != '1.1.1.1': data['gl2_statement_geom'] = gl2_statement_base(data['factorsRR_geom'], r'\(\overline{\Q}\)') data['end_statement_geom'] = """Endomorphism %s over \(\overline{\Q}\):""" %("ring" if is_curve else "algebra") + \ end_statement(data['factorsQQ_geom'], data['factorsRR_geom'], field=r'\overline{\Q}', ring=data['end_ring_geom'] if is_curve else None) data['real_geom_end_alg_name'] = real_geom_end_alg_name(curve['real_geom_end_alg']) data['geom_end_alg_name'] = geom_end_alg_name(curve['geom_end_alg']) # Endomorphism data over intermediate fields not already treated (only for curves, not necessarily isogeny invariant): if is_curve: data['end_lattice'] = (endo['lattice'])[1:-1] if data['end_lattice']: data['end_lattice_statement'] = end_lattice_statement(data['end_lattice']) # Field over which the Jacobian decomposes (base field if Jacobian is geometrically simple) data['is_simple_geom'] = endo['is_simple_geom'] data['split_field_label'] = endo['spl_fod_label'] data['split_field_poly'] = intlist_to_poly(endo['spl_fod_coeffs']) data['split_field_statement'] = split_field_statement(data['is_simple_geom'], data['split_field_label'], data['split_field_poly']) # Elliptic curve factors for non-simple Jacobians if not data['is_simple_geom']: data['split_coeffs'] = endo['spl_facs_coeffs'] if 'spl_facs_labels' in endo and len(endo['spl_facs_labels']) == len(endo['spl_facs_coeffs']): data['split_labels'] = endo['spl_facs_labels'] data['split_condnorms'] = endo['spl_facs_condnorms'] data['split_statement'] = split_statement(data['split_coeffs'], data.get('split_labels'), data['split_condnorms']) # Properties self.properties = properties = [('Label', data['label'])] if is_curve: self.plot = encode_plot(eqn_list_to_curve_plot(data['min_eqn'], data['rat_pts'].split(',') if 'rat_pts' in data else [])) plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format(self.plot) properties += [ (None, plot_link), ('Conductor',str(data['cond'])), ('Discriminant', str(data['disc'])), ] properties += [ ('Sato-Tate group', data['st_group_link']), ('\(\\End(J_{\\overline{\\Q}}) \\otimes \\R\)', '\(%s\)' % data['real_geom_end_alg_name']), ('\(\\End(J_{\\overline{\\Q}}) \\otimes \\Q\)', '\(%s\)' % data['geom_end_alg_name']), ('\(\\overline{\\Q}\)-simple', bool_pretty(data['is_simple_geom'])), ('\(\mathrm{GL}_2\)-type', bool_pretty(data['is_gl2_type'])), ] # Friends self.friends = friends = [] if is_curve: friends.append(('Isogeny class %s.%s' % (data['slabel'][0], data['slabel'][1]), url_for(".by_url_isogeny_class_label", cond=data['slabel'][0], alpha=data['slabel'][1]))) # first deal with EC ecs = [] if 'split_labels' in data: for friend_label in data['split_labels']: if is_curve: ecs.append(("Elliptic curve " + friend_label, url_for_ec(friend_label))) else: ecs.append(("Isogeny class " + ec_label_class(friend_label), url_for_ec_class(friend_label))) ecs.sort(key=lambda x: key_for_numerically_sort(x[0])) # then again EC from lfun instances = [] for elt in db.lfunc_instances.search({'Lhash':data['Lhash'], 'type' : 'ECQP'}, 'url'): instances.extend(elt.split('|')) # and then the other isogeny friends instances.extend([ elt['url'] for elt in get_instances_by_Lhash_and_trace_hash(data["Lhash"], 4, int(data["Lhash"]) ) ]) exclude = {elt[1].rstrip('/').lstrip('/') for elt in self.friends if elt[1]} exclude.add(data['lfunc_url'].lstrip('/L/').rstrip('/')) for elt in ecs + names_and_urls(instances, exclude=exclude): # because of the splitting we must use G2C specific code add_friend(friends, elt) if is_curve: friends.append(('Twists', url_for(".index_Q", g20=str(data['g2'][0]), g21=str(data['g2'][1]), g22=str(data['g2'][2])))) friends.append(('L-function', data['lfunc_url'])) # Breadcrumbs self.bread = bread = [ ('Genus 2 Curves', url_for(".index")), ('$\Q$', url_for(".index_Q")), ('%s' % data['slabel'][0], url_for(".by_conductor", cond=data['slabel'][0])), ('%s' % data['slabel'][1], url_for(".by_url_isogeny_class_label", cond=data['slabel'][0], alpha=data['slabel'][1])) ] if is_curve: bread += [ ('%s' % data['slabel'][2], url_for(".by_url_isogeny_class_discriminant", cond=data['slabel'][0], alpha=data['slabel'][1], disc=data['slabel'][2])), ('%s' % data['slabel'][3], url_for(".by_url_curve_label", cond=data['slabel'][0], alpha=data['slabel'][1], disc=data['slabel'][2], num=data['slabel'][3])) ] # Title self.title = "Genus 2 " + ("Curve " if is_curve else "Isogeny Class ") + data['label'] # Code snippets (only for curves) if not is_curve: return self.code = code = {} code['show'] = {'sage':'','magma':''} # use default show names code['curve'] = {'sage':'R.<x> = PolynomialRing(QQ); C = HyperellipticCurve(R(%s), R(%s))'%(data['min_eqn'][0],data['min_eqn'][1]), 'magma':'R<x> := PolynomialRing(Rationals()); C := HyperellipticCurve(R!%s, R!%s);'%(data['min_eqn'][0],data['min_eqn'][1])} if data['abs_disc'] % 4096 == 0: ind2 = [a[0] for a in data['bad_lfactors']].index(2) bad2 = data['bad_lfactors'][ind2][1] magma_cond_option = ': ExcFactors:=[*<2,Valuation('+str(data['cond'])+',2),R!'+str(bad2)+'>*]' else: magma_cond_option = '' code['cond'] = {'magma': 'Conductor(LSeries(C%s)); Factorization($1);'% magma_cond_option} code['disc'] = {'magma':'Discriminant(C); Factorization(Integers()!$1);'} code['igusa_clebsch'] = {'sage':'C.igusa_clebsch_invariants(); [factor(a) for a in _]', 'magma':'IgusaClebschInvariants(C); [Factorization(Integers()!a): a in $1];'} code['igusa'] = {'magma':'IgusaInvariants(C); [Factorization(Integers()!a): a in $1];'} code['g2'] = {'magma':'G2Invariants(C);'} code['aut'] = {'magma':'AutomorphismGroup(C); IdentifyGroup($1);'} code['autQbar'] = {'magma':'AutomorphismGroup(ChangeRing(C,AlgebraicClosure(Rationals()))); IdentifyGroup($1);'} code['num_rat_wpts'] = {'magma':'#Roots(HyperellipticPolynomials(SimplifiedModel(C)));'} if ratpts: code['rat_pts'] = {'magma': '[' + ','.join(["C![%s,%s,%s]"%(p[0],p[1],p[2]) for p in ratpts['rat_pts']]) + '];' } code['two_selmer'] = {'magma':'TwoSelmerGroup(Jacobian(C)); NumberOfGenerators($1);'} code['has_square_sha'] = {'magma':'HasSquareSha(Jacobian(C));'} code['locally_solvable'] = {'magma':'f,h:=HyperellipticPolynomials(C); g:=4*f+h^2; HasPointsEverywhereLocally(g,2) and (#Roots(ChangeRing(g,RealField())) gt 0 or LeadingCoefficient(g) gt 0);'} code['torsion_subgroup'] = {'magma':'TorsionSubgroup(Jacobian(SimplifiedModel(C))); AbelianInvariants($1);'}
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 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. # 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 = "\(\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 try: qc = self.q_curve if qc == True: self.qc = "yes" elif qc == 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 = "\(\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 # 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.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) 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 = [ ('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 = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format(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 += [('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 += [('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']) 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_galmap_object(self, galmap): from lmfdb.belyi.main import url_for_belyi_passport_label # all information about the map goes in the data dictionary # most of the data from the database gets polished/formatted before we put it in the data dictionary data = self.data = {} # the stuff that does not need to be polished for elt in ("label", "plabel", "triples_cyc", "orbit_size", "g", "abc", "deg"): data[elt] = galmap[elt] nt = galmap["group"].split("T") data["group"] = group_display_knowl(int(nt[0]), int(nt[1])) data["geomtype"] = geomtypelet_to_geomtypename_dict[galmap["geomtype"]] data["lambdas"] = [str(c)[1:-1] for c in galmap["lambdas"]] data["isQQ"] = False data["in_LMFDB"] = False F = belyi_base_field(galmap) if F._data is None: fld_coeffs = galmap["base_field"] pol = PolynomialRing(QQ, "x")(fld_coeffs) data["base_field"] = latex(pol) else: data["in_LMFDB"] = True if F.poly().degree() == 1: data["isQQ"] = True F.latex_poly = web_latex(F.poly()) data["base_field"] = F crv_str = galmap["curve"] if crv_str == "PP1": data["curve"] = r"\mathbb{P}^1" else: data["curve"] = make_curve_latex(crv_str) #if galmap['curve_label']: # data['curve_label'] = galmap['curve_label'] # change pairs of floats to complex numbers embeds = galmap["embeddings"] embed_strs = [] for el in embeds: if el[1] < 0: el_str = str(el[0]) + str(el[1]) + r"\sqrt{-1}" else: el_str = str(el[0]) + "+" + str(el[1]) + r"\sqrt{-1}" embed_strs.append(el_str) data["map"] = make_map_latex(galmap["map"]) data["embeddings_and_triples"] = [] if data["isQQ"]: for i in range(0, len(data["triples_cyc"])): triple_cyc = data["triples_cyc"][i] data["embeddings_and_triples"].append([ r"\text{not applicable (over $\mathbb{Q}$)}", triple_cyc[0], triple_cyc[1], triple_cyc[2], ]) else: for i in range(0, len(data["triples_cyc"])): triple_cyc = data["triples_cyc"][i] data["embeddings_and_triples"].append([ embed_strs[i], triple_cyc[0], triple_cyc[1], triple_cyc[2] ]) data["lambdas"] = [str(c)[1:-1] for c in galmap["lambdas"]] # Properties self.plot = db.belyi_galmap_portraits.lucky({"label": galmap['label']}, projection="portrait") plot_link = '<a href="{0}"><img src="{0}" width="200" height="200" style="background-color: white;"/></a>'.format( self.plot) properties = [("Label", galmap["label"])] if self.plot: properties += [(None, plot_link)] properties += [ ("Group", str(galmap["group"])), ("Orders", "$%s$" % (galmap["abc"])), ("Genus", prop_int_pretty(galmap["g"])), ("Size", prop_int_pretty(galmap["orbit_size"])), ] self.properties = properties # Friends self.friends = [("Passport", url_for_belyi_passport_label(galmap["plabel"]))] self.friends.extend(names_and_urls(galmap['friends'])) # Downloads if galmap["g"] <= 2: self.downloads = [ ( "Code to Magma", url_for(".belyi_galmap_magma_download", label=data["label"]), ), ( "Code to SageMath", url_for(".belyi_galmap_sage_download", label=data["label"]), ), ( "All data to text", url_for(".belyi_galmap_text_download", label=data["label"]), ), ] else: self.downloads = [] # Breadcrumbs label_spl = data["label"].split("-") groupstr = label_spl[0] letnum = label_spl[2] gstr = str(data['g']) sigmas = label_spl[1] sigma0, sigma1, sigmaoo = sigmas.split("_") abcstr = str(data['abc']).replace(' ', '') # does lambdasstr need to be updated? lambdasstr = "%s-%s-%s" % (sigma0, sigma1, sigmaoo) lambdasgstr = lambdasstr + "-" + gstr self.bread = [ ("Belyi Maps", url_for(".index")), (groupstr, url_for(".by_url_belyi_search_group", group=groupstr)), ( abcstr, url_for(".by_url_belyi_search_group_triple", group=groupstr, abc=abcstr), ), ( lambdasgstr, url_for( ".by_url_belyi_passport_label", group=groupstr, abc=abcstr, sigma0=sigma0, sigma1=sigma1, sigmaoo=sigmaoo, g=gstr, ), ), ( letnum, url_for( ".by_url_belyi_galmap_label", group=groupstr, abc=abcstr, sigma0=sigma0, sigma1=sigma1, sigmaoo=sigmaoo, g=gstr, letnum=letnum, ), ), ] # Title self.title = "Belyi map orbit " + data["label"] # Code snippets (only for curves) self.code = {} return