def factorize_matrix(m,M): #assert is_in_Gamma_1(m,M,determinant_condition = False) assert m.det().abs() == 1 a,b,c,d = m.list() if QQ(a).denominator() != 1: raise RuntimeError #return [m] assert a % M == 1 aabs = ZZ(a).abs() Zm = Zmod(M) for alphamul in sorted(range(-aabs.sqrt(50)/M,aabs.sqrt(50)/M),key = lambda x: ZZ(x).abs()): alpha = 1 + M*alphamul if alpha.abs() >= aabs: continue delta0 = (Zm(alpha)**(-1)).lift() for delta in xrange(delta0-10*M,delta0+10*M,M): if alpha * delta == 1: continue gamma0 = ZZ( (alpha*delta -1) / M) c1vec = gamma0.divisors() for c1 in c1vec: ulentry = (a*delta-b*c1*M).abs() urentry = (b*alpha-a*gamma0/c1).abs() if ulentry < aabs and urentry < b.abs(): gamma = c1*M beta = ZZ(gamma0/c1) r1 = Matrix(QQ,2,2,[alpha,beta,gamma,delta]) r2 = m*r1.adjugate() assert r1.determinant() == 1 assert is_in_Gamma_1(r1,M,determinant_condition = False) assert is_in_Gamma_1(r1,M,determinant_condition = False) V1 = factorize_matrix(r1,M) V2 = factorize_matrix(r2,M) return V2 + V1 return [m]
def get_Up_reps_bianchi(self, pi, pi_bar): B = self.small_group().B Upreps0 = [ Matrix(self.F, 2, 2, [pi, a, 0, 1]) for a in range(self.prime()) ] Upreps_bar0 = [ Matrix(self.F, 2, 2, [pi_bar, a, 0, 1]) for a in range(self.prime()) ] Upreps = [ B([(o[0, 0] + o[1, 1]) / 2, (o[0, 0] - o[1, 1]) / 2, (-o[0, 1] - o[1, 0]) / 2, (-o[0, 1] + o[1, 0]) / 2]) for o in Upreps0 ] Upreps_bar = [ B([(o[0, 0] + o[1, 1]) / 2, (o[0, 0] - o[1, 1]) / 2, (-o[0, 1] - o[1, 0]) / 2, (-o[0, 1] + o[1, 0]) / 2]) for o in Upreps_bar0 ] for o in Upreps: set_immutable(o) for o in Upreps_bar: set_immutable(o) return Upreps, Upreps_bar
def find_optimal_embeddings(F,use_magma = False,extra_conductor = 1,magma = None): w=F.maximal_order().ring_generators()[0] D = F.discriminant() ## this matrix gives an optimal embedding of conductor 1 if use_magma == True or extra_conductor != 1: if magma is None: from sage.interfaces.magma import magma tmp = magma.ReducedForms(str(D*extra_conductor**2),nvals = 1) G = [[tmp[i+1][j]._sage_() for j in [1,2,3]] for i in range(len(tmp))] elif F.class_number() == 1: assert extra_conductor == 1 return [Matrix(QQ,2,2,[w.trace(),-w.norm(),1,0])] else: assert extra_conductor == 1 fact = F(1) if D > 0 else w - 1/2 * w.trace() G = [] for I in [F.ideal(cl.gens()) for cl in F.class_group()]: alpha,beta = I.integral_basis() if QQ((alpha*beta.conjugate() - beta*alpha.conjugate())/fact) < 0: alpha,beta = beta,alpha nrm = I.norm() a = ZZ(alpha.norm()/nrm) c = ZZ(beta.norm()/nrm) b = ZZ((alpha+beta).norm()/nrm) - a - c G.append((a,b,c)) delta = extra_conductor * F.gen() if D%4 == 1 else 2*extra_conductor * F.gen() # delta = sqrt{discriminant} r,s = delta.coordinates_in_terms_of_powers()(w) # w = r + s*delta return [Matrix(QQ,2,2,[r-s*B,-2*s*C,2*s*A,r+s*B]) for A,B,C in G] # There's a typo in Darmon-Pollack pg.12, fixed by Juan Restrepo
def space(self): r''' Calculates the homology space as a Z-module. ''' verb = get_verbose() set_verbose(0) V = self.coefficient_module() R = V.base_ring() Vdim = V.dimension() G = self.group() gens = G.gens() ambient = R**(Vdim * len(gens)) if self.trivial_action(): cycles = ambient else: # Now find the subspace of cycles A = Matrix(R, Vdim, 0) for g in gens: for v in V.gens(): A = A.augment(matrix(R,Vdim,1,list(vector(g**-1 * v - v)))) K = A.right_kernel_matrix() cycles = ambient.submodule([ambient(list(o)) for o in K.rows()]) boundaries = [] for r in G.get_relation_words(): grad = self.twisted_fox_gradient(G(r).word_rep) for v in V.gens(): boundaries.append(cycles(ambient(sum([list(a * vector(v)) for a in grad],[])))) boundaries = cycles.submodule(boundaries) ans = cycles.quotient(boundaries) set_verbose(verb) return ans
def factorize_matrix(m,M): #assert is_in_Gamma_1(m,M,determinant_condition = False) assert m.det().abs() == 1 a,b,c,d = m.list() if QQ(a).denominator() != 1: raise RuntimeError #return [m] assert a % M == 1 aabs = ZZ(a).abs() Zm = Zmod(M) for alphamul in sorted(range(-aabs.sqrt(50)/M,aabs.sqrt(50)/M),key = lambda x: ZZ(x).abs()): alpha = 1 + M*alphamul if alpha.abs() >= aabs: continue delta0 = (Zm(alpha)**(-1)).lift() for delta in xrange(delta0-10*M,delta0+10*M,M): if alpha * delta == 1: continue gamma0 = ZZ( (alpha*delta -1) / M) c1vec = gamma0.divisors() for c1 in c1vec: ulentry = (a*delta-b*c1*M).abs() urentry = (b*alpha-a*gamma0/c1).abs() if ulentry < aabs and urentry < b.abs(): gamma = c1*M beta = ZZ(gamma0/c1) r1 = Matrix(QQ,2,2,[alpha,beta,gamma,delta]) r2 = m*r1.adjoint() assert r1.determinant() == 1 assert is_in_Gamma_1(r1,M,determinant_condition = False) assert is_in_Gamma_1(r1,M,determinant_condition = False) V1 = factorize_matrix(r1,M) V2 = factorize_matrix(r2,M) return V2 + V1 return [m]
def _convert_matrix_from_modsyms(symbs, T): r""" Given a space of modular symbols and a matrix T acting on it, calculate the matrix of the corresponding operator on the echelon-form basis of the corresponding space of modular forms. The matrix T *must* commute with the Hecke operators! We use this when T is either a Hecke operator, or a diamond operator. This will *not work* for the Atkin-Lehner operators, for instance, when there are oldforms present. OUTPUT: A pair `(T_e, ps)` with `T_e` the converted matrix and `ps` a list of pivot elements of the echelon basis. EXAMPLE:: sage: CuspForms(Gamma1(5), 6).diamond_bracket_matrix(3) # indirect doctest [ -1 0 0] [ 3 5 -12] [ 1 2 -5] """ d = symbs.rank() # create a vector space of appropriate dimension to # contain our q-expansions A = symbs.base_ring() r = symbs.sturm_bound() X = A ** r Y = X.zero_submodule() basis = [] basis_images = [] # we repeatedly use these matrices below, so we store them # once as lists to save time. hecke_matrix_ls = [symbs.hecke_matrix(m).list() for m in range(1, r + 1)] hecke_image_ls = [(T * symbs.hecke_matrix(m)).list() for m in range(1, r + 1)] # compute the q-expansions of some cusp forms and their # images under T_n for i in xrange(d ** 2): v = X([hecke_matrix_ls[m][i] for m in xrange(r)]) Ynew = Y.span(Y.basis() + [v]) if Ynew.rank() > Y.rank(): basis.append(v) basis_images.append(X([hecke_image_ls[m][i] for m in xrange(r)])) Y = Ynew if len(basis) == d: break # now we can compute the matrix acting on the echelonized space of mod forms # need to pass A as base ring since otherwise there are problems when the # space has dimension 0 bigmat = Matrix(A, basis).augment(Matrix(A, basis_images)) bigmat.echelonize() pivs = bigmat.pivots() return bigmat.matrix_from_rows_and_columns(range(d), [r + x for x in pivs]), pivs
def decompose(gtau,lmb,uu): if uu == 0: return [gtau] E_lambda = Matrix(QQ,2,2,[1,lmb,0,1]) #we know that E_lambda*gtau is a matrix [a,b,c,d] such that c=uu+ta for some unit uu; now we find uu and t MM=(E_lambda*gtau).change_ring(QQ) a,b,c,d=MM.list() t = QQ(c-uu)/QQ(a) E1i=Matrix(QQ,2,2,[1,0,uu*(1-a),1]) E2i=Matrix(QQ,2,2,[1,-1/uu,0,1]) E34i=Matrix(QQ,2,2,[1,0,c+t*(1-a),1]) E_x=(E34i*E2i*E1i)**(-1)*MM return [E_lambda**(-1), E34i,E2i,E1i,E_x]
def _find_initial_embedding_list(v0, M, W, orientation, OD, u): r''' . ''' F = v0.domain() p = v0.codomain().base_ring().prime() emblist = [] wD = OD.ring_generators()[0] u0vec = wD.coordinates_in_terms_of_powers()(u) u0vec_inv = wD.coordinates_in_terms_of_powers()(u**-1) assert wD.minpoly() == W.minpoly() Blist = [M2Z([1, 0, 0, 1])] + [ M2Z([ZZ(M / d1), i, 0, d1]) for d1 in ZZ(M).divisors() for i in range(-2 * ZZ(QQ(d1 / 2).ceil()), 2 * ZZ(QQ(d1 / 2).ceil()) + 1) ] for B in Blist: W_M = B * W * B**-1 if all([x.is_integral() for x in W_M.list()]) and ZZ(W_M[1, 0]) % M == 0: if orientation is not None: for ell, r in ZZ(M).factor(): if W_M[0, 0] % ell != orientation % ell: Wl = Matrix(ZZ, 2, 2, [0, -1, ell, 0]) W_M = Wl**(-1) * W_M * Wl assert all([ W_M[0, 0] % ell == orientation % ell for ell, r in ZZ(M).factor() ]) # Computation of tau_0: it's one of the roots of the minimal polynomial of W. tau0 = compute_tau0(v0, W_M, wD, return_exact=True) if F.class_number() > 1 and find_containing_affinoid( p, v0(tau0)).determinant().valuation( p) % 2 == 1 and orientation is not None: Wp = Matrix(ZZ, 2, 2, [0, -1, p, 0]) W_M = Wp**(-1) * W_M * Wp assert all([ W_M[0, 0] % ell == orientation % ell for ell, r in ZZ(M).factor() ]) tau0 = compute_tau0(v0, W_M, wD, return_exact=True) assert find_containing_affinoid( p, v0(tau0)).determinant().valuation(p) % 2 == 0 gtau_orig_1 = u0vec[0] + u0vec[1] * W_M gtau_orig_2 = u0vec_inv[0] + u0vec_inv[1] * W_M emblist.extend([(tau0, gtau_orig_1), (tau0, gtau_orig_2)]) if len(emblist) == 0: raise RuntimeError('No embeddings found !') verbose("Found %s initial embeddings." % len(emblist)) return emblist
def __init__(self, G, V, trivial_action = False): self._group = G self._coeffmodule = V self._trivial_action = trivial_action self._gen_pows = [] self._gen_pows_neg = [] if trivial_action: self._acting_matrix = lambda x, y: matrix(V.base_ring(),V.dimension(),V.dimension(),1) gens_local = [ (None, None) for g in G.gens() ] else: def acting_matrix(x,y): try: return V.acting_matrix(x,y) except: return V.acting_matrix(G.embed(x.quaternion_rep,V.base_ring().precision_cap()), y) self._acting_matrix = acting_matrix gens_local = [ (g, g**-1) for g in G.gens() ] onemat = G(1) try: dim = V.dimension() except AttributeError: dim = len(V.basis()) one = Matrix(V.base_ring(),dim,dim,1) for g, ginv in gens_local: A = self._acting_matrix(g, dim) self._gen_pows.append([one, A]) Ainv = self._acting_matrix(ginv, dim) self._gen_pows_neg.append([one, Ainv]) Parent.__init__(self) return
def get_Up_reps(self): if self._hardcode_matrices: B = self.small_group().B try: pi = self.ideal_p.gens_reduced()[0] pinorm = pi.norm() alist = list(self.ideal_p.residues()) except AttributeError: pi = self.prime() pinorm = pi alist = [a for a in range(pinorm)] Upreps0 = [Matrix(self.F, 2, 2, [pi, a, 0, 1]) for a in alist] Upreps = [ self.small_group().matrix_to_quaternion(o) for o in Upreps0 ] for o in Upreps: set_immutable(o) return Upreps wp = self.wp() if self.F == QQ and self.discriminant == 1: lam = -wp.determinant() else: lam = -wp.reduced_norm() tmp = [lam * o**-1 * wp**-1 for o in self.get_BT_reps()[1:]] for o in tmp: set_immutable(o) return tmp
def _convert_matrix_from_modsyms_eis(self, A): r""" Given a matrix acting on the space of modular symbols corresponding to this space, calculate the matrix of the operator it induces on this space itself. Used for Hecke and diamond operators. This is a minor modification of the code used for cusp forms, which is required because modular symbols "don't see the constant term": the modular symbol method calculates the matrix of the operator with respect to the unique basis of the modular forms space for which the *non-constant* coefficients are in echelon form, and we need to modify this to get a matrix with respect to the basis we're actually using. EXAMPLES:: sage: EisensteinForms(Gamma1(6), 3).hecke_matrix(3) # indirect doctest [ 1 0 72 0] [ 0 0 36 -9] [ 0 0 9 0] [ 0 1 -4 10] """ from .cuspidal_submodule import _convert_matrix_from_modsyms symbs = self.modular_symbols(sign=0) d = self.rank() wrong_mat, pivs = _convert_matrix_from_modsyms(symbs, A) c = Matrix(self.base_ring(), d, [self.basis()[i][j + 1] for i in range(d) for j in pivs]) return c * wrong_mat * ~c
def get_action_data(self, g, K=None): a, b, c, d = g.list() prec = self._prec if K is None: if hasattr(a, 'lift'): a, b, c, d = a.lift(), b.lift(), c.lift(), d.lift() p = g.parent().base_ring().prime() K = ZpCA(p, prec) else: K = g.parent().base_ring() Ps = PowerSeriesRing(K, 't', default_prec=prec) z = Ps.gen() zz = (d * z - b) / (-c * z + a) zz_ps0 = Ps(zz).add_bigoh(prec) if self._dlog: zz_ps = ((a * d - b * c) * (-c * z + a)**-2).add_bigoh(prec) else: zz_ps = Ps(1).add_bigoh(prec) if self.is_additive(): M = Matrix(ZZ, prec, prec, 0) for j in range(prec): for i, aij in enumerate(zz_ps.list()): M[i, j] = aij if j < prec - 1: # Don't need the last multiplication zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec) else: return M else: ans = [Ps(1), zz_ps] for _ in range(prec - 1): zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec) ans.append(zz_ps) return ans
def _compute_hecke_matrix(self, n): r""" EXAMPLES:: sage: CuspForms(GammaH(31, [7]), 1).hecke_matrix(7) [-1] sage: C = CuspForms(GammaH(124, [33]), 1) # long time sage: C.hecke_matrix(2) # long time [ 0 0 -1 -1 0 1 0] [ 1 0 0 -1 -1 -1 0] [ 0 0 0 -1 1 1 -1] [ 0 1 0 -1 0 0 0] [ 0 0 -1 0 0 1 1] [ 0 0 0 -1 0 0 -1] [ 0 0 0 0 0 1 0] sage: C.hecke_matrix(7) # long time [ 0 1 0 -1 0 0 1] [ 0 -1 0 0 0 0 0] [ 0 1 -1 0 0 0 1] [ 0 0 0 -1 0 0 0] [ 0 1 1 0 0 -1 0] [ 1 0 -1 -1 -1 0 1] [ 0 1 0 0 1 0 0] sage: C.hecke_matrix(23) == 0 # long time True """ chars = self.group().characters_mod_H(sign=-1, galois_orbits=True) A = Matrix(QQ, 0, 0) for c in chars: chi = c.minimize_base_ring() d = weight1.dimension_wt1_cusp_forms(chi) e = chi.base_ring().degree() H = Matrix(QQ, d * e, d * e) from .constructor import CuspForms M = CuspForms(chi, 1).hecke_matrix(n) if e == 1: H = M else: for i in range(d): for j in range(d): H[e * i:e * (i + 1), e * j:e * (j + 1)] = M[i, j].matrix().transpose() A = A.block_sum(H) t = self._transformation_matrix() return t * A * ~t
def get_Up_reps_bianchi(self, pi, pi_bar): if not self._hardcode_matrices: raise NotImplementedError('For Bianchi, need to hardcode matrices') B = self.small_group().B # alist = range(self.prime()) alist = list(self.ideal_p.residues()) Upreps0 = [Matrix(self.F, 2, 2, [pi, a, 0, 1]) for a in alist] Upreps_bar0 = [Matrix(self.F, 2, 2, [pi_bar, a, 0, 1]) for a in alist] Upreps = [self.small_group().matrix_to_quaternion(o) for o in Upreps0] Upreps_bar = [ self.small_group().matrix_to_quaternion(o) for o in Upreps_bar0 ] for o in Upreps: set_immutable(o) for o in Upreps_bar: set_immutable(o) return Upreps, Upreps_bar
def find_matrix_from_cusp(self, cusp): r''' Returns a matrix gamma and a cusp representative modulo Gamma0(N) (c2:d2), represented as a matrix (a,b;c,d), such that gamma * cusp = (c2:d2). ''' a, c = cusp reduction_table, _ = self.cusp_reduction_table() P = self.get_P1List() if hasattr(P.N(),'number_field'): K = P.N().number_field() else: K = QQ # Find a matrix g = [a,b,c,d] in SL2(O_K) such that g * a/c = oo # Define (c1:d1) to be the rep in P1(O_K/N) such that (c1:d1) == (c:d). if c == 0: ## case cusp infinity: (a,c) should equal (1,0) a = 1 g = Matrix(2,2,[1,0,0,1]) c1, d1 = P.normalize(0, 1) else: if K == QQ: g0, d, b = ZZ(a).xgcd(-c) if g0 != 1: a /= g0 c /= g0 else: """ Compute gcd if a,c are coprime in F, and x,y such that ax + cy = 1. """ if a.parent() != c.parent(): raise ValueError('a,c not in the same field.') if a.gcd(c) != 1: raise ValueError('a,c not coprime.') d = next(o for o in K.ideal(c).residues() if a * o - 1 in K.ideal(c)) b = (a * d - 1) / c g = Matrix(2,2,[[d,-b],[-c,a]]) # the inverse c1, d1 = P.normalize(c, d) assert g.determinant() == 1 A, T = reduction_table[(c1,d1)] gamma = A.parent()(A * T * g) return gamma, A
def make_twoadic_data(self): if not self.cm: self.twoadicdata = twoadicdata = db.ec_2adic.lookup( self.lmfdb_label) from sage.matrix.all import Matrix twoadicdata['gen_matrices'] = ','.join( latex(Matrix(2, 2, M)) for M in twoadicdata['twoadic_gens']) twoadicdata['rouse_url'] = ''.join( [ROUSE_URL_PREFIX, twoadicdata['twoadic_label'], ".html"])
def _convert_matrix_from_modsyms(symbs, T): r""" Given a space of modular symbols and a matrix T acting on it, calculate the matrix of the corresponding operator on the echelon-form basis of the corresponding space of modular forms. The matrix T *must* commute with the Hecke operators! We use this when T is either a Hecke operator, or a diamond operator. This will *not work* for the Atkin-Lehner operators, for instance, when there are oldforms present. OUTPUT: A pair `(T_e, ps)` with `T_e` the converted matrix and `ps` a list of pivot elements of the echelon basis. EXAMPLE:: sage: CuspForms(Gamma1(5), 6).diamond_bracket_matrix(3) # indirect doctest [ -1 0 0] [ 3 5 -12] [ 1 2 -5] """ d = symbs.rank() # create a vector space of appropriate dimension to # contain our q-expansions A = symbs.base_ring() r = symbs.sturm_bound() X = A**r Y = X.zero_submodule() basis = [] basis_images = [] # we repeatedly use these matrices below, so we store them # once as lists to save time. hecke_matrix_ls = [symbs.hecke_matrix(m).list() for m in range(1, r + 1)] hecke_image_ls = [(T * symbs.hecke_matrix(m)).list() for m in range(1, r + 1)] # compute the q-expansions of some cusp forms and their # images under T_n for i in xrange(d**2): v = X([hecke_matrix_ls[m][i] for m in xrange(r)]) Ynew = Y.span(Y.basis() + [v]) if Ynew.rank() > Y.rank(): basis.append(v) basis_images.append(X([hecke_image_ls[m][i] for m in xrange(r)])) Y = Ynew if len(basis) == d: break # now we can compute the matrix acting on the echelonized space of mod forms # need to pass A as base ring since otherwise there are problems when the # space has dimension 0 bigmat = Matrix(A, basis).augment(Matrix(A, basis_images)) bigmat.echelonize() pivs = bigmat.pivots() return bigmat.matrix_from_rows_and_columns(range(d), [r + x for x in pivs]), pivs
def get_BT_reps(self): reps = [self.Gn.B(1)] + [None for i in xrange(self.p)] emb = self.get_embedding(20) matrices = [(i + 1, matrix(QQ, 2, 2, [i, 1, -1, 0])) for i in xrange(self.p)] if self._matrix_group: verbose('Using hard-coded matrices for BT (Bianchi)') if F == QQ: wp = self.wp() return [self.Gn(1).quaternion_rep] + [ 1 / self.p * wp * matrix(QQ, 2, 2, [1, -i, 0, self.p]) for i in xrange(self.p) ] else: pi = self.ideal_p.gens_reduced()[0] B = self.Gn.B BTreps0 = [ Matrix(self.F, 2, 2, [0, -1, 1, -i]) for a in range(self.prime()) ] BTreps = [self.Gn(1).quaternion_rep] + [ self.Gn( B([(o[0, 0] + o[1, 1]) / 2, (o[0, 0] - o[1, 1]) / 2, (-o[0, 1] - o[1, 0]) / 2, (-o[0, 1] + o[1, 0]) / 2])).quaternion_rep for o in BTreps0 ] return BTreps for n_iters, elt in enumerate(self.Gn.enumerate_elements()): new_inv = elt**(-1) embelt = emb(elt) if (embelt[0, 0] - 1).valuation() > 0 and all([ not self.is_in_Gpn_order(o * new_inv) for o in reps if o is not None ]): if hasattr(self.Gpn, 'nebentypus'): tmp = self.do_tilde(embelt)**-1 tmp = tmp[0, 0] / (self.p**tmp[0, 0].valuation()) tmp = ZZ(tmp.lift()) % self.Gpn.level if tmp not in self.Gpn.nebentypus: continue for idx, o1 in enumerate(matrices): i, mat = o1 if is_in_Gamma0loc(embelt * mat, det_condition=False): reps[i] = set_immutable(elt) del matrices[idx] verbose( '%s, len = %s/%s' % (n_iters, self.p + 1 - len(matrices), self.p + 1)) if len(matrices) == 0: return reps break
def _compute_diamond_matrix(self, d): r""" EXAMPLES:: sage: CuspForms(GammaH(31, [7]), 1).diamond_bracket_matrix(3) [-1] sage: C = CuspForms(GammaH(124, [33]), 1) # long time sage: D = C.diamond_bracket_matrix(3); D # long time [ 0 0 0 -1 1 0 0] [ 0 -1 0 0 0 0 0] [ 2 1 1 -2 -1 -2 -1] [ 0 0 0 -1 0 0 0] [-1 0 0 1 1 0 0] [ 2 0 0 -2 -1 -1 0] [ 0 2 1 0 0 -1 0] sage: t = C._transformation_matrix(); ~t * D * t # long time [ 1 1 0 0 0 0 0] [-1 0 0 0 0 0 0] [ 0 0 1 1 0 0 0] [ 0 0 -1 0 0 0 0] [ 0 0 0 0 -1 0 0] [ 0 0 0 0 0 -1 0] [ 0 0 0 0 0 0 -1] """ chars=self.group().characters_mod_H(sign=-1, galois_orbits=True) A = Matrix(QQ, 0, 0) for c in chars: chi = c.minimize_base_ring() dim = weight1.dimension_wt1_cusp_forms(chi) if chi.base_ring() == QQ: m = Matrix(QQ, 1, 1, [chi(d)]) else: m = chi(d).matrix().transpose() for i in range(dim): A = A.block_sum(m) t = self._transformation_matrix() return t * A * ~t
def is_irred_rep(self, image, n, force=False): """ Returns `True` if the map generated by mapping the generators to the matrices defined in `image` is an `n`-dimensional irreducible representation of `self`, and `False` otherwise. Like above, the entries of `image` must be `n`-by-`n` matrices with entries in the algebraic closure of the base field of `self`. Its length must match the number of generators of `self.` Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') if not self.is_rep(image, n): return False from sage.matrix.all import Matrix from sage.rings.number_field.number_field import is_NumberField import math M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) image = [M(image[i]).list() for i in range(len(image))] if n <= 6 and is_NumberField(self.base_field()): p = 2 * n else: p = int( math.floor(n * math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) + n / float(2) - 3)) prod_set = list(self._create_prod_set(image, n, p)) prod_set.append(M.one()) vector = [mat.list() for mat in prod_set] return 0 not in Matrix(vector).echelon_form().diagonal()
def base_change_matrix(self): r""" Return the base change from the standard basis of the vector space of ``self`` to the basis given by the independent roots of ``self``. .. TODO:: For non-well-generated groups there is a conflict with construction of the matrix for an element. EXAMPLES:: sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: W.base_change_matrix() # optional - gap3 [1 0] [0 1] sage: W = ReflectionGroup(23) # optional - gap3 sage: W.base_change_matrix() # optional - gap3 [1 0 0] [0 1 0] [0 0 1] sage: W = ReflectionGroup((3,1,2)) # optional - gap3 sage: W.base_change_matrix() # optional - gap3 [1 0] [1 1] sage: W = ReflectionGroup((4,2,2)) # optional - gap3 sage: W.base_change_matrix() # optional - gap3 [ 1 0] [E(4) 1] """ from sage.matrix.all import Matrix return Matrix(list(self.independent_roots())).inverse()
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 make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} data['ainvs'] = [ZZ(ai) for ai in self.ainvs] data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) # extract data about MW rank, generators, heights and torsion: self.make_mw() # get more data from the database entry data['equation'] = self.equation local_data = self.local_data D = self.signD * prod([ld['p']**ld['ord_disc'] for ld in local_data]) for ld in local_data: ld['kod'] = ld['kod'].replace("\\\\", "\\") data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] data['minq_label'] = self.min_quad_twist[ 'lmfdb_label'] if self.label_type == 'LMFDB' else self.min_quad_twist[ 'label'] data['minq_info'] = '(itself)' if minqD == 1 else '(by {})'.format( minqD) if self.degree is None: data['degree'] = 0 # invalid, but will be displayed nicely else: data['degree'] = self.degree try: data['an'] = self.anlist data['ap'] = self.aplist except AttributeError: r = db.ec_curves.lucky({'lmfdb_iso': self.lmfdb_iso, 'number': 1}) data['an'] = r['anlist'] data['ap'] = r['aplist'] data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['galois_images'] = [ trim_galois_image_code(s) for s in self.mod_p_images ] data['non_maximal_primes'] = self.non_maximal_primes data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_maximal_primes'], data['galois_images'])] data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.non_maximal_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join([str(p) for p in data['cm_ramp']]) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.one_deg = ZZ(self.class_deg).is_prime() isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & other BSD data self.make_bsd() # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago): this is proved for N up to # OPTIMALITY_BOUND (and when there is only one curve in an # isogeny class, obviously) and expected for all N. # Column 'optimality' is 1 for certainly optimal curves, 0 for # certainly non-optimal curves, and is n>1 if the curve is one # of n in the isogeny class which may be optimal given current # knowledge. # Column "manin_constant' is the correct Manin constant # assuming that the optimal curve in the class is known, or # otherwise if it is the curve with (Cremona) number 1. # The code here allows us to update the display correctly by # changing one line in this file (defining OPTIMALITY_BOUND) # without changing the data. data['optimality_bound'] = OPTIMALITY_BOUND data[ 'manin_constant'] = self.manin_constant # (conditional on data['optimality_known']) if N < OPTIMALITY_BOUND: data['optimality_code'] = int( self.number == (3 if self.iso == '990h' else 1)) data['optimality_known'] = True data['manin_known'] = True if self.label_type == 'Cremona': data[ 'optimal_label'] = '990h3' if self.iso == '990h' else self.iso + '1' else: data[ 'optimal_label'] = '990.i3' if self.lmfdb_iso == '990.i' else self.lmfdb_iso + '1' else: data['optimality_code'] = self.optimality data['optimality_known'] = (self.optimality < 2) if self.optimality == 1: data['manin_known'] = True data[ 'optimal_label'] = self.label if self.label_type == 'Cremona' else self.lmfdb_label else: if self.number == 1: data['manin_known'] = False data[ 'optimal_label'] = self.label if self.label_type == 'Cremona' else self.lmfdb_label else: # find curve #1 in this class and its optimailty code: opt_curve = db.ec_curves.lucky( { 'iso': self.iso, 'number': 1 }, projection=['label', 'lmfdb_label', 'optimality']) data['manin_known'] = (opt_curve['optimality'] == 1) data['optimal_label'] = opt_curve[ 'label' if self.label_type == 'Cremona' else 'lmfdb_label'] data['p_adic_data_exists'] = False if data['optimality_code'] == 1: data['p_adic_data_exists'] = db.ec_padic.exists( {'lmfdb_iso': self.lmfdb_iso}) # Iwasawa data (where present) self.make_iwasawa() # Torsion growth data (where present) self.make_torsion_growth() data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = ".".join( [str(cond), str(2), 'a', iso]) self.newform_link = url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso) self.newform_exists_in_db = db.mf_newforms.label_exists( self.newform_label) self._code = None if self.label_type == 'Cremona': self.class_url = url_for(".by_ec_label", label=self.iso) self.class_name = self.iso else: self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.class_name = self.lmfdb_iso data['class_name'] = self.class_name data['number'] = self.number self.friends = [('Isogeny class ' + self.class_name, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_ec_label", label=data['minq_label'])), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv))] lfun_url = url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso) origin_url = lfun_url.lstrip('/L/').rstrip('/') if db.lfunc_instances.exists({'url': origin_url}): self.friends += [('L-function', lfun_url)] else: self.friends += [('L-function not available', "")] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.lmfdb_label)), ('Code to Magma', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Code to SageMath', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Code to GP', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [ ('Label', self.label if self.label_type == 'Cremona' else self.lmfdb_label), (None, self.plot_link), ('Conductor', '%s' % data['conductor']), ('Discriminant', '%s' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '%s' % self.mw['rank']), ('Torsion Structure', '\(%s\)' % self.mw['tor_struct']) ] if self.label_type == 'Cremona': self.title = "Elliptic Curve with Cremona label {} (LMFDB label {})".format( self.label, self.lmfdb_label) else: self.title = "Elliptic Curve with LMFDB label {} (Cremona label {})".format( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} try: data['ainvs'] = [int(c) for c in self.xainvs[1:-1].split(',')] except AttributeError: data['ainvs'] = [int(ai) for ai in self.ainvs] data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) mw = self.mw = {} mw['rank'] = self.rank mw['int_points'] = '' if self.xintcoords: a1, a2, a3, a4, a6 = [ZZ(a) for a in data['ainvs']] def lift_x(x): f = ((x + a2) * x + a4) * x + a6 b = (a1 * x + a3) d = (b * b + 4 * f).sqrt() return (x, (-b + d) / 2) mw['int_points'] = ', '.join( web_latex(lift_x(x)) for x in self.xintcoords) mw['generators'] = '' mw['heights'] = [] if self.gens: mw['generators'] = [ web_latex(tuple(P)) for P in parse_points(self.gens) ] mw['tor_order'] = self.torsion tor_struct = [int(c) for c in self.torsion_structure] if mw['tor_order'] == 1: mw['tor_struct'] = '\mathrm{Trivial}' mw['tor_gens'] = '' else: mw['tor_struct'] = ' \\times '.join( ['\Z/{%s}\Z' % n for n in tor_struct]) mw['tor_gens'] = ', '.join( web_latex(tuple(P)) for P in parse_points(self.torsion_generators)) # try to get all the data we need from the database entry (now in self) try: data['equation'] = self.equation local_data = self.local_data D = self.signD * prod( [ld['p']**ld['ord_disc'] for ld in local_data]) data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] minq_label = self.min_quad_twist['label'] data['minq_label'] = db_ec().find_one( {'label': minq_label}, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD try: data['degree'] = self.degree except AttributeError: data['degree'] = 0 # invalid, but will be displayed nicely mw['heights'] = self.heights if self.number == 1: data['an'] = self.anlist data['ap'] = self.aplist else: r = db_ec().find_one({ 'lmfdb_iso': self.lmfdb_iso, 'number': 1 }, ['anlist', 'aplist']) data['an'] = r['anlist'] data['ap'] = r['aplist'] # otherwise fall back to computing it from the curve except AttributeError: print("Falling back to constructing E") self.E = EllipticCurve(data['ainvs']) data['equation'] = web_latex(self.E) data['disc'] = D = self.E.discriminant() Nfac = N.factor() Dfac = D.factor() bad_primes = [p for p, e in Nfac] try: data['degree'] = self.degree except AttributeError: try: data['degree'] = self.E.modular_degree() except RuntimeError: data['degree'] = 0 # invalid, but will be displayed nicely minq, minqD = self.E.minimal_quadratic_twist() data['minq_D'] = minqD if minqD == 1: data['minq_label'] = self.lmfdb_label data['minq_info'] = '(itself)' else: # This relies on the minimal twist being in the # database, which is true when the database only # contains the Cremona database. It would be a good # idea if, when the database is extended, we ensured # that for any curve included, all twists of smaller # conductor are also included. minq_ainvs = [str(c) for c in minq.ainvs()] data['minq_label'] = db_ec().find_one( { 'jinv': str(self.E.j_invariant()), 'ainvs': minq_ainvs }, ['lmfdb_label'])['lmfdb_label'] data['minq_info'] = '(by %s)' % minqD if self.gens: self.generators = [self.E(g) for g in parse_points(self.gens)] mw['heights'] = [P.height() for P in self.generators] data['an'] = self.E.anlist(20, python_ints=True) data['ap'] = self.E.aplist(100, python_ints=True) self.local_data = local_data = [] for p in bad_primes: ld = self.E.local_data(p, algorithm="generic") local_data_p = {} local_data_p['p'] = p local_data_p['cp'] = ld.tamagawa_number() local_data_p['kod'] = web_latex(ld.kodaira_symbol()).replace( '$', '') local_data_p['red'] = ld.bad_reduction_type() rootno = -ld.bad_reduction_type() if rootno == 0: rootno = self.E.root_number(p) local_data_p['rootno'] = rootno local_data_p['ord_cond'] = ld.conductor_valuation() local_data_p['ord_disc'] = ld.discriminant_valuation() local_data_p['ord_den_j'] = max( 0, -self.E.j_invariant().valuation(p)) local_data.append(local_data_p) # If we got the data from the database, the root numbers may # not have been stored there, so we have to compute them. If # there are additive primes this means constructing the curve. for ld in self.local_data: if not 'rootno' in ld: rootno = -ld['red'] if rootno == 0: try: E = self.E except AttributeError: self.E = E = EllipticCurve(data['ainvs']) rootno = E.root_number(ld['p']) ld['rootno'] = rootno minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label']) data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] try: data['galois_images'] = [ trim_galois_image_code(s) for s in self.galois_images ] data['non_surjective_primes'] = self.non_surjective_primes except AttributeError: #print "No Galois image data" data['galois_images'] = [] data['non_surjective_primes'] = [] data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_surjective_primes'], data['galois_images'])] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.ncurves = db_ec().count({'lmfdb_iso': self.lmfdb_iso}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & BSD data bsd = self.bsd = {} r = self.rank if r >= 2: bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r, r) elif r: bsd['lder_name'] = "L'(E,1)" else: bsd['lder_name'] = "L(E,1)" bsd['reg'] = self.regulator bsd['omega'] = self.real_period bsd['sha'] = int(0.1 + self.sha_an) bsd['lder'] = self.special_value # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago) if self.iso == '990h': data['Gamma0optimal'] = bool(self.number == 3) else: data['Gamma0optimal'] = bool(self.number == 1) data['p_adic_data_exists'] = False if data['Gamma0optimal']: data['p_adic_data_exists'] = (padic_db().find({ 'lmfdb_iso': self.lmfdb_iso }).count()) > 0 tamagawa_numbers = [ZZ(ld['cp']) for ld in local_data] cp_fac = [cp.factor() for cp in tamagawa_numbers] cp_fac = [ latex(cp) if len(cp) < 2 else '(' + latex(cp) + ')' for cp in cp_fac ] bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac) bsd['tamagawa_product'] = prod(tamagawa_numbers) data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = newform_label( cond, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self._code = None self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)), ('L-function', url_for("l_functions.l_function_ec_page", label=self.lmfdb_label))] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', label=self.lmfdb_iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)), ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Download Sage code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot self.properties = [('Label', self.lmfdb_label), (None, self.plot_link), ('Conductor', '\(%s\)' % data['conductor']), ('Discriminant', '\(%s\)' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '\(%s\)' % mw['rank']), ('Torsion Structure', '\(%s\)' % mw['tor_struct'])] self.title = "Elliptic Curve %s (Cremona label %s)" % ( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def is_Gamma0_equivalent(self, other, N, Transformation=False): r""" Checks if cusps ``self`` and ``other`` are `\Gamma_0(N)`- equivalent. INPUT: - ``other`` -- a number field cusp or a list of two number field elements which define a cusp. - ``N`` -- an ideal of the number field (level) OUTPUT: - bool -- ``True`` if the cusps are equivalent. - a transformation matrix -- (if ``Transformation=True``) a list of integral elements [a, b, c, d] which are the entries of a 2x2 matrix M in `\Gamma_0(N)` such that M * ``self`` = ``other`` if ``other`` and ``self`` are `\Gamma_0(N)`- equivalent. If ``self`` and ``other`` are not equivalent it returns zero. EXAMPLES: :: sage: K.<a> = NumberField(x^3-10) sage: N = K.ideal(a-1) sage: alpha = NFCusp(K, 0) sage: beta = NFCusp(K, oo) sage: alpha.is_Gamma0_equivalent(beta, N) False sage: alpha.is_Gamma0_equivalent(beta, K.ideal(1)) True sage: b, M = alpha.is_Gamma0_equivalent(beta, K.ideal(1),Transformation=True) sage: alpha.apply(M) Cusp Infinity of Number Field in a with defining polynomial x^3 - 10 :: sage: k.<a> = NumberField(x^2+23) sage: N = k.ideal(3) sage: alpha1 = NFCusp(k, a+1, 4) sage: alpha2 = NFCusp(k, a-8, 29) sage: alpha1.is_Gamma0_equivalent(alpha2, N) True sage: b, M = alpha1.is_Gamma0_equivalent(alpha2, N, Transformation=True) sage: alpha1.apply(M) == alpha2 True sage: M[2] in N True """ k = self.number_field() other = NFCusp(k, other) if not (self.ideal() / other.ideal()).is_principal(): if not Transformation: return False else: return False, 0 reps = list_of_representatives(N) alpha1 = NFCusp(k, self, lreps=reps) alpha2 = NFCusp(k, other, lreps=reps) delta = k.ideal(alpha1.__b) + N if (k.ideal(alpha2.__b) + N) != delta: if not Transformation: return False else: return False, 0 M1 = alpha1.ABmatrix() M2 = alpha2.ABmatrix() A = alpha1.ideal() B = k.ideal(M1[1], M1[3]) ABdelta = A * B * delta * delta units = units_mod_ideal(ABdelta) for u in units: if (M2[2] * M1[3] - u * M1[2] * M2[3]) in ABdelta: if not Transformation: return True else: AuxCoeff = [1, 0, 0, 1] Aux = M2[2] * M1[3] - u * M1[2] * M2[3] if Aux in A * B * N: if not u == 1: AuxCoeff[3] = u else: A1 = (A * B * N) / ABdelta A2 = B * k.ideal(M1[2] * M2[2]) / (A * ABdelta) f = A1.element_1_mod(A2) w = ((1 - f) * Aux) / (M1[2] * M2[2]) AuxCoeff[3] = u AuxCoeff[1] = w from sage.matrix.all import Matrix Maux = Matrix(k, 2, AuxCoeff) M1inv = Matrix(k, 2, M1).inverse() Mtrans = Matrix(k, 2, M2) * Maux * M1inv assert Mtrans[1][0] in N return True, Mtrans.list() if not Transformation: return False else: return False, 0
def make_class(self): self.CM = self.cm N, iso, number = split_lmfdb_label(self.lmfdb_iso) # Extract the size of the isogeny class from the database ncurves = self.class_size # Create a list of the curves in the class from the database self.curves = [db_ec().find_one({'iso':self.iso, 'lmfdb_number': i+1}) for i in range(ncurves)] # Set optimality flags. The optimal curve is number 1 except # in one case which is labeled differently in the Cremona tables for c in self.curves: c['optimal'] = (c['number']==(3 if self.label == '990h' else 1)) c['ai'] = parse_ainvs(c['xainvs']) c['url'] = url_for(".by_triple_label", conductor=N, iso_label=iso, number=c['lmfdb_number']) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) # 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.newform = web_latex(PowerSeriesRing(QQ, 'q')(self.anlist, 20, check=True)) self.newform_label = newform_label(N,2,1,iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self.lfunction_link = url_for("l_functions.l_function_ec_page", conductor_label = N, isogeny_class_label = iso) self.friends = [('L-function', self.lfunction_link)] if not self.CM: self.CM = "no" if int(N)<=300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor = N, isogeny = iso))] if int(N)<=50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor = N, isogeny = iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.properties = [('Label', self.lmfdb_iso), ('Number of curves', str(ncurves)), ('Conductor', '\(%s\)' % N), ('CM', '%s' % self.CM), ('Rank', '\(%s\)' % self.rank), ('Graph', ''),(None, self.graph_link) ] self.downloads = [('Download coefficients of newform', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=1000)), ('Download stored data for all curves', url_for(".download_EC_all", label=self.lmfdb_iso))] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % (self.lmfdb_iso, self.iso) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, ' ')] self.code = {} self.code['show'] = {'sage':''} # use default show names self.code['class'] = {'sage':'E = EllipticCurve("%s1")\n'%(self.lmfdb_iso) + '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 berlekamp_welsh(deg, points): r""" Reconstruct polynomial with Berlekamp-Welsh algorithm. INPUT: - ``deg`` -- degree of polynomial to reconstruct. - ``points`` -- array of points (list of (x,y)-tuples). OUTPUT: Reconstructed polynomial. EXAMPLES:: sage: from sage.crypto.smc.berlekamp_welsh import berlekamp_welsh sage: from sage.rings.finite_rings.constructor import FiniteField sage: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing Reconstruction with errors:: sage: order = 2**8 sage: F = FiniteField(order, 'a') sage: P = PolynomialRing(F, 'x') sage: n = 7 sage: deg = 2 sage: poly = F.fetch_int(42) sage: for i in range(1, deg+1): poly += F.random_element() * P.gen()**i sage: # evaluate polynomial at different points (shares) sage: points = [(F.fetch_int(i), poly(F.fetch_int(i))) for i in range(1, n+1)] sage: poly == berlekamp_welsh(deg, points) True sage: # introduce error sage: points[0] = (points[0][0], points[0][1] + F.fetch_int(9)) sage: poly == berlekamp_welsh(deg, points) True """ # check input vector F = points[0][0].parent() if not F.is_field(): raise TypeError("points must be of field type.") for x, y in points: if x.parent() != F or y.parent() != F: raise TypeError("all points must be from same field.") # generate and solve system of linear equations from sage.functions.all import floor deg_E = floor((len(points) - (deg + 1)) / 2.) deg_Q = deg_E + deg from sage.matrix.all import Matrix from sage.all import vector sys_size = deg_Q + 1 + deg_E A = Matrix(F, sys_size) b = vector(F, sys_size) for n, (x, y) in enumerate(points): A[n] = ([x**i for i in range(deg_Q+1)]+[-y * x**i for i in range(deg_E)]) b[n] = (y * x**deg_E) QE = A.solve_right(b) # reconstruct polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing P = PolynomialRing(F, 'x') Q = sum([coeff * P.gen()**i for i, coeff in enumerate(QE[:deg_Q+1])]); E = P.gen()**deg_E E += sum([coeff * P.gen()**i for i, coeff in enumerate(QE[deg_Q+1:])]); P = Q.quo_rem(E)[0] return P
def make_class(self): self.ainvs_str = self.ainvs self.ainvs = [int(a) for a in self.ainvs_str] self.E = EllipticCurve(self.ainvs) self.CM = self.E.has_cm() try: # Extract the isogeny degree matrix from the database size = len(self.isogeny_matrix) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) except AttributeError: # Failsafe: construct it from scratch self.isogeny_matrix = self.E.isogeny_class(order="lmfdb").matrix() size = self.isogeny_matrix.nrows() self.ncurves = size # 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 # Create a list of the curves in the class from the database self.db_curves = [self.E] self.optimal_flags = [False] * size self.degrees = [0] * size if self.degree: self.degrees[0] = self.degree else: try: self.degrees[0] = self.E.modular_degree() except RuntimeError: pass # Fill in the curves in the class by looking each one up in the db: self.cremona_labels = [self.label] + [0] * (size - 1) if self.number == 1: self.optimal_flags[0] = True for i in range(2, size + 1): Edata = db_ec().find_one({'lmfdb_label': self.lmfdb_iso + str(i)}) Ei = EllipticCurve([int(a) for a in Edata['ainvs']]) self.cremona_labels[i - 1] = Edata['label'] if Edata['number'] == 1: self.optimal_flags[i - 1] = True if 'degree' in Edata: self.degrees[i - 1] = Edata['degree'] else: try: self.degrees[i - 1] = Ei.modular_degree() except RuntimeError: pass self.db_curves.append(Ei) if self.iso == '990h': # this isogeny class is labeled wrong in Cremona's tables self.optimal_flags = [False, False, True, False] self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) N, iso, number = split_lmfdb_label(self.lmfdb_iso) self.newform = web_latex(self.E.q_eigenform(10)) self.newform_label = newform_label(N, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=1, label=iso) newform_exists_in_db = is_newform_in_db(self.newform_label) self.lfunction_link = url_for("l_functions.l_function_ec_page", label=self.lmfdb_iso) self.curves = [ dict([('label', self.lmfdb_iso + str(i + 1)), ('url', url_for(".by_triple_label", conductor=N, iso_label=iso, number=i + 1)), ('cremona_label', self.cremona_labels[i]), ('ainvs', str(list(c.ainvs()))), ('torsion', c.torsion_order()), ('degree', self.degrees[i]), ('optimal', self.optimal_flags[i])]) for i, c in enumerate(self.db_curves) ] self.friends = [('L-function', self.lfunction_link)] if not self.CM: if int(N) <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso))] if int(N) <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', label=self.lmfdb_iso))] if newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.properties = [('Label', self.lmfdb_iso), ('Number of curves', str(self.ncurves)), ('Conductor', '\(%s\)' % N), ('CM', '%s' % self.CM), ('Rank', '\(%s\)' % self.rank), ('Graph', ''), (None, self.graph_link)] self.downloads = [('Download coefficients of newform', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=100)), ('Download stored data for all curves', url_for(".download_EC_all", label=self.lmfdb_iso))] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % ( self.lmfdb_iso, self.iso) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, ' ')]
def make_curve(self): # To start with the data fields of self are just those from # the database. We need to reformat these. # Old version: required constructing the actual elliptic curve # E, and computing some further data about it. # New version (May 2016): extra data fields now in the # database so we do not have to construct the curve or do any # computation with it on the fly. As a failsafe the old way # is still included. data = self.data = {} data['ainvs'] = self.ainvs data['conductor'] = N = ZZ(self.conductor) data['j_invariant'] = QQ(str(self.jinv)) data['j_inv_factor'] = latex(0) if data['j_invariant']: # don't factor 0 data['j_inv_factor'] = latex(data['j_invariant'].factor()) data['j_inv_str'] = unicode(str(data['j_invariant'])) data['j_inv_latex'] = web_latex(data['j_invariant']) # extract data about MW rank, generators, heights and torsion: self.make_mw() # get more data from the database entry data['equation'] = self.equation local_data = self.local_data D = self.signD * prod([ld['p']**ld['ord_disc'] for ld in local_data]) data['disc'] = D Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond']) for ld in local_data]) Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD)) data['minq_D'] = minqD = self.min_quad_twist['disc'] minq_label = self.min_quad_twist['label'] data['minq_label'] = db.ec_curves.lucky({'label': minq_label}, 'lmfdb_label') data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD if self.degree is None: data['degree'] = 0 # invalid, but will be displayed nicely else: data['degree'] = self.degree if self.number == 1: data['an'] = self.anlist data['ap'] = self.aplist else: r = db.ec_curves.lucky({'lmfdb_iso': self.lmfdb_iso, 'number': 1}) data['an'] = r['anlist'] data['ap'] = r['aplist'] minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label']) data['disc_factor'] = latex(Dfac) data['cond_factor'] = latex(Nfac) data['disc_latex'] = web_latex(D) data['cond_latex'] = web_latex(N) data['galois_images'] = [ trim_galois_image_code(s) for s in self.mod_p_images ] data['non_maximal_primes'] = self.non_maximal_primes data['galois_data'] = [{ 'p': p, 'image': im } for p, im in zip(data['non_maximal_primes'], data['galois_images'])] data['CMD'] = self.cm data['CM'] = "no" data['EndE'] = "\(\Z\)" if self.cm: data['cm_ramp'] = [ p for p in ZZ(self.cm).support() if not p in self.non_maximal_primes ] data['cm_nramp'] = len(data['cm_ramp']) if data['cm_nramp'] == 1: data['cm_ramp'] = data['cm_ramp'][0] else: data['cm_ramp'] = ", ".join([str(p) for p in data['cm_ramp']]) data['cm_sqf'] = ZZ(self.cm).squarefree_part() data['CM'] = "yes (\(D=%s\))" % data['CMD'] if data['CMD'] % 4 == 0: d4 = ZZ(data['CMD']) // 4 data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4 else: data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD'] data['ST'] = st_link_by_name(1, 2, 'N(U(1))') else: data['ST'] = st_link_by_name(1, 2, 'SU(2)') data['p_adic_primes'] = [ p for i, p in enumerate(prime_range(5, 100)) if (N * data['ap'][i]) % p != 0 ] cond, iso, num = split_lmfdb_label(self.lmfdb_label) self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.one_deg = ZZ(self.class_deg).is_prime() self.ncurves = db.ec_curves.count({'lmfdb_iso': self.lmfdb_iso}) isodegs = [str(d) for d in self.isogeny_degrees if d > 1] if len(isodegs) < 3: data['isogeny_degrees'] = " and ".join(isodegs) else: data['isogeny_degrees'] = " and ".join( [", ".join(isodegs[:-1]), isodegs[-1]]) if self.twoadic_gens: from sage.matrix.all import Matrix data['twoadic_gen_matrices'] = ','.join( [latex(Matrix(2, 2, M)) for M in self.twoadic_gens]) data[ 'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html" # Leading term of L-function & other BSD data self.make_bsd() # Optimality (the optimal curve in the class is the curve # whose Cremona label ends in '1' except for '990h' which was # labelled wrongly long ago) if self.iso == '990h': data['Gamma0optimal'] = bool(self.number == 3) else: data['Gamma0optimal'] = bool(self.number == 1) data['p_adic_data_exists'] = False if data['Gamma0optimal']: data['p_adic_data_exists'] = db.ec_padic.exists( {'lmfdb_iso': self.lmfdb_iso}) # Iwasawa data (where present) self.make_iwasawa() # Torsion growth data (where present) self.make_torsion_growth() data['newform'] = web_latex( PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)) data['newform_label'] = self.newform_label = newform_label( cond, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self._code = None self.class_url = url_for(".by_double_iso_label", conductor=N, iso_label=iso) self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url), ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)), ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)), ('L-function', url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso))] if not self.cm: if N <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if N <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)), ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)), ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')), ('Download SageMath code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')), ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))] try: self.plot = encode_plot(self.E.plot()) except AttributeError: self.plot = encode_plot(EllipticCurve(data['ainvs']).plot()) self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format( self.plot) self.properties = [('Label', self.lmfdb_label), (None, self.plot_link), ('Conductor', '\(%s\)' % data['conductor']), ('Discriminant', '\(%s\)' % data['disc']), ('j-invariant', '%s' % data['j_inv_latex']), ('CM', '%s' % data['CM']), ('Rank', '\(%s\)' % self.mw['rank']), ('Torsion Structure', '\(%s\)' % self.mw['tor_struct'])] self.title = "Elliptic Curve %s (Cremona label %s)" % ( self.lmfdb_label, self.label) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)), ('%s' % num, ' ')]
def is_Gamma0_equivalent(self, other, N, Transformation=False): r""" Checks if cusps ``self`` and ``other`` are `\Gamma_0(N)`- equivalent. INPUT: - ``other`` -- a number field cusp or a list of two number field elements which define a cusp. - ``N`` -- an ideal of the number field (level) OUTPUT: - bool -- ``True`` if the cusps are equivalent. - a transformation matrix -- (if ``Transformation=True``) a list of integral elements [a, b, c, d] which are the entries of a 2x2 matrix M in `\Gamma_0(N)` such that M * ``self`` = ``other`` if ``other`` and ``self`` are `\Gamma_0(N)`- equivalent. If ``self`` and ``other`` are not equivalent it returns zero. EXAMPLES: :: sage: K.<a> = NumberField(x^3-10) sage: N = K.ideal(a-1) sage: alpha = NFCusp(K, 0) sage: beta = NFCusp(K, oo) sage: alpha.is_Gamma0_equivalent(beta, N) False sage: alpha.is_Gamma0_equivalent(beta, K.ideal(1)) True sage: b, M = alpha.is_Gamma0_equivalent(beta, K.ideal(1),Transformation=True) sage: alpha.apply(M) Cusp Infinity of Number Field in a with defining polynomial x^3 - 10 :: sage: k.<a> = NumberField(x^2+23) sage: N = k.ideal(3) sage: alpha1 = NFCusp(k, a+1, 4) sage: alpha2 = NFCusp(k, a-8, 29) sage: alpha1.is_Gamma0_equivalent(alpha2, N) True sage: b, M = alpha1.is_Gamma0_equivalent(alpha2, N, Transformation=True) sage: alpha1.apply(M) == alpha2 True sage: M[2] in N True """ k = self.number_field() other = NFCusp(k, other) if not (self.ideal()/other.ideal()).is_principal(): if not Transformation: return False else: return False, 0 reps = list_of_representatives(N) alpha1 = NFCusp(k, self, lreps=reps) alpha2 = NFCusp(k, other, lreps=reps) delta = k.ideal(alpha1.__b) + N if (k.ideal(alpha2.__b) + N)!= delta: if not Transformation: return False else: return False, 0 M1 = alpha1.ABmatrix() M2 = alpha2.ABmatrix() A = alpha1.ideal() B = k.ideal(M1[1], M1[3]) ABdelta = A*B*delta*delta units = units_mod_ideal(ABdelta) for u in units: if (M2[2]*M1[3] - u*M1[2]*M2[3]) in ABdelta: if not Transformation: return True else: AuxCoeff = [1, 0, 0, 1] Aux = M2[2]*M1[3] - u*M1[2]*M2[3] if Aux in A*B*N: if not u==1: AuxCoeff[3] = u else: A1 = (A*B*N)/ABdelta A2 = B*k.ideal(M1[2]*M2[2])/(A*ABdelta) f = A1.element_1_mod(A2) w = ((1 - f)*Aux)/(M1[2]*M2[2]) AuxCoeff[3] = u AuxCoeff[1] = w from sage.matrix.all import Matrix Maux = Matrix(k, 2, AuxCoeff) M1inv = Matrix(k, 2, M1).inverse() Mtrans = Matrix(k, 2, M2)*Maux*M1inv assert Mtrans[1][0] in N return True, Mtrans.list() if not Transformation: return False else: return False, 0
class ECisog_class(object): """ Class for an isogeny class of elliptic curves over Q """ def __init__(self, dbdata): """ Arguments: - dbdata: the data from the database """ logger.debug("Constructing an instance of ECisog_class") self.__dict__.update(dbdata) self.make_class() @staticmethod def by_label(label): """ Searches for a specific elliptic curve isogeny class in the curves collection by its label, which can be either a curve label (e.g. "11.a1") or a class label (e.g. "11.a") in either LMFDB or Cremona format. """ #print "label = %s" % label try: N, iso, number = split_lmfdb_label(label) if number: data = db_ec().find_one({"lmfdb_label" : label}) else: data = db_ec().find_one({"lmfdb_label" : label+"1"}) except AttributeError: try: N, iso, number = split_cremona_label(label) if number: data = db_ec().find_one({"label" : label}) else: data = db_ec().find_one({"label" : label+"1"}) except AttributeError: return "Invalid label" # caller must catch this and raise an error if data: return ECisog_class(data) return "Class not found" # caller must catch this and raise an error def make_class(self): self.ainvs_str = self.ainvs self.ainvs = [int(a) for a in self.ainvs_str] self.E = EllipticCurve(self.ainvs) self.CM = self.E.has_cm() try: # Extract the isogeny degree matrix from the database size = len(self.isogeny_matrix) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) except AttributeError: # Failsafe: construct it from scratch self.isogeny_matrix = self.E.isogeny_class(order="lmfdb").matrix() size = self.isogeny_matrix.nrows() self.ncurves = size # 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 # Create a list of the curves in the class from the database self.db_curves = [self.E] self.optimal_flags = [False] * size self.degrees = [0] * size if self.degree: self.degrees[0] = self.degree else: try: self.degrees[0] = self.E.modular_degree() except RuntimeError: pass # Fill in the curves in the class by looking each one up in the db: self.cremona_labels = [self.label] + [0] * (size - 1) if self.number == 1: self.optimal_flags[0] = True for i in range(2, size + 1): Edata = db_ec().find_one({'lmfdb_label': self.lmfdb_iso + str(i)}) Ei = EllipticCurve([int(a) for a in Edata['ainvs']]) self.cremona_labels[i - 1] = Edata['label'] if Edata['number'] == 1: self.optimal_flags[i - 1] = True if 'degree' in Edata: self.degrees[i - 1] = Edata['degree'] else: try: self.degrees[i - 1] = Ei.modular_degree() except RuntimeError: pass self.db_curves.append(Ei) if self.iso == '990h': # this isogeny class is labeled wrong in Cremona's tables self.optimal_flags = [False, False, True, False] self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) N, iso, number = split_lmfdb_label(self.lmfdb_iso) self.newform = web_latex(self.E.q_eigenform(10)) self.newform_label = self.lmfdb_iso.replace('.', '.2') self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=0, label=iso) self.lfunction_link = url_for("l_functions.l_function_ec_page", label=self.lmfdb_iso) self.curves = [dict([('label',self.lmfdb_iso + str(i + 1)), ('url',url_for(".by_triple_label", conductor=N, iso_label=iso, number=i+1)), ('cremona_label',self.cremona_labels[i]), ('ainvs',str(list(c.ainvs()))), ('torsion',c.torsion_order()), ('degree',self.degrees[i]), ('optimal',self.optimal_flags[i])]) for i, c in enumerate(self.db_curves)] self.friends = [ ('L-function', self.lfunction_link), ('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso)), ('Symmetric 4th power L-function', url_for("l_functions.l_function_ec_sym_page", power='4', label=self.lmfdb_iso)), ('Modular form ' + self.newform_label, self.newform_link)] self.properties = [('Label', self.lmfdb_iso), ('Number of curves', str(self.ncurves)), ('Conductor', '\(%s\)' % N), ('CM', '%s' % self.CM), ('Rank', '\(%s\)' % self.rank), ('Graph', ''),(None, self.graph_link) ] self.downloads = [('Download coeffients of newform', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=100)), ('Download stored data for all curves', url_for(".download_EC_all", label=self.lmfdb_iso))] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % (self.lmfdb_iso, self.iso) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, ' ')]
def to_matrix(self, side="right", on_space="primal"): r""" Return ``self`` as a matrix acting on the underlying vector space. - ``side`` -- optional (default: ``"right"``) whether the action of ``self`` is on the ``"left"`` or on the ``"right"`` - ``on_space`` -- optional (default: ``"primal"``) whether to act as the reflection representation on the given basis, or to act on the dual reflection representation on the dual basis EXAMPLES:: sage: W = ReflectionGroup(['A',2]) # optional - gap3 sage: for w in W: # optional - gap3 ....: w.reduced_word() # optional - gap3 ....: [w.to_matrix(), w.to_matrix(on_space="dual")] # optional - gap3 [] [ [1 0] [1 0] [0 1], [0 1] ] [2] [ [ 1 1] [ 1 0] [ 0 -1], [ 1 -1] ] [1] [ [-1 0] [-1 1] [ 1 1], [ 0 1] ] [1, 2] [ [-1 -1] [ 0 -1] [ 1 0], [ 1 -1] ] [2, 1] [ [ 0 1] [-1 1] [-1 -1], [-1 0] ] [1, 2, 1] [ [ 0 -1] [ 0 -1] [-1 0], [-1 0] ] TESTS:: sage: W = ReflectionGroup(['F',4]) # optional - gap3 sage: all(w.to_matrix(side="left") == W.from_reduced_word(reversed(w.reduced_word())).to_matrix(side="right").transpose() for w in W) # optional - gap3 True sage: all(w.to_matrix(side="right") == W.from_reduced_word(reversed(w.reduced_word())).to_matrix(side="left").transpose() for w in W) # optional - gap3 True """ W = self.parent() if W._reflection_representation is None: if side == "left": w = ~self elif side == "right": w = self else: raise ValueError('side must be "left" or "right"') Delta = W.independent_roots() Phi = W.roots() M = Matrix([Phi[w(Phi.index(alpha)+1)-1] for alpha in Delta]) mat = W.base_change_matrix() * M else: refl_repr = W._reflection_representation id_mat = identity_matrix(QQ, refl_repr[W.index_set()[0]].nrows()) mat = prod([refl_repr[i] for i in self.reduced_word()], id_mat) if on_space == "primal": if side == "left": mat = mat.transpose() elif on_space == "dual": if side == "left": mat = mat.inverse() else: mat = mat.inverse().transpose() else: raise ValueError('on_space must be "primal" or "dual"') mat.set_immutable() return mat
def bilinear_form(self, R=None): """ Return the bilinear form over ``R`` associated to ``self``. INPUT: - ``R`` -- (default: universal cyclotomic field) a ring used to compute the bilinear form EXAMPLES:: sage: CoxeterType(['A', 2, 1]).bilinear_form() [ 1 -1/2 -1/2] [-1/2 1 -1/2] [-1/2 -1/2 1] sage: CoxeterType(['H', 3]).bilinear_form() [ 1 -1/2 0] [ -1/2 1 1/2*E(5)^2 + 1/2*E(5)^3] [ 0 1/2*E(5)^2 + 1/2*E(5)^3 1] sage: C = CoxeterMatrix([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) sage: C.bilinear_form() [ 1 -1 -1] [-1 1 -1] [-1 -1 1] """ n = self.rank() mat = self.coxeter_matrix()._matrix from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField UCF = UniversalCyclotomicField() if R is None: R = UCF # if UCF.has_coerce_map_from(base_ring): # R = UCF # else: # R = base_ring # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. E = UCF.gen if R is UCF: def val(x): if x > -1: return (E(2 * x) + ~E(2 * x)) / R(-2) else: return R(x) elif is_QuadraticField(R): def val(x): if x > -1: return R( (E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) / R(-2) else: return R(x) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x > -1: return -R(cos(pi / SR(x))) else: return R(x) entries = [ SparseEntry(i, j, val(mat[i, j])) for i in range(n) for j in range(n) if mat[i, j] != 2 ] bilinear = Matrix(R, n, entries) bilinear.set_immutable() return bilinear
def make_class(self): self.ainvs_str = self.ainvs self.ainvs = [int(a) for a in self.ainvs_str] self.E = EllipticCurve(self.ainvs) self.CM = self.E.has_cm() try: # Extract the isogeny degree matrix from the database size = len(self.isogeny_matrix) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) except AttributeError: # Failsafe: construct it from scratch self.isogeny_matrix = self.E.isogeny_class(order="lmfdb").matrix() size = self.isogeny_matrix.nrows() self.ncurves = size # 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 # Create a list of the curves in the class from the database self.db_curves = [self.E] self.optimal_flags = [False] * size self.degrees = [0] * size if self.degree: self.degrees[0] = self.degree else: try: self.degrees[0] = self.E.modular_degree() except RuntimeError: pass # Fill in the curves in the class by looking each one up in the db: self.cremona_labels = [self.label] + [0] * (size - 1) if self.number == 1: self.optimal_flags[0] = True for i in range(2, size + 1): Edata = db_ec().find_one({"lmfdb_label": self.lmfdb_iso + str(i)}) Ei = EllipticCurve([int(a) for a in Edata["ainvs"]]) self.cremona_labels[i - 1] = Edata["label"] if Edata["number"] == 1: self.optimal_flags[i - 1] = True if "degree" in Edata: self.degrees[i - 1] = Edata["degree"] else: try: self.degrees[i - 1] = Ei.modular_degree() except RuntimeError: pass self.db_curves.append(Ei) if self.iso == "990h": # this isogeny class is labeled wrong in Cremona's tables self.optimal_flags = [False, False, True, False] self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) N, iso, number = split_lmfdb_label(self.lmfdb_iso) self.newform = web_latex(self.E.q_eigenform(10)) self.newform_label = newform_label(N, 2, 1, iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=1, label=iso) newform_exists_in_db = is_newform_in_db(self.newform_label) self.lfunction_link = url_for("l_functions.l_function_ec_page", label=self.lmfdb_iso) self.curves = [ dict( [ ("label", self.lmfdb_iso + str(i + 1)), ("url", url_for(".by_triple_label", conductor=N, iso_label=iso, number=i + 1)), ("cremona_label", self.cremona_labels[i]), ("ainvs", str(list(c.ainvs()))), ("torsion", c.torsion_order()), ("degree", self.degrees[i]), ("optimal", self.optimal_flags[i]), ] ) for i, c in enumerate(self.db_curves) ] self.friends = [("L-function", self.lfunction_link)] if not self.CM: self.friends += [ ( "Symmetric square L-function", url_for("l_functions.l_function_ec_sym_page", power="2", label=self.lmfdb_iso), ), ( "Symmetric 4th power L-function", url_for("l_functions.l_function_ec_sym_page", power="4", label=self.lmfdb_iso), ), ] if newform_exists_in_db: self.friends += [("Modular form " + self.newform_label, self.newform_link)] self.properties = [ ("Label", self.lmfdb_iso), ("Number of curves", str(self.ncurves)), ("Conductor", "\(%s\)" % N), ("CM", "%s" % self.CM), ("Rank", "\(%s\)" % self.rank), ("Graph", ""), (None, self.graph_link), ] self.downloads = [ ("Download coefficients of newform", url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=100)), ("Download stored data for all curves", url_for(".download_EC_all", label=self.lmfdb_iso)), ] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % (self.lmfdb_iso, self.iso) self.bread = [ ("Elliptic Curves", url_for("ecnf.index")), ("$\Q$", url_for(".rational_elliptic_curves")), ("%s" % N, url_for(".by_conductor", conductor=N)), ("%s" % iso, " "), ]
def make_class(self): self.ainvs_str = self.ainvs self.ainvs = [int(a) for a in self.ainvs_str] self.E = EllipticCurve(self.ainvs) self.CM = self.E.has_cm() try: # Extract the isogeny degree matrix from the database size = len(self.isogeny_matrix) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) except AttributeError: # Failsafe: construct it from scratch self.isogeny_matrix = self.E.isogeny_class(order="lmfdb").matrix() size = self.isogeny_matrix.nrows() # 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 # Create a list of the curves in the class from the database self.db_curves = [self.E] self.optimal_flags = [False] * size self.degrees = [0] * size if self.degree: self.degrees[0] = self.degree else: try: self.degrees[0] = self.E.modular_degree() except RuntimeError: pass # Fill in the curves in the class by looking each one up in the db: self.cremona_labels = [self.label] + [0] * (size - 1) if self.number == 1: self.optimal_flags[0] = True for i in range(2, size + 1): Edata = db_ec().find_one({'lmfdb_label': self.lmfdb_iso + str(i)}) Ei = EllipticCurve([int(a) for a in Edata['ainvs']]) self.cremona_labels[i - 1] = Edata['label'] if Edata['number'] == 1: self.optimal_flags[i - 1] = True if 'degree' in Edata: self.degrees[i - 1] = Edata['degree'] else: try: self.degrees[i - 1] = Ei.modular_degree() except RuntimeError: pass self.db_curves.append(Ei) if self.iso == '990h': # this isogeny class is labeled wrong in Cremona's tables self.optimal_flags = [False, False, True, False] self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) N, iso, number = lmfdb_label_regex.match(self.lmfdb_iso).groups() self.newform = web_latex(self.E.q_eigenform(10)) self.newform_label = self.lmfdb_iso.replace('.', '.2') self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=0, label=iso) self.lfunction_link = url_for("l_functions.l_function_ec_page", label=self.lmfdb_iso) self.curves = [[self.lmfdb_iso + str(i + 1), self.cremona_labels[i], str(list(c.ainvs())), c.torsion_order(), self.degrees[i], self.optimal_flags[i]] for i, c in enumerate(self.db_curves)] self.friends = [ ('L-function', self.lfunction_link), ('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso)), ('Symmetric 4th power L-function', url_for("l_functions.l_function_ec_sym_page", power='4', label=self.lmfdb_iso)), ('Modular form ' + self.newform_label, self.newform_link)] self.properties = [('Label', self.lmfdb_iso), (None, self.graph_link), ('Conductor', '\(%s\)' % N), ('CM', '%s' % self.CM), ('Rank', '\(%s\)' % self.rank) ] self.downloads = [('Download coeffients of newform', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=100)), ('Download stored data for all curves', url_for(".download_EC_all", label=self.lmfdb_iso))] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % (self.lmfdb_iso, self.iso) self.bread = [('Elliptic Curves ', url_for(".rational_elliptic_curves")), ('isogeny class %s' % self.lmfdb_iso, ' ')]
def Mestre_conic(i, xyz=False, names='u,v,w'): r""" Return the conic equation from Mestre's algorithm given the Igusa-Clebsch invariants. It has a rational point if and only if a hyperelliptic curve corresponding to the invariants exists. INPUT: - ``i`` - list or tuple of length 4 containing the four Igusa-Clebsch invariants: I2, I4, I6, I10 - ``xyz`` - Boolean (default: False) if True, the algorithm also returns three invariants x,y,z used in Mestre's algorithm - ``names`` (default: 'u,v,w') - the variable names for the Conic OUTPUT: A Conic object EXAMPLES: A standard example:: sage: Mestre_conic([1,2,3,4]) Projective Conic Curve over Rational Field defined by -2572155000*u^2 - 317736000*u*v + 1250755459200*v^2 + 2501510918400*u*w + 39276887040*v*w + 2736219686912*w^2 Note that the algorithm works over number fields as well:: sage: k = NumberField(x^2-41,'a') sage: a = k.an_element() sage: Mestre_conic([1,2+a,a,4+a]) Projective Conic Curve over Number Field in a with defining polynomial x^2 - 41 defined by (-801900000*a + 343845000)*u^2 + (855360000*a + 15795864000)*u*v + (312292800000*a + 1284808579200)*v^2 + (624585600000*a + 2569617158400)*u*w + (15799910400*a + 234573143040)*v*w + (2034199306240*a + 16429854656512)*w^2 And over finite fields:: sage: Mestre_conic([GF(7)(10),GF(7)(1),GF(7)(2),GF(7)(3)]) Projective Conic Curve over Finite Field of size 7 defined by -2*u*v - v^2 - 2*u*w + 2*v*w - 3*w^2 An example with xyz:: sage: Mestre_conic([5,6,7,8], xyz=True) (Projective Conic Curve over Rational Field defined by -415125000*u^2 + 608040000*u*v + 33065136000*v^2 + 66130272000*u*w + 240829440*v*w + 10208835584*w^2, 232/1125, -1072/16875, 14695616/2109375) ALGORITHM: The formulas are taken from pages 956 - 957 of [LY2001]_ and based on pages 321 and 332 of [M1991]_. See the code or [LY2001]_ for the detailed formulae defining x, y, z and L. """ from sage.structure.sequence import Sequence k = Sequence(i).universe() try: k = k.fraction_field() except (TypeError, AttributeError, NotImplementedError): pass I2, I4, I6, I10 = i #Setting x,y,z as in Mestre's algorithm (Using Lauter and Yang's formulas) x = 8*(1 + 20*I4/(I2**2))/225 y = 16*(1 + 80*I4/(I2**2) - 600*I6/(I2**3))/3375 z = -64*(-10800000*I10/(I2**5) - 9 - 700*I4/(I2**2) + 3600*I6/(I2**3) + 12400*I4**2/(I2**4) - 48000*I4*I6/(I2**5))/253125 L = Matrix([[x+6*y , 6*x**2+2*y , 2*z ], [6*x**2+2*y, 2*z , 9*x**3 + 4*x*y + 6*y**2 ], [2*z , 9*x**3+4*x*y+6*y**2, 6*x**2*y + 2*y**2 + 3*x*z]]) try: L = L*L.denominator() # clears the denominator except (AttributeError, TypeError): pass u, v, w = PolynomialRing(k, names).gens() MConic = Conic(k, L, names) if xyz: return MConic, x, y, z return MConic
def make_class(self): self.CM = self.cm N, iso, number = split_lmfdb_label(self.lmfdb_iso) self.conductor = int(N) # Extract the size of the isogeny class from the database self.ncurves = ncurves = self.class_size # Create a list of the curves in the class from the database number_key = 'number' if self.label_type == 'Cremona' else 'lmfdb_number' self.curves = [ db.ec_curves.lucky({ 'iso': self.iso, number_key: i + 1 }) for i in range(ncurves) ] # Set optimality flags. The optimal curve is number 1 except # in one case which is labeled differently in the Cremona tables self.optimality_known = (self.ncurves == 1) or (self.conductor < OPTIMALITY_BOUND) self.optimality_bound = OPTIMALITY_BOUND for c in self.curves: c['optimal'] = (c['number'] == (3 if self.iso == '990h' else 1)) c['ai'] = c['ainvs'] c['curve_url_lmfdb'] = url_for(".by_triple_label", conductor=N, iso_label=iso, number=c['lmfdb_number']) c['curve_url_cremona'] = url_for(".by_ec_label", label=c['label']) if self.label_type == 'Cremona': c['curve_label'] = c['label'] _, c_iso, c_number = split_cremona_label(c['label']) 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 if self.label_type == 'Cremona': # permute rows/cols perm = lambda i: (c for c in self.curves if c['number'] == i + 1 ).next()['lmfdb_number'] - 1 self.isogeny_matrix = [[ self.isogeny_matrix[perm(i)][perm(j)] for i in range(ncurves) ] for j in range(ncurves)] self.isogeny_matrix = Matrix(self.isogeny_matrix) self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) # Create isogeny graph with appropriate vertex labels: self.graph = make_graph(self.isogeny_matrix, [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 = web_latex( PowerSeriesRing(QQ, 'q')(self.anlist, 20, check=True)) self.newform_label = db.mf_newforms.lucky( { 'level': N, 'weight': 2, 'related_objects': { '$contains': 'EllipticCurve/Q/%s/%s' % (N, iso) } }, 'label') self.newform_exists_in_db = self.newform_label is not None if self.newform_label is not None: char_orbit, hecke_orbit = self.newform_label.split('.')[2:] self.newform_link = url_for("cmf.by_url_newform_label", level=N, weight=2, char_orbit_label=char_orbit, hecke_orbit=hecke_orbit) self.lfunction_link = url_for("l_functions.l_function_ec_page", conductor_label=N, isogeny_class_label=iso) self.friends = [('L-function', self.lfunction_link)] if not self.CM: self.CM = "no" if int(N) <= 300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor=N, isogeny=iso))] if int(N) <= 50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor=N, isogeny=iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] if self.label_type == 'Cremona': self.title = "Elliptic Curve Isogeny Class with Cremona label {} (LMFDB label {})".format( self.iso, self.lmfdb_iso) self.iso_label = self.iso else: self.title = "Elliptic Curve Isogeny Class with LMFDB label {} (Cremona label {})".format( self.lmfdb_iso, self.iso) self.iso_label = self.lmfdb_iso self.properties = [ ('Label', self.iso if self.label_type == 'Cremona' else self.lmfdb_iso), ('Number of curves', str(ncurves)), ('Conductor', '%s' % N), ('CM', '%s' % self.CM), ('Rank', '%s' % self.rank) ] if self.ncurves > 1: self.properties += [('Graph', ''), (None, self.graph_link)] self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=1000)), ('All stored data to text', url_for(".download_EC_all", label=self.lmfdb_iso))] self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, ' ')] self.code = {} self.code['show'] = {'sage': ''} # use default show names self.code['class'] = { 'sage': 'E = EllipticCurve("%s1")\n' % (self.lmfdb_iso) + '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 _compute_q_expansion_basis(self, prec=None): r""" Compute q-expansion basis using Schaeffer's algorithm. EXAMPLES:: sage: CuspForms(GammaH(31, [7]), 1).q_expansion_basis() # indirect doctest [ q - q^2 - q^5 + O(q^6) ] A more elaborate example (two Galois-conjugate characters each giving a 2-dimensional space):: sage: CuspForms(GammaH(124, [85]), 1).q_expansion_basis() [ q - q^4 - q^6 + O(q^7), q^2 + O(q^7), q^3 + O(q^7), q^5 - q^6 + O(q^7) ] """ if prec is None: prec = self.prec() else: prec = Integer(prec) chars=self.group().characters_mod_H(sign=-1, galois_orbits=True) B = [] dim = 0 for c in chars: chi = c.minimize_base_ring() Bchi = [weight1.modular_ratio_to_prec(chi, f, prec) for f in weight1.hecke_stable_subspace(chi) ] if Bchi == []: continue if chi.base_ring() == QQ: B += [f.padded_list(prec) for f in Bchi] dim += len(Bchi) else: d = chi.base_ring().degree() dim += d * len(Bchi) for f in Bchi: w = f.padded_list(prec) for i in range(d): B.append([x[i] for x in w]) basis_mat = Matrix(QQ, B) if basis_mat.is_zero(): return [] # Daft thing: "transformation=True" parameter to echelonize # is ignored for rational matrices! big_mat = basis_mat.augment(identity_matrix(dim)) big_mat.echelonize() c = big_mat.pivots()[-1] echelon_basis_mat = big_mat[:, :prec] R = self._q_expansion_ring() if c >= prec: verbose("Precision %s insufficient to determine basis" % prec, level=1) else: verbose("Minimal precision for basis: %s" % (c+1), level=1) t = big_mat[:, prec:] assert echelon_basis_mat == t * basis_mat self.__transformation_matrix = t self._char_basis = [R(f.list(), c+1) for f in basis_mat.rows()] return [R(f.list(), prec) for f in echelon_basis_mat.rows() if f != 0]
def make_class(self): self.ainvs_str = self.ainvs self.ainvs = [int(a) for a in self.ainvs_str] self.E = EllipticCurve(self.ainvs) self.CM = self.E.has_cm() try: # Extract the isogeny degree matrix from the database size = len(self.isogeny_matrix) from sage.matrix.all import Matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) except AttributeError: # Failsafe: construct it from scratch self.isogeny_matrix = self.E.isogeny_class(order="lmfdb").matrix() size = self.isogeny_matrix.nrows() self.ncurves = size # 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 # Create a list of the curves in the class from the database self.db_curves = [self.E] self.optimal_flags = [False] * size self.degrees = [0] * size if self.degree: self.degrees[0] = self.degree else: try: self.degrees[0] = self.E.modular_degree() except RuntimeError: pass # Fill in the curves in the class by looking each one up in the db: self.cremona_labels = [self.label] + [0] * (size - 1) if self.number == 1: self.optimal_flags[0] = True for i in range(2, size + 1): Edata = db_ec().find_one({'lmfdb_label': self.lmfdb_iso + str(i)}) Ei = EllipticCurve([int(a) for a in Edata['ainvs']]) self.cremona_labels[i - 1] = Edata['label'] if Edata['number'] == 1: self.optimal_flags[i - 1] = True if 'degree' in Edata: self.degrees[i - 1] = Edata['degree'] else: try: self.degrees[i - 1] = Ei.modular_degree() except RuntimeError: pass self.db_curves.append(Ei) if self.iso == '990h': # this isogeny class is labeled wrong in Cremona's tables self.optimal_flags = [False, False, True, False] self.isogeny_matrix_str = latex(matrix(self.isogeny_matrix)) N, iso, number = split_lmfdb_label(self.lmfdb_iso) self.newform = web_latex(self.E.q_eigenform(10)) self.newform_label = newform_label(N,2,1,iso) self.newform_link = url_for("emf.render_elliptic_modular_forms", level=N, weight=2, character=1, label=iso) self.newform_exists_in_db = is_newform_in_db(self.newform_label) self.lfunction_link = url_for("l_functions.l_function_ec_page", label=self.lmfdb_iso) self.curves = [dict([('label',self.lmfdb_iso + str(i + 1)), ('url',url_for(".by_triple_label", conductor=N, iso_label=iso, number=i+1)), ('cremona_label',self.cremona_labels[i]), ('ainvs',str(list(c.ainvs()))), ('torsion',c.torsion_order()), ('degree',self.degrees[i]), ('optimal',self.optimal_flags[i])]) for i, c in enumerate(self.db_curves)] self.friends = [('L-function', self.lfunction_link)] if not self.CM: if int(N)<=300: self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso))] if int(N)<=50: self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', label=self.lmfdb_iso))] if self.newform_exists_in_db: self.friends += [('Modular form ' + self.newform_label, self.newform_link)] self.properties = [('Label', self.lmfdb_iso), ('Number of curves', str(self.ncurves)), ('Conductor', '\(%s\)' % N), ('CM', '%s' % self.CM), ('Rank', '\(%s\)' % self.rank), ('Graph', ''),(None, self.graph_link) ] self.downloads = [('Download coefficients of newform', url_for(".download_EC_qexp", label=self.lmfdb_iso, limit=1000)), ('Download stored data for all curves', url_for(".download_EC_all", label=self.lmfdb_iso))] if self.lmfdb_iso == self.iso: self.title = "Elliptic Curve Isogeny Class %s" % self.lmfdb_iso else: self.title = "Elliptic Curve Isogeny Class %s (Cremona label %s)" % (self.lmfdb_iso, self.iso) self.bread = [('Elliptic Curves', url_for("ecnf.index")), ('$\Q$', url_for(".rational_elliptic_curves")), ('%s' % N, url_for(".by_conductor", conductor=N)), ('%s' % iso, ' ')] self.code = {} self.code['show'] = {'sage':''} # use default show names self.code['class'] = {'sage':'E = EllipticCurve("%s1")\n'%(self.lmfdb_iso) + '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)'}