def make_order_key(order): order1 = int(ZZ(order).log(10)) return '%03d%s'%(order1,str(order))
def decodedisc(ads, s): return ZZ(ads[3:]) * s
def rd(self): return RealField(300)(ZZ(self._data['disc_abs'])).nth_root( self.degree())
def from_symbolic(exp, dR=None): r''' Method to transform a symbolic expression into a :class:`~ajpastor.dd_functions.ddFunction.DDFunction`. This method takes a symbolic expression an tries to cast it into a differentially definable function. This is closed related with the names that the module :mod:`~ajpastor.dd_functions.ddExamples` gives to the functions. This method is the inverse of :func:`symbolic`, meaning that the output of this method after applying :func:`symbolic` is always an object that is equal to the first input. INPUT: * ``exp``: symbolic expression or an object that can be casted into one. We also allow lists, tuples and sets as possible inputs, applying this method recursively to each of its elements. * ``dR``: :class:`~ajpastor.dd_functions.ddFunction.DDRing` in which hierarchy we try to include the symbolic expression ``exp``. If ``None`` is given, we compute a :class:`~ajpastor.dd_functions.ddFunction.DDRing` using `x` as a variable and all other variables appearing in ``exp`` considered as parameters. OUTPUT: A :class:`~ajpastor.dd_functions.ddFunction.DDFunction` representation of ``exp``. TODO: add more cases from ddExamples EXAMPLES:: sage: from ajpastor.dd_functions import * sage: var('y', 'm', 'n') (y, m, n) sage: from_symbolic(exp(x)) == Exp(x) True sage: sin_yx = from_symbolic(sin(y*x)) sage: parent(sin_yx) DD-Ring over (Univariate Polynomial Ring in x over Rational Field) with parameter (y) sage: sin_yx.init(6, True) [0, y, 0, -y^3, 0, y^5] sage: from_symbolic(cos(x)) == Cos(x) True sage: from_symbolic(sinh(x)) == Sinh(x) True sage: from_symbolic(cosh(x)) == Cosh(x) True sage: from_symbolic(tan(x)) == Tan(x) True sage: from_symbolic(log(x + 1)) == Log(x+1) True sage: from_symbolic(bessel_J(4, x)) == BesselD(4) True sage: from_symbolic(hypergeometric([1,2,3],[5,6,7],x)) == GenericHypergeometricFunction((1,2,3),(5,6,7)) True ''' logger.debug("Looking for a DD-finite representation of %s", exp) if (isinstance(exp, list)): logger.debug("List case: converting each element") return [from_symbolic(el, dR) for el in exp] elif (isinstance(exp, tuple)): logger.debug("Tuple case: converting each element") return tuple([from_symbolic(el, dR) for el in exp]) elif (isinstance(exp, set)): logger.debug("Set case: converting each element") return set([from_symbolic(el, dR) for el in exp]) ## If it is not from the basic structures, we required a symbolic expression exp = SR(exp) logger.debug("Deciding the starting DDRing") if (not dR is None): logger.debug("User required solution to be in the hierarchy of %s", dR) if (any(v not in list(dR.variables(True)) + list(dR.parameters(True)) for v in exp.variables())): raise TypeError("The symbolic expression has unknown variables") else: logger.debug("DDRing not specified: we compute one") params = [str(v) for v in exp.variables() if str(v) != 'x'] if (len(params) > 0): dR = ParametrizedDDRing( DFinite, [str(v) for v in exp.variables() if str(v) != 'x']) else: dR = DFinite name = None try: name = exp.operator().__name__ except AttributeError: try: name = exp.operator().name() except AttributeError: pass operands = exp.operands() if (name in ("list", "tuple")): # special tuple and list cases logger.debug("Found a symbolic list or tuple") return tuple([from_symbolic(el, dR) for el in operands]) elif (name == "set"): # special set case logger.debug("Found a symbolic set") return set([from_symbolic(el, dR) for el in operands]) elif (exp.is_rational_expression()): # rational expression case logger.debug("Rational expression found") num = exp.numerator().polynomial(ring=dR.original_ring()) den = exp.denominator().polynomial(ring=dR.original_ring()) if (den == 1): return num return num / den elif (name == "add_vararg"): logger.debug("Found an addition") return sum([from_symbolic(el, dR) for el in operands]) elif (name == "mul_vararg"): logger.debug("Found an product") return prod([from_symbolic(el, dR) for el in operands]) elif (name == "pow"): logger.debug("Found an power") if (not operands[1] in QQ): raise TypeError("The exponent has to be a rational number") if (operands[1] in ZZ): logger.debug("Integer power: simple output") return pow(from_symbolic(operands[0], dR), ZZ(operands[1])) else: base = from_symbolic(operands[0], dR) if (is_DDFunction(base)): logger.debug("Fractional power: base DDFunction") return pow(base, QQ(operands[1])) else: logger.debug("Fractional power: base rational function") params = [str(el) for el in dR.parameters()] i = 0 while ("y%d" % i in params): i += 1 R = PolynomialRing(dR.original_ring(), "y%d" % i) y = R.gens()[0] pq = QQ(operands[1]) p = pq.numerator() q = pq.denominator() poly = y**q - R(str(operands[0]))**p x = dR.variables(True)[0] init = [ dR.coeff_field(exp.derivative(x, i)(**{ str(x): 0 })) for i in range(p + q) ] return DAlgebraic(poly, init, dR.to_depth(1)) elif (name == "sin"): logger.debug("Found an sine") return Sin(operands[0]) elif (name == "cos"): logger.debug("Found an cosine") return Cos(operands[0]) elif (name == "sinh"): logger.debug("Found a hyperbolic sine") return Sinh(operands[0]) elif (name == "cosh"): logger.debug("Found an hyperbolic cosine") return Cosh(operands[0]) elif (name == "tan"): logger.debug("Found a tangent") return Tan(operands[0]) elif (name == "log"): logger.debug("Found a logarithm") return Log(operands[0]) elif (name == "exp"): logger.debug("Found an exponential") return Exp(operands[0]) elif (name == "bessel_J"): logger.debug("Found an Bessel function of first kind") if (not (operands[0] in ZZ and operands[0] >= 0)): raise ValueError( "The Bessel has to have a non-negative integer index") return BesselD(ZZ(operands[0]))(from_symbolic(operands[1], dR)) elif (name == "legendre_P"): logger.debug("Found an Legendre function of first kind") return LegendreD(operands[0], 0, 1)(from_symbolic(operands[1], dR)) elif (name == "gen_legendre_P"): logger.debug("Found an generic Legendre function of first kind") return LegendreD(operands[0], operands[1], 1)(from_symbolic(operands[1], dR)) elif (name == "legendre_Q"): logger.debug("Found an Legendre function of second kind") return LegendreD(operands[0], 0, 2)(from_symbolic(operands[1], dR)) elif (name == "gen_legendre_Q"): logger.debug("Found an generic Legendre function of second kind") return LegendreD(operands[0], operands[1], 2)(from_symbolic(operands[1], dR)) elif (name == "chebyshev_T"): logger.debug("Found an Chebyshev function of first kind") return ChebyshevD(operands[0], 1)(from_symbolic(operands[1], dR)) elif (name == "chebyshev_U"): logger.debug("Found an Chebyshev function of second kind") return ChebyshevD(operands[0], 2)(from_symbolic(operands[1], dR)) elif (name == "hypergeometric"): return GenericHypergeometricFunction(from_symbolic(operands[0], dR), from_symbolic(operands[1], dR))(from_symbolic( operands[2], dR)) else: raise NotImplementedError( "The operator %s is not valid for 'from_symbolic'" % name)
def set_info_for_web_newform(level=None, weight=None, character=None, label=None, **kwds): r""" Set the info for on modular form. """ info = to_dict(kwds) info['level'] = level info['weight'] = weight info['character'] = character info['label'] = label if level is None or weight is None or character is None or label is None: s = "In set info for one form but do not have enough args!" s += "level={0},weight={1},character={2},label={3}".format( level, weight, character, label) emf_logger.critical(s) emf_logger.debug("In set_info_for_one_mf: info={0}".format(info)) prec = my_get(info, 'prec', default_prec, int) bprec = my_get(info, 'bprec', default_display_bprec, int) emf_logger.debug("PREC: {0}".format(prec)) emf_logger.debug("BITPREC: {0}".format(bprec)) try: WNF = WebNewForm_cached(level=level, weight=weight, character=character, label=label) if not WNF.has_updated(): raise IndexError( "Unfortunately, we do not have this newform in the database.") info['character_order'] = WNF.character.order info['code'] = WNF.code emf_logger.debug("defined webnewform for rendering!") except IndexError as e: info['error'] = e.message url0 = url_for("mf.modular_form_main_page") url1 = url_for("emf.render_elliptic_modular_forms") url2 = url_for("emf.render_elliptic_modular_forms", level=level) url3 = url_for("emf.render_elliptic_modular_forms", level=level, weight=weight) url4 = url_for("emf.render_elliptic_modular_forms", level=level, weight=weight, character=character) bread = [(MF_TOP, url0), (EMF_TOP, url1)] bread.append(("Level %s" % level, url2)) bread.append(("Weight %s" % weight, url3)) bread.append(("Character \( %s \)" % (WNF.character.latex_name), url4)) bread.append( ("Newform %d.%d.%d.%s" % (level, weight, int(character), label), '')) info['bread'] = bread properties2 = list() friends = list() space_url = url_for('emf.render_elliptic_modular_forms', level=level, weight=weight, character=character) friends.append( ('\( S_{%s}(%s, %s)\)' % (WNF.weight, WNF.level, WNF.character.latex_name), space_url)) if hasattr(WNF.base_ring, "lmfdb_url") and WNF.base_ring.lmfdb_url: friends.append(('Number field ' + WNF.base_ring.lmfdb_pretty, WNF.base_ring.lmfdb_url)) if hasattr(WNF.coefficient_field, "lmfdb_url") and WNF.coefficient_field.lmfdb_label: friends.append(('Number field ' + WNF.coefficient_field.lmfdb_pretty, WNF.coefficient_field.lmfdb_url)) friends = uniq(friends) friends.append(("Dirichlet character \(" + WNF.character.latex_name + "\)", WNF.character.url())) if WNF.dimension == 0 and not info.has_key('error'): info['error'] = "This space is empty!" info['title'] = 'Newform ' + WNF.hecke_orbit_label info['learnmore'] = [('History of modular forms', url_for('.holomorphic_mf_history'))] if 'error' in info: return info ## Until we have figured out how to do the embeddings correctly we don't display the Satake ## parameters for non-trivial characters.... ## Example to illustrate the different cases ## base = CyclotomicField(n) -- of degree phi(n) ## coefficient_field = NumberField( p(x)) for some p in base['x'] of degree m ## we would then have cdeg = m*phi(n) and bdeg = phi(n) ## and rdeg = m ## Unfortunately, for e.g. base = coefficient_field = CyclotomicField(6) ## we get coefficient_field.relative_degree() == 2 although it should be 1 cdeg = WNF.coefficient_field.absolute_degree() bdeg = WNF.base_ring.absolute_degree() if cdeg == 1: rdeg = 1 else: ## just setting rdeg = WNF.coefficient_field.relative_degree() does not give correct result... ## rdeg = QQ(cdeg) / QQ(bdeg) cf_is_QQ = (cdeg == 1) br_is_QQ = (bdeg == 1) if cf_is_QQ: info['satake'] = WNF.satake if WNF.complexity_of_first_nonvanishing_coefficients( ) > default_max_height: info['qexp'] = "" info['qexp_display'] = '' info['hide_qexp'] = True n, c = WNF.first_nonvanishing_coefficient() info['trace_nv'] = latex(WNF.first_nonvanishing_coefficient_trace()) info['norm_nv'] = '\\approx ' + latex( WNF.first_nonvanishing_coefficient_norm().n()) info['index_nv'] = n else: if WNF.prec < prec: #get WNF record at larger prec WNF.prec = prec WNF.update_from_db() info['qexp'] = WNF.q_expansion_latex(prec=10, name='\\alpha ') info['qexp_display'] = url_for(".get_qexp_latex", level=level, weight=weight, character=character, label=label) info["hide_qexp"] = False info['max_cn_qexp'] = WNF.q_expansion.prec() ## All combinations should be tested... ## 13/4/4/a -> base ring = coefficient_field = QQ(zeta_6) ## 13/3/8/a -> base_ring = QQ(zeta_4), coefficient_field has poly x^2+(2\zeta_4+2x-3\zeta_$ over base_ring ## 13/4/3/a -> base_ring = coefficient_field = QQ(zeta_3) ## 13/4/1/a -> all rational ## 13/6/1/a/ -> base_ring = QQ, coefficient_field = Q(sqrt(17)) ## These are variables which needs to be set properly below info['polvars'] = {'base_ring': 'x', 'coefficient_field': '\\alpha'} if not cf_is_QQ: if rdeg > 1: # not WNF.coefficient_field == WNF.base_ring: ## Here WNF.base_ring should be some cyclotomic field and we have an extension over this. p1 = WNF.coefficient_field.relative_polynomial() c_pol_ltx = web_latex_poly(p1, '\\alpha') # make the variable \alpha c_pol_ltx_x = web_latex_poly(p1, 'x') zeta = p1.base_ring().gens()[0] # p2 = zeta.minpoly() #this is not used anymore # b_pol_ltx = web_latex_poly(p2, latex(zeta)) #this is not used anymore z1 = zeta.multiplicative_order() info['coeff_field'] = [ WNF.coefficient_field.absolute_polynomial_latex('x'), c_pol_ltx_x, z1 ] if hasattr(WNF.coefficient_field, "lmfdb_url") and WNF.coefficient_field.lmfdb_url: info['coeff_field_pretty'] = [ WNF.coefficient_field.lmfdb_url, WNF.coefficient_field.lmfdb_pretty, WNF.coefficient_field.lmfdb_label ] if z1 == 4: info[ 'polynomial_st'] = '<div class="where">where</div> {0}\(\mathstrut=0\) and \(\zeta_4=i\).</div><br/>'.format( c_pol_ltx) info['polvars']['base_ring'] = 'i' elif z1 <= 2: info[ 'polynomial_st'] = '<div class="where">where</div> {0}\(\mathstrut=0\).</div><br/>'.format( c_pol_ltx) else: info[ 'polynomial_st'] = '<div class="where">where</div> %s\(\mathstrut=0\) and \(\zeta_{%s}=e^{\\frac{2\\pi i}{%s}}\) ' % ( c_pol_ltx, z1, z1) info['polvars']['base_ring'] = '\zeta_{{ {0} }}'.format(z1) if z1 == 3: info[ 'polynomial_st'] += 'is a primitive cube root of unity.' else: info[ 'polynomial_st'] += 'is a primitive {0}-th root of unity.'.format( z1) elif not br_is_QQ: ## Now we have base and coefficient field being equal, meaning that since the coefficient field is not QQ it is some cyclotomic field ## generated by some \zeta_n p1 = WNF.coefficient_field.absolute_polynomial() z1 = WNF.coefficient_field.gens()[0].multiplicative_order() c_pol_ltx = web_latex_poly(p1, '\\zeta_{{{0}}}'.format(z1)) c_pol_ltx_x = web_latex_poly(p1, 'x') info['coeff_field'] = [ WNF.coefficient_field.absolute_polynomial_latex('x'), c_pol_ltx_x ] if hasattr(WNF.coefficient_field, "lmfdb_url") and WNF.coefficient_field.lmfdb_url: info['coeff_field_pretty'] = [ WNF.coefficient_field.lmfdb_url, WNF.coefficient_field.lmfdb_pretty, WNF.coefficient_field.lmfdb_label ] if z1 == 4: info[ 'polynomial_st'] = '<div class="where">where \(\zeta_4=e^{{\\frac{{\\pi i}}{{ 2 }} }}=i \).</div>'.format( c_pol_ltx) info['polvars']['coefficient_field'] = 'i' elif z1 <= 2: info['polynomial_st'] = '' else: info[ 'polynomial_st'] = '<div class="where">where \(\zeta_{{{0}}}=e^{{\\frac{{2\\pi i}}{{ {0} }} }}\) '.format( z1) info['polvars']['coefficient_field'] = '\zeta_{{{0}}}'.format( z1) if z1 == 3: info[ 'polynomial_st'] += 'is a primitive cube root of unity.</div>' else: info[ 'polynomial_st'] += 'is a primitive {0}-th root of unity.</div>'.format( z1) else: info['polynomial_st'] = '' if info["hide_qexp"]: info['polynomial_st'] = '' info['degree'] = int(cdeg) if cdeg == 1: info['is_rational'] = 1 info['coeff_field_pretty'] = [ WNF.coefficient_field.lmfdb_url, WNF.coefficient_field.lmfdb_pretty ] else: info['is_rational'] = 0 emf_logger.debug("PREC2: {0}".format(prec)) info['embeddings'] = WNF._embeddings[ 'values'] #q_expansion_embeddings(prec, bprec,format='latex') info['embeddings_len'] = len(info['embeddings']) properties2 = [('Level', str(level)), ('Weight', str(weight)), ('Character', '$' + WNF.character.latex_name + '$'), ('Label', WNF.hecke_orbit_label), ('Dimension of Galois orbit', str(WNF.dimension))] if (ZZ(level)).is_squarefree(): info['twist_info'] = WNF.twist_info if isinstance(info['twist_info'], list) and len(info['twist_info']) > 0: info['is_minimal'] = info['twist_info'][0] if (info['twist_info'][0]): s = 'Is minimal<br>' else: s = 'Is a twist of lower level<br>' properties2 += [('Twist info', s)] else: info['twist_info'] = 'Twist info currently not available.' properties2 += [('Twist info', 'not available')] args = list() for x in range(5, 200, 10): args.append({'digits': x}) alev = None CM = WNF._cm_values if CM is not None: if CM.has_key('tau') and len(CM['tau']) != 0: info['CM_values'] = CM info['is_cm'] = WNF.is_cm if WNF.is_cm == 1: info['cm_field'] = "2.0.{0}.1".format(-WNF.cm_disc) info['cm_disc'] = WNF.cm_disc info['cm_field_knowl'] = nf_display_knowl( info['cm_field'], getDBConnection(), field_pretty(info['cm_field'])) info['cm_field_url'] = url_for("number_fields.by_label", label=info["cm_field"]) if WNF.is_cm is None or WNF.is_cm == -1: s = '- Unknown (insufficient data)<br>' elif WNF.is_cm == 1: s = 'Yes<br>' else: s = 'No<br>' properties2.append(('CM', s)) alev = WNF.atkin_lehner_eigenvalues() info['atkinlehner'] = None if isinstance(alev, dict) and len(alev.keys()) > 0 and level != 1: s1 = " Atkin-Lehner eigenvalues " s2 = "" for Q in alev.keys(): s2 += "\( \omega_{ %s } \) : %s <br>" % (Q, alev[Q]) properties2.append((s1, s2)) emf_logger.debug("properties={0}".format(properties2)) # alev = WNF.atkin_lehner_eigenvalues_for_all_cusps() # if isinstance(alev,dict) and len(alev.keys())>0: # emf_logger.debug("alev={0}".format(alev)) # info['atkinlehner'] = list() # for Q in alev.keys(): # s = "\(" + latex(c) + "\)" # Q = alev[c][0] # ev = alev[c][1] # info['atkinlehner'].append([Q, c, ev]) if (level == 1): poly = WNF.explicit_formulas.get('as_polynomial_in_E4_and_E6', '') if poly != '': d, monom, coeffs = poly emf_logger.critical("poly={0}".format(poly)) info['explicit_formulas'] = '\(' for i in range(len(coeffs)): c = QQ(coeffs[i]) s = "" if d > 1 and i > 0 and c > 0: s = "+" if c < 0: s = "-" if c.denominator() > 1: cc = "\\frac{{ {0} }}{{ {1} }}".format( abs(c.numerator()), c.denominator()) else: cc = str(abs(c)) s += "{0} \cdot ".format(cc) a = monom[i][0] b = monom[i][1] if a == 1: a = "" if b == 1: b = "" if a == 0 and b != 0: s += "E_6^{{ {0} }}(z)".format(b) elif b == 0 and a != 0: s += "E_4^{{ {0} }}(z)".format(a) else: s += "E_4^{{ {0} }}(z) \cdot E_6^{{ {1} }}(z)".format(a, b) info['explicit_formulas'] += s info['explicit_formulas'] += " \)" # cur_url = '?&level=' + str(level) + '&weight=' + str(weight) + '&character=' + str(character) + '&label=' + str(label) # never used if len(WNF.parent.hecke_orbits) > 1: for label_other in WNF.parent.hecke_orbits.keys(): if (label_other != label): s = 'Modular form ' if character: s += newform_label(level, weight, character, label_other) else: s += newform_label(level, weight, 1, label_other) url = url_for('emf.render_elliptic_modular_forms', level=level, weight=weight, character=character, label=label_other) friends.append((s, url)) s = 'L-Function ' if character: s += newform_label(level, weight, character, label) else: s += newform_label(level, weight, 1, label) # url = # "/L/ModularForm/GL2/Q/holomorphic?level=%s&weight=%s&character=%s&label=%s&number=%s" # %(level,weight,character,label,0) url = '/L' + url_for('emf.render_elliptic_modular_forms', level=level, weight=weight, character=character, label=label) if WNF.coefficient_field_degree > 1: for h in range(WNF.coefficient_field_degree): s0 = s + ".{0}".format(h) url0 = url + "{0}/".format(h) friends.append((s0, url0)) else: friends.append((s, url)) # if there is an elliptic curve over Q associated to self we also list that if WNF.weight == 2 and WNF.coefficient_field_degree == 1: llabel = str(level) + '.' + label s = 'Elliptic curve isogeny class ' + llabel url = '/EllipticCurve/Q/' + llabel friends.append((s, url)) info['properties2'] = properties2 info['friends'] = friends info['max_cn'] = WNF.max_available_prec() return info
def nice_coset_reps(G): r""" Compute a better/nicer list of right coset representatives [V_j] i.e. SL2Z = \cup G V_j Use this routine for known congruence subgroups. EXAMPLES:: sage: G=MySubgroup(Gamma0(5)) sage: G._get_coset_reps_from_G(Gamma0(5)) [[1 0] [0 1], [ 0 -1] [ 1 0], [ 0 -1] [ 1 1], [ 0 -1] [ 1 -1], [ 0 -1] [ 1 2], [ 0 -1] [ 1 -2]] """ cl = list() S, T = SL2Z.gens() lvl = G.generalised_level() # Start with identity rep. cl.append(SL2Z([1, 0, 0, 1])) if (not S in G): cl.append(S) # If the original group is given as a Gamma0 then # the reps are not the one we want # I.e. we like to have a fundamental domain in # -1/2 <=x <= 1/2 for Gamma0, Gamma1, Gamma for j in range(1, ZZ(ceil(RR(lvl / 2.0)) + 2)): for ep in [1, -1]: if (len(cl) >= G.index()): break # The ones about 0 are all of this form A = SL2Z([0, -1, 1, ep * j]) # just make sure they are inequivalent try: for V in cl: if ((A <> V and A * V**-1 in G) or cl.count(A) > 0): raise StopIteration() cl.append(A) except StopIteration: pass # We now addd the rest of the "flips" of these reps. # So that we end up with a connected domain i = 1 while (True): lold = len(cl) for V in cl: for A in [S, T, T**-1]: B = V * A try: for W in cl: if ((B * W**-1 in G) or cl.count(B) > 0): raise StopIteration() cl.append(B) except StopIteration: pass if (len(cl) >= G.index() or lold >= len(cl)): # If we either did not addd anything or if we addded enough # we exit break # If we missed something (which is unlikely) if (len(cl) <> G.index()): print "cl=", cl raise ValueError, "Problem getting coset reps! Need %s and got %s" % ( G.index(), len(cl)) return cl
def elliptic_curve_search(info, query): parse_rational(info, query, 'jinv', 'j-invariant') parse_ints(info, query, 'conductor') parse_ints(info, query, 'torsion', 'torsion order') parse_ints(info, query, 'rank') parse_ints(info, query, 'sha', 'analytic order of Ш') parse_ints(info, query, 'num_int_pts', 'num_int_pts') parse_floats(info, query, 'regulator', 'regulator') parse_bool(info, query, 'semistable', 'semistable') parse_bracketed_posints(info, query, 'torsion_structure', maxlength=2, check_divisibility='increasing') # speed up slow torsion_structure searches by also setting torsion #if 'torsion_structure' in query and not 'torsion' in query: # query['torsion'] = reduce(mul,[int(n) for n in query['torsion_structure']],1) if 'include_cm' in info: if info['include_cm'] == 'exclude': query['cm'] = 0 elif info['include_cm'] == 'only': query['cm'] = {'$ne': 0} #parse_ints(info,query,field='cm_disc',qfield='cm') if 'cm_disc' in info: query['cm'] = info['cm_disc'] parse_element_of(info, query, field='isodeg', qfield='isogeny_degrees', split_interval=1000) #parse_ints(info,query,field='isodeg',qfield='isogeny_degrees') parse_primes(info, query, 'surj_primes', name='maximal primes', qfield='nonmax_primes', mode='exclude') parse_primes(info, query, 'nonsurj_primes', name='non-maximal primes', qfield='nonmax_primes', mode=info.get('surj_quantifier'), radical='nonmax_rad') parse_primes(info, query, 'bad_primes', name='bad primes', qfield='bad_primes', mode=info.get('bad_quantifier')) # The button which used to be labelled Optimal only no/yes" # (default no) has been renamed "Curves per isogeny class all/one" # (default one) but the only change in behavious is that we no # longer treat class 990h (where the optial curve is #3 not #1) as # special: the "one" option just restricts to curves whose # 'number' is 1. if 'optimal' in info and info['optimal'] == 'on': query.update({'number': 1}) # Old behaviour was as follows: # For all isogeny classes except 990h the optimal curve is number 1, while for class 990h it is number 3. # So setting query['number'] = 1 is nearly correct, but fails on 990h3. # Instead, we use this more complicated query: # query.update({"$or":[{'iso':'990h', 'number':3}, {'iso':{'$ne':'990h'},'number':1}]}) info['curve_ainvs'] = lambda dbc: str([ZZ(ai) for ai in dbc['ainvs']]) info['curve_url_LMFDB'] = lambda dbc: url_for(".by_triple_label", conductor=dbc['conductor'], iso_label=split_lmfdb_label( dbc['lmfdb_iso'])[1], number=dbc['lmfdb_number']) info['iso_url_LMFDB'] = lambda dbc: url_for(".by_double_iso_label", conductor=dbc['conductor'], iso_label=split_lmfdb_label( dbc['lmfdb_iso'])[1]) info['curve_url_Cremona'] = lambda dbc: url_for(".by_ec_label", label=dbc['label']) info['iso_url_Cremona'] = lambda dbc: url_for(".by_ec_label", label=dbc['iso'])
reduction_cost_model=BKZ.sieve)) # BKZ cost models: CLASSICAL - 0.292*beta + 16.4 + log(8*d,2) - dual # i.e. BKZ.sieve = lambda beta, d, B: ZZ(2)**RR(0.292*beta + 16.4 + log(8*d,2)) print("CLASSICAL DUAL") print( dual_scale(n, alpha, q, secret_distribution=secret_distribution, m=m, success_probability=success_probability, reduction_cost_model=BKZ.sieve)) # For more conservative parameters, both classical and quantum # BKZ cost models: CLASSICAL - 0.292 beta - primal reduction_cost_model = lambda beta, d, B: ZZ(_sage_const_2)**RR( _sage_const_0p292 * beta) print("CLASSICAL PRIMAL (conservative)") print( primal_usvp(n, alpha, q, secret_distribution=secret_distribution, m=m, success_probability=success_probability, reduction_cost_model=reduction_cost_model)) # BKZ cost models: CLASSICAL - 0.292 beta - dual print("CLASSICAL DUAL (conservative)") print( dual_scale(n, alpha,
def init_tensor_product(self, V, W): """ We are given two Galois representations and we will return their tensor product. """ self.original_object = V.original_object + W.original_object self.object_type = "tensorproduct" self.V1 = V self.V2 = W self.dim = V.dim * W.dim self.motivic_weight = V.motivic_weight + W.motivic_weight self.langlands = False # status 2014 :) self.besancon_bound = min(V.besancon_bound, W.besancon_bound) bad2 = ZZ(W.conductor).prime_factors() bad_primes = [x for x in ZZ(V.conductor).prime_factors() if x in bad2] for p in bad_primes: if (p not in V.bad_semistable_primes and p not in W.bad_semistable_primes): # this condition above only applies to the current type of objects # for general reps we would have to test the lines below # to be certain that the formulae are correct. #if ((p not in V.bad_semistable_primes or p not in W.bad_pot_good) and #(p not in W.bad_semistable_primes or p not in V.bad_pot_good) and #(p not in V.bad_semistable_primes or p not in W.bad_semistable_primes)): raise NotImplementedError( "Currently tensor products of Galois representations are only implemented under some conditions.", "The behaviour at %d is too wild (both factors must be semistable)." % p) # check for the possibily of getting poles if V.weight == W.weight and V.conductor == W.conductor: Vans = V.algebraic_coefficients(50) Wans = W.algebraic_coefficients(50) CC = ComplexField() if ((Vans[2] in ZZ and Wans[2] in ZZ and all(Vans[n] == Wans[n] for n in range(1, 50))) or all(CC(Vans[n]) == CC(Wans[n]) for n in range(1, 50))): raise NotImplementedError( "It seems you are asking to tensor a " + "Galois representation with its dual " + "which results in the L-function having " + "a pole. This is not implemented here.") scommon = [ x for x in V.bad_semistable_primes if x in W.bad_semistable_primes ] N = W.conductor**V.dim N *= V.conductor**W.dim for p in bad_primes: n1_tame = V.dim - V.local_euler_factor(p).degree() n2_tame = W.dim - W.local_euler_factor(p).degree() nn = n1_tame * n2_tame N = N // p**nn if p in scommon: # both are degree 1 in this case N = N // p self.conductor = N h1 = selberg_to_hodge(V.motivic_weight, V.mu_fe, V.nu_fe) h2 = selberg_to_hodge(W.motivic_weight, W.mu_fe, W.nu_fe) h = tensor_hodge(h1, h2) w, m, n = hodge_to_selberg(h) self.mu_fe = m self.nu_fe = n _, self.gammaV = gamma_factors(h) # this is used in getting the Dirichlet coefficients. self.bad_primes_info = [] for p in bad_primes: # we have to check if this works in all bad cases ! f1 = V.local_euler_factor(p) f2 = W.local_euler_factor(p) # might be dodgy if f1 or f2 is an approx to the Euler factor if p in scommon: E = tensor_local_factors(f1, f2, V.dim * W.dim) T = f1.parent().gens()[0] # right answer is E(T)*E(pT) self.bad_primes_info.append( [p, E * E(p * T), 1 - T] ) #bad_primes_info.append() with 1-T as the second argument is equivalent to taking the first argument as the results (it does a convolution, as in the next line) else: self.bad_primes_info.append([p, f1, f2]) CC = ComplexField() I = CC.gens()[0] self.sign = I**root_number_at_oo(h) self.sign /= I**(root_number_at_oo(h1) * V.dim) self.sign /= I**(root_number_at_oo(h2) * W.dim) self.sign *= V.sign**W.dim self.sign *= W.sign**V.dim for p in bad_primes: if p not in V.bad_semistable_primes or p not in V.bad_semistable_primes: f1 = V.local_euler_factor(p) f2 = W.local_euler_factor(p) det1 = f1.leading_coefficient() * (-1)**f1.degree() det2 = f2.leading_coefficient() * (-1)**f2.degree() n1_tame = V.dim - f1.degree() n2_tame = W.dim - f2.degree() n1_wild = ZZ(V.conductor).valuation(p) - n1_tame n2_wild = ZZ(W.conductor).valuation(p) - n2_tame # additionally, we would need to correct this by # replacing det1 by chi1(p) if p is semistable for V # however for all the possible input this currently does # not affect the sign if p in V.bad_semistable_primes: chi1p = 1 # here else: chi1p = det1 if p in W.bad_semistable_primes: chi2p = 1 # here else: chi2p = det2 corr = chi1p**n2_wild corr *= det1**n2_tame corr *= chi2p**n1_wild corr *= det2**n1_tame corr *= (-1)**(n1_tame * n2_tame) self.sign *= corr / corr.abs() #self.primitive = False self.set_dokchitser_Lfunction() # maybe we should change this to take as many coefficients as implemented # in other Lfunctions self.set_number_of_coefficients() someans = self.algebraic_coefficients(50) # why not. if all(x in ZZ for x in someans): self.selfdual = True else: CC = ComplexField() self.selfdual = all(CC(an).imag().abs() < 0.0001 for an in someans) self.coefficient_type = max(V.coefficient_type, W.coefficient_type) self.coefficient_period = ZZ(V.coefficient_period).lcm( W.coefficient_period) self.ld.gp().quit()
def make_class(self): # Extract the size of the isogeny class from the database classdata = db.ec_classdata.lucky({'lmfdb_iso': self.lmfdb_iso}) self.class_size = ncurves = classdata['class_size'] # Create a list of the curves in the class from the database number_key = 'Cnumber' if self.label_type == 'Cremona' else 'lmfdb_number' self.curves = [ db.ec_curvedata.lucky({ 'lmfdb_iso': self.lmfdb_iso, number_key: i + 1 }) for i in range(ncurves) ] # Set optimality flags. The optimal curve is conditionally # number 1 except in one case which is labeled differently in # the Cremona tables. We know which curve is optimal iff the # optimality code for curve #1 is 1 (except for class 990h). # Note that self is actually an elliptic curve, with number=1. # The code here allows us to update the display correctly by # changing one line in this file (defining OPTIMALITY_BOUND) # without changing the data. self.cremona_bound = CREMONA_BOUND self.optimality_bound = OPTIMALITY_BOUND self.optimality_known = (self.conductor < OPTIMALITY_BOUND) or ( (self.conductor < CREMONA_BOUND) and ((self.optimality == 1) or (self.Ciso == '990h'))) self.optimal_label = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label if self.conductor < OPTIMALITY_BOUND: for c in self.curves: c['optimal'] = (c['Cnumber'] == (3 if self.Ciso == '990h' else 1)) c['optimality_known'] = True elif self.conductor < CREMONA_BOUND: for c in self.curves: c['optimal'] = (c['optimality'] > 0 ) # this curve possibly optimal c['optimality_known'] = (c['optimality'] == 1 ) # this curve certainly optimal else: for c in self.curves: c['optimal'] = None c['optimality_known'] = False for c in self.curves: c['ai'] = c['ainvs'] c['curve_url_lmfdb'] = url_for(".by_triple_label", conductor=self.conductor, iso_label=self.iso_label, number=c['lmfdb_number']) c['curve_url_cremona'] = url_for( ".by_ec_label", label=c['Clabel']) if self.conductor < CREMONA_BOUND else "N/A" if self.label_type == 'Cremona': c['curve_label'] = c['Clabel'] _, c_iso, c_number = split_cremona_label(c['Clabel']) else: c['curve_label'] = c['lmfdb_label'] _, c_iso, c_number = split_lmfdb_label(c['lmfdb_label']) c['short_label'] = "{}{}".format(c_iso, c_number) from sage.matrix.all import Matrix M = classdata['isogeny_matrix'] # permute rows/cols to match labelling: the rows/cols in the # ec_classdata table are with respect to LMFDB ordering. if self.label_type == 'Cremona': perm = lambda i: next(c for c in self.curves if c['Cnumber'] == i + 1)['lmfdb_number'] - 1 M = [[M[perm(i)][perm(j)] for i in range(ncurves)] for j in range(ncurves)] M = Matrix(M) self.isogeny_matrix_str = latex(M) # Create isogeny graph with appropriate vertex labels: self.graph = make_graph(M, [c['short_label'] for c in self.curves]) P = self.graph.plot(edge_labels=True, vertex_size=1000) self.graph_img = encode_plot(P) self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img self.newform = raw_typeset( PowerSeriesRing(QQ, 'q')(classdata['anlist'], 20, check=True)) self.newform_label = ".".join( [str(self.conductor), str(2), 'a', self.iso_label]) self.newform_exists_in_db = db.mf_newforms.label_exists( self.newform_label) if self.newform_exists_in_db: char_orbit, hecke_orbit = self.newform_label.split('.')[2:] self.newform_link = url_for("cmf.by_url_newform_label", level=self.conductor, weight=2, char_orbit_label=char_orbit, hecke_orbit=hecke_orbit) self.lfunction_link = url_for("l_functions.l_function_ec_page", conductor_label=self.conductor, isogeny_class_label=self.iso_label) self.friends = [('L-function', self.lfunction_link)] if self.cm: # set CM field for Properties box. D = ZZ(self.cm).squarefree_part() coeffs = [(1 - D) // 4, -1, 1] if D % 4 == 1 else [-D, 0, 1] lab = db.nf_fields.lucky({'coeffs': coeffs}, projection='label') self.CMfield = field_pretty(lab) else: self.CMfield = "no" if self.conductor <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=self.conductor, isogeny=self.iso_label))] if self.conductor <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=self.conductor, isogeny=self.iso_label))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] if self.label_type == 'Cremona': self.title = "Elliptic curve isogeny class with Cremona label {} (LMFDB label {})".format( self.Ciso, self.lmfdb_iso) elif self.conductor < CREMONA_BOUND: self.title = "Elliptic curve isogeny class with LMFDB label {} (Cremona label {})".format( self.lmfdb_iso, self.Ciso) else: self.title = "Elliptic curve isogeny class with LMFDB label {}".format( self.lmfdb_iso) self.properties = [ ('Label', self.Ciso if self.label_type == 'Cremona' else self.lmfdb_iso), ('Number of curves', prop_int_pretty(ncurves)), ('Conductor', prop_int_pretty(self.conductor)), ('CM', '%s' % self.CMfield), ('Rank', prop_int_pretty(self.rank)) ] if ncurves > 1: self.properties += [('Graph', ''), (None, self.graph_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.iso_label, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.iso_label))] self.bread = [('Elliptic curves', url_for("ecnf.index")), (r'$\Q$', url_for(".rational_elliptic_curves")), ('%s' % self.conductor, url_for(".by_conductor", conductor=self.conductor)), ('%s' % self.iso_label, ' ')] self.code = {} self.code['show'] = {'sage': ''} # use default show names self.code['class'] = { 'sage': 'E = EllipticCurve("%s1")\n' % (self.iso_label) + 'E.isogeny_class()\n' } self.code['curves'] = {'sage': 'E.isogeny_class().curves'} self.code['rank'] = {'sage': 'E.rank()'} self.code['q_eigenform'] = {'sage': 'E.q_eigenform(10)'} self.code['matrix'] = {'sage': 'E.isogeny_class().matrix()'} self.code['plot'] = { 'sage': 'E.isogeny_graph().plot(edge_labels=True)' }
def render_group_webpage(args): data = None info = {} if 'label' in args: label = clean_input(args['label']) label = label.replace('t', 'T') C = base.getDBConnection() data = C.transitivegroups.groups.find_one({'label': label}) if data is None: bread = get_bread([("Search error", ' ')]) info['err'] = "Group " + label + " was not found in the database." info['label'] = label return search_input_error(info, bread) data['label_raw'] = label.lower() title = 'Galois Group: ' + label wgg = WebGaloisGroup.from_data(data) n = data['n'] t = data['t'] data['yesno'] = yesno order = data['order'] data['orderfac'] = latex(ZZ(order).factor()) orderfac = latex(ZZ(order).factor()) data['ordermsg'] = "$%s=%s$" % (order, latex(orderfac)) if order == 1: data['ordermsg'] = "$1$" if ZZ(order).is_prime(): data['ordermsg'] = "$%s$ (is prime)" % order pgroup = len(ZZ(order).prime_factors()) < 2 if n == 1: G = gap.SmallGroup(n, t) else: G = gap.TransitiveGroup(n, t) if ZZ(order) < ZZ('10000000000'): ctable = chartable(n, t) else: ctable = 'Group too large' data['gens'] = generators(n, t) if n == 1 and t == 1: data['gens'] = 'None needed' data['chartable'] = ctable data['parity'] = "$%s$" % data['parity'] data['cclasses'] = conjclasses(G, n) data['subinfo'] = subfield_display(C, n, data['subs']) data['resolve'] = resolve_display(C, data['resolve']) data['otherreps'] = wgg.otherrep_list() if len(data['otherreps']) == 0: data['otherreps'] = "There is no other low degree representation." query = {'galois': bson.SON([('n', n), ('t', t)])} C = base.getDBConnection() intreps = C.transitivegroups.Gmodules.find({ 'n': n, 't': t }).sort('index', pymongo.ASCENDING) # turn cursor into a list intreps = [z for z in intreps] if len(intreps) > 0: data['int_rep_classes'] = [str(z[0]) for z in intreps[0]['gens']] for onerep in intreps: onerep['gens'] = [ list_to_latex_matrix(z[1]) for z in onerep['gens'] ] data['int_reps'] = intreps data['int_reps_complete'] = int_reps_are_complete(intreps) dcq = data['moddecompuniq'] if dcq[0] == 0: data['decompunique'] = 0 else: data['decompunique'] = dcq[0] data['isoms'] = [[mult2mult(z[0]), mult2mult(z[1])] for z in dcq[1]] data['isoms'] = [[ modules2string(n, t, z[0]), modules2string(n, t, z[1]) ] for z in data['isoms']] #print dcq[1] #print data['isoms'] friends = [] one = C.numberfields.fields.find_one(query) if one: friends.append( ('Number fields with this Galois group', url_for('number_fields.number_field_render_webpage') + "?galois_group=%dT%d" % (n, t))) prop2 = [ ('Label', label), ('Order', '\(%s\)' % order), ('n', '\(%s\)' % data['n']), ('Cyclic', yesno(data['cyc'])), ('Abelian', yesno(data['ab'])), ('Solvable', yesno(data['solv'])), ('Primitive', yesno(data['prim'])), ('$p$-group', yesno(pgroup)), ] pretty = group_display_pretty(n, t, C) if len(pretty) > 0: prop2.extend([('Group:', pretty)]) info['pretty_name'] = pretty data['name'] = re.sub(r'_(\d+)', r'_{\1}', data['name']) data['name'] = re.sub(r'\^(\d+)', r'^{\1}', data['name']) info.update(data) bread = get_bread([(label, ' ')]) return render_template("gg-show-group.html", credit=GG_credit, title=title, bread=bread, info=info, properties2=prop2, friends=friends)
def find_inverse_images_of_twists(k, N=1, chi=0, fi=0, prec=10, verbose=0): r""" Checks if f is minimal and if not, returns the associated minimal form to precision prec. INPUT: - ''k'' -- positive integer : the weight - ''N'' -- positive integer (default 1) : level - ''chi'' -- non-neg. integer (default 0) use character nr. chi - ''fi'' -- non-neg. integer (default 0) We want to use the element nr. fi f=Newforms(N,k)[fi] - ''prec'' -- integer (the number of coefficients to get) - ''verbose'' -- integer OUTPUT: -''[t,l]'' -- tuple of a Bool t and a list l. The list l contains all tuples of forms which twists to the given form. The actual minimal one is the first element of this list. EXAMPLES:: """ (t, f) = _get_newform(k, N, chi, fi) if (not t): return f if (is_squarefree(ZZ(N))): return [True, f] # We need to check all square factors of N logger.info("investigating: %s" % f) N_sqfree = squarefree_part(ZZ(N)) Nsq = ZZ(N / N_sqfree) twist_candidates = list() KF = f.base_ring() # check how many Hecke eigenvalues we need to check max_nump = number_of_hecke_to_check(f) maxp = max(primes_first_n(max_nump)) for d in divisors(N): # we look at all d such that d^2 divdes N if (not ZZ(d**2).divides(ZZ(N))): continue D = DirichletGroup(d) # check possible candidates to twist into f # g in S_k(M,chi) wit M=N/d^2 M = ZZ(N / d**2) logger.info("Checking level %s" % M) for xig in range(euler_phi(M)): (t, glist) = _get_newform(k, M, xig) if (not t): return glist for g in glist: logger.debug("Comparing to function %s" % g) KG = g.base_ring() # we now see if twisting of g by xi in D gives us f for xi in D: try: for p in primes_first_n(max_nump): if (ZZ(p).divides(ZZ(N))): continue bf = f.q_expansion(maxp + 1)[p] bg = g.q_expansion(maxp + 1)[p] if (bf == 0 and bg == 0): continue elif (bf == 0 and bg <> 0 or bg == 0 and bf <> 0): raise StopIteration() if (ZZ(p).divides(xi.conductor())): raise ArithmeticError, "" xip = xi(p) # make a preliminary check that the base rings match with respect to being real or not try: QQ(xip) XF = QQ if (KF <> QQ or KG <> QQ): raise StopIteration except TypeError: # we have a non-rational (i.e. complex) value of the character XF = xip.parent() if ((KF == QQ or KF.is_totally_real()) and (KG == QQ or KG.is_totally_real())): raise StopIteration ## it is diffcult to compare elements from diferent rings in general but we make some checcks ## is it possible to see if there is a larger ring which everything can be coerced into? ok = False try: a = KF(bg / xip) b = KF(bf) ok = True if (a <> b): raise StopIteration() except TypeError: pass try: a = KG(bg) b = KG(xip * bf) ok = True if (a <> b): raise StopIteration() except TypeError: pass if ( not ok ): # we could coerce and the coefficients were equal return "Could not compare against possible candidates!" # otherwise if we are here we are ok and found a candidate twist_candidates.append([dd, g.q_expansion(prec), xi]) except StopIteration: # they are not equal pass #logger.debug("Candidates=%s" % twist_candidates) if (len(twist_candidates) == 0): return (True, None) else: return (False, twist_candidates)
def get_satake_parameters(k, N=1, chi=0, fi=0, prec=10, bits=53, angles=False): r""" Compute the Satake parameters and return an html-table. INPUT: - ''k'' -- positive integer : the weight - ''N'' -- positive integer (default 1) : level - ''chi'' -- non-neg. integer (default 0) use character nr. chi - ''fi'' -- non-neg. integer (default 0) We want to use the element nr. fi f=Newforms(N,k)[fi] -''prec'' -- compute parameters for p <=prec -''bits'' -- do real embedings intoi field of bits precision -''angles''-- return the angles t_p instead of the alpha_p here alpha_p=p^((k-1)/2)exp(i*t_p) """ (t, f) = _get_newform(k, N, chi, fi) if (not t): return f K = f.base_ring() RF = RealField(bits) CF = ComplexField(bits) if (K <> QQ): M = len(K.complex_embeddings()) ems = dict() for j in range(M): ems[j] = list() ps = prime_range(prec) alphas = list() for p in ps: ap = f.coefficients(ZZ(prec))[p] if (K == QQ): f1 = QQ(4 * p**(k - 1) - ap**2) alpha_p = (QQ(ap) + I * f1.sqrt()) / QQ(2) #beta_p=(QQ(ap)-I*f1.sqrt())/QQ(2) #satake[p]=(alpha_p,beta_p) ab = RF(p**((k - 1) / 2)) norm_alpha = alpha_p / ab t_p = CF(norm_alpha).argument() if (angles): alphas.append(t_p) else: alphas.append(alpha_p) else: for j in range(M): app = ap.complex_embeddings(bits)[j] f1 = (4 * p**(k - 1) - app**2) alpha_p = (app + I * f1.sqrt()) / RealField(bits)(2) ab = RF(p**((k - 1) / 2)) norm_alpha = alpha_p / ab t_p = CF(norm_alpha).argument() if (angles): ems[j].append(t_p) else: ems[j].append(alpha_p) tbl = dict() tbl['headersh'] = ps if (K == QQ): tbl['headersv'] = [""] tbl['data'] = [alphas] tbl['corner_label'] = "$p$" else: tbl['data'] = list() tbl['headersv'] = list() tbl['corner_label'] = "Embedding \ $p$" for j in ems.keys(): tbl['headersv'].append(j) tbl['data'].append(ems[j]) #logger.debug(tbl) s = html_table(tbl) return s
def get_values_at_CM_points(k, N=1, chi=0, fi=0, digits=12, verbose=0): r""" Computes and returns a list of values of f at a collection of CM points as complex floating point numbers. INPUT: - ''k'' -- positive integer : the weight - ''N'' -- positive integer (default 1) : level - ''chi'' -- non-neg. integer (default 0) use character nr. chi - ''fi'' -- non-neg. integer (default 0) We want to use the element nr. fi f=Newforms(N,k)[fi] -''digits'' -- we want this number of corrrect digits in the value OUTPUT: -''s'' string representation of a dictionary {I:f(I):rho:f(rho)}. TODO: Get explicit, algebraic values if possible! """ (t, f) = _get_newform(k, N, chi, fi) if (not t): return f bits = max(53, ceil(digits * 4)) CF = ComplexField(bits) RF = ComplexField(bits) eps = RF(10**-(digits + 1)) logger.debug("eps=" % eps) K = f.base_ring() cm_vals = dict() # the points we want are i and rho. More can be added later... rho = CyclotomicField(3).gen() zi = CyclotomicField(4).gen() points = [rho, zi] maxprec = 1000 # max size of q-expansion minprec = 10 # max size of q-expansion for tau in points: q = CF(exp(2 * pi * I * tau)) fexp = dict() if (K == QQ): v1 = CF(0) v2 = CF(1) try: for prec in range(minprec, maxprec, 10): logger.debug("prec=%s" % prec) v2 = f.q_expansion(prec)(q) err = abs(v2 - v1) logger.debug("err=%s" % err) if (err < eps): raise StopIteration() v1 = v2 cm_vals[tau] = "" except StopIteration: cm_vals[tau] = str(fq) else: v1 = dict() v2 = dict() err = dict() cm_vals[tau] = dict() for h in range(K.degree()): v1[h] = 1 v2[h] = 0 try: for prec in range(minprec, maxprec, 10): logger.debug("prec=%s" % prec) for h in range(K.degree()): fexp[h] = list() v2[h] = 0 for n in range(prec): c = f.coefficients(ZZ(prec))[n] cc = c.complex_embeddings(CF.prec())[h] v2[h] = v2[h] + cc * q**n err[h] = abs(v2[h] - v1[h]) logger.debug("v1[%s]=%s" % (h, v1[h])) logger.debug("v2[%s]=%s" % (h, v2[h])) logger.debug("err[%s]=%s" % (h, err[h])) if (max(err.values()) < eps): raise StopIteration() v1[h] = v2[h] except StopIteration: pass for h in range(K.degree()): if (err[h] < eps): cm_vals[tau][h] = v2[h] else: cm_vals[tau][h] = "" logger.debug("vals=%s" % cm_vals) logger.debug("errs=%s" % err) tbl = dict() tbl['corner_label'] = ['$\tau$'] tbl['headersh'] = ['$\\rho=\zeta_{3}$', '$i$'] if (K == QQ): tbl['headersv'] = ['$f(\\tau)$'] tbl['data'] = [cm_vals] else: tbl['data'] = list() tbl['headersv'] = list() for h in range(K.degree()): tbl['headersv'].append("$\sigma_{%s}(f(\\tau))$" % h) row = list() for tau in points: row.append(cm_vals[tau][h]) tbl['data'].append(row) s = html_table(tbl) #s=html.table([cm_vals.keys(),cm_vals.values()]) return s
def test_all(self): todo = [] from lmfdb import db maxNk2 = db.mf_newforms.max('Nk2') for Nk2 in range(1, maxNk2 + 1): for N in ZZ(Nk2).divisors(): k = sqrt(Nk2 / N) if k in ZZ: todo.append((N, int(k))) formerrors = list(self.all_newforms(todo)) spaceserrors = list(self.all_newspaces(todo)) errors = [] res = [] for k, io in enumerate([formerrors, spaceserrors]): for i, o in io: if not isinstance(o, list): if k == 0: command = "all_newforms" else: command = "all_newspaces" errors.append([command, i]) else: res.extend(o) if not errors: print("No errors while running the tests!") else: print("Unexpected errors occurring while running:") for e in errors: print(e) broken_urls = [u for l, u in res if u is None] working_urls = [(l, u) for l, u in res if u is not None] working_urls.sort(key=lambda elt: elt[0]) just_times = [l for l, u in working_urls] total = len(working_urls) if not broken_urls: print("All the pages passed the tests") if total > 0: print("Average loading time: %.2f" % (sum(just_times) / total, )) print("Min: %.2f Max %.2f" % (just_times[0], just_times[-1])) print("Quartiles: %.2f %.2f %.2f" % tuple([ just_times[max(0, int(total * f) - 1)] for f in [0.25, 0.5, 0.75] ])) print("Slowest pages:") for t, u in working_urls[-10:]: print("%.2f - %s" % (t, u)) if total > 2: print("Histogram") h = 0.5 nbins = (just_times[-1] - just_times[0]) / h while nbins < 50: h *= 0.5 nbins = (just_times[-1] - just_times[0]) / h nbins = ceil(nbins) bins = [0] * nbins i = 0 for elt in just_times: while elt > (i + 1) * h + just_times[0]: i += 1 bins[i] += 1 for i, b in enumerate(bins): d = 100 * float(b) / total print('%.2f\t|' % ((i + 0.5) * h + just_times[0]) + '-' * (int(d) - 1) + '| - %.2f%%' % d) else: print("These pages didn't pass the tests:") for u in broken_urls: print(u)
def __init__(self, data): # Need to set mf_dim, eis_dim, cusp_dim, new_dim, old_dim self.__dict__.update(data) if self.level == 1 or ZZ(self.level).is_prime(): self.factored_level = '' else: self.factored_level = ' = ' + ZZ(self.level).factor()._latex_() self.has_projective_image_types = all(typ + '_dim' in data for typ in ('dihedral', 'a4', 's4', 'a5')) # The following can be removed once we change the behavior of lucky to include Nones self.num_forms = data.get('num_forms') self.trace_bound = data.get('trace_bound') self.has_trace_form = (data.get('traces') is not None) self.char_conrey = self.conrey_indexes[0] self.char_conrey_str = '\chi_{%s}(%s,\cdot)' % (self.level, self.char_conrey) self.newforms = list( db.mf_newforms.search({'space_label': self.label}, projection=2)) oldspaces = db.mf_subspaces.search( { 'label': self.label, 'sub_level': { '$ne': self.level } }, [ 'sub_level', 'sub_char_orbit_index', 'sub_conrey_indexes', 'sub_mult' ]) self.oldspaces = [(old['sub_level'], old['sub_char_orbit_index'], old['sub_conrey_indexes'][0], old['sub_mult']) for old in oldspaces] self.dim_grid = DimGrid.from_db(data) self.plot = db.mf_newspace_portraits.lookup(self.label, projection="portrait") # Properties self.properties = [('Label', self.label)] if self.plot is not None and self.dim > 0: self.properties += [ (None, '<img src="{0}" width="200" height="200"/>'.format(self.plot)) ] self.properties += [ ('Level', str(self.level)), ('Weight', str(self.weight)), ('Character orbit', self.char_orbit_label), ('Rep. character', r'\(%s\)' % self.char_conrey_str), ('Character field', r'\(\Q%s\)' % ('' if self.char_degree == 1 else r'(\zeta_{%s})' % self.char_order)), ('Dimension', str(self.dim)), ] if self.num_forms is not None: self.properties.append(('Newforms', str(self.num_forms))) self.properties.append(('Sturm bound', str(self.sturm_bound))) if data.get('trace_bound') is not None: self.properties.append(('Trace bound', str(self.trace_bound))) # Work around search results not including None if data.get('num_forms') is None: self.num_forms = None # Breadcrumbs self.bread = get_bread(level=self.level, weight=self.weight, char_orbit_label=self.char_orbit_label) # Downloads self.downloads = [ ('Trace form to text', url_for('cmf.download_traces', label=self.label)), ('All stored data to text', url_for('.download_newspace', label=self.label)), ] if self.conrey_indexes[0] == 1: self.trivial_character = True character_str = "Trivial Character" if self.dim == 0: self.dim_str = r"\(%s\)" % (self.dim) else: self.minus_dim = self.dim - self.plus_dim self.dim_str = r"\(%s + %s\)" % (self.plus_dim, self.minus_dim) else: self.trivial_character = False character_str = r"Character {level}.{orbit_label}".format( level=self.level, orbit_label=self.char_orbit_label) # character_str = r"Character \(\chi_{{{level}}}({conrey}, \cdot)\)".format(level=self.level, conrey=self.conrey_indexes[0]) self.dim_str = r"\(%s\)" % (self.dim) self.title = r"Space of Modular Forms of Level %s, Weight %s, and %s" % ( self.level, self.weight, character_str) gamma1_link = '/ModularForm/GL2/Q/holomorphic/%d/%d' % (self.level, self.weight) self.friends = [('Newspace %d.%d' % (self.level, self.weight), gamma1_link)]
def make_cond_key(D): D1 = ZZ(D) if D1 < 1: D1 = ZZ(1) D1 = int(D1.log(10)) return '%04d%s' % (D1, str(D))
def __init__(self, level, weight): data = db.mf_gamma1.lucky({'level': level, 'weight': weight}) if data is None: raise ValueError("Space not in database") self.__dict__.update(data) self.weight_parity = -1 if (self.weight % 2) == 1 else 1 if level == 1 or ZZ(level).is_prime(): self.factored_level = '' else: self.factored_level = ' = ' + ZZ(level).factor()._latex_() self.has_projective_image_types = all(typ + '_dim' in data for typ in ('dihedral', 'a4', 's4', 'a5')) # The following can be removed once we change the behavior of lucky to include Nones self.num_forms = data.get('num_forms') self.num_spaces = data.get('num_spaces') self.trace_bound = data.get('trace_bound') self.has_trace_form = (data.get('traces') is not None) # by default we sort on char_orbit_index newspaces = list( db.mf_newspaces.search({ 'level': level, 'weight': weight, 'char_parity': self.weight_parity })) oldspaces = db.mf_gamma1_subspaces.search( { 'level': level, 'sub_level': { '$ne': level }, 'weight': weight }, ['sub_level', 'sub_mult']) self.oldspaces = [(old['sub_level'], old['sub_mult']) for old in oldspaces] self.dim_grid = sum( DimGrid.from_db(space) for space in newspaces) if newspaces else DimGrid() #self.mf_dim = sum(space['mf_dim'] for space in newspaces) #self.eis_dim = sum(space['eis_dim'] for space in newspaces) #self.eis_new_dim = sum(space['eis_new_dim'] for space in newspaces) #self.eis_old_dim = self.eis_dim - self.eis_new_dim #self.cusp_dim = sum(space['cusp_dim'] for space in newspaces) self.new_dim = sum(space['dim'] for space in newspaces) self.old_dim = sum( (space['cusp_dim'] - space['dim']) for space in newspaces) self.decomp = [] newforms = list( db.mf_newforms.search({ 'level': level, 'weight': weight }, [ 'label', 'space_label', 'dim', 'level', 'char_orbit_label', 'hecke_orbit', 'char_degree' ])) self.has_uncomputed_char = False for space in newspaces: if space.get('num_forms') is None: self.decomp.append((space, None)) self.has_uncomputed_char = True else: self.decomp.append((space, [ form for form in newforms if form['space_label'] == space['label'] ])) self.plot = db.mf_gamma1_portraits.lookup(self.label, projection="portrait") self.properties = [ ('Label', self.label), ] if self.plot is not None and self.new_dim > 0: self.properties += [ (None, '<a href="{0}"><img src="{0}" width="200" height="200"/></a>'. format(self.plot)) ] self.properties += [('Level', str(self.level)), ('Weight', str(self.weight)), ('Dimension', str(self.new_dim))] if self.num_spaces is not None: self.properties.append(('Nonzero newspaces', str(self.num_spaces))) if self.num_forms is not None: self.properties.append(('Newforms', str(self.num_forms))) self.properties.append(('Sturm bound', str(self.sturm_bound))) if self.trace_bound is not None: self.properties.append(('Trace bound', str(self.trace_bound))) self.bread = get_bread(level=self.level, weight=self.weight) # Downloads self.downloads = [('Trace form to text', url_for('cmf.download_traces', label=self.label)), ('All stored data to text', url_for('cmf.download_full_space', label=self.label))] self.title = r"Space of Modular Forms of Level %s and Weight %s" % ( self.level, self.weight) self.friends = []
def make_object(self, curve, endo, 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'] = 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['disc_key'][3:] ) # use disc_key rather than abs_disc (will work when abs_disc > 2^63) data['disc'] = curve['disc_sign'] * curve['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" data['locally_solvable'] = "yes" if curve[ 'locally_solvable'] else "no" 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'] else: # invariants specific to isogeny class curves_data = g2c_db_curves().find({ "class": curve['class'] }, { '_id': int(0), 'label': int(1), 'eqn': int(1), 'disc_key': int(1) }).sort([("disc_key", ASCENDING), ("label", ASCENDING)]) 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 = g2c_db_lfunction_by_hash(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'] = end_alg_name( curve['real_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'])) plot_link = '<img src="%s" width="200" height="150"/>' % 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']), ('\(\\overline{\\Q}\)-simple', bool_pretty(data['is_simple_geom'])), ('\(\mathrm{GL}_2\)-type', bool_pretty(data['is_gl2_type'])), ] # Friends self.friends = friends = [('L-function', data['lfunc_url'])] 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]))) for friend in g2c_db_lfunction_instances().find( {'Lhash': data['Lhash']}, { '_id': False, 'url': True }): if 'url' in friend: add_friend(friends, lfunction_friend_from_url(friend['url'])) if 'urls' in friend: for url in friends['urls']: add_friend(friends, lfunction_friend_from_url(friend['url'])) if 'split_labels' in data: for friend_label in data['split_labels']: if is_curve: add_friend(friends, ("Elliptic curve " + friend_label, url_for_ec(friend_label))) else: add_friend( friends, ("EC isogeny class " + ec_label_class(friend_label), url_for_ec_class(friend_label))) 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])))) # 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)));' } 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; HasPointsLocallyEverywhere(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 field_of_constant_degree_of_polynomial(G, return_field=False): r""" Return the degree of the field of constants of a polynomial. INPUT: - ``G`` -- an irreducible monic polynomial over a rational function field - ``return_field`` -- a boolean (default:`False`) OUTPUT: the degree of the field of constants of the function field defined by ``G``. If ``return_field`` is ``True`` then the actual field of constants is returned. This is currently implemented for finite fields only. This is a helper function for ``SmoothProjectiveCurve.field_of_constants_degree``. """ from sage.rings.function_field.function_field import RationalFunctionField from sage.rings.function_field.constructor import FunctionField from sage.rings.number_field.number_field import NumberFields from sage.arith.misc import primes from mclf.semistable_reduction.reduction_trees import make_function_field F = G.base_ring() assert isinstance(F, RationalFunctionField) K = F.constant_base_field() R = F._ring # the polynomial ring underlying F n = G.degree() if K.is_finite(): d = 1 # will be the degree of the field of constants at the end for p in primes(2,n+1): while p.divides(n): try: K1 = K.extension(p) except: # if K is not a true finite field the above fails # we use a helper function which construct an extension # of the desired degree K1 = extension_of_finite_field(K, p) F1 = FunctionField(K1, F.variable_name()) G1 = G.change_ring(F1) G2 = G1.factor()[0][0] # the first irreducible factor of G1 if G2.degree() < n: # G becomes reducible over K1 d = d*p # we replace G by G2 and adapt K = K1 # the values of n, d, K, G G = G2 n = G.degree() else: # G is irreducible over K1 break # we try the next prime if return_field: return K else: return d elif K in NumberFields(): from sage.rings.integer_ring import ZZ from sage.rings.all import GaussValuation if return_field: raise NotImplementedError('Computation of field of constants for number fields is not yet implemented.') d = n count = 0 for p in K.primes_of_bounded_norm_iter(ZZ(1000)): vp = K.valuation(p) v0 = F.valuation(GaussValuation(R, vp)) v = GaussValuation(G.parent(), v0) if v(G) == 0: Gb = v.reduce(G) Fb, _, _ = make_function_field(Gb.base_ring()) Gb = Gb.change_ring(Fb) if Gb.is_irreducible(): dp = field_of_constant_degree_of_polynomial(Gb) d = d.gcd(dp) count += 1 if d == 1 or count > 10: break return d else: raise NotImplementedError('Constant base field has to be finite or a number field.')
def render_by_label(label): """ render html page for Sato-Tate group sepecified by label """ if re.match(MU_LABEL_RE, label): n = ZZ(label.split('.')[2]) if n > 10**20: flash_error( "number of components %s is too large, it should be less than 10^{20}$.", n) return redirect(url_for(".index")) return render_st_group(mu_info(n), portrait=mu_portrait(n)) if re.match(NU1_MU_LABEL_RE, label): w = ZZ(label.split('.')[0]) n = ZZ(label.split('.')[3][1:]) if 2 * n > 10**20: flash_error( "number of components %s is too large, it should be less than 10^{20}$.", 2 * n) return redirect(url_for(".index")) return render_st_group(nu1_mu_info(w, n), portrait=nu1_mu_portrait(n)) if re.match(SU2_MU_LABEL_RE, label): w = ZZ(label.split('.')[0]) n = ZZ(label.split('.')[3][1:]) if n > 10**20: flash_error( "number of components %s is too large, it should be less than 10^{20}$.", n) return redirect(url_for(".index")) return render_st_group(su2_mu_info(w, n), portrait=su2_mu_portrait(n)) data = db.gps_sato_tate.lookup(label) info = {} if data is None: flash_error( "%s is not the label of a Sato-Tate group currently in the database.", label) return redirect(url_for(".index")) for attr in [ 'label', 'weight', 'degree', 'name', 'pretty', 'real_dimension', 'components' ]: info[attr] = data[attr] info['ambient'] = st_ambient(info['weight'], info['degree']) info['connected'] = boolean_name(info['components'] == 1) info['rational'] = boolean_name(info.get('rational', True)) st0 = db.gps_sato_tate0.lucky({'name': data['identity_component']}) if not st0: flash_error( "%s is not the label of a Sato-Tate identity component currently in the database.", data['identity_component']) return redirect(url_for(".index")) info['st0_name'] = st0['name'] info['identity_component'] = st0['pretty'] info['st0_description'] = st0['description'] G = db.gps_small.lookup(data['component_group']) if not G: flash_error( "%s is not the label of a Sato-Tate component group currently in the database.", data['component_group']) return redirect(url_for(".index")) info['component_group'] = G['pretty'] info['cyclic'] = boolean_name(G['cyclic']) info['abelian'] = boolean_name(G['abelian']) info['solvable'] = boolean_name(G['solvable']) info['gens'] = comma_separated_list( [string_matrix(m) for m in data['gens']]) info['numgens'] = len(info['gens']) info['subgroups'] = comma_separated_list( [st_link(sub) for sub in data['subgroups']]) info['supgroups'] = comma_separated_list( [st_link(sup) for sup in data['supgroups']]) if data['moments']: info['moments'] = [['x'] + [ '\\mathrm{E}[x^{%d}]' % m for m in range(len(data['moments'][0]) - 1) ]] info['moments'] += data['moments'] if data['counts']: c = data['counts'] info['probabilities'] = [[ '\\mathrm{P}[%s=%d]=\\frac{%d}{%d}' % (c[i][0], c[i][1][j][0], c[i][1][j][1], data['components']) for j in range(len(c[i][1])) ] for i in range(len(c))] return render_st_group(info, portrait=data.get('trace_histogram'))
def genus(self): r""" Return the genus of the curve. The genus of the curve is defined as the dimension of the cohomology group `H^1(X,\mathcal{O}_X)`, as a vector space *over the field of constants `k_c`*. The genus `g` of the curve `X` is computed using the Riemann-Hurwitz formula, applied to the cover `X\to\mathbb{P}^1` corresponding to the underlying realization of the function field of `X` as a finite separable extension of a rational function field. See: - Hartshorne, *Algebraic Geometry*, Corollary IV.2.4 If the constant base field is finite, we compute the degree of the 'ramification divisor'. If it is not, we assume that the characteristic is zero, and we use the 'tame' Riemann Hurwitz Formula. EXAMPLES:: sage: from mclf import * sage: F0.<x> = FunctionField(GF(2)) sage: R.<T> = F0[] sage: G = T^2 + T + x^3 + x + 1 sage: F.<y> = F0.extension(G) sage: Y = SmoothProjectiveCurve(F) sage: Y.genus() 1 sage: G = T^2 + x^3 + x + 1 sage: F.<y> = F0.extension(G) sage: Y = SmoothProjectiveCurve(F) sage: Y.genus() 0 """ if hasattr(self,"_genus"): return self._genus if not self._is_separable: self._genus = self.separable_model().genus() return self._genus FY = self.function_field() FX = self.rational_function_field() if FX is FY: return 0 n = self.covering_degree()/self.field_of_constants_degree() if self.constant_base_field().is_finite(): r = self.degree(self.ramification_divisor()) # if k is finite, we can't use the 'tame' RHF else: # if k has characteristic zero, we use the RHF G = FY.polynomial() V = [(FX.valuation(1/FX.gen()), 1)] D = G.discriminant() for f, m in D.factor(): V.append((FX.valuation(f), f.numerator().degree())) r = 0 for v, d in V: v = v/v(v.uniformizer()) W = v.extensions(FY) for w in W: e, f = e_f_of_valuation(w) r += d*f*(e-1) g = ZZ(-n + r/2 + 1) self._genus = g return self._genus
def _call_(self, x): return ZZ(x.get_str())
def ideal_from_label(K, lab): r"""Returns the ideal with label lab in the quadratic field K. """ N, c, d = [ZZ(c) for c in lab.split(".")] a = N // d return K.ideal(a, K([c, d]))
def make_E(self): K = self.field.K() # a-invariants self.ainvs = parse_ainvs(K, self.ainvs) self.latex_ainvs = web_latex(self.ainvs) self.numb = str(self.number) # Conductor, discriminant, j-invariant N = ideal_from_string(K, self.conductor_ideal) self.cond = web_latex(N) self.cond_norm = web_latex(self.conductor_norm) local_data = self.local_data # NB badprimes is a list of primes which divide the # discriminant of this model. At most one of these might # actually be a prime of good reduction, if the curve has no # global minimal model. badprimes = [ideal_from_string(K, ld['p']) for ld in local_data] badnorms = [ZZ(ld['normp']) for ld in local_data] mindisc_ords = [ld['ord_disc'] for ld in local_data] # Assumption: the curve models stored in the database are # either global minimal models or minimal at all but one # prime, so the list here has length 0 or 1: self.non_min_primes = [ideal_from_string(K, P) for P in self.non_min_p] self.is_minimal = (len(self.non_min_primes) == 0) self.has_minimal_model = self.is_minimal disc_ords = [ld['ord_disc'] for ld in local_data] if not self.is_minimal: Pmin = self.non_min_primes[0] P_index = badprimes.index(Pmin) self.non_min_prime = web_latex(Pmin) disc_ords[P_index] += 12 if self.conductor_norm == 1: # since the factorization of (1) displays as "1" self.fact_cond = self.cond self.fact_cond_norm = self.cond else: Nfac = Factorization([(P, ld['ord_cond']) for P, ld in zip(badprimes, local_data)]) self.fact_cond = web_latex_ideal_fact(Nfac) Nnormfac = Factorization([(q, ld['ord_cond']) for q, ld in zip(badnorms, local_data)]) self.fact_cond_norm = web_latex(Nnormfac) # D is the discriminant ideal of the model D = prod([P**e for P, e in zip(badprimes, disc_ords)], K.ideal(1)) self.disc = web_latex(D) Dnorm = D.norm() self.disc_norm = web_latex(Dnorm) if Dnorm == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc self.fact_disc_norm = self.disc else: Dfac = Factorization([(P, e) for P, e in zip(badprimes, disc_ords)]) self.fact_disc = web_latex_ideal_fact(Dfac) Dnormfac = Factorization([(q, e) for q, e in zip(badnorms, disc_ords)]) self.fact_disc_norm = web_latex(Dnormfac) if not self.is_minimal: Dmin = ideal_from_string(K, self.minD) self.mindisc = web_latex(Dmin) Dmin_norm = Dmin.norm() self.mindisc_norm = web_latex(Dmin_norm) if Dmin_norm == 1: # since the factorization of (1) displays as "1" self.fact_mindisc = self.mindisc self.fact_mindisc_norm = self.mindisc else: Dminfac = Factorization([ (P, e) for P, edd in zip(badprimes, mindisc_ords) ]) self.fact_mindisc = web_latex_ideal_fact(Dminfac) Dminnormfac = Factorization([ (q, e) for q, e in zip(badnorms, mindisc_ords) ]) self.fact_mindisc_norm = web_latex(Dminnormfac) j = self.field.parse_NFelt(self.jinv) # if j: # d = j.denominator() # n = d * j # numerator exists for quadratic fields only! # g = GCD(list(n)) # n1 = n / g # self.j = web_latex(n1) # if d != 1: # if n1 > 1: # # self.j = "("+self.j+")\(/\)"+web_latex(d) # self.j = web_latex(r"\frac{%s}{%s}" % (self.j, d)) # else: # self.j = web_latex(d) # if g > 1: # if n1 > 1: # self.j = web_latex(g) + self.j # else: # self.j = web_latex(g) self.j = web_latex(j) self.fact_j = None # See issue 1258: some j factorizations work but take too long # (e.g. EllipticCurve/6.6.371293.1/1.1/a/1). Note that we do # store the factorization of the denominator of j and display # that, which is the most interesting part. # Images of Galois representations if not hasattr(self, 'galois_images'): #print "No Galois image data" self.galois_images = "?" self.non_surjective_primes = "?" self.galois_data = [] else: self.galois_data = [{ 'p': p, 'image': im } for p, im in zip(self.non_surjective_primes, self.galois_images)] # CM and End(E) self.cm_bool = "no" self.End = "\(\Z\)" if self.cm: self.rational_cm = K(self.cm).is_square() self.cm_sqf = ZZ(self.cm).squarefree_part() self.cm_bool = "yes (\(%s\))" % self.cm if self.cm % 4 == 0: d4 = ZZ(self.cm) // 4 self.End = "\(\Z[\sqrt{%s}]\)" % (d4) else: self.End = "\(\Z[(1+\sqrt{%s})/2]\)" % self.cm # Galois images in CM case: if self.cm and self.galois_images != '?': self.cm_ramp = [ p for p in ZZ(self.cm).support() if not p in self.non_surjective_primes ] self.cm_nramp = len(self.cm_ramp) if self.cm_nramp == 1: self.cm_ramp = self.cm_ramp[0] else: self.cm_ramp = ", ".join([str(p) for p in self.cm_ramp]) # Sato-Tate: # The lines below will need to change once we have curves over non-quadratic fields # that contain the Hilbert class field of an imaginary quadratic field if self.cm: if self.signature == [0, 1] and ZZ( -self.abs_disc * self.cm).is_square(): self.ST = st_link_by_name(1, 2, 'U(1)') else: self.ST = st_link_by_name(1, 2, 'N(U(1))') else: self.ST = st_link_by_name(1, 2, 'SU(2)') # Q-curve / Base change self.qc = "no" try: if self.q_curve: self.qc = "yes" except AttributeError: # in case the db entry does not have this field set pass # Torsion self.ntors = web_latex(self.torsion_order) self.tr = len(self.torsion_structure) if self.tr == 0: self.tor_struct_pretty = "Trivial" if self.tr == 1: self.tor_struct_pretty = "\(\Z/%s\Z\)" % self.torsion_structure[0] if self.tr == 2: self.tor_struct_pretty = r"\(\Z/%s\Z\times\Z/%s\Z\)" % tuple( self.torsion_structure) torsion_gens = [parse_point(K, P) for P in self.torsion_gens] self.torsion_gens = ",".join([web_point(P) for P in torsion_gens]) # Rank or bounds try: self.rk = web_latex(self.rank) except AttributeError: self.rk = "?" try: self.rk_bnds = "%s...%s" % tuple(self.rank_bounds) except AttributeError: self.rank_bounds = [0, Infinity] self.rk_bnds = "not available" # Generators try: gens = [parse_point(K, P) for P in self.gens] self.gens = ", ".join([web_point(P) for P in gens]) if self.rk == "?": self.reg = "not available" else: if gens: try: self.reg = self.reg except AttributeError: self.reg = "not available" pass # self.reg already set else: self.reg = 1 # otherwise we only get 1.00000... except AttributeError: self.gens = "not available" self.reg = "not available" try: if self.rank == 0: self.reg = 1 except AttributeError: pass # Local data for P, ld in zip(badprimes, local_data): ld['p'] = web_latex(P) ld['norm'] = P.norm() ld['kod'] = web_latex(ld['kod']).replace('$', '') # URLs of self and related objects: self.urls = {} # It's useful to be able to use this class out of context, when calling url_for will fail: try: self.urls['curve'] = url_for(".show_ecnf", nf=self.field_label, conductor_label=quote( self.conductor_label), class_label=self.iso_label, number=self.number) except RuntimeError: return self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=quote( self.conductor_label), class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=quote( self.conductor_label)) self.urls['field'] = url_for(".show_ecnf1", nf=self.field_label) # Isogeny information if self.number == 1: isogmat = self.isogeny_matrix else: isogmat = db_ecnf().find_one({ 'class_label': self.class_label, 'number': 1 })['isogeny_matrix'] self.class_deg = max([max(d) for d in isogmat]) self.one_deg = ZZ(self.class_deg).is_prime() self.ncurves = db_ecnf().count({'class_label': self.class_label}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: self.isogeny_degrees = " and ".join(isodegs) else: self.isogeny_degrees = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) sig = self.signature totally_real = sig[1] == 0 imag_quadratic = sig == [0, 1] if totally_real: self.hmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.urls['hmf'] = url_for('hmf.render_hmf_webpage', field_label=self.field.label, label=self.hmf_label) self.urls['Lfunction'] = url_for("l_functions.l_function_hmf_page", field=self.field_label, label=self.hmf_label, character='0', number='0') if imag_quadratic: self.bmf_label = "-".join( [self.field.label, self.conductor_label, self.iso_label]) self.friends = [] self.friends += [('Isogeny class ' + self.short_class_label, self.urls['class'])] self.friends += [('Twists', url_for('ecnf.index', field=self.field_label, jinv=rename_j(j)))] if totally_real: self.friends += [('Hilbert Modular Form ' + self.hmf_label, self.urls['hmf'])] self.friends += [('L-function', self.urls['Lfunction'])] if imag_quadratic: self.friends += [ ('Bianchi Modular Form %s not available' % self.bmf_label, '') ] self.properties = [('Base field', self.field.field_pretty()), ('Label', self.label)] # Plot if K.signature()[0]: self.plot = encode_plot( EC_nf_plot(K, self.ainvs, self.field.generator_name())) self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot self.properties += [(None, self.plot_link)] self.properties += [ ('Conductor', self.cond), ('Conductor norm', self.cond_norm), # See issue #796 for why this is hidden (can be very large) # ('j-invariant', self.j), ('CM', self.cm_bool) ] if self.base_change: self.properties += [ ('base-change', 'yes: %s' % ','.join([str(lab) for lab in self.base_change])) ] else: self.base_change = [] # in case it was False instead of [] self.properties += [('Q-curve', self.qc)] r = self.rk if r == "?": r = self.rk_bnds self.properties += [ ('Torsion order', self.ntors), ('Rank', r), ] for E0 in self.base_change: self.friends += [('Base-change of %s /\(\Q\)' % E0, url_for("ec.by_ec_label", label=E0))] self._code = None # will be set if needed by get_code()
def make_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 disc(self): return ZZ(self._data['disc_abs']) * self._data['disc_sign']
def nf_string_to_label(F): # parse Q, Qsqrt2, Qsqrt-4, Qzeta5, etc if F == 'Q': return '1.1.1.1' if F == 'Qi' or F == 'Q(i)': return '2.0.4.1' # Change unicode dash with minus sign F = F.replace(u'\u2212', '-') # remove non-ascii characters from F F = F.decode('utf8').encode('ascii', 'ignore') if len(F) == 0: raise ValueError("Entry for the field was left blank. You need to enter a field label, field name, or a polynomial.") if F[0] == 'Q': if '(' in F and ')' in F: F=F.replace('(','').replace(')','') if F[1:5] in ['sqrt', 'root']: try: d = ZZ(str(F[5:])).squarefree_part() except (TypeError, ValueError): d = 0 if d == 0: raise ValueError("After {0}, the remainder must be a nonzero integer. Use {0}5 or {0}-11 for example.".format(F[:5])) if d == 1: return '1.1.1.1' if d % 4 in [2, 3]: D = 4 * d else: D = d absD = D.abs() s = 0 if D < 0 else 2 return '2.%s.%s.1' % (s, str(absD)) if F[1:5] == 'zeta': if '_' in F: F = F.replace('_','') try: d = ZZ(str(F[5:])) except ValueError: d = 0 if d < 1: raise ValueError("After {0}, the remainder must be a positive integer. Use {0}5 for example.".format(F[:5])) if d % 4 == 2: d /= 2 # Q(zeta_6)=Q(zeta_3), etc) if d == 1: return '1.1.1.1' deg = euler_phi(d) if deg > 23: raise ValueError('%s is not in the database.' % F) adisc = CyclotomicField(d).discriminant().abs() # uses formula! return '%s.0.%s.1' % (deg, adisc) raise ValueError('It is not a valid field name or label, or a defining polynomial.') # check if a polynomial was entered F = F.replace('X', 'x') if 'x' in F: F1 = F.replace('^', '**') # print F from lmfdb.number_fields.number_field import poly_to_field_label F1 = poly_to_field_label(F1) if F1: return F1 raise ValueError('%s does not define a number field in the database.'%F) # Expand out factored labels, like 11.11.11e20.1 if not re.match(r'\d+\.\d+\.[0-9e_]+\.\d+',F): raise ValueError("It must be of the form d.r.D.n, such as 2.2.5.1.") parts = F.split(".") def raise_power(ab): if ab.count("e") == 0: return ZZ(ab) elif ab.count("e") == 1: a,b = ab.split("e") return ZZ(a)**ZZ(b) else: raise ValueError("Malformed absolute discriminant. It must be a sequence of strings AeB for A and B integers, joined by _s. For example, 2e7_3e5_11.") parts[2] = str(prod(raise_power(c) for c in parts[2].split("_"))) return ".".join(parts)
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) if Dnorm == 1: # since the factorization of (1) displays as "1" self.fact_disc = self.disc self.fact_disc_norm = '1' else: self.fact_disc = latex_factorization(badprimes, disc_ords) self.fact_disc_norm = latex_factorization(badnorms, disc_ords) 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 == 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) 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.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 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 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"], ["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]))) 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_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(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: 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) 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 except AttributeError: self.reg = "not available" # 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 += [(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', "")]