def upsert_embedding(id_number, skip=True): rowcc = db.mf_hecke_cc.lucky({'id': id_number}, projection=[ 'an_normalized', 'hecke_orbit_code', 'id', 'lfunction_label', 'embedding_root_imag', 'embedding_root_real' ]) if rowcc is None: return if skip: if rowcc.get("embedding_root_imag", None) is not None: if rowcc.get("embedding_root_real", None) is not None: return row_embeddings = {} hecke_orbit_code = rowcc['hecke_orbit_code'] newform = db.mf_newforms.lucky({'hecke_orbit_code': hecke_orbit_code}, ['label', 'weight', 'field_poly', 'dim']) if newform is None: # No newform in db return if newform['dim'] == 1: row_embeddings['embedding_root_imag'] = 0 row_embeddings['embedding_root_real'] = 0 elif newform['weight'] == 1: return elif newform.get('field_poly', None) is None: return else: # print rowcc['lfunction_label'] HF = NumberField(ZZx(newform['field_poly']), "v") hecke_nf = db.mf_hecke_nf.lucky( {'hecke_orbit_code': hecke_orbit_code}, [ 'hecke_ring_cyclotomic_generator', 'an', 'field_poly', 'field_poly_root_of_unity', 'hecke_ring_numerators', 'hecke_ring_denominators', 'hecke_ring_power_basis' ]) assert hecke_nf is not None assert newform['field_poly'] == hecke_nf['field_poly'] assert hecke_nf['hecke_ring_cyclotomic_generator'] == 0 if hecke_nf['hecke_ring_power_basis'] or ( hecke_nf['field_poly_root_of_unity'] != 0): v = HF.gens()[0] betas = [v**i for i in range(len(newform['field_poly']))] else: numerators = hecke_nf.get('hecke_ring_numerators') denominators = hecke_nf.get('hecke_ring_denominators') betas = [ HF(elt) / denominators[i] for i, elt in enumerate(numerators) ] embeddings = HF.complex_embeddings(prec=2000) an_nf = hecke_nf['an'] betas_embedded = [map(elt, betas) for elt in embeddings] CCC = betas_embedded[0][0].parent() normalization = -CCC(newform['weight'] - 1).real() / 2 qexp = [ convert_eigenvals_to_qexp(elt, an_nf, normalization) for elt in betas_embedded ] min_len = min(len(rowcc['an_normalized']), len(qexp[0])) an_cc = vector( CCC, map(lambda x: CCC(x[0], x[1]), rowcc['an_normalized'][:min_len])) #qexp_diff = [ (vector(CCC, elt[:min_len]) - an_cc).norm() for elt in qexp ] # normalized, to avoid the unstability coming from large weight qexp_diff = [ vector([(elt - an_cc[i]) / elt.abs() for i, elt in enumerate(q) if elt != 0]).norm() for j, q in enumerate(qexp) ] qexp_diff_sorted = sorted(qexp_diff) min_diff = qexp_diff_sorted[0] #assuring that is something close to zero, and that no other value is close to it assert min_diff < 1e-6 and min_diff / qexp_diff_sorted[ 1] < 1e-15, "id = %d label = %s\nmin_diff = %.2e \t min_diff/2nd = %.2e\nan_cc = %s\nqexp = %s" % ( id_number, rowcc['lfunction_label'], min_diff, min_diff / qexp_diff_sorted[1], vector(ComplexField(20), an_cc[:5]), matrix(ComplexField(20), [elt[:5] for elt in qexp])) for i, elt in enumerate(qexp_diff): if elt == min_diff: row_embeddings['embedding_root_real'] = float(embeddings[i]( HF.gen()).real()) row_embeddings['embedding_root_imag'] = float(embeddings[i]( HF.gen()).imag()) break assert len(row_embeddings) == 2 db.mf_hecke_cc.upsert({'id': rowcc['id']}, row_embeddings)
def add_trace_and_norm_ladic(g, D, alpha_geo, verbose=True): if verbose: print "add_trace_and_norm_ladic()" #load Fields L = D['L'] if L is QQ: QQx = PolynomialRing(QQ, "x") L = NumberField(QQx.gen(), "b") prec = D['prec'] CCap = ComplexField(prec) # load endo alpha = D['alpha'] rosati = bound_rosati(alpha_geo) if alpha.base_ring() is not L: alpha_K = copy(alpha) alpha = Matrix(L, 2, 2) #shift alpha field from K to L for i, row in enumerate(alpha_K.rows()): for j, elt in enumerate(row): if elt not in L: assert elt.base_ring().absolute_polynomial( ) == L.absolute_polynomial() alpha[i, j] = L(elt.list()) else: alpha[i, j] = L(elt) # load algx_poly algx_poly_coeff = D['algx_poly'] #sometimes, by mistake the algx_poly is defined over K where K == L, but with a different name for i, elt in enumerate(algx_poly_coeff): if elt not in L: assert elt.base_ring().absolute_polynomial( ) == L.absolute_polynomial() algx_poly_coeff[i] = L(elt.list()) else: algx_poly_coeff[i] = L(elt) x_poly = vector(CCap, D['x_poly']) for i in [0, 1]: assert almost_equal( x_poly[i], algx_poly_coeff[i]), "%s != %s" % (algx_poly_coeff[i], x_poly[i]) # load P P0 = vector(L, [D['P'][0], D['P'][1]]) for i, elt in enumerate(P0): if elt not in L: assert elt.base_ring().absolute_polynomial( ) == L.absolute_polynomial() P0[i] = L(elt.list()) else: P0[i] = L(elt) if verbose: print "P0 = %s" % (P0, ) # load image points, P1 and P2 L_poly = PolynomialRing(L, "xL") xL = L_poly.gen() Xpoly = L_poly(algx_poly_coeff) if Xpoly.is_irreducible(): M = Xpoly.root_field("c") else: # this avoids bifurcation later on in the code, we don't want to be always checking if M is L M = NumberField(xL, "c") # trying to be sure that we keep the same complex_embedding... M_complex_embedding = 0 if L.gen() not in QQ: M_complex_embedding = None Lgen_CC = toCCap(L.gen(), prec=prec) for i, _ in enumerate(M.complex_embeddings()): if norm(Lgen_CC - M(L.gen()).complex_embedding(prec=prec, i=i) ) < CCap(2)**(-0.7 * Lgen_CC.prec()) * Lgen_CC.abs(): M_complex_embedding = i assert M_complex_embedding is not None, "\nL = %s\n%s = %s\n%s" % ( L, L.gen(), Lgen_CC, M.complex_embeddings()) M_poly = PolynomialRing(M, "xM") xM = M_poly.gen() # convert everything to M P0_M = vector(M, [elt for elt in P0]) alpha_M = Matrix(M, [[elt for elt in row] for row in alpha.rows()]) Xpoly_M = M_poly(Xpoly) for i in [0, 1]: assert almost_equal(x_poly[i], Xpoly_M.list()[i], ithcomplex_embedding=M_complex_embedding ), "%s != %s" % (Xpoly_M.list()[i], x_poly[i]) P1 = vector(M, 2) P1_ap = vector(CCap, D['R'][0]) P2 = vector(M, 2) P2_ap = vector(CCap, D['R'][1]) M_Xpoly_roots = Xpoly_M.roots() assert len(M_Xpoly_roots) > 0 Ypoly_M = prod([xM**2 - g(root) for root, _ in M_Xpoly_roots]) # also \in L_poly assert sum(m for _, m in Ypoly_M.roots(M)) == Ypoly_M.degree( ), "%s != %s\n%s\n%s" % (sum(m for _, m in Ypoly_M.roots(M)), Ypoly_M.degree(), Ypoly_M, Ypoly_M.roots(M)) if len(M_Xpoly_roots) == 1: # we have a double root P1[0] = M_Xpoly_roots[0][0] P2[0] = M_Xpoly_roots[0][0] ae_prec = prec * 0.4 else: assert len(M_Xpoly_roots) == 2 ae_prec = prec # we have two distinct roots P1[0] = M_Xpoly_roots[0][0] P2[0] = M_Xpoly_roots[1][0] if not_equal(P1_ap[0], P1[0], ithcomplex_embedding=M_complex_embedding): P1[0] = M_Xpoly_roots[1][0] P2[0] = M_Xpoly_roots[0][0] assert almost_equal( P1_ap[0], P1[0], ithcomplex_embedding=M_complex_embedding, prec=ae_prec), "\n%s = %s \n != %s" % ( P1[0], toCCap( P1[0], ithcomplex_embedding=M_complex_embedding), CC(P1_ap[0])) assert almost_equal( P2_ap[0], P2[0], ithcomplex_embedding=M_complex_embedding, prec=ae_prec), "\n%s = %s \n != %s" % ( P2[0], toCCap( P2[0], ithcomplex_embedding=M_complex_embedding), CC(P2_ap[0])) # figure out the right square root # pick the default branch P1[1] = sqrt(g(P1[0])) P2[1] = sqrt(g(P2[0])) if 0 in [P1[1], P2[1]]: print "one of image points is a Weirstrass point" print P1 print P2 raise ZeroDivisionError #switch if necessary if not_equal(P1_ap[1], P1[1], ithcomplex_embedding=M_complex_embedding, prec=ae_prec): P1[1] *= -1 if not_equal(P2_ap[1], P2[1], ithcomplex_embedding=M_complex_embedding, prec=ae_prec): P2[1] *= -1 # double check for i in [0, 1]: assert almost_equal(P1_ap[i], P1[i], ithcomplex_embedding=M_complex_embedding, prec=ae_prec), "%s != %s" % (P1_ap[i], P1[i]) assert almost_equal(P2_ap[i], P2[i], ithcomplex_embedding=M_complex_embedding, prec=ae_prec), "%s != %s" % (P2_ap[i], P2[i]) # now alpha, P0 \in L # P1, P2 \in L if verbose: print "P1 = %s\nP2 = %s" % (P1, P2) print "Computing the trace and the norm ladically\n" trace_and_norm = trace_and_norm_ladic(L, M, P0_M, P1, P2, g, 2 * alpha_M, 16 * rosati, primes=ceil(prec * L.degree() / 61)) else: trace_and_norm = trace_and_norm_ladic(L, M, P0_M, P1, P2, g, 2 * alpha_M, 16 * rosati, primes=ceil(prec * L.degree() / 61)) # Convert the coefficients to polynomials trace_numerator, trace_denominator, norm_numerator, norm_denominator = [ L_poly(coeff) for coeff in trace_and_norm ] assert trace_numerator(P0[0]) == trace_denominator( P0[0]) * -algx_poly_coeff[1], "%s/%s (%s) != %s" % ( trace_numerator, trace_denominator, P0[0], -algx_poly_coeff[1]) assert norm_numerator(P0[0]) == norm_denominator( P0[0]) * algx_poly_coeff[0], "%s/%s (%s) != %s" % ( norm_numerator, norm_denominator, P0[0], algx_poly_coeff[0]) buffer = "# x1 + x2 = degree %d/ degree %d\n" % ( trace_numerator.degree(), trace_denominator.degree()) buffer += "# = (%s) / (%s) \n" % (trace_numerator, trace_denominator) buffer += "# max(%d, %d) <= %d\n\n" % ( trace_numerator.degree(), trace_denominator.degree(), 16 * rosati) buffer += "# x1 * x2 = degree %d/ degree %d\n" % ( norm_numerator.degree(), norm_denominator.degree()) buffer += "# = (%s) / (%s) \n" % (norm_numerator, norm_denominator) buffer += "# max(%d, %d) <= %d\n" % ( norm_numerator.degree(), norm_denominator.degree(), 16 * rosati) if verbose: print buffer print "\n" assert max(trace_numerator.degree(), trace_denominator.degree()) <= 16 * rosati assert max(norm_numerator.degree(), norm_denominator.degree()) <= 16 * rosati if verbose: print "Veritfying if x1*x2 and x1 + x2 are correct..." verified = verify_algebraically(g, P0, alpha, trace_and_norm, verbose=verbose) if verbose: print "\nDoes it act on the tangent space as expected? %s\n" % verified print "Done add_trace_and_norm_ladic()" return verified, [ trace_numerator.list(), trace_denominator.list(), norm_numerator.list(), norm_denominator.list() ]
def upsert_embedding(id_number, skip=False): rowcc = db.mf_hecke_cc.lucky({'id': id_number}, projection=[ 'an', 'hecke_orbit_code', 'id', 'lfunction_label', 'embedding_root_imag', 'embedding_root_real' ]) if rowcc is None: return if skip: if rowcc.get("embedding_root_imag", None) is not None: if rowcc.get("embedding_root_real", None) is not None: return row_embeddings = {} hecke_orbit_code = rowcc['hecke_orbit_code'] newform = db.mf_newforms.lucky({'hecke_orbit_code': hecke_orbit_code}) if newform is None: # No newform in db return if newform['dim'] == 1: row_embeddings['embedding_root_imag'] = 0 row_embeddings['embedding_root_real'] = 0 elif newform['weight'] == 1: return elif newform.get('field_poly', None) is None: return else: # print rowcc['lfunction_label'] HF = NumberField(ZZx(newform['field_poly']), "v") numerators = newform['hecke_ring_numerators'] denominators = newform['hecke_ring_denominators'] betas = [HF(elt) / denominators[i] for i, elt in enumerate(numerators)] embeddings = HF.complex_embeddings(prec=2000) an_nf = list( db.mf_hecke_nf.search({'hecke_orbit_code': hecke_orbit_code}, ['n', 'an'], sort=['n'])) betas_embedded = [map(elt, betas) for elt in embeddings] CCC = betas_embedded[0][0].parent() qexp = [ convert_eigenvals_to_qexp(elt, an_nf) for elt in betas_embedded ] min_len = min(len(rowcc['an']), len(qexp[0])) an_cc = vector(CCC, map(lambda x: CCC(x[0], x[1]), rowcc['an'][:min_len])) #qexp_diff = [ (vector(CCC, elt[:min_len]) - an_cc).norm() for elt in qexp ] # normalized, to avoid the unstability comming from large weight qexp_diff = [ vector([(elt - an_cc[i]) / elt.abs() for i, elt in enumerate(q) if elt != 0]).norm() for j, q in enumerate(qexp) ] qexp_diff_sorted = sorted(qexp_diff) min_diff = qexp_diff_sorted[0] #print "min_diff = %.2e \t min_diff/2nd = %.2e" % (min_diff, min_diff/qexp_diff_sorted[1]) #assuring that is something close to zero, and that no other value is close to it assert min_diff < 1e-6 assert min_diff / qexp_diff_sorted[1] < 1e-15 for i, elt in enumerate(qexp_diff): if elt == min_diff: row_embeddings['embedding_root_real'] = float(embeddings[i]( HF.gen()).real()) row_embeddings['embedding_root_imag'] = float(embeddings[i]( HF.gen()).imag()) break assert len(row_embeddings) == 2 db.mf_hecke_cc.upsert({'id': rowcc['id']}, row_embeddings)
def __classcall__(cls, embed, category=None): r""" Normalize parameters so embedded real number fields are unique:: sage: from pyeantic import eantic, RealEmbeddedNumberField sage: K = NumberField(x**2 - 2, 'a', embedding=sqrt(AA(2))) sage: K = RealEmbeddedNumberField(K) sage: L = NumberField(x**2 - 2, 'a') sage: L = RealEmbeddedNumberField(L.embeddings(AA)[1]) sage: M = eantic.renf_class.make("a^2 - 2", "a", "1.4 +/- .1") sage: M = RealEmbeddedNumberField(M) sage: K is L True sage: K is M True """ if isinstance(embed, eantic.renf_class): # Since it is quite annoying to convert an fmpz polynomial, we parse # the printed representation of the renf_class. This is of course # not very robust… import re match = re.match("^NumberField\\(([^,]+), (\\[[^\\]]+\\])\\)$", repr(embed)) assert match, "renf_class printed in an unexpected way" minpoly = match.group(1) root_str = match.group(2) match = re.match("^\\d*\\*?([^\\^ *]+)[\\^ ]", minpoly) assert match, "renf_class printed leading coefficient in an unexpected way" minpoly = QQ[match.group(1)](minpoly) roots = [] AA_roots = minpoly.roots(AA, multiplicities=False) for prec in [53, 64, 128, 256]: R = RealBallField(prec) root = R(root_str) roots = [aa for aa in AA_roots if R(aa).overlaps(root)] if len(roots) == 1: break if len(roots) != 1: raise RuntimeError( "cannot distinguish roots with limited ball field precision" ) embed = NumberField(minpoly, minpoly.variable_name(), embedding=roots[0]) if embed in NumberFields(): if not RR.has_coerce_map_from(embed): raise ValueError( "number field must be endowed with an embedding into the reals" ) if not embed.is_absolute(): raise NotImplementedError("number field must be absolute") # We recreate our NumberField from the embedding since number # fields with the same embedding might differ by other parameters # and therefore do not serve immediately as unique keys. embed = AA.coerce_map_from(embed) if isinstance(embed, Map): K = embed.domain() if K not in NumberFields(): raise ValueError("domain must be a number field") if not AA.has_coerce_map_from(embed.codomain()): raise ValueError("codomain must coerce into RR") if not K.is_absolute(): raise NotImplementedError("number field must be absolute") # We explicitly construct an embedding from the given embedding to # make sure that we get a useable key. minpoly = (QQ['x'].gen() - 1) if K is QQ else K.polynomial() minpoly = minpoly.change_variable_name('x') embedding = AA.one() if K is QQ else embed(K.gen()) embed = NumberField(minpoly, K.variable_name(), embedding=embedding) else: raise TypeError( "cannot build RealEmbeddedNumberField from embedding %s" % (type(embed))) category = category or Fields() return super(RealEmbeddedNumberField, cls).__classcall__(cls, embed, category)
ar = ArtinRepresentation(label) outfile = open(label, 'w') cf = a['CharacterField'] cfz = ZZ(cf) nf = ar.nf() nfcc = nf.conjugacy_classes() nfcc = [int(z.order()) for z in nfcc] nfcc = lcm(nfcc) if not cfz.divides(nfcc): print("Failure " + str(cfz) + " divides " + str(nfcc) + " from " + label) sys.exit() R, x = QQ['x'].objgen() pol1 = R.cyclotomic_polynomial(nfcc) K, z = NumberField(R.cyclotomic_polynomial(nfcc), 'z').objgen() RR, y = K['y'].objgen() zsmall = z**(nfcc / cfz) allpols = [ sum(y**k * sum(pp[k][j] * zsmall**j for j in range(len(pp[k]))) for k in range(len(pp))) for pp in ar.local_factors_table() ] allroots = [myroots(pp, nfcc, z) for pp in allpols] outfile.write(str(nfcc) + "\n") outfile.write(str(allroots) + "\n") j = 0 p = 1 while j < bound: p = next_prime(p) outfile.write(str(ar.any_prime_to_cc_index(p)) + "\n")
def extension(self, f, embedding=False): r""" Return a `p`-adic extension such that ``f`` has a root in an unramified extension of it. INPUT: - ``f`` -- a monic univariate polynomial over the number field `K_0` underlying this p-adic extension `K`, which is irreducible over `K`. - ``embedding`` -- a boolean (default: ``False``) OUTPUT: A `p`-adic extension `L` of `K` such that ``f`` has a root in an unramified extension of `L`, or (if ``embedding`` is ``True``) the pair `(L,\phi)`, where `\phi` is the canonical embedding of `K` into `L`. If `K_0` is the underlying number field, then `f\in K_0[x]` is irreducible over the completion `K`; the resulting finite extension `L/K` is a subextension of the extension of `K` .. MATH:: K[x]/(f). However, the number field `L_0` underlying `L` is in general not equal to `K_0[x]/(f)`, and there may not exist any embedding of `K_0` into `L_0`. """ # print("entering extension with ") # print("K = ", self) # print("f = ", f) # print() K0 = self.number_field() assert K0.has_coerce_map_from(f.parent().base_ring()) f = f.change_ring(K0) vK = self.valuation() if embedding: raise NotImplementedError V = vK.mac_lane_approximants(f) assert len(V) == 1, "f is not irreducible" v = LimitValuation(V[0], f) pix = v.uniformizer() r = v(pix) for m in range(2,10): pix1 = pix.map_coefficients(lambda c:self.reduce(c, m)) if v(pix1) == r: break assert m < 9, "m too small" # now pix1 is a polynomial over `K_0` which maps to a uniformizer for the # unique extension of vK to the relative extension L=K[x]/(f)/K g = self.characteristic_polynomial(f, pix1) # now g is the relative characteristic polynomial of the image pi of pix1 in L # we need the minimal polynomial, so we do a square free factorization g = g.squarefree_decomposition()[0][0] # now we compute the absolute characteristic polynomial of a root # of g done = False N = m+5 while not done: Pmod = self.characteristic_polynomial_mod(g, N) # this is very heuristic; there should be a conclusive test whether # the result is correct, or the choice of the precision should be made correctly and # optimal from the start P = Pmod.map_coefficients(lambda c:c.lift(), QQ) P = P.squarefree_decomposition()[0][0] L0 = NumberField(P, 'pi%s'%P.degree()) V = QQ.valuation(self.p()).extensions(L0) vL = V[0] e = ZZ(1/vL(vL.uniformizer())) f = vL.residue_field().degree() if 1/e == r: if len(V) == 1: return FakepAdicCompletion(L0, vL) else: QQp = FakepAdicCompletion(QQ, self.base_valuation()) g = QQp.approximate_irreducible_factor(P) return QQp.extension(g) N = N + 5
def make_form(self): # To start with the data fields of self are just those from # the database. We need to reformat these and compute some # further (easy) data about it. # from lmfdb.ecnf.WebEllipticCurve import FIELD self.field = FIELD(self.field_label) pretty_field = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, pretty_field) try: dims = db.bmf_dims.lucky( { 'field_label': self.field_label, 'level_label': self.level_label }, projection='gl2_dims') self.newspace_dimension = dims[str(self.weight)]['new_dim'] except TypeError: self.newspace_dimension = 'not available' self.newspace_label = "-".join([self.field_label, self.level_label]) self.newspace_url = url_for(".render_bmf_space_webpage", field_label=self.field_label, level_label=self.level_label) K = self.field.K() if self.dimension > 1: Qx = PolynomialRing(QQ, 'x') self.hecke_poly = Qx(str(self.hecke_poly)) F = NumberField(self.hecke_poly, 'z') self.hecke_poly = web_latex(self.hecke_poly) def conv(ap): if '?' in ap: return 'not known' else: return F(str(ap)) self.hecke_eigs = [conv(str(ap)) for ap in self.hecke_eigs] self.nap = len(self.hecke_eigs) self.nap0 = min(50, self.nap) self.hecke_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(primes_iter(K), self.hecke_eigs[:self.nap0])] level = ideal_from_label(K, self.level_label) self.level_ideal2 = web_latex(level) badp = level.prime_factors() self.have_AL = self.AL_eigs[0] != '?' if self.have_AL: self.AL_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(badp, self.AL_eigs)] self.sign = 'not determined' if self.sfe == 1: self.sign = "+1" elif self.sfe == -1: self.sign = "-1" if self.Lratio == '?': self.Lratio = "not determined" self.anrank = "not determined" else: self.Lratio = QQ(self.Lratio) self.anrank = "\(0\)" if self.Lratio != 0 else "odd" if self.sfe == -1 else "\(\ge2\), even" self.properties2 = [('Base field', pretty_field), ('Weight', str(self.weight)), ('Level norm', str(self.level_norm)), ('Level', self.level_ideal2), ('Label', self.label), ('Dimension', str(self.dimension))] if self.CM == '?': self.CM = 'not determined' elif self.CM == 0: self.CM = 'no' self.properties2.append(('CM', str(self.CM))) self.bc_extra = '' self.bcd = 0 self.bct = self.bc != '?' and self.bc != 0 if self.bc == '?': self.bc = 'not determined' elif self.bc == 0: self.bc = 'no' elif self.bc == 1: self.bcd = self.bc self.bc = 'yes' elif self.bc > 1: self.bcd = self.bc self.bc = 'yes' self.bc_extra = ', of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + '})\)' elif self.bc == -1: self.bc = 'no' self.bc_extra = ', but is a twist of the base-change of a form over \(\mathbb{Q}\)' elif self.bc < -1: self.bcd = -self.bc self.bc = 'no' self.bc_extra = ', but is a twist of the base-change of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + '})\)' self.properties2.append(('Base-change', str(self.bc))) curve_bc = db.ec_nfcurves.lucky({'class_label': self.label}, projection="base_change") if curve_bc is not None: self.ec_status = 'exists' self.ec_url = url_for("ecnf.show_ecnf_isoclass", nf=self.field_label, conductor_label=self.level_label, class_label=self.label_suffix) curve_bc_parts = [split_lmfdb_label(lab) for lab in curve_bc] bc_urls = [ url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) for cond, iso, num in curve_bc_parts ] bc_labels = [ newform_label(cond, 2, 1, iso) for cond, iso, num in curve_bc_parts ] bc_exists = [is_newform_in_db(lab) for lab in bc_labels] self.bc_forms = [{ 'exists': ex, 'label': lab, 'url': url } for ex, lab, url in zip(bc_exists, bc_labels, bc_urls)] else: self.bc_forms = [] if self.bct: self.ec_status = 'none' else: self.ec_status = 'missing' self.properties2.append(('Sign', self.sign)) self.properties2.append(('Analytic rank', self.anrank)) self.friends = [] if self.dimension == 1: if self.ec_status == 'exists': self.friends += [ ('Elliptic curve isogeny class {}'.format(self.label), self.ec_url) ] elif self.ec_status == 'missing': self.friends += [ ('Elliptic curve {} missing'.format(self.label), "") ] else: self.friends += [('No elliptic curve', "")] self.friends += [('Newspace {}'.format(self.newspace_label), self.newspace_url)] self.friends += [('L-function not available', '')]
def field_intersection_matrix(matrix_defining_polynomials): r""" INPUT: - a matrix of polynomials defining numberfields (f_ij) OUTPUT: - returns a pair per column entry (Ak, Bk) after picking a row with a small intersection, WLOG i = 0 Let S_ij denote all the polynomials defining subfields of QQ[x]/fij Then Bk = S_0k \cap (\cap_{i > 0) \cup_j S_ij) If the list Bk can be obtained as list of subfields of one subfield then such subfield, otherwise Ak is None """ if len(matrix_defining_polynomials[0]) == 1: f = field_intersection_list( [elt[0] for elt in matrix_defining_polynomials]) return [[f, subfields_polynomials(f)]] # we would like to pick the row which has the smallest intersection # an easy to get something close to that,is by picking the one with # lowest GCD of the discriminants Di = None Dmin = None for i, row in enumerate(matrix_defining_polynomials): D = GCD([elt.discriminant() for elt in row]) if Dmin is None or D < Dmin: Dmin = D Di = i if Dmin == 1: break working_row = matrix_defining_polynomials[Di] subfields_matrix = [None] * len(matrix_defining_polynomials) output = [None] * len(working_row) for k, f in enumerate(working_row): Lsubfields = Set(subfields_polynomials(f)) for i, row in enumerate(matrix_defining_polynomials): if i != Di: if subfields_matrix[i] is None: U = Set([]) for g in row: Sg = Set(subfields_polynomials(g)) U = U.union(Sg) subfields_matrix[i] = U # try to discard some subfields Lsubfields = Lsubfields.intersection(subfields_matrix[i]) if Lsubfields.cardinality() == 1: # QQ is the only possible subfield break Lsubfields = list(Lsubfields) Lsubfields.sort() output[k] = [None, Lsubfields] # try to produce a single polynomial, if Lsubfields are all the subfields of a common subfield if len(Lsubfields) == 1: output[k][0] = f.parent().gen() else: if Lsubfields[-1].degree() != Lsubfields[-2].degree(): # there is one field of maximal degree, that might contain all the others K = NumberField(Lsubfields[-1], 'a') maxsubfields = [ polredabs(sK.absolute_polynomial()) for sK, _, _ in K.subfields() ] if maxsubfields == Lsubfields: output[k][0] = Lsubfields[-1] return output
def DiscriminantBound(LPolys, alreadyTwisted=False): # returns a pair (type, bound), where type is one of "Z", "RM", # "QuadraticCM", "FullCM" and bound is a bound on the discriminant of the # field of endomorphisms discBound = 0 CMField = QQ RMField = QQ FullCM = True RM = True for p in range(2, maxP): if LPolys[p] <> 0: q = LPolys[p] g = q.degree() / 2 extDegree = extensionBounds[g] if alreadyTwisted: extDegree = 1 break for p in range(2, maxP): if is_prime(p) and LPolys[p] <> 0: q = LPolys[p] # print q # print "Testing p =", p # print "Twisting the L-poly" q = twistPolynomial(q, extDegree) # print "Prime", p, "Polynomial", q; # print "Twisting ends, now test for irreducibility" if (q.is_irreducible()): # print "Irreducible polynomial" if (q.coefficients(sparse=False)[g] % p <> 0): # print "Ordinary" # print "Define K1" K1 = NumberField(q, 'b') # print K1 # print K1.discriminant() # print "Define K1Real" K1Real = K1.maximal_totally_real_subfield()[0] # print "Comparisons" if (CMField == QQ): CMField = K1 if (RMField == QQ): RMField = K1Real if (FullCM and not CMField.is_isomorphic(K1)): FullCM = False if (RM and RMField.degree() == 1): RM = False if (RM and not RMField.is_isomorphic(K1Real)): RM = False FieldDiscriminant = K1.discriminant() discBound = GCD(discBound, FieldDiscriminant) # print "Current discriminant bound", discBound if discBound <= 4: return "Z", 1 if FullCM: return "FullCM", discBound if RM: return "RM", discBound ^ (1 / g) return "Quadratic", discBound ^ (1 / g)
def list_to_factored_poly_otherorder(s, galois=False, vari='T', prec=None, p=None): """ Either return the polynomial in a nice factored form, or return a pair, with first entry the factored polynomial and the second entry a list describing the Galois groups of the factors. vari allows to choose the variable of the polynomial to be returned. """ gal_list = [] if len(s) == 1: if galois: return [str(s[0]), [[0, 0]]] return str(s[0]) sfacts = factor(PolynomialRing(ZZ, 'T')(s)) sfacts_fc = [[v[0], v[1]] for v in sfacts] if sfacts.unit() == -1: sfacts_fc[0][0] *= -1 outstr = '' for v in sfacts_fc: this_poly = v[0] # if the factor is -1+T^2, replace it by 1-T^2 # this should happen an even number of times, mod powers if this_poly.substitute(T=0) == -1: this_poly = -1 * this_poly v[0] = this_poly if galois: this_degree = this_poly.degree() # hack because currently sage only handles monic polynomials: this_poly = this_poly.reverse() this_number_field = NumberField(this_poly, "a") this_gal = this_number_field.galois_group(type='pari') this_t_number = this_gal.group().__pari__()[2].sage() gal_list.append([this_degree, this_t_number]) vcf = v[0].list() terms = 0 if len(sfacts) > 1 or v[1] > 1: outstr += '(' for i in range(len(vcf)): if vcf[i] != 0: if terms > 0 and vcf[i] > 0: outstr += '+' if i == 0: outstr += str(vcf[i]) else: if i == 1: variableterm = vari elif i > 1: variableterm = vari + '^{' + str(i) + '}' if terms == prec and i != len(vcf) - 1: if vcf[i] < 0: outstr += '+' # we haven't added the + outstr += 'O(%s)' % variableterm break if vcf[i] == 1: outstr += variableterm elif abs(vcf[i]) != 1: if p is None or vcf[i] % p != 0: outstr += str(vcf[i]) + variableterm else: # we try to factor p res = vcf[i] e = 0 while res % p == 0: res /= p e += 1 assert e != 0 pfactor = 'p^{%d}' % e if e > 1 else 'p' if res == 1: res = '' elif res == -1: res = '-' else: res = str(res) outstr += '%s %s %s' % (res, pfactor, variableterm) elif vcf[i] == -1: outstr += '-' + variableterm terms += 1 if len(sfacts) > 1 or v[1] > 1: outstr += ')' if v[1] > 1: outstr += '^{' + str(v[1]) + '}' if galois: if galois and len(sfacts_fc) == 2: if sfacts[0][0].degree() == 2 and sfacts[1][0].degree() == 2: troubletest = sfacts[0][0].disc() * sfacts[1][0].disc() if troubletest.is_square(): gal_list = [[2, 1]] return [outstr, gal_list] return outstr
def subfields_polynomials(f): return [ polredabs(sL.absolute_polynomial()) for sL, _, _ in NumberField(f, 'a').subfields() ]
def get_form_data_from_file(nf, data_dir=NF_DIR, max_dim=50, verbose=False): """Fill in data for an incomplete WebNewform object by reading from a data file. If a suitable file does not exist, the original WebNewform object is returned unchanged, with a message output. The data files only contain the extra data for dimensions less than some bound. If the newform's dimension is greater then we do nothing. """ if not nf.field_poly is None: return nf fname = label = nf.label datafile = "/".join([data_dir, fname]) try: data = read_dtp(datafile, verbose=False) except FileNotFoundError: fname = fname[:fname.rindex(".")] datafile = "/".join([data_dir, fname]) try: data = read_dtp(datafile, verbose=False) except FileNotFoundError: print("No file {} or {} found: no data available".format(datafile,label)) return nf if verbose: print("Successfully read data for {} from file {}".format(label, fname)) # Now we have data. It is a dict with a single key (N,k,o) where # o is the character orbit number and value so we just extract the # single value for this key, which is another dict: data = list(data.values())[0] # This dict has keys ['dims', 'traces', 'ALs', 'polys', # 'eigdata'], and each value is a list, one per newform, so we # need to extract just one item from the relevant lists, according # to the newform we want. This is determined by the 4th (last) # part of the label we have, which we need to convert to an # integer (from 0). # NB eigdata is a list of dicts (see later comment for details) # but *only* for components of dimension>1 nf_index = class_to_int(label.split(".")[3]) dims = data['dims'] nf_eigdata_index = nf_index - dims.count(1) if verbose: # debug print("Newform label = {}".format(label)) print("nf_index = {}".format(nf_index)) print("len(polys) = {}".format(len(data['polys']))) print("len(dims) = {}".format(len(dims))) print("#dims>1 = {}".format(len(dims)-dims.count(1))) print("len(eigdata) = {}".format(len(data['eigdata']))) print("nf_eigdata_index = {}".format(nf_eigdata_index)) dim = data['dims'][nf_index] assert dim == nf.dim nf.field_poly = data['polys'][nf_index] eigdata = data['eigdata'][nf_eigdata_index] # eigdata is a dict with keys ['poly', 'basis', 'n', 'm', 'ans', 'char'] chi_gens, chi_vals = eigdata['char'] nf.hecke_ring_character_values = list(zip(chi_gens, chi_vals)) # we will not need to access the char_order since this space has # already been selected to have an appropriate character. if verbose: print("hecke_ring_character_values = {}".format(nf.hecke_ring_character_values)) Qx = PolynomialRing(QQ,'x') nf.hecke_field = K = NumberField(Qx(nf.field_poly), 'a_') nf.betas = [K(b) for b in eigdata['basis']] nf.an = eigdata['ans'] nf.ap = [nf.an[p-1] for p in primes(len(nf.an))] if verbose: # debug print("We have {} a_n and {} a_p for newforms {}".format(len(nf.an), len(nf.ap), label)) return nf
def get_forms(N, k, ell, max_dim=50, verbose=False): """ Input: N (level), k (weight), ell (prime); and a verbosity flag Output: 2 lists of newforms of level N, weight k, whose character chi has order compatible with ell. (1) forms for which we have the Hecke field and which have at least one mod-ell reduction. The reductions are stored as an attribute (.reductions) for each of these newforms. (2) forms for which we do not have the Hecke field in the database, for which we currently do nothing. """ if N%ell==0: return [], [] nfs = db.mf_newforms heckes = db.mf_hecke_nf # for more ap forms = [WebNewform.by_label(f_label) for f_label in nfs.search({'level':N, 'weight':k}, projection='label')] if verbose: print("forms with (N,k)=({},{}): {}".format(N,k,[f.label for f in forms])) # omit forms whose character order is invalid: forms = [f for f in forms if char_order_valid(f.char_order, ell)] if verbose: print("After char order check, forms with (N,k,ell)=({},{},{}): {}".format(N,k,ell,[(f.label,f.dim) for f in forms])) # for forms with no Hecke field, try to read from a data file forms_with_no_field = [f for f in forms if f.field_poly is None] if forms_with_no_field: if verbose: print("Before reading data files, {} forms have no Hecke field data: {}".format(len(forms_with_no_field), [f.label for f in forms_with_no_field])) for f in forms: if f.field_poly is None and f.dim <= max_dim: if verbose: print("looking for a data file for form {}".format(f.label)) f = get_form_data_from_file(f, max_dim=max_dim, verbose=verbose) forms_with_no_field = [(f.label,f.dim,ell) for f in forms if f.field_poly is None] if verbose: print("After reading data files, {} forms have no Hecke field data".format(len(forms_with_no_field))) # Now exclude any forms which still have no Hecke data: forms = [f for f in forms if f.field_poly] # find all mod-ell reduction maps from the Hecke order Qx = PolynomialRing(QQ,'x') for f in forms: f.hecke_field = NumberField(Qx(f.field_poly), 'a_') if verbose: print("making reductions for {} mod {}".format(f.label, ell)) print("Hecke field is {}".format(f.hecke_field)) f.reductions = make_reductions(f, ell, verbose) if verbose: print("finished making reductions for {} mod {}".format(f.label, ell)) try: assert f.an except AttributeError: # get extra ap from the second table: anap = heckes.lucky({'label':f.label}, projection=['ap','an']) f.an = anap['an'] f.ap = anap['ap'] if verbose: nap = len(f.ap) print("Found {} ap and {} an in the second table".format(nap,len(f.an))) # Exclude forms with no mod-ell reductions: forms = [f for f in forms if f.reductions] if forms and verbose: print("{}.{} forms with mod-{} reductions:".format(N,k,ell)) for f in forms: print("{} has {} reductions mod {}".format(f.label,len(f.reductions), ell)) return forms, forms_with_no_field
def find_newform_label(level, weight, character, field, aps): r""" Find the label of the newform orbit in the database which matches the input. INPUT: - 'level' -- the level, - 'weight' -- the weight - 'character' -- the character' - 'field' -- the field, given in terms of a list of integer coefficients for the absolute polynomial - 'aps' -- the coefficients - given as a dictionary of lists giving the coefficient in terms of the generator of the field as above. EXAMPLE: sage: find_newform_label(9,16,1,[-119880,0,1],{2:[0,1]}) u'e' sage: find_newform_label(71,2,1,[-3,-4,1,1],{3:[0,-1,0]}) u'a' sage: find_newform_label(71,2,1,[-3,-4,1,1],{5:[5,1,-1]}) u'a' NOTE: We implicitly assume that the input given is correct in the sense that if there is a unique orbit with a coefficient field of the same degree as the input then we simply return that label. (This will save a lot of time...) """ from web_modform_space import WebModFormSpace from sage.all import NumberField, QQ M = WebModFormSpace(level=level, weight=weight, character=character) if M.dimension_new_cusp_forms == 1: return 'a' orbits = M.hecke_orbits ## construct field from field input... if not isinstance(field, list): raise ValueError, "Need to give field as a list!" if not isinstance(aps, dict): raise ValueError, "Need to give aps as a dict!" if field == [1]: NF = QQ else: NF = NumberField(QQ['x'](field), names='x') degree_of_input = NF.absolute_degree() degrees = map(lambda x: x[1].coefficient_field_degree, orbits.viewitems()) if degrees.count(degree_of_input) == 0: raise ValueError, "No newform with this level, weight, character and field degree!" if degrees.count(degree_of_input) == 1: ## If there is a unique mathcing field we return this orbit label. l = filter(lambda x: x[1].coefficient_field_degree == degree_of_input, orbits.viewitems()) return l[0][0] aps_input = {p: NF(a) for p, a in aps.viewitems()} possible_labels = orbits.keys() for label, f in orbits.viewitems(): if f.coefficient_field_degree != degree_of_input: possible_labels.remove(label) continue try: for p, ap_input in aps_input.viewitems(): if f.coefficient_field == QQ: homs = [lambda x: x] else: homs = f.coefficient(p).parent().Hom(NF) for h in homs: ap = h(f.coefficient(p)) if ap_input != ap: possible_labels.remove(label) raise StopIteration except StopIteration: continue if len(possible_labels) > 1: raise ArithmeticError, "Not sufficient data (or errors) to determine orbit!" if len(possible_labels) == 0: raise ArithmeticError, "Not sufficient data (or errors) to determine orbit! NO matching label found!" return possible_labels[0]
... print nm, p, t.charpoly().factor() 5 a + 2 (x - 6) * (x^2 + 4*x - 1) * (x^2 - x - 4)^2 9 3 (x - 10) * (x^2 + 3*x - 9) * (x^4 - 5*x^3 + 3*x^2 + 6*x - 4) 11 a + 3 (x - 12) * (x + 3)^2 * (x^4 - 17*x^2 + 68) 11 2*a + 3 (x - 12) * (x^2 + 5*x + 5) * (x^4 - x^3 - 23*x^2 + 18*x + 52) """ from __future__ import print_function from __future__ import absolute_import from __future__ import division from builtins import range from builtins import object from sage.all import NumberField, polygen, QQ, ZZ, QuaternionAlgebra, cached_function, disk_cached_function x = polygen(QQ, 'x') F = NumberField(x**2 - x - 1, 'a') O_F = F.ring_of_integers() B = QuaternionAlgebra(F, -1, -1, 'i,j,k') def modp_splitting(p): """ INPUT: - p -- ideal of the number field K = B.base() with ring O of integers. OUTPUT: - matrices I, J in M_2(O/p) such that i |--> I and j |--> J defines an algebra morphism, i.e., I^2=a, J^2=b, I*J=-J*I.
def process_pari_nf(pari_nf, dmax=20, Detail=0): r""" Input is a dict with keys 'Nko' (N,k,chi_number), 'chipoly', 'SB' (Sturm bound), 'pari_newform', 'poly', 'ans', 'ALeigs', 'traces' Output adds 'traces' (unless already computed), and also 'eigdata' if 1<dimension<=dmax We do not yet use polredbest or optimize an coeffients NB This must be called on each newform *before* the GP process associated with the data has been terminated. """ Nko = pari_nf['Nko'] chipoly = pari_nf['chipoly'] poly = pari_nf['poly'] SB = pari_nf['SB'] ALeigs = pari_nf['ALeigs'] traces = pari_nf['traces'] assert traces # not None ans = pari_nf['ans'] basis = pari_nf['basis'] # initialize with data needing no more processing: newform = {'Nko': Nko, 'chipoly': chipoly, 'SB': SB, 'ALeigs':ALeigs, 'traces':traces} # Set the polynomial. poly is the relative polynomial, in x (so # just x if the top degree is 1) with coefficients either integers # (if the bottom degree is 1) or polmods with modulus chipoly. # rel_poly is in Qchi[x]. Qx = PolynomialRing(QQ,'x') #pari_Qx_poly_to_sage = lambda f: Qx(gen_to_sage(f.Vecrev())) Qchi = NumberField(chipoly,'t') chi_degree = chipoly.degree() # NB the only reason for negating the chi_degree parameter here is to get around a bug in the Sage/pari interface pari_Qchi_to_sage = lambda elt: Qchi(gen_to_sage(elt.lift().Vecrev(chi_degree))) Qchi_x = PolynomialRing(Qchi,'x') pari_Qchix_poly_to_sage = lambda f: Qchi_x([pari_Qchi_to_sage(co) for co in f.Vecrev()]) rel_poly = pari_Qchix_poly_to_sage(poly) rel_degree = rel_poly.degree() newform['dim'] = dim = chi_degree*rel_degree small = (dmax==0) or (dim<=dmax) # for 'small' spaces we compute more data, where 'small' means # dimension<=dmax (unless dmax==0 in which case all spaces are # deemed small). However spaces of dimension 1 never need the # additional data. if Detail: print("{}: degree = {} = {}*{}".format(Nko, dim, chi_degree, rel_degree)) if Detail>1: print("rel_poly for {} is {}".format(Nko,rel_poly)) # The newform has its 'traces' field set already. We will have set # its 'ans' field where relevant (1<dim<=dmax) to a list of lists # of coefficients in Qchi. In the genuinely relative case we'll # need to absolutise these. if Detail>1: print("raw ans = {}".format(ans)) # dimension 1 spaces: little to do if dim==1: # e.g. (11,2,1)[0] newform['poly'] = Qx.gen() return newform # no more data required for non-small spaces: if not small: return newform if chi_degree==1: # e.g. (13,4,1)[1]; now rel_degree>1 # field is not relative, ans are lists of integers, coeffs w.r.t. basis t0=time.time() ancs = [gen_to_sage(an) for an in ans] t1 = time.time() if Detail: print("time for converting an coeffs to QQ = {}".format(t1-t0)) if Detail>1: print("Coefficient vectors of ans: {}".format(ancs)) newform['poly'] = rel_poly # basis is a pari matrix over Q basis = gen_to_sage(basis) newform['eigdata'] = { 'pol': rel_poly.list(), 'bas': basis, 'n': 0, # temporary 'm': 0, # temporary 'ancs': ancs, } return newform if rel_degree==1: # e.g. (25,2,4)[0]; now chi_degree>1 # the field is not relative; ans is a lists of 1-lists of # t_POLMOD modulo a GP poly in t, so we lift them and take # their coefficient vector to get the coordinates w.r.t. a # power basis for the field defined by chipoly. t0=time.time() ancs = [gen_to_sage(an[0].lift().Vecrev(dim)) for an in ans] # list of lists of integers/rationals t1 = time.time() if Detail: print("time for converting an coeffs to QQ = {}".format(t1-t0)) if Detail>1: print("Coefficient vectors of ans: {}".format(ancs)) newform['poly'] = chipoly # basis is a 1x1 gp matrix over Qchi, so we want the power basis for Qchi: trivial basis = [[int(i==j) for j in range(dim)] for i in range(dim)] newform['eigdata'] = { 'pol': chipoly.list(), 'bas': basis, 'n': 0, # temporary 'm': 0, # temporary 'ancs': ancs, } return newform # Now we are in the genuinely relative case where chi_degree>1 and rel_degree>1 # e.g. (25,2,5) # Now ans is a (python) list of nan (GP) lists of d_rel elements # of Qchi, and basis is a GP d_rel x d_rel matrix over Qchi #Setting the Hecke field as a relative extension of Qchi and as an absolute field: t0=time.time() Frel = Qchi.extension(rel_poly,'b') newform['poly'] = abs_poly = Frel.absolute_polynomial()(Qx.gen()) t1 = time.time() if Detail: print("absolute poly = {}".format(abs_poly)) if Detail>1: print("Frel = {}".format(Frel)) print("Time to construct Frel and find absolute poly = {}".format(t1-t0)) Fabs = Frel.absolute_field('a') if Detail>1: print("Fabs = {}".format(Fabs)) rel2abs = Fabs.structure()[1] # the isomorphism Frel --> Fabs z = rel2abs(Qchi.gen()) zpow = [z**i for i in range(chi_degree)] # convert basis to a Sage list of lists of elements of Qchi: our_basis_coeffs = [[pari_Qchi_to_sage(basis[i,j]) for j in range(rel_degree)] for i in range(rel_degree)] #print("our basis coeffs: {} (parent {})".format(our_basis_coeffs, our_basis_coeffs[0][0].parent())) our_basis_rel = [Frel(b) for b in our_basis_coeffs] #print("our basis (Sage, relative): {}".format(our_basis_rel)) our_basis_abs = [rel2abs(b) for b in our_basis_rel] #print("our basis (Sage, absolute): {}".format(our_basis_abs)) basis = sum([[(zpowi*yj).list() for zpowi in zpow] for yj in our_basis_abs],[]) #print("basis (Sage, matrix/Q): {}".format(basis)) t2 = time.time() if Detail>1: print("Time to construct Fabs and y,z in Fabs and basis matrix = {}".format(t2-t1)) # Convert coordinates of the an. After lifting these are lists # of lists of polynomials in Q[t] so simply extracting # coefficient vectors gives their coordinates in terms of the # basis z^i*y_j where Qchi=Q(z) and [y_1,...,y_d_rel] is the # Qchi-basis of Frel. To relate these to the power basis of Fabs # we only need the change of basis matrix whose rows give the # power basis coefficients of each z^i*y_j (in the right order). ancs = [[gen_to_sage(c.lift().Vecrev(chi_degree)) for c in an] for an in ans] t4 = time.time() if Detail>1: print("Time to construct ancs) = {}".format(t4-t2)) ancs = [sum([anci for anci in anc],[]) for anc in ancs] if Detail>1: print("Coefficient vectors of ans: {}".format(ancs)) newform['eigdata'] = { 'pol': abs_poly.list(), 'bas': basis, 'n': 0, # temporary 'm': 0, # temporary 'ancs': ancs, } return newform
def __init__(self, E, kernel=None, rational_maps=None, degree=None, kernel_polynomial=None, frobenius_power=0, element=None, ring=None, isogeny=None): self._domain = E self._ring = ring self._field = NumberField(E.frobenius_polynomial(), 'c') self._order_element = element if isinstance(element, tuple): c = self._field.gen() self._order_element = element[0] + element[1] * c if isinstance(element, Integer): self._order_element = self._field(element) self._trace = None if isogeny != None: Isogeny.__init__(self, E, rational_maps=isogeny.rational_maps(), codomain=E) # Construction using provided order element if element != None: # 0 endomorphism has to be dealt with separately if element == 0: Isogeny.__init__(self, E, 0) self._order_element = element self._trace = 0 else: k1 = self._order_element.polynomial()[0] k2 = self._order_element.polynomial()[1] denom = k2.denominator().lcm(k1.denominator()) a = k1.numerator() * (denom // k1.denominator()) b = k2.numerator() * (denom // k2.denominator()) A = multiplication_end(E, a) B = multiplication_end(E, b) frob = frobenius(E) rational_maps = add_maps(A, compose_endomorphisms(B, frob, E), E) if denom == 1: Isogeny.__init__(self, E, rational_maps=rational_maps, codomain=E) else: kernel = [] isg = Isogeny(E, rational_maps=rational_maps, codomain=E) for P in isg.kernel(): Q = denom * P if Q != (Q - Q): kernel.append(Q) kernel = list(dict.fromkeys(kernel)) Isogeny.__init__(self, E, kernel=kernel, codomain=E) # Otherwise use Isogeny class else: if degree != None: Isogeny.__init__(self, E, degree=degree, codomain=E) else: Isogeny.__init__(self, E, kernel=kernel, rational_maps=rational_maps, kernel_polynomial=kernel_polynomial, frobenius_power=frobenius_power, codomain=E)
def process_pari_nf_v1(pari_nf, dmax=20, Detail=0): r""" Input is a dict with keys 'Nko' (N,k,chi_number), 'chipoly', 'SB' (Sturm bound), 'pari_newform', 'poly', 'ans', 'ALeigs', 'traces' Output adds 'traces' (unless already computed), and also 'eigdata' if 1<dimension<=dmax We do not yet use polredbest or optimize an coeffients """ Nko = pari_nf['Nko'] chipoly = pari_nf['chipoly'] poly = pari_nf['poly'] SB = pari_nf['SB'] ALeigs = pari_nf['ALeigs'] traces = pari_nf['traces'] # initialize with data needing no more processing: newform = {'Nko': Nko, 'chipoly': chipoly, 'SB': SB, 'ALeigs':ALeigs, 'traces':traces} # Set the polynomial. This is a polynomial in y, (just y if the # top degree is 1) with coefficients either integers (if the # bottom degree is 1) or polmods with modulus chipoly. In all # cases rel_poly will be in Qchi[y]. Qx = PolynomialRing(QQ,'x') #pari_Qx_poly_to_sage = lambda f: Qx(gen_to_sage(f.Vecrev())) Qchi = NumberField(chipoly,'t') chi_degree = chipoly.degree() # NB the only reason for negating the chi_degree parameter here is to get around a bug in the Sage/pari interface pari_Qchi_to_sage = lambda elt: Qchi(gen_to_sage(elt.lift().Vecrev(chi_degree))) Qchi_x = PolynomialRing(Qchi,'x') pari_Qchix_poly_to_sage = lambda f: Qchi_x([pari_Qchi_to_sage(co) for co in f.Vecrev()]) rel_poly = pari_Qchix_poly_to_sage(poly) rel_degree = rel_poly.degree() newform['dim'] = dim = chi_degree*rel_degree small = (dmax==0) or (dim<=dmax) # for 'small' spaces we compute more data, where 'small' means # dimension<=dmax (unless dmax==0 in which case all spaces are # deemed small). However spaces of dimension 1 never need the # additional data. if Detail: print("{}: degree = {} = {}*{}".format(Nko, dim, chi_degree, rel_degree)) if Detail>1: print("rel_poly for {} is {}".format(Nko,rel_poly)) # the newform will have its 'traces' field set already if it is # the only newform in its (N,k,chi)-newspace. Otherwise we will # have set its 'ans' field and now compute the traces from that. # The 'ans' field will be None if we don't need the an, which is # if (1) the dimension is greater than dmax and (2) the # (N,k,chi)-newspace is irreducible. ans = pari_nf['ans'] if Detail>1: print("raw ans = {}".format(ans)) # dimension 1 spaces: special case simpler traces, and no more to do: x = Qx.gen() if dim==1: # e.g. (11,2,1)[0] #traces = gen_to_sage(ans)[1:] newform['poly'] = x if Detail>1: print("traces = {}".format(newform['traces'])) return newform # All dimensions >1: traces if newform['traces']==None: traces = [abstrace(an,dim) for an in ans][1:] # fix up trace(a_1) traces[0]=dim if Detail>1: print("traces = {}".format(traces)) newform['traces'] = traces # no more data required for non-small spaces: if not small: return newform if chi_degree==1 or rel_degree==1: # e.g. (13,4,1)[1] or (25,2,4)[0] respectively # field is not relative, ans are t_POLMOD modulo pari_pol in y # or t, so if we lift them and take their coefficient vector # we'll get the coordinates w.r.t. a power basis for the field # defined by either rel_poly or chipoly. t0=time.time() # for an in ans[:20]: # print("an = {}".format(an)) # print("an.lift() = {}".format(an.lift())) # print("an.lift().Vecrev(dim) = {}".format(an.lift().Vecrev(dim))) # print("gen_to_sage(an.lift().Vecrev(dim)) = {}".format(gen_to_sage(an.lift().Vecrev(dim)))) ancs = [gen_to_sage(an.lift().Vecrev(dim)) for an in ans][1:] t1 = time.time() if Detail: print("time for converting an coeffs to QQ = {}".format(t1-t0)) basis = [[int(i==j) for j in range(dim)] for i in range(dim)] newform['poly'] = poly = Qx(rel_poly) if chi_degree==1 else Qx(chipoly) if Detail>1: print("Coefficient vectors of ans: {}".format(ancs)) newform['eigdata'] = { 'pol': poly.list(), 'bas': basis, 'n': 0, # temporary 'm': 0, # temporary 'ancs': ancs, } return newform # Now we are in the genuinely relative case where chi_degree>1 and rel_degree>1 # e.g. (25,2,5) #Setting the Hecke field as a relative extension of Qchi and as an absolute field: t0=time.time() Frel = Qchi.extension(rel_poly,'b') abs_poly = Frel.absolute_polynomial() newform['poly'] = abs_poly(x) t1 = time.time() if Detail: print("absolute poly = {}".format(abs_poly)) if Detail>1: print("Time to construct Frel and find absolute poly = {}".format(t1-t0)) Fabs = Frel.absolute_field('a') rel2abs = Fabs.structure()[1] # the isomorphism Frel --> Fabs z = rel2abs(Qchi.gen()) y = rel2abs(Frel.gen()) zpow = [z**i for i in range(chi_degree)] ypow = [y**j for j in range(rel_degree)] basis = sum([[(zpowi*ypowj).list() for zpowi in zpow] for ypowj in ypow],[]) t2 = time.time() if Detail>1: print("Time to construct Fabs and y,z in Fabs and basis matrix = {}".format(t2-t1)) # Get coordinates of the an. After lifting twice these are # polynomials in Q[t][y] so simply extracting coefficient vectors # gives their coordinates in terms of the basis z^i*y^j where # Qchi=Q(z) and F=Qchi(y). To relate these to the power basis of # Fabs we only need the change of basis matrix whose rows give # the power basis coefficients of each z^i*y^j (in the right # order). ancs = [[gen_to_sage(c.lift().Vecrev(chi_degree)) for c in a.lift().Vecrev(rel_degree)] for a in ans][1:] t4 = time.time() if Detail>1: print("Time to construct ancs) = {}".format(t4-t2)) ancs = [sum([anci for anci in anc],[]) for anc in ancs] if Detail>1: print("Coefficient vectors of ans: {}".format(ancs)) newform['eigdata'] = { 'pol': abs_poly.list(), 'bas': basis, 'n': 0, # temporary 'm': 0, # temporary 'ancs': ancs, } return newform
def make_form(self, nap0=50): # To start with the data fields of self are just those from # the database. We need to reformat these and compute some # further (easy) data about it. # from lmfdb.ecnf.WebEllipticCurve import FIELD self.field = FIELD(self.field_label) pretty_field = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, pretty_field) try: dims = db.bmf_dims.lucky( { 'field_label': self.field_label, 'level_label': self.level_label }, projection='gl2_dims') self.newspace_dimension = dims[str(self.weight)]['new_dim'] except TypeError: self.newspace_dimension = 'not available' self.newspace_label = "-".join([self.field_label, self.level_label]) self.newspace_url = url_for(".render_bmf_space_webpage", field_label=self.field_label, level_label=self.level_label) K = self.field.K() # 'hecke_poly_obj' is the non-LaTeX version of hecke_poly self.hecke_poly_obj = self.hecke_poly if self.dimension > 1: Qx = PolynomialRing(QQ, 'x') self.hecke_poly = Qx(str(self.hecke_poly)) F = NumberField(self.hecke_poly, 'z') self.hecke_poly = web_latex(self.hecke_poly) def conv(ap): if '?' in ap: return 'not known' else: return F(str(ap)) self.hecke_eigs = [conv(str(ap)) for ap in self.hecke_eigs] self.level = ideal_from_label(K, self.level_label) self.level_ideal2 = web_latex(self.level) badp = self.level.prime_factors() self.nap = len(self.hecke_eigs) self.nap0 = min(nap0, self.nap) self.neigs = self.nap0 + len(badp) self.hecke_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(primes_iter(K), self.hecke_eigs[:self.neigs]) if not p in badp] self.have_AL = self.AL_eigs[0] != '?' if self.have_AL: self.AL_table = [[ web_latex(p.norm()), ideal_label(p), web_latex(p.gens_reduced()[0]), web_latex(ap) ] for p, ap in zip(badp, self.AL_eigs)] # The following helps to create Sage download data self.AL_table_data = [[p.gens_reduced(), ap] for p, ap in zip(badp, self.AL_eigs)] self.sign = 'not determined' try: if self.sfe == 1: self.sign = "$+1$" elif self.sfe == -1: self.sign = "$-1$" except AttributeError: self.sfe = '?' if self.Lratio == '?': self.Lratio = "not determined" self.anrank = "not determined" else: self.Lratio = QQ(self.Lratio) self.anrank = r"\(0\)" if self.Lratio != 0 else "odd" if self.sfe == -1 else r"\(\ge2\), even" self.properties = [('Label', self.label), ('Base field', pretty_field), ('Weight', prop_int_pretty(self.weight)), ('Level norm', prop_int_pretty(self.level_norm)), ('Level', self.level_ideal2), ('Dimension', prop_int_pretty(self.dimension))] try: if self.CM == '?': self.CM = 'not determined' elif self.CM == 0: self.CM = 'no' else: if int(self.CM) % 4 in [2, 3]: self.CM = 4 * int(self.CM) self.CM = "$%s$" % self.CM except AttributeError: self.CM = 'not determined' self.properties.append(('CM', str(self.CM))) self.bc_extra = '' self.bcd = 0 self.bct = self.bc != '?' and self.bc != 0 if self.bc == '?': self.bc = 'not determined' elif self.bc == 0: self.bc = 'no' elif self.bc == 1: self.bcd = self.bc self.bc = 'yes' elif self.bc > 1: self.bcd = self.bc self.bc = 'yes' self.bc_extra = r', of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + r'})\)' elif self.bc == -1: self.bc = 'no' self.bc_extra = r', but is a twist of the base change of a form over \(\mathbb{Q}\)' elif self.bc < -1: self.bcd = -self.bc self.bc = 'no' self.bc_extra = r', but is a twist of the base change of a form over \(\mathbb{Q}\) with coefficients in \(\mathbb{Q}(\sqrt{' + str( self.bcd) + r'})\)' self.properties.append(('Base change', str(self.bc))) curve_bc = db.ec_nfcurves.lucky({'class_label': self.label}, projection="base_change") if curve_bc is not None: if curve_bc and "." not in curve_bc[0]: curve_bc = [ cremona_label_to_lmfdb_label(lab) for lab in curve_bc ] self.ec_status = 'exists' self.ec_url = url_for("ecnf.show_ecnf_isoclass", nf=self.field_label, conductor_label=self.level_label, class_label=self.label_suffix) curve_bc_parts = [split_lmfdb_label(lab) for lab in curve_bc] bc_urls = [ url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) for cond, iso, num in curve_bc_parts ] bc_labels = [ ".".join([str(cond), str(2), 'a', iso]) for cond, iso, _ in curve_bc_parts ] bc_exists = [db.mf_newforms.label_exists(lab) for lab in bc_labels] self.bc_forms = [{ 'exists': ex, 'label': lab, 'url': url } for ex, lab, url in zip(bc_exists, bc_labels, bc_urls)] else: self.bc_forms = [] if self.bct or self.label in bmfs_with_no_curve: self.ec_status = 'none' else: self.ec_status = 'missing' self.properties.append(('Sign', self.sign)) self.properties.append(('Analytic rank', self.anrank)) self.friends = [] self.friends += [('Newspace {}'.format(self.newspace_label), self.newspace_url)] url = 'ModularForm/GL2/ImaginaryQuadratic/{}'.format( self.label.replace('-', '/')) Lfun = get_lfunction_by_url(url) if Lfun: instances = get_instances_by_Lhash_and_trace_hash( Lfun['Lhash'], Lfun['degree'], Lfun['trace_hash']) # This will also add the EC/G2C, as this how the Lfun was computed # and not add itself self.friends = names_and_urls(instances, exclude={url}) self.friends.append(('L-function', '/L/' + url)) else: # old code if self.dimension == 1: if self.ec_status == 'exists': self.friends += [('Isogeny class {}'.format(self.label), self.ec_url)] elif self.ec_status == 'missing': self.friends += [ ('Isogeny class {} missing'.format(self.label), "") ] else: self.friends += [('No elliptic curve', "")] self.friends += [('L-function not available', '')]
def verify_algebraically_PS(g, P0, alpha, trace_and_norm, verbose=True): # input: # * P0 (only necessary to shift the series) # * [trace_numerator, trace_denominator, norm_numerator, norm_denominator] # output: # a boolean if verbose: print "verify_algebraically()" L = P0.base_ring() assert alpha.base_ring() is L L_poly = PolynomialRing(L, "xL") xL = L_poly.gen() # shifting the series makes our life easier trace_numerator, trace_denominator, norm_numerator, norm_denominator = [ L_poly(coeff)(L_poly.gen() + P0[0]) for coeff in trace_and_norm ] L_fpoly = L_poly.fraction_field() trace = L_fpoly(trace_numerator) / L_fpoly(trace_denominator) norm = L_fpoly(norm_numerator) / L_fpoly(norm_denominator) Xpoly = L_poly([norm(0), -trace(0), 1]) if verbose: print "xpoly = %s" % Xpoly if Xpoly.is_irreducible(): M = Xpoly.root_field("c") else: # this avoids bifurcation later on in the code M = NumberField(xL, "c") if verbose: print M xi_degree = max( [elt.degree() for elt in [trace_denominator, norm_denominator]]) D = 2 * xi_degree hard_bound = D + (4 + 2) soft_bound = hard_bound + 5 M_ps = PowerSeriesRing(M, "T", default_prec=soft_bound) T = M_ps.gen() Tsub = T + P0[0] trace_M = M_ps(trace) norm_M = M_ps(norm) sqrtdisc = sqrt(trace_M**2 - 4 * norm_M) x1 = (trace_M - sqrtdisc) / 2 x2 = (trace_M + sqrtdisc) / 2 y1 = sqrt(g(x1)) y2 = sqrt(g(x2)) iy = 1 / sqrt(g(Tsub)) dx1 = x1.derivative(T) dx2 = x2.derivative(T) dx1_y1 = dx1 / y1 dx2_y2 = dx2 / y2 eq1 = Matrix([[-2 * M_ps(alpha.row(0).list())(Tsub) * iy, dx1_y1, dx2_y2]]) eq2 = Matrix( [[-2 * M_ps(alpha.row(1).list())(Tsub) * iy, x1 * dx1_y1, x2 * dx2_y2]]) branches = Matrix([[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]]).transpose() meq1 = eq1 * branches meq2 = eq2 * branches algzero = False for j in range(4): if meq1[0, j] == 0 and meq2[0, j] == 0: algzero = True break if verbose: print "Done, verify_algebraically() = %s" % algzero return algzero
def list_to_factored_poly_otherorder(s, galois=False): """ Either return the polynomial in a nice factored form, or return a pair, with first entry the factored polynomial and the second entry a list describing the Galois groups of the factors. """ gal_list = [] if len(s) == 1: if galois: return [str(s[0]), [[0, 0]]] return str(s[0]) sfacts = factor(PolynomialRing(ZZ, 'T')(s)) sfacts_fc = [[v[0], v[1]] for v in sfacts] if sfacts.unit() == -1: sfacts_fc[0][0] *= -1 outstr = '' x = var('x') for v in sfacts_fc: this_poly = v[0] # if the factor is -1+T^2, replace it by 1-T^2 # this should happen an even number of times, mod powers if this_poly.substitute(T=0) == -1: this_poly = -1 * this_poly v[0] = this_poly if galois: this_degree = this_poly.degree() # hack because currently sage only handles monic polynomials: this_poly = expand(x**this_degree * this_poly.substitute(T=1 / x)) this_number_field = NumberField(this_poly, "a") this_gal = this_number_field.galois_group(type='pari') this_t_number = this_gal.group()._pari_()[2]._sage_() gal_list.append([this_degree, this_t_number]) vcf = v[0].list() started = False if len(sfacts) > 1 or v[1] > 1: outstr += '(' for i in range(len(vcf)): if vcf[i] != 0: if started and vcf[i] > 0: outstr += '+' started = True if i == 0: outstr += str(vcf[i]) else: if abs(vcf[i]) != 1: outstr += str(vcf[i]) elif vcf[i] == -1: outstr += '-' if i == 1: outstr += 'T' elif i > 1: outstr += 'T^{' + str(i) + '}' if len(sfacts) > 1 or v[1] > 1: outstr += ')' if v[1] > 1: outstr += '^{' + str(v[1]) + '}' if galois: if galois and len(sfacts_fc) == 2: if sfacts[0][0].degree() == 2 and sfacts[1][0].degree() == 2: troubletest = sfacts[0][0].disc() * sfacts[1][0].disc() if troubletest.is_square(): gal_list = [[2, 1]] return [outstr, gal_list] return outstr
def rational_univariate_representation(ideal): """ Suppose an ideal I in QQ[x_1,...,x_n] is 0 dimensional, and we want to describe all the points of the finite set V(I) in CC^n. A rational univariate representation (RUR) of V(I), is a collection of univariate polynomials h, g_0, g_1, ... , g_n in QQ[t] where deg(h) = #V(I) and deg(g_i) < deg(h) such that the points of V(I) correspond precisely to (g_1(z)/g_0(z), (g_2(z)/g_0(z), ... (g_n(z)/g_0(z)) where z in CC is a root of h. In this variant, we factor h into irreducibles return each part of the RUR individually. Example: sage: R = PolynomialRing(QQ, ['x', 'y', 'z']) sage: x, y, z = R.gens() sage: I = R.ideal([x + y + z*z, x*y*z - 3, x*x + y*y + z*z - 2]) sage: ans = rational_univariate_representation(I) sage: len(ans) 1 sage: K, rep, mult = ans[0] sage: mult 1 sage: h = K.polynomial(); h x^10 - 2*x^9 - 4*x^8 + 6*x^7 + 7*x^6 - 13*x^5 - 17/2*x^4 + 36*x^3 + 63/2*x^2 + 81/2 sage: rep[y] a sage: 1215 * rep[x] # Here g0 = 1215 8*a^9 + 8*a^8 - 8*a^7 - 246*a^6 + 128*a^5 + 550*a^4 - 308*a^3 - 636*a^2 + 639*a + 1917 sage: I.subs(rep).is_zero() True Here is an example using a Ptolemy variety: sage: M = Manifold('t00000') sage: obs = M.ptolemy_generalized_obstruction_classes(2)[1] sage: V = M.ptolemy_variety(2, obs) sage: I = V.ideal_with_non_zero_condition sage: ans = rational_univariate_representation(I) sage: ans[0][0].polynomial() x^8 - 4*x^7 - 2*x^6 + 14*x^5 + 14*x^4 - 7*x^3 - 13*x^2 - x + 5 For more, see: https://en.wikipedia.org/wiki/System_of_polynomial_equations#Rational_univariate_representation """ R = ideal.ring() vars = list(R.gens()) J = giac(ideal.gens()) rur = J.gbasis(vars, 'rur') # Yikes: giacpy_sage vs the giac interface give different types. # So using repr. if repr(rur[0]) != 'rur': raise ValueError(('Could not find RUR, got %r instead. ' 'Is the variety 0-dimensional?') % rur[0]) # [x.sage() for rur[4:]] doesn't work with the giac interface (it tries # to convert the slicing to a giac expression that giac does not understand) # # However, rur.sage()[0] gave some trouble, too. # # So we convert with .sage() after having checked we have rur, but before # we inspect the other items. rur = rur.sage() # Defining polynomial of number field, as a symbolic expression. p = rur[2] assert len(p.variables()) == 1 v = p.variables()[0] S = PolynomialRing(QQ, repr(v)) T = PolynomialRing(QQ, 'x') p = T(S(p)) p = p/p.leading_coefficient() ans = [] for q, e in p.factor(): K = NumberField(q, 'a') def toK(f): return K(T(S(f))) denom = toK(rur[3]) rep = [toK(f)/denom for f in rur[4:]] sub_dict = dict(zip(vars, rep)) assert all(g.subs(sub_dict) == 0 for g in ideal.gens()) ans.append((K, sub_dict, e)) return ans
def field(self): if not self.__field: f = PolynomialRing(ZZ, name='x')(str(self.__field_poly)) self.__field = QQ if f.degree() == 1 else NumberField(f, 'a') return self.__field
def pretty_print_polynomial_list(pol, base, str_var='x', str_gen='r'): if pol[0] in QQ: return str(PolynomialRing(QQ, str_var)(pol)) R = PolynomialRing(QQ, 't') F = NumberField(R(base), str_gen) return str(PolynomialRing(F, str_var)(pol))
def K(self): if not self.haskey('K'): self._data['K'] = NumberField(self.poly(), self.gen_name) return self._data['K']
def endomorphisms_upper_bound(frob_list, eta_char0=None): r""" INPUT: - ``frob_list`` -- a list of Frobenius polynomials of A - ``eta_char0`` -- a putative value for eta(A) OUTPUT: - a boolean = did we manage to provide a putative upper bound on the centers? - a message, explaining what we have achieved to do. - None or a putative value for eta(A) (if this matches the input value, then we proved that this value is correct) - None or the number of factors assuming that eta(A) computed above is correct - None or a list of tuples [ (e_j n_j, n_j * dim A_j, L_j, RR_j ) for j in range(1, t + 1)], where we are assuming that the eta(A) and t above are correct and therefore we have A^{al} = A^{al} = (A_1)^n_1 x ... x (A_k)^n_t where e_j ^2 is the dimension of End(A_j ^{al}) over its center and its center is a subfield of L_j. Assuming that L_j is indeed the center, then RR_j = End( A_j ^{n_j} ) \otimes RR. - Assuming eta(A) and t are correct, an upper bound for dim_Q End(A) """ g = int(frob_list[0][1].degree() / 2) if eta_char0 is None: eta = 4 * g * g # max value found for eta_p else: eta = 2 * eta_char0 t = g eta_lower = [] for p, f in frob_list: dimtotal, fieldext, endo = endomorphism_frob(f) if dimtotal < eta: eta = dimtotal t = len(endo) eta_lower = [] if dimtotal == eta: if len(endo) < t: t = len(endo) eta_lower = [] if len(endo) == t: eta_lower.append([p, f, len(endo), dimtotal, fieldext, endo]) if len(eta_lower) == 0: return False, "We did not manage to find any prime where eta(A_p) = 2 * eta(A)", None, None, None, None eta_char0 = eta / 2 multiset_char0 = None frob_factors = [[None] * len(eta_lower) for _ in range(t)] for i, (_, _, tp, etap, _, endo) in enumerate(eta_lower): assert t == tp assert eta == etap # endo[j] = mpj, mpj*deg(hpj), hpj # the multiset_char0 in the paper has y divided by 2 multiset = sorted([(x, y) for x, y, _ in endo]) if multiset_char0 is None: multiset_char0 = multiset frob_factors = {} for pair in Set(multiset): frob_factors[pair] = [[] for _ in eta_lower] if multiset_char0 != multiset: # we only managed to bound eta message = "We only managed to find an upper bound for eta." message += " If the upper bound for eta indeed is eta, then the number of factors is a strict upper bound" return False, message, eta_char0, t, None, None for x, y, hpj in endo: # endo[j] = mpj, mpj*deg(hpj), hpj frob_factors[(x, y)][i].append(polredabs(hpj)) # it looks like we have a consistent upper bound for eta and t message = "We have putatively computed eta and t." message += " Under this assumption, we bounded the corresponding centers." #We can try to bound the center of each factor output = [] total_dim = 0 for pair, frob_matrix in frob_factors.iteritems(): L = field_intersection_matrix(frob_matrix) ejnj, njdimAj = pair njdimAj = njdimAj // 2 for Lj in L: # the only real functionality of the NumberField that we use is # Ljmax.is_CM() # so it doesn't matter which field of maximal degree we take Ljmax = NumberField(Lj[-1][-1], 'a') RRj = RR_representation(njdimAj, Ljmax, ejnj) output.append((ejnj, njdimAj, Lj, RRj)) total_dim += ejnj**2 * Ljmax.degree() return True, message, eta_char0, t, output, total_dim
def coeff_to_nf(c): return NumberField(coeff_to_poly(c), 'a')
def __call__(self, hmult, vmult): r""" INPUT: - ``hmult`` -- multiplicities of the horizontal twists - ``vmult`` -- multiplicities of the vertical twists """ if len(hmult) != self._num_hcyls or len(vmult) != self._num_vcyls: raise ValueError("invalid input lengths") E = self._E H = E * diagonal_matrix(vmult) V = E.transpose() * diagonal_matrix(hmult) if self._num_hcyls < self._num_vcyls: F = H * V else: F = V * H p = F.charpoly() assert F.nrows() == F.ncols() == min( [self._num_hcyls, self._num_vcyls]) pf = max(p.roots(AA, False)) mp = pf.minpoly() if mp.degree() == 1: K = QQ pf = QQ(pf) else: fwd, bck, q = do_polred(pf.minpoly()) im_gen = fwd(pf) K = NumberField(q, 'a', embedding=im_gen) pf = bck(K.gen()) # Compute widths of the cylinders via Perron-Frobenius if self._num_hcyls < self._num_vcyls: hcirc = (F - pf).right_kernel_matrix() assert hcirc.nrows() == 1 assert hcirc.ncols() == self._num_hcyls hcirc = hcirc[0] assert all(x > 0 for x in hcirc) vcirc = V * hcirc c = 1 d = pf else: vcirc = (F - pf).right_kernel_matrix() assert vcirc.nrows() == 1 assert vcirc.ncols() == self._num_vcyls vcirc = vcirc[0] assert all(x > 0 for x in vcirc) hcirc = H * vcirc d = 1 c = pf # Solve linear systems to get heights h = [hcirc[i] * hmult[i] / c for i in range(self._num_hcyls)] v = [vcirc[i] * vmult[i] / d for i in range(self._num_vcyls)] C = ConvexPolygons(K) P = [] for i in range(self._o.nb_squares()): hi = h[self._hcycles[i]] vi = v[self._vcycles[i]] P.append(C(edges=[(vi, 0), (0, hi), (-vi, 0), (0, -hi)])) surface = Surface_list(base_ring=K) for p in P: surface.add_polygon(p) r = self._o.r_tuple() u = self._o.u_tuple() for i in range(self._o.nb_squares()): surface.set_edge_pairing(i, 1, r[i], 3) surface.set_edge_pairing(i, 0, u[i], 2) surface.set_immutable() return TranslationSurface(surface)