def _frobenius_matrix(self, N=None): """ Compute p-adic frobenius matrix to precision p^N. If N not supplied, a default value is selected, which is the minimum needed to recover the charpoly unambiguously. Currently only implemented using hypellfrob, which means only works over GF(p^1), and must have p > (2g+1)(2N-1). TESTS:: sage: R.<t> = PolynomialRing(GF(37)) sage: H = HyperellipticCurve(t^5 + t + 2) sage: H._frobenius_matrix() [1258 + O(37^2) 925 + O(37^2) 132 + O(37^2) 587 + O(37^2)] [1147 + O(37^2) 814 + O(37^2) 241 + O(37^2) 1011 + O(37^2)] [1258 + O(37^2) 1184 + O(37^2) 1105 + O(37^2) 482 + O(37^2)] [1073 + O(37^2) 999 + O(37^2) 772 + O(37^2) 929 + O(37^2)] """ p = self.base_ring().characteristic() f, h = self.hyperelliptic_polynomials() if h != 0: # need y^2 = f(x) raise NotImplementedError, "only implemented for curves y^2 = f(x)" sign = 1 if not f.is_monic(): # at this time we need a monic f c = f.leading_coefficient() f = f / c if c.is_square(): # solutions of $y^2 = c * f(x)$ correspond naturally to # solutions of $(sqrt(c) y)^2 = f(x)$ pass else: # we'll count points on a twist and then correct by untwisting... sign = -1 assert f.is_monic() if N is None: N = self._frobenius_coefficient_bound() matrix_of_frobenius = hypellfrob(p, N, f) matrix_of_frobenius = sign * matrix_of_frobenius return matrix_of_frobenius
def compute_frob_matrix_and_cp_H2(f, p, prec, **kwargs): """ Return a p-adic matrix approximating the action of Frob on H^2 of a surface or abelian variety, and its characteristic polynomial over ZZ Input: - f defining the curve or surface - p, prime - prec, a lower bound for the desired precision to run the computations, this increases the time exponentially - kwargs, keyword arguments to be passed to controlledreduction Output: - `prec`, the minimum digits absolute precision for approximation of the Frobenius - a matrix representing an approximation of Frob matrix with at least `prec` digits of absolute precision - characteristic polynomial of Frob on H^2 - the number of classes omitted by working with primitive cohomology Note: if given two or one univariate polynomial, we will try to change the model over Qpbar, in order to work with an odd and monic model """ K = Qp(p, prec=prec + 10) OK = ZpCA(p, prec=prec) Rf = f.parent() R = f.base_ring() if len(Rf.gens()) == 2: if min(f.degrees()) != 2: raise NotImplementedError("Affine curves must be hyperelliptic") x, y = f.variables() if f.degree(x) == 2: f = f.substitute(x=y, y=x) # Get Weierstrass equation # y^2 + a*y + b == 0 b, a, _ = map(R['x'], R['x']['y'](f).monic()) # y^2 + a*y + b == 0 --> (2y + a)^2 = a^2 - 4 b f = a**2 - 4 * b f = find_monic_and_odd_model(f.change_ring(K), p) cp1 = HyperellipticCurve(f.change_ring( GF(p))).frobenius_polynomial().reverse() F1 = hypellfrob(p, max(3, prec), f.lift()) F1 = F1.change_ring(OK) cp, frob_matrix = from_H1_to_H2(cp1, F1, tensor=kwargs.get('tensor', False)) frob_matrix = frob_matrix.change_ring(K) shift = 0 elif len(Rf.gens()) == 3 and f.total_degree() == 4 and f.is_homogeneous(): # Quartic plane curve if p < 17: prec = max(4, prec) else: prec = max(3, prec) if 'find_better_model' in kwargs: model = kwargs['find_better_model'] else: # there is a speed up, but we may also lose some nice numerical stability from the original sparseness model = binomial(2 + (prec - 1) * f.total_degree(), 2) < 2 * len(list(f**(prec - 1))) cp1, F1 = controlledreduction( f, p, min_abs_precision=prec, frob_matrix=True, threads=1, find_better_model=model, ) # change ring to OK truncates precision accordingly F1 = F1.change_ring(OK) cp, frob_matrix = from_H1_to_H2(cp1, F1, tensor=kwargs.get('tensor', False)) shift = 0 elif len(Rf.gens()) == 4 and f.total_degree() in [4, 5 ] and f.is_homogeneous(): shift = 1 # we will not see the polarization # Quartic surface if f.total_degree() == 4: if p == 3: prec = max(5, prec) elif p == 5: prec = max(4, prec) elif p < 43: prec = max(3, prec) else: prec = max(2, prec) elif f.total_degree() == 5: if p in [3, 5]: prec = max(7, prec) elif p <= 23: prec = max(6, prec) else: prec = max(5, prec) OK = ZpCA(p, prec=prec) # a rough estimate if it is worth to find a non degenerate mode for f if 'find_better_model' in kwargs: model = kwargs['find_better_model'] else: # there is a speed up, but we may also lose some nice numerical stability from the original sparseness model = binomial(3 + (prec - 1) * f.total_degree(), 3) < 4 * len(list(f**(prec - 1))) threads = kwargs.get('threads', ncpus) cp, frob_matrix = controlledreduction(f, p, min_abs_precision=prec, frob_matrix=True, find_better_model=model, threads=threads) frob_matrix = frob_matrix.change_ring(OK).change_ring(K) else: raise NotImplementedError("At the moment we only support:\n" " - Quartic or quintic surfaces\n" " - Jacobians of quartic curves\n" " - Jacobians of hyperelliptic curves\n") return prec, cp, frob_matrix, shift
def modular_symbols_from_curve(C, N, num_factors=3): """ Find the modular symbols spaces that shoudl correspond to the Jacobian of the given hyperelliptic curve, up to the number of factors we consider. INPUT: - C -- a hyperelliptic curve over QQ - N -- a positive integer - num_factors -- number of Euler factors to verify match up; this is important, because if, e.g., there is only one factor of degree g(C), we don't want to just immediately conclude that Jac(C) = A_f. OUTPUT: - list of all sign 1 simple modular symbols factor of level N that correspond to a simple modular abelian A_f that is isogenous to Jac(C). EXAMPLES:: sage: from psage.modform.rational.unfiled import modular_symbols_from_curve sage: R.<x> = ZZ[] sage: f = x^7+4*x^6+5*x^5+x^4-3*x^3-2*x^2+1 sage: C1 = HyperellipticCurve(f) sage: modular_symbols_from_curve(C1, 284) [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field] sage: f = x^7-7*x^5-11*x^4+5*x^3+18*x^2+4*x-11 sage: C2 = HyperellipticCurve(f) sage: modular_symbols_from_curve(C2, 284) [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field] """ # We will use the Eichler-Shimura relation and David Harvey's # p-adic point counting hypellfrob. Harvey's code has the # constraint: p > (2*g + 1)*(2*prec - 1). # So, find the smallest p not dividing N that satisfies the # above constraint, for our given choice of prec. f, f2 = C.hyperelliptic_polynomials() if f2 != 0: raise NotImplementedError, "curve must be of the form y^2 = f(x)" if f.degree() % 2 == 0: raise NotImplementedError, "curve must be of the form y^2 = f(x) with f(x) odd" prec = 1 g = C.genus() B = (2 * g + 1) * (2 * prec - 1) from sage.rings.all import next_prime p = B # We use that if F(X) is the characteristic polynomial of the # Hecke operator T_p, then X^g*F(X+p/X) is the characteristic # polynomial of Frob_p, because T_p = Frob_p + p/Frob_p, according # to Eichler-Shimura. Use this to narrow down the factors. from sage.all import ModularSymbols, Integers, get_verbose D = ModularSymbols( N, sign=1).cuspidal_subspace().new_subspace().decomposition() D = [A for A in D if A.dimension() == g] from sage.schemes.hyperelliptic_curves.hypellfrob import hypellfrob while num_factors > 0: p = next_prime(p) while N % p == 0: p = next_prime(p) R = Integers(p**prec)['X'] X = R.gen() D2 = [] # Compute the charpoly of Frobenius using hypellfrob M = hypellfrob(p, 1, f) H = R(M.charpoly()) for A in D: F = R(A.hecke_polynomial(p)) # Compute charpoly of Frobenius from F(X) G = R(F.parent()(X**g * F(X + p / X))) if get_verbose(): print(p, G, H) if G == H: D2.append(A) D = D2 num_factors -= 1 return D
def modular_symbols_from_curve(C, N, num_factors=3): """ Find the modular symbols spaces that shoudl correspond to the Jacobian of the given hyperelliptic curve, up to the number of factors we consider. INPUT: - C -- a hyperelliptic curve over QQ - N -- a positive integer - num_factors -- number of Euler factors to verify match up; this is important, because if, e.g., there is only one factor of degree g(C), we don't want to just immediately conclude that Jac(C) = A_f. OUTPUT: - list of all sign 1 simple modular symbols factor of level N that correspond to a simple modular abelian A_f that is isogenous to Jac(C). EXAMPLES:: sage: from psage.modform.rational.unfiled import modular_symbols_from_curve sage: R.<x> = ZZ[] sage: f = x^7+4*x^6+5*x^5+x^4-3*x^3-2*x^2+1 sage: C1 = HyperellipticCurve(f) sage: modular_symbols_from_curve(C1, 284) [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field] sage: f = x^7-7*x^5-11*x^4+5*x^3+18*x^2+4*x-11 sage: C2 = HyperellipticCurve(f) sage: modular_symbols_from_curve(C2, 284) [Modular Symbols subspace of dimension 3 of Modular Symbols space of dimension 39 for Gamma_0(284) of weight 2 with sign 1 over Rational Field] """ # We will use the Eichler-Shimura relation and David Harvey's # p-adic point counting hypellfrob. Harvey's code has the # constraint: p > (2*g + 1)*(2*prec - 1). # So, find the smallest p not dividing N that satisfies the # above constraint, for our given choice of prec. f, f2 = C.hyperelliptic_polynomials() if f2 != 0: raise NotImplementedError, "curve must be of the form y^2 = f(x)" if f.degree() % 2 == 0: raise NotImplementedError, "curve must be of the form y^2 = f(x) with f(x) odd" prec = 1 g = C.genus() B = (2*g + 1)*(2*prec - 1) from sage.rings.all import next_prime p = B # We use that if F(X) is the characteristic polynomial of the # Hecke operator T_p, then X^g*F(X+p/X) is the characteristic # polynomial of Frob_p, because T_p = Frob_p + p/Frob_p, according # to Eichler-Shimura. Use this to narrow down the factors. from sage.all import ModularSymbols, Integers, get_verbose D = ModularSymbols(N,sign=1).cuspidal_subspace().new_subspace().decomposition() D = [A for A in D if A.dimension() == g] from sage.schemes.hyperelliptic_curves.hypellfrob import hypellfrob while num_factors > 0: p = next_prime(p) while N % p == 0: p = next_prime(p) R = Integers(p**prec)['X'] X = R.gen() D2 = [] # Compute the charpoly of Frobenius using hypellfrob M = hypellfrob(p, 1, f) H = R(M.charpoly()) for A in D: F = R(A.hecke_polynomial(p)) # Compute charpoly of Frobenius from F(X) G = R(F.parent()(X**g * F(X + p/X))) if get_verbose(): print (p, G, H) if G == H: D2.append(A) D = D2 num_factors -= 1 return D