diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 347b1b4d44c..d79307c7aa4 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -7,7 +7,7 @@ (E.g., see [BeukersHeckman]_) The computation of Euler factors is currently only supported for primes `p` -of good reduction. That is, it is required that `v_p(t) = v_p(t-1) = 0`. +of good or tame reduction. AUTHORS: @@ -50,8 +50,8 @@ """ # **************************************************************************** -# Copyright (C) 2017 Frédéric Chapoton -# Kiran S. Kedlaya +# Copyright (C) 2017--2024 Frédéric Chapoton +# Kiran S. Kedlaya # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -62,8 +62,7 @@ from collections import defaultdict from itertools import combinations - -from sage.arith.misc import divisors, gcd, euler_phi, moebius, is_prime +from sage.arith.misc import divisors, gcd, euler_phi, is_prime, moebius from sage.arith.misc import gauss_sum, kronecker_symbol from sage.combinat.integer_vector_weighted import WeightedIntegerVectors from sage.functions.generalized import sgn @@ -80,6 +79,7 @@ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.fraction_field import FractionField from sage.rings.integer_ring import ZZ +from sage.rings.padics.factory import Zp from sage.rings.padics.padic_generic_element import gauss_table from sage.rings.polynomial.polynomial_ring import polygen, polygens from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -89,7 +89,7 @@ from sage.schemes.generic.spec import Spec -def characteristic_polynomial_from_traces(traces, d, q, i, sign): +def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_fe=True): r""" Given a sequence of traces `t_1, \dots, t_k`, return the corresponding characteristic polynomial with Weil numbers as roots. @@ -115,10 +115,18 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign): - ``sign`` -- integer, the sign + - ``deg`` -- an integer or ``None`` + + - ``use_fe`` -- a boolean (default: ``True``) + OUTPUT: a polynomial + If ``deg`` is specified, only the coefficients up to this degree (inclusive) are computed. + + If ``use_fe`` is ``False``, we ignore the local functional equation. + EXAMPLES:: sage: from sage.modular.hypergeometric_motive import characteristic_polynomial_from_traces @@ -137,15 +145,20 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign): sage: characteristic_polynomial_from_traces([12], 3, 13, 2, -1) -2197*T^3 + 156*T^2 - 12*T + 1 - sage: characteristic_polynomial_from_traces([36,7620], 4, 17, 3, 1) + sage: characteristic_polynomial_from_traces([36, 7620], 4, 17, 3, 1) 24137569*T^4 - 176868*T^3 - 3162*T^2 - 36*T + 1 - sage: characteristic_polynomial_from_traces([-4,276], 4, 5, 3, 1) + sage: characteristic_polynomial_from_traces([-4, 276], 4, 5, 3, 1) 15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1 - sage: characteristic_polynomial_from_traces([4,-276], 4, 5, 3, 1) + sage: characteristic_polynomial_from_traces([4, -276], 4, 5, 3, 1) 15625*T^4 - 500*T^3 + 146*T^2 - 4*T + 1 sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1) -923521*T^4 + 21142*T^3 - 22*T + 1 + sage: characteristic_polynomial_from_traces([22], 4, 31, 2, -1, deg=1) + -22*T + 1 + sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1, deg=4) + -923521*T^4 + 21142*T^3 - 22*T + 1 + TESTS:: sage: characteristic_polynomial_from_traces([-36], 4, 17, 3, 1) @@ -153,26 +166,30 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign): ... ValueError: not enough traces were given """ - if len(traces) < d // 2: + if use_fe: + bound = d // 2 if deg is None else min(d // 2, deg) + else: + bound = d if deg is None else min(d, deg) + if len(traces) < bound: raise ValueError('not enough traces were given') - if i % 2 and d % 2: + if i % 2 and d % 2 and use_fe: raise ValueError('i and d may not both be odd') t = PowerSeriesRing(QQ, 't').gen() ring = PolynomialRing(ZZ, 'T') series = sum(- api * t**(i + 1) / (i + 1) for i, api in enumerate(traces)) - series = series.O(d // 2 + 1).exp() + series = series.O(bound + 1).exp() coeffs = list(series) - coeffs += [0] * max(0, d // 2 + 1 - len(coeffs)) + coeffs += [0] * max(0, bound + 1 - len(coeffs)) - data = [0 for _ in range(d + 1)] - for k in range(d // 2 + 1): + fulldeg = d if deg is None else deg + data = [0] * (fulldeg + 1) + for k in range(bound + 1): data[k] = coeffs[k] - for k in range(d // 2 + 1, d + 1): + for k in range(bound + 1, fulldeg + 1): data[k] = sign * coeffs[d - k] * q**(i * (k - d / 2)) return ring(data) - def enumerate_hypergeometric_data(d, weight=None): r""" Return an iterator over parameters of hypergeometric motives (up to swapping). @@ -247,9 +264,9 @@ def cyclotomic_to_alpha(cyclo) -> list: [1/2] sage: cyclotomic_to_alpha([5]) [1/5, 2/5, 3/5, 4/5] - sage: cyclotomic_to_alpha([1,2,3,6]) + sage: cyclotomic_to_alpha([1, 2, 3, 6]) [0, 1/6, 1/3, 1/2, 2/3, 5/6] - sage: cyclotomic_to_alpha([2,3]) + sage: cyclotomic_to_alpha([2, 3]) [1/3, 1/2, 2/3] """ alpha = [QQ((k, d)) for d in cyclo @@ -276,11 +293,11 @@ def alpha_to_cyclotomic(alpha) -> list: [1] sage: alpha_to_cyclotomic([1/2]) [2] - sage: alpha_to_cyclotomic([1/5,2/5,3/5,4/5]) + sage: alpha_to_cyclotomic([1/5, 2/5, 3/5, 4/5]) [5] sage: alpha_to_cyclotomic([0, 1/6, 1/3, 1/2, 2/3, 5/6]) [1, 2, 3, 6] - sage: alpha_to_cyclotomic([1/3,2/3,1/2]) + sage: alpha_to_cyclotomic([1/3, 2/3, 1/2]) [2, 3] """ cyclo = [] @@ -314,7 +331,7 @@ def capital_M(n): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import capital_M - sage: [capital_M(i) for i in range(1,8)] + sage: [capital_M(i) for i in range(1, 8)] [1, 4, 27, 64, 3125, 432, 823543] """ n = ZZ(n) @@ -383,7 +400,7 @@ def gamma_list_to_cyclotomic(galist): sage: gamma_list_to_cyclotomic([-1, 2, 3, -4]) ([3], [4]) - sage: gamma_list_to_cyclotomic([8,2,2,2,-6,-4,-3,-1]) + sage: gamma_list_to_cyclotomic([8, 2, 2, 2, -6, -4, -3, -1]) ([2, 2, 8], [3, 3, 6]) """ resu = defaultdict(int) @@ -423,15 +440,15 @@ def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(cyclotomic=([2],[1])) + sage: Hyp(cyclotomic=([2], [1])) Hypergeometric data for [1/2] and [0] - sage: Hyp(alpha_beta=([1/2],[0])) + sage: Hyp(alpha_beta=([1/2], [0])) Hypergeometric data for [1/2] and [0] - sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5],[0,0,0,0])) + sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5], [0,0,0,0])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] - sage: Hyp(gamma_list=([5],[1,1,1,1,1])) + sage: Hyp(gamma_list=([5], [1,1,1,1,1])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] sage: Hyp(gamma_list=([5,-1,-1,-1,-1,-1])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] @@ -509,8 +526,8 @@ def __eq__(self, other) -> bool: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H1 = Hyp(alpha_beta=([1/2],[0])) - sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H1 = Hyp(alpha_beta=([1/2], [0])) + sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1])) sage: H1 == H1 True sage: H1 == H2 @@ -526,8 +543,8 @@ def __ne__(self, other) -> bool: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H1 = Hyp(alpha_beta=([1/2],[0])) - sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H1 = Hyp(alpha_beta=([1/2], [0])) + sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1])) sage: H1 != H1 False sage: H1 != H2 @@ -542,7 +559,7 @@ def __hash__(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H1 = Hyp(alpha_beta=([1/2],[0])) + sage: H1 = Hyp(alpha_beta=([1/2], [0])) sage: h = hash(H1) """ return hash((self._alpha, self._beta)) @@ -555,7 +572,7 @@ def cyclotomic_data(self) -> tuple: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).cyclotomic_data() + sage: Hyp(alpha_beta=([1/2], [0])).cyclotomic_data() ([2], [1]) """ return (list(self._cyclo_up), list(self._cyclo_down)) @@ -567,7 +584,7 @@ def alpha_beta(self) -> tuple: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).alpha_beta() + sage: Hyp(alpha_beta=([1/2], [0])).alpha_beta() ([1/2], [0]) """ return (list(self._alpha), list(self._beta)) @@ -579,7 +596,7 @@ def alpha(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).alpha() + sage: Hyp(alpha_beta=([1/2], [0])).alpha() [1/2] """ return list(self._alpha) @@ -591,7 +608,7 @@ def beta(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).beta() + sage: Hyp(alpha_beta=([1/2], [0])).beta() [0] """ return list(self._beta) @@ -603,7 +620,7 @@ def defining_polynomials(self) -> tuple: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).defining_polynomials() + sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).defining_polynomials() (x^2 + 1, x^2 - 2*x + 1) """ up = prod(cyclotomic_polynomial(d) for d in self._cyclo_up) @@ -621,9 +638,9 @@ def gamma_array(self) -> dict: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).gamma_array() + sage: Hyp(alpha_beta=([1/2], [0])).gamma_array() {1: -2, 2: 1} - sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_array() + sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_array() {1: -3, 3: -1, 6: 1} """ return dict(self._gamma_array) @@ -637,13 +654,13 @@ def gamma_list(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).gamma_list() + sage: Hyp(alpha_beta=([1/2], [0])).gamma_list() [-1, -1, 2] - sage: Hyp(cyclotomic=([6,2],[1,1,1])).gamma_list() + sage: Hyp(cyclotomic=([6,2], [1,1,1])).gamma_list() [-1, -1, -1, -3, 6] - sage: Hyp(cyclotomic=([3],[4])).gamma_list() + sage: Hyp(cyclotomic=([3], [4])).gamma_list() [-1, 2, 3, -4] """ gamma = self.gamma_array() @@ -659,9 +676,9 @@ def wild_primes(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(cyclotomic=([3],[4])).wild_primes() + sage: Hyp(cyclotomic=([3], [4])).wild_primes() [2, 3] - sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])).wild_primes() + sage: Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9])).wild_primes() [2, 3, 5] """ gamma = self.gamma_array() @@ -682,10 +699,10 @@ def zigzag(self, x, flip_beta=False): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8])) + sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8])) sage: [H.zigzag(x) for x in [0, 1/3, 1/2]] [0, 1, 0] - sage: H = Hyp(cyclotomic=([5],[1,1,1,1])) + sage: H = Hyp(cyclotomic=([5], [1,1,1,1])) sage: [H.zigzag(x) for x in [0,1/6,1/4,1/2,3/4,5/6]] [-4, -4, -3, -2, -1, 0] @@ -707,36 +724,36 @@ def weight(self): With rational inputs:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).weight() + sage: Hyp(alpha_beta=([1/2], [0])).weight() 0 - sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).weight() + sage: Hyp(alpha_beta=([1/4,3/4], [0,0])).weight() 1 - sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[0,0,1/4,3/4])).weight() + sage: Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [0,0,1/4,3/4])).weight() 1 - sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8])) + sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8])) sage: H.weight() 1 With cyclotomic inputs:: - sage: Hyp(cyclotomic=([6,2],[1,1,1])).weight() + sage: Hyp(cyclotomic=([6,2], [1,1,1])).weight() 2 - sage: Hyp(cyclotomic=([6],[1,2])).weight() + sage: Hyp(cyclotomic=([6], [1,2])).weight() 0 - sage: Hyp(cyclotomic=([8],[1,2,3])).weight() + sage: Hyp(cyclotomic=([8], [1,2,3])).weight() 0 - sage: Hyp(cyclotomic=([5],[1,1,1,1])).weight() + sage: Hyp(cyclotomic=([5], [1,1,1,1])).weight() 3 - sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).weight() + sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).weight() 1 - sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).weight() + sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).weight() 2 - sage: Hyp(cyclotomic=([3,3],[2,2,4])).weight() + sage: Hyp(cyclotomic=([3,3], [2,2,4])).weight() 1 With gamma list input:: - sage: Hyp(gamma_list=([8,2,2,2],[6,4,3,1])).weight() + sage: Hyp(gamma_list=([8,2,2,2], [6,4,3,1])).weight() 3 """ alpha = self._alpha @@ -757,15 +774,15 @@ def degree(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=([1/2],[0])).degree() + sage: Hyp(alpha_beta=([1/2], [0])).degree() 1 - sage: Hyp(gamma_list=([2,2,4],[8])).degree() + sage: Hyp(gamma_list=([2,2,4], [8])).degree() 4 - sage: Hyp(cyclotomic=([5,6],[1,1,2,2,3])).degree() + sage: Hyp(cyclotomic=([5,6], [1,1,2,2,3])).degree() 6 - sage: Hyp(cyclotomic=([3,8],[1,1,1,2,6])).degree() + sage: Hyp(cyclotomic=([3,8], [1,1,1,2,6])).degree() 6 - sage: Hyp(cyclotomic=([3,3],[2,2,4])).degree() + sage: Hyp(cyclotomic=([3,3], [2,2,4])).degree() 4 """ return self._deg @@ -781,19 +798,19 @@ def hodge_numbers(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[6])) + sage: H = Hyp(cyclotomic=([3], [6])) sage: H.hodge_numbers() [1, 1] - sage: H = Hyp(cyclotomic=([4],[1,2])) + sage: H = Hyp(cyclotomic=([4], [1,2])) sage: H.hodge_numbers() [2] - sage: H = Hyp(gamma_list=([8,2,2,2],[6,4,3,1])) + sage: H = Hyp(gamma_list=([8,2,2,2], [6,4,3,1])) sage: H.hodge_numbers() [1, 2, 2, 1] - sage: H = Hyp(gamma_list=([5],[1,1,1,1,1])) + sage: H = Hyp(gamma_list=([5], [1,1,1,1,1])) sage: H.hodge_numbers() [1, 1, 1, 1] @@ -832,10 +849,10 @@ def hodge_polynomial(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([6,10],[3,12])) + sage: H = Hyp(cyclotomic=([6,10], [3,12])) sage: H.hodge_polynomial() (T^3 + 2*T^2 + 2*T + 1)/T^2 - sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])) + sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9])) sage: H.hodge_polynomial() (T^5 + 3*T^4 + 3*T^3 + 3*T^2 + 3*T + 1)/T^2 """ @@ -860,7 +877,7 @@ def hodge_function(self, x): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([6,10],[3,12])) + sage: H = Hyp(cyclotomic=([6,10], [3,12])) sage: H.hodge_function(3) 2 sage: H.hodge_function(4) @@ -892,10 +909,10 @@ def hodge_polygon_vertices(self) -> list: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([6,10],[3,12])) + sage: H = Hyp(cyclotomic=([6,10], [3,12])) sage: H.hodge_polygon_vertices() [(0, 0), (1, 0), (3, 2), (5, 6), (6, 9)] - sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9])) + sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6], [1,1,4,5,9])) sage: H.hodge_polygon_vertices() [(0, 0), (1, 0), (4, 3), (7, 9), (10, 18), (13, 30), (14, 35)] """ @@ -915,7 +932,7 @@ def E_polynomial(self, vars=None): INPUT: - - ``vars`` -- optional pair of variables (default `u,v`) + - ``vars`` -- optional pair of variables (default: `u,v`) REFERENCES: @@ -925,7 +942,7 @@ def E_polynomial(self, vars=None): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData - sage: H = HypergeometricData(gamma_list=[-30,-1,6,10,15]) + sage: H = HypergeometricData(gamma_list=[-30, -1, 6, 10, 15]) sage: H.E_polynomial() 8*u*v + 7*u + 7*v + 8 @@ -992,12 +1009,12 @@ def M_value(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6],[1/8,3/8,5/8,7/8])) + sage: H = Hyp(alpha_beta=([1/6,1/3,2/3,5/6], [1/8,3/8,5/8,7/8])) sage: H.M_value() 729/4096 - sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2],[0,0,0,0]))).M_value() + sage: Hyp(alpha_beta=(([1/2,1/2,1/2,1/2], [0,0,0,0]))).M_value() 256 - sage: Hyp(cyclotomic=([5],[1,1,1,1])).M_value() + sage: Hyp(cyclotomic=([5], [1,1,1,1])).M_value() 3125 """ return self._M_value @@ -1013,7 +1030,7 @@ def is_primitive(self) -> bool: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(cyclotomic=([3],[4])).is_primitive() + sage: Hyp(cyclotomic=([3], [4])).is_primitive() True sage: Hyp(gamma_list=[-2, 4, 6, -8]).is_primitive() False @@ -1033,7 +1050,7 @@ def primitive_index(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(cyclotomic=([3],[4])).primitive_index() + sage: Hyp(cyclotomic=([3], [4])).primitive_index() 1 sage: Hyp(gamma_list=[-2, 4, 6, -8]).primitive_index() 2 @@ -1052,7 +1069,7 @@ def has_symmetry_at_one(self) -> bool: EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: Hyp(alpha_beta=[[1/2]*16,[0]*16]).has_symmetry_at_one() + sage: Hyp(alpha_beta=[[1/2]*16, [0]*16]).has_symmetry_at_one() True REFERENCES: @@ -1064,18 +1081,18 @@ def has_symmetry_at_one(self) -> bool: def lfunction(self, t, prec=53): """ - Return the L-function of ``self``. + Return the `L`-function of ``self``. - The result is a wrapper around a PARI L-function. + The result is a wrapper around a PARI `L`-function. INPUT: - - ``prec`` -- precision (default 53) + - ``prec`` -- precision (default: 53) EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: L = H.lfunction(1/64); L PARI L-function associated to Hypergeometric data for [1/3, 2/3] and [1/4, 3/4] sage: L(4) @@ -1095,7 +1112,7 @@ def canonical_scheme(self, t=None): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: H.gamma_list() [-1, 2, 3, -4] sage: H.canonical_scheme() @@ -1187,13 +1204,13 @@ def twist(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/2],[0])) + sage: H = Hyp(alpha_beta=([1/2], [0])) sage: H.twist() Hypergeometric data for [0] and [1/2] sage: H.twist().twist() == H True - sage: Hyp(cyclotomic=([6],[1,2])).twist().cyclotomic_data() + sage: Hyp(cyclotomic=([6], [1,2])).twist().cyclotomic_data() ([3], [1, 2]) """ alpha = [x + QQ((1, 2)) for x in self._alpha] @@ -1207,7 +1224,7 @@ def swap_alpha_beta(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/2],[0])) + sage: H = Hyp(alpha_beta=([1/2], [0])) sage: H.swap_alpha_beta() Hypergeometric data for [0] and [1/2] """ @@ -1225,7 +1242,7 @@ def primitive_data(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: H2 = Hyp(gamma_list=[-2, 4, 6, -8]) sage: H2.primitive_data() == H True @@ -1247,7 +1264,7 @@ def gauss_table(self, p, f, prec): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: H.gauss_table(2, 2, 4) (4, [1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3, 1 + 2 + 2^2 + 2^3]) """ @@ -1277,7 +1294,7 @@ def gauss_table_full(self): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: H.euler_factor(2, 7, cache_p=True) 7*T^2 - 3*T + 1 sage: H.gauss_table_full()[(7, 1)] @@ -1285,12 +1302,12 @@ def gauss_table_full(self): Clearing cached values:: - sage: H = Hyp(cyclotomic=([3],[4])) + sage: H = Hyp(cyclotomic=([3], [4])) sage: H.euler_factor(2, 7, cache_p=True) 7*T^2 - 3*T + 1 sage: d = H.gauss_table_full() sage: d.clear() # Delete all entries of this dict - sage: H1 = Hyp(cyclotomic=([5],[12])) + sage: H1 = Hyp(cyclotomic=([5], [12])) sage: d1 = H1.gauss_table_full() sage: len(d1.keys()) # No cached values 0 @@ -1313,11 +1330,11 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): INPUT: - - `p` -- a prime number + - ``p`` -- a prime number - - `f` -- an integer such that `q = p^f` + - ``f`` -- an integer such that `q = p^f` - - `t` -- a rational parameter + - ``t`` -- a rational parameter - ``prec`` -- precision (optional) @@ -1332,7 +1349,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): From Benasque report [Benasque2009]_, page 8:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) + sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4)) sage: [H.padic_H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.padic_H_value(5,i,-1) for i in range(1,3)] @@ -1353,8 +1370,8 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): Check issue from :issue:`28404`:: - sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2])) - sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2])) + sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1])) sage: [H1.padic_H_value(5,1,i) for i in range(2,5)] [1, -4, -4] sage: [H2.padic_H_value(5,1,i) for i in range(2,5)] @@ -1362,13 +1379,13 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): Check for potential overflow:: - sage: H = Hyp(cyclotomic=[[10,6],[5,4]]) + sage: H = Hyp(cyclotomic=[[10,6], [5,4]]) sage: H.padic_H_value(101, 2, 2) -1560629 Check issue from :issue:`29778`:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) sage: try: ....: print(H.padic_H_value(373, 4, 2)) ....: except ValueError as s: @@ -1377,7 +1394,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): Check error handling for wild and tame primes:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) sage: try: ....: print(H.padic_H_value(5, 1, 2)) ....: except NotImplementedError as s: @@ -1391,7 +1408,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): Check that :issue:`37910` is resolved:: - sage: H = Hyp(alpha_beta=[[1/2,1/2,1/2,1/2,1/2,1/3,2/3,1/6,5/6], [0,0,0,0,0,0,0,0,0]]) + sage: H = Hyp(alpha_beta=[[1/2,1/2,1/2,1/2,1/2,1/3,2/3,1/6,5/6], [0,0,0,0,0,0,0,0,0]]) sage: H.padic_H_value(151, 2, -512000) 50178940126155881 @@ -1406,7 +1423,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): raise ValueError('p not prime') if not all(x.denominator() % p for x in self._alpha + self._beta): raise NotImplementedError('p is wild') - if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0): + if t.numerator() % p == 0 or t.denominator() % p == 0: raise NotImplementedError('p is tame') if 0 in alpha: @@ -1441,6 +1458,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs) sigma = trcoeffs[p - 2] p_ring = sigma.parent() + teich = p_ring.teichmuller(M / t) for i in range(p - 3, -1, -1): sigma = sigma * teich + trcoeffs[i] @@ -1457,13 +1475,13 @@ def H_value(self, p, f, t, ring=None): INPUT: - - `p` -- a prime number + - ``p`` -- a prime number - - `f` -- an integer such that `q = p^f` + - ``f`` -- an integer such that `q = p^f` - - `t` -- a rational parameter + - ``t`` -- a rational parameter - - ``ring`` -- optional (default :class:`UniversalCyclotomicfield`) + - ``ring`` -- optional (default: :class:`UniversalCyclotomicfield`) The ring could be also ``ComplexField(n)`` or ``QQbar``. @@ -1484,7 +1502,7 @@ def H_value(self, p, f, t, ring=None): With values in the UniversalCyclotomicField (slow):: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) + sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4)) sage: [H.H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.H_value(5,i,-1) for i in range(1,3)] @@ -1503,8 +1521,8 @@ def H_value(self, p, f, t, ring=None): Check issue from :issue:`28404`:: - sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2])) - sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H1 = Hyp(cyclotomic=([1,1,1], [6,2])) + sage: H2 = Hyp(cyclotomic=([6,2], [1,1,1])) sage: [H1.H_value(5,1,i) for i in range(2,5)] [1, -4, -4] sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)] @@ -1514,7 +1532,7 @@ def H_value(self, p, f, t, ring=None): Check issue from :issue:`29778`:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(cyclotomic=[[5,5], [4,7]]) sage: try: ....: print(H.padic_H_value(373, 4, 2)) ....: except ValueError as s: @@ -1523,7 +1541,7 @@ def H_value(self, p, f, t, ring=None): Check error handling for wild and tame primes:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(cyclotomic=[[5,5], [4,7]]) sage: try: ....: print(H.padic_H_value(5, 1, 2)) ....: except NotImplementedError as s: @@ -1589,19 +1607,19 @@ def sign(self, t, p): Return the sign of the functional equation for the Euler factor of the motive `H_t` at the prime `p`. For odd weight, the sign of the functional equation is +1. For even - weight, the sign is computed by a recipe found in 11.1 of [Watkins]_ + weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_ (when 0 is not in alpha). EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H = Hyp(cyclotomic=([6,2], [1,1,1])) sage: H.weight(), H.degree() (2, 3) sage: [H.sign(1/4,p) for p in [5,7,11,13,17,19]] [1, 1, -1, -1, 1, 1] - sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2])) + sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2])) sage: H.weight(), H.degree() (2, 4) sage: t = -5 @@ -1610,7 +1628,7 @@ def sign(self, t, p): We check that :issue:`28404` is fixed:: - sage: H = Hyp(cyclotomic=([1,1,1],[6,2])) + sage: H = Hyp(cyclotomic=([1,1,1], [6,2])) sage: [H.sign(4,p) for p in [5,7,11,13,17,19]] [1, 1, -1, -1, 1, 1] @@ -1629,16 +1647,115 @@ def sign(self, t, p): sign = kronecker_symbol(t * (t - 1) * self._sign_param, p) return sign + def euler_factor_tame_contribution(self, t, p, mo, deg=None): + """ + Return a contribution to the Euler factor of the motive `H_t` at a tame prime. + + The output is only nontrivial when `t` has nonzero `p`-adic valuation. + The algorithm is described in Section 11.4.1 of [Watkins]_. + + INPUT: + + - ``t`` -- rational number, not 0 or 1 + + - ``p`` -- prime number of good reduction + + - ``mo`` -- integer + + - ``deg`` -- integer (optional) + + OUTPUT: + + a polynomial + + If ``deg`` is specified, the output is truncated to that degree (inclusive). + + EXAMPLES:: + + sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp + sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]]) + sage: H.euler_factor_tame_contribution(11^2, 11, 4) + 1 + sage: H.euler_factor_tame_contribution(11^20, 11, 4) + 1331*T^2 + 1 + sage: H.euler_factor_tame_contribution(11^20, 11, 4, deg=1) + 1 + sage: H.euler_factor_tame_contribution(11^20, 11, 5) + 1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1 + sage: H.euler_factor_tame_contribution(11^20, 11, 5, deg=3) + 161051*T^3 + 6171*T^2 + 121*T + 1 + sage: H.euler_factor_tame_contribution(11^20, 11, 6) + 1 + """ + t = QQ(t) + if t in [0, 1]: + raise ValueError('invalid t') + if not is_prime(p): + raise ValueError('p not prime') + if not all(x.denominator() % p for x in self._alpha + self._beta): + raise NotImplementedError('p is wild') + + e = t.valuation(p) + t0 = t / p**e + if e > 0: + mul = self.cyclotomic_data()[1].count(mo) + elif e < 0: + mul = self.cyclotomic_data()[0].count(mo) + else: + mul = None + if e % mo or not mul: + return ZZ.one() + d = euler_phi(mo) + f = IntegerModRing(mo)(p).multiplicative_order() + if deg is None: + deg = d + if deg < f: + return ZZ.one() + q = p ** f + prec = ceil(deg*(self.weight()+1-mul)/2 + log(2*d + 1, p)) + k = (q-1) // mo + flip = (f == 1 and prec == 1) + gtab_prec, gtab = self.gauss_table(p, f, prec) + try: + p_ring = gtab[0].parent() + except AttributeError: + p_ring = Zp(p, prec, 'fixed-mod') + M = self.M_value() + teich = p_ring.teichmuller(M / t0) + m = {r: self._beta.count(QQ((r, q - 1))) for r in range(q - 1)} + D = -min(self.zigzag(x, flip_beta=True) for x in self._alpha + self._beta) + gamma = self.gamma_array() + l = [] + for j in range(mo): + if gcd(j, mo) == 1: + r = j * k + term = teich**r * ZZ(-1)**m[0] + ct = 0 + for v, gv in gamma.items(): + r1 = v * r % (q-1) + ct += gv * sum(r1.digits(p)) + term *= p_ring(gtab[r1]) ** (-gv if flip else gv) + ct //= p - 1 + term *= ZZ(-1) ** ct + ct += f * (D + m[0] - m[r]) + l.append(term * p**ct) + traces = [0 if j % f else sum(i ** (j//f) for i in l) for j in range(1,d+1)] + R = IntegerModRing(p**prec) + traces = [R(i).lift_centered() for i in traces] + return characteristic_polynomial_from_traces(traces, d, p, 0, 1, deg, use_fe=False) + @cached_method - def euler_factor(self, t, p, cache_p=False): + def euler_factor(self, t, p, deg=None, cache_p=False): """ Return the Euler factor of the motive `H_t` at prime `p`. INPUT: - - `t` -- rational number, not 0 or 1 + - ``t`` -- rational number, not 0 or 1 + + - ``p`` -- prime number of good reduction - - `p` -- prime number of good reduction + - ``deg`` -- integer or ``None`` OUTPUT: @@ -1647,12 +1764,18 @@ def euler_factor(self, t, p, cache_p=False): See [Benasque2009]_ for explicit examples of Euler factors. For odd weight, the sign of the functional equation is +1. For even - weight, the sign is computed by a recipe found in 11.1 of [Watkins]_. + weight, the sign is computed by a recipe found in Section 11.1 of [Watkins]_. + + If ``deg`` is specified, then the polynomial is only computed up to degree + ``deg`` (inclusive). + + The prime `p` may be tame, but not wild. When `v_p(t-1)` is nonzero and even, + the Euler factor includes a linear term described in Section 11.2 of [Watkins]_. EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) + sage: H = Hyp(alpha_beta=([1/2]*4, [0]*4)) sage: H.euler_factor(-1, 5) 15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1 @@ -1668,7 +1791,7 @@ def euler_factor(self, t, p, cache_p=False): 23*T^2 + 8*T + 1, 29*T^2 + 2*T + 1] - sage: H = Hyp(cyclotomic=([6,2],[1,1,1])) + sage: H = Hyp(cyclotomic=([6,2], [1,1,1])) sage: H.weight(), H.degree() (2, 3) sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]] @@ -1679,7 +1802,7 @@ def euler_factor(self, t, p, cache_p=False): 4913*T^3 + 323*T^2 + 19*T + 1, 6859*T^3 - 57*T^2 - 3*T + 1] - sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2])) + sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12], [0,1/2,1/2,1/2])) sage: H.weight(), H.degree() (2, 4) sage: t = -5 @@ -1696,52 +1819,107 @@ def euler_factor(self, t, p, cache_p=False): sage: H = Hyp(cyclotomic=([11], [7, 12])) sage: H.euler_factor(2, 13) 371293*T^10 - 85683*T^9 + 26364*T^8 + 1352*T^7 - 65*T^6 + 394*T^5 - 5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1 + sage: H.euler_factor(2, 13, deg=4) + -5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1 sage: H.euler_factor(2, 19) # long time 2476099*T^10 - 651605*T^9 + 233206*T^8 - 77254*T^7 + 20349*T^6 - 4611*T^5 + 1071*T^4 - 214*T^3 + 34*T^2 - 5*T + 1 + This is an example of tame primes:: + + sage: H = Hyp(cyclotomic=[[4,2,2], [3,1,1]]) + sage: H.euler_factor(8, 7) + -7*T^3 + 7*T^2 - T + 1 + sage: H.euler_factor(50, 7) + -7*T^3 + 7*T^2 - T + 1 + sage: H.euler_factor(7, 7) + -T + 1 + sage: H.euler_factor(1/7^2, 7) + T + 1 + sage: H.euler_factor(1/7^4, 7) + 7*T^3 + 7*T^2 + T + 1 + TESTS:: - sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2])) + sage: H1 = Hyp(alpha_beta=([1,1,1], [1/2,1/2,1/2])) sage: H2 = H1.swap_alpha_beta() sage: H1.euler_factor(-1, 3) 27*T^3 + 3*T^2 + T + 1 sage: H2.euler_factor(-1, 3) 27*T^3 + 3*T^2 + T + 1 - sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5])) + sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3], [1/2,1/5,2/5,3/5,4/5])) sage: H.euler_factor(5,7) 16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1 Check for precision downsampling:: - sage: H = Hyp(cyclotomic=[[3],[4]]) + sage: H = Hyp(cyclotomic=[[3], [4]]) sage: H.euler_factor(2, 11, cache_p=True) 11*T^2 - 3*T + 1 - sage: H = Hyp(cyclotomic=[[12],[1,2,6]]) + sage: H = Hyp(cyclotomic=[[12], [1,2,6]]) sage: H.euler_factor(2, 11, cache_p=True) -T^4 + T^3 - T + 1 Check issue from :issue:`29778`:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(cyclotomic=[[5,5], [4,7]]) sage: try: ....: print(H.euler_factor(2, 373)) ....: except ValueError as s: ....: print(s) p^f cannot exceed 2^31 - Check error handling for wild and tame primes:: + Check handling of some tame cases:: - sage: H = Hyp(alpha_beta=([1/5,2/5,3/5,4/5,1/5,2/5,3/5,4/5], [1/4,3/4,1/7,2/7,3/7,4/7,5/7,6/7])) + sage: H = Hyp(cyclotomic=[[4,2,2,2], [3,1,1,1]]) + sage: H.euler_factor(8, 7) + 2401*T^4 - 392*T^3 + 46*T^2 - 8*T + 1 + sage: H.euler_factor(50, 7) + 16807*T^5 - 343*T^4 - 70*T^3 - 10*T^2 - T + 1 + sage: H = Hyp(cyclotomic=[[3,7], [4,5,6]]) + sage: H.euler_factor(11, 11) + 1 + sage: H.euler_factor(11**4, 11) + 1331*T^2 + 1 + sage: H.euler_factor(11**5, 11) + 1771561*T^4 + 161051*T^3 + 6171*T^2 + 121*T + 1 + sage: H.euler_factor(11**5, 11, deg=3) + 161051*T^3 + 6171*T^2 + 121*T + 1 + sage: H.euler_factor(11**-3, 11) + 1331*T^2 + 1 + sage: H.euler_factor(11**-7, 11) + 2357947691*T^6 - 58564*T^3 + 1 + sage: H = Hyp(cyclotomic=[[7], [5,1,1]]) + sage: H.euler_factor(2, 2) + -T + 1 + sage: H.euler_factor(2^-7, 2) + 8*T^6 - 2*T^3 + 1 + sage: H.euler_factor(3, 2) + 4*T^5 + 4*T^4 + 2*T^3 + 2*T^2 + T + 1 + + More examples of tame primes from [Watkins]_:: + + sage: H = Hyp(cyclotomic=[[3,12], [6,6,1,1]]) + sage: H.euler_factor(1/8, 7).factor() + (-1) * (7*T - 1) * (117649*T^4 + 2744*T^3 + 105*T^2 + 8*T + 1) + sage: H.euler_factor(1/12, 11).factor() + (11*T + 1) * (1771561*T^4 - 18634*T^3 + 22*T^2 - 14*T + 1) + sage: H = Hyp(cyclotomic=[[4,4,4],[6,2,1,1,1]]) + sage: H.euler_factor(1/8, 7).factor() + (49*T + 1) * (5764801*T^4 - 86436*T^3 + 2758*T^2 - 36*T + 1) + sage: H.euler_factor(1/12, 11).factor() + (-1) * (121*T - 1) * (214358881*T^4 - 527076*T^3 + 12694*T^2 - 36*T + 1) + sage: H = Hyp(cyclotomic=[[10,4,2], [18,1]]) + sage: H.euler_factor(1/14, 13) + -4826809*T^6 + 114244*T^5 + 2197*T^4 - 13*T^2 - 4*T + 1 + + Check error handling for wild primes:: + + sage: H = Hyp(cyclotomic=[[5,5], [4,7]]) sage: try: ....: print(H.euler_factor(2, 5)) ....: except NotImplementedError as s: ....: print(s) p is wild - sage: try: - ....: print(H.euler_factor(3, 3)) - ....: except NotImplementedError as s: - ....: print(s) - p is tame REFERENCES: @@ -1751,19 +1929,33 @@ def euler_factor(self, t, p, cache_p=False): t = QQ(t) if t in [0, 1]: raise ValueError('invalid t') - alpha = self._alpha - if 0 in alpha: - return self._swap.euler_factor(~t, p) - if not is_prime(p): raise ValueError('p not prime') if not all(x.denominator() % p for x in self._alpha + self._beta): raise NotImplementedError('p is wild') - if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0): - raise NotImplementedError('p is tame') - # now p is good - d = self.degree() + if 0 in self._alpha: + return self._swap.euler_factor(~t, p) + P = PolynomialRing(ZZ, 'T') + if t.numerator() % p == 0 or t.denominator() % p == 0: + ans = P.one() + for m in set(j for i in self.cyclotomic_data() for j in i): + ans *= self.euler_factor_tame_contribution(t, p, m, deg) + if deg is not None: + ans = ans.truncate(deg + 1) + return ans + # now p is good, or p is tame and t is a p-adic unit + elif (t-1) % p == 0: + typ = "mult" + d = self.degree() - 1 + if d % 2: + d -= 1 + else: + typ = "good" + d = self.degree() bound = d // 2 + if deg is not None: + bound = min(deg, bound) + if p ** bound > 2 ** 31: raise ValueError("p^f cannot exceed 2^31") @@ -1771,5 +1963,55 @@ def euler_factor(self, t, p, cache_p=False): for i in range(bound)] w = self.weight() - sign = self.sign(t, p) - return characteristic_polynomial_from_traces(traces, d, p, w, sign) + m1 = self.cyclotomic_data()[1].count(1) + + # In the multiplicative case, we sometimes need to pull out a linear factor + # in order to apply the functional equation. + if typ == "mult": + if self.degree() % 2 == 0: + sign = 1 + if w % 2: + assert m1 % 2 == 0 + u = (-1) ** (m1//2) + u *= prod(v ** gv for v, gv in self.gamma_array().items()) + c = kronecker_symbol(u, p) * p**((w-1)//2) + else: + u = (-1) ** (1 + self.degree()//2 + (m1-1)//2) + num, den = self.defining_polynomials() + x = num.parent().gen() + num = num(-x) + num /= (x-1) ** num.valuation(x-1) + den /= (x-1) ** den.valuation(x-1) + u *= 2 * num(1) / den(1) + c = kronecker_symbol(u, p) * p**(w//2) + cpow = c + for j in range(len(traces)): + traces[j] -= cpow + cpow *= c + tmp = 1 - c*P.gen() + else: + u = (-1) ** (1+(self.degree()-1)//2) + num, den = self.defining_polynomials() + x = num.parent().gen() + den = den(-x) + num /= (x-1) ** num.valuation(x-1) + den /= (x-1) ** den.valuation(x-1) + u *= num(1) / den(1) + sign = kronecker_symbol(u, p) + else: + sign = self.sign(t, p) + + ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg) + + # In the multiplicative case, we sometimes need to add extra factors. + if typ == "mult": + if self.degree() % 2 == 0: + ans *= tmp + if w % 2 == 0 and (t-1).valuation(p) % 2 == 0: + K = (-1) ** ((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list()) + t0 = (~t-1) / p**((t-1).valuation(p)) + c = kronecker_symbol(K*t0, p) * p**(w//2) + ans *= 1 - c*P.gen() + if deg is not None: + ans = ans.truncate(deg + 1) + return ans