From 6fc851831f123ca099bbc88bc0b54db7831ec5e3 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Fri, 26 Apr 2024 11:54:15 -0700 Subject: [PATCH 01/19] Implement hypergeometric Euler factors for some tame primes --- src/sage/modular/hypergeometric_motive.py | 87 +++++++++++++++++++---- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 281ee7e11be..be620d74784 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -83,7 +83,7 @@ from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField -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. @@ -108,10 +108,18 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign): - ``i`` -- integer, the weight in the motivic sense - ``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:: @@ -140,6 +148,11 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign): 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) @@ -147,26 +160,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 = 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 for _ in range(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). @@ -1357,7 +1374,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()*t.denominator() % p == 0): raise NotImplementedError('p is tame') if 0 in alpha: @@ -1581,7 +1598,7 @@ def sign(self, t, p): return sign @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`. @@ -1590,6 +1607,8 @@ def euler_factor(self, t, p, cache_p=False): - `t` -- rational number, not 0 or 1 - `p` -- prime number of good reduction + + - `deg` -- integer or None OUTPUT: @@ -1599,6 +1618,11 @@ def euler_factor(self, t, p, cache_p=False): 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]_. + + If ``deg`` is specified, then the polynomial is only computed up to degree + ``deg`` (inclusive). + + The prime `p` may not be wild. It may be tame if `t` is a `p`-adic unit. EXAMPLES:: @@ -1647,6 +1671,8 @@ 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 @@ -1680,6 +1706,19 @@ def euler_factor(self, t, p, cache_p=False): ....: print(s) p^f cannot exceed 2^31 + Check handling of some tame cases:: + + 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 = 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 + 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])) @@ -1710,11 +1749,22 @@ def euler_factor(self, t, p, 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()*t.denominator() % p == 0): raise NotImplementedError('p is tame') # now p is good - d = self.degree() - bound = d // 2 + if (t-1) % p == 0: + d = self.degree()-1 + bound = d + if deg is not None: + bound = min(deg, bound) + deg = bound + use_fe = False + else: + d = self.degree() + bound = d // 2 + if deg is not None: + bound = min(deg, bound) + use_fe = True if p ** bound > 2 ** 31: raise ValueError("p^f cannot exceed 2^31") @@ -1723,4 +1773,11 @@ def euler_factor(self, t, p, cache_p=False): w = self.weight() sign = self.sign(t, p) - return characteristic_polynomial_from_traces(traces, d, p, w, sign) + ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg, use_fe=use_fe) + if (w % 2 == 0 and (t-1) % p == 0 and QQ(t-1).valuation(p) % 2 == 0): + m1 = self.cyclotomic_data()[1].count(1) + K = (-1)**((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list()) + t0 = (~t-1)/p**(QQ(t-1).valuation(p)) + c = kronecker_symbol(K*t0, p)*p**(w//2) + ans *= 1 - c*ans.parent().gen() + return ans From 71b2c9fbaff13c1595d23b0e28a67f1d6ae3695c Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 27 Apr 2024 10:40:27 -0700 Subject: [PATCH 02/19] Add Roberts-Rodriguez Villegas Notices article to references --- src/doc/en/reference/references/index.rst | 3 +++ src/sage/modular/hypergeometric_motive.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 09b78514e1c..9c9f4205bd5 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5471,6 +5471,9 @@ REFERENCES: Hecke algebras of type A*. J. Algebraic Combin. **6** (1997), 59-87. +.. [RR2022] David Roberts and Fernando Rodriguez Villegas, Hypergeometric motives, + Notices of the American Mathematical Society 69 (2022), 914-929. + .. [RSS] :wikipedia:`Residual_sum_of_squares`, accessed 13th October 2009. diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index be620d74784..1bb46cb5c5b 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`. +for which `v_p(t) = 0`. AUTHORS: @@ -42,6 +42,8 @@ - [Roberts2015]_ +- [RR2022]_ + - [BeCoMe]_ - [Watkins]_ @@ -60,7 +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 @@ -1623,6 +1625,8 @@ def euler_factor(self, t, p, deg=None, cache_p=False): ``deg`` (inclusive). The prime `p` may not be wild. It may be tame if `t` is a `p`-adic unit. + When `v_p(t-1)` is nonzero and even, the Euler factor includes a linear term + described in 11.2 of [Watkins]_. EXAMPLES:: @@ -1749,9 +1753,10 @@ def euler_factor(self, t, p, deg=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') + e = t.valuation(p) if (t.numerator()*t.denominator() % p == 0): raise NotImplementedError('p is tame') - # now p is good + # now p is good, or p is tame and t is a p-adic unit if (t-1) % p == 0: d = self.degree()-1 bound = d @@ -1774,10 +1779,12 @@ def euler_factor(self, t, p, deg=None, cache_p=False): w = self.weight() sign = self.sign(t, p) ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg, use_fe=use_fe) - if (w % 2 == 0 and (t-1) % p == 0 and QQ(t-1).valuation(p) % 2 == 0): + + # Add an extra factor when w is even and t-1 has nonzero even valuation. + if (w % 2 == 0 and (t-1) % p == 0 and (t-1).valuation(p) % 2 == 0): m1 = self.cyclotomic_data()[1].count(1) K = (-1)**((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list()) - t0 = (~t-1)/p**(QQ(t-1).valuation(p)) + t0 = (~t-1)/p**((t-1).valuation(p)) c = kronecker_symbol(K*t0, p)*p**(w//2) ans *= 1 - c*ans.parent().gen() return ans From ed77891cee9f0da772d1846f49902ccb7e6feb46 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 15:38:01 -0700 Subject: [PATCH 03/19] First attempt to compute Hg traces when t has nonzero valuation --- src/sage/modular/hypergeometric_motive.py | 82 ++++++++++++++++------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 2de9485a591..845208b6377 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -168,7 +168,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f if use_fe: bound = d // 2 if deg is None else min(d // 2, deg) else: - bound = deg + 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 and use_fe: @@ -1422,8 +1422,6 @@ 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): - raise NotImplementedError('p is tame') if 0 in alpha: return self._swap.padic_H_value(p, f, ~t, prec) @@ -1457,10 +1455,39 @@ 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] - resu = ZZ(-1) ** m[0] * sigma / (1 - q) + + if t.numerator() % p == 0 or t.denominator() % p == 0: + e = t.valuation(p) + t0 = t / p**e + teich = p_ring.teichmuller(M / t0) + resu = p_ring.zero() + flip = (f == 1 and prec == 1) + index = 1 if t.numerator() % p == 0 else 0 + for mo in set(self.cyclotomic_data()[index]): + if (q-1)%mo == 0 and e%mo == 0: + k = (q-1)//mo + for j in range(mo): + if gcd(j,mo) == 1: + r = j*k + term = teich**r + ct = 0 + for v, gv in gamma.items(): + r1 = v*r%(q-1) + ct += gv*sum(r1.digits(p)) + gv = -gv if flip else gv + tmp = p_ring(gtab[r]) + if gv < 0: + tmp = 1/tmp + for _ in range(abs(gv)): + term *= tmp + term *= ZZ(-1) ** m[0] + ct //= (p-1) + resu += term * p**(ct+f*(D + m[0] - m[r])) + else: + teich = p_ring.teichmuller(M / t) + for i in range(p - 3, -1, -1): + sigma = sigma * teich + trcoeffs[i] + resu = ZZ(-1) ** m[0] * sigma / (1 - q) return IntegerModRing(p**prec)(resu).lift_centered() trace = padic_H_value @@ -1791,31 +1818,34 @@ def euler_factor(self, t, p, deg=None, 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') - e = t.valuation(p) - if (t.numerator()*t.denominator() % p == 0): - raise NotImplementedError('p is tame') + if 0 in self._alpha: + return self._swap.euler_factor(~t, p) + if t.numerator() % p == 0 or t.denominator() % p == 0: + typ = "tame" + d = 0 + e = t.valuation(p) + index = 1 if t.numerator() % p == 0 else 0 + for m in set(self.cyclotomic_data()[index]): + if e%m == 0: + d += euler_phi(m) + if d == 0: + return PolynomialRing(ZZ, 'T').one() # now p is good, or p is tame and t is a p-adic unit - if (t-1) % p == 0: - d = self.degree()-1 - bound = d - if deg is not None: - bound = min(deg, bound) - deg = bound - use_fe = False + elif (t-1) % p == 0: + typ = "mult" + d = self.degree() - 1 else: + typ = "good" d = self.degree() - bound = d // 2 - if deg is not None: - bound = min(deg, bound) - use_fe = True + use_fe = (typ == "good") + bound = d // 2 if use_fe else d + if deg is not None: + bound = min(deg, bound) + if p ** bound > 2 ** 31: raise ValueError("p^f cannot exceed 2^31") @@ -1827,7 +1857,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg, use_fe=use_fe) # Add an extra factor when w is even and t-1 has nonzero even valuation. - if (w % 2 == 0 and (t-1) % p == 0 and (t-1).valuation(p) % 2 == 0): + if (w % 2 == 0 and typ == "mult" and (t-1).valuation(p) % 2 == 0): m1 = self.cyclotomic_data()[1].count(1) K = (-1)**((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list()) t0 = (~t-1)/p**((t-1).valuation(p)) From 8ec2854420d276b3e207b130ef0414dc32213dd1 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 17:54:39 -0700 Subject: [PATCH 04/19] Better attempt to compute tame traces --- src/sage/modular/hypergeometric_motive.py | 118 +++++++++++++--------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 845208b6377..069eedd2aee 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -79,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 @@ -1422,6 +1423,8 @@ 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() % p == 0 or t.denominator() % p == 0: + raise NotImplementedError('p is tame') if 0 in alpha: return self._swap.padic_H_value(p, f, ~t, prec) @@ -1456,38 +1459,10 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): sigma = trcoeffs[p - 2] p_ring = sigma.parent() - if t.numerator() % p == 0 or t.denominator() % p == 0: - e = t.valuation(p) - t0 = t / p**e - teich = p_ring.teichmuller(M / t0) - resu = p_ring.zero() - flip = (f == 1 and prec == 1) - index = 1 if t.numerator() % p == 0 else 0 - for mo in set(self.cyclotomic_data()[index]): - if (q-1)%mo == 0 and e%mo == 0: - k = (q-1)//mo - for j in range(mo): - if gcd(j,mo) == 1: - r = j*k - term = teich**r - ct = 0 - for v, gv in gamma.items(): - r1 = v*r%(q-1) - ct += gv*sum(r1.digits(p)) - gv = -gv if flip else gv - tmp = p_ring(gtab[r]) - if gv < 0: - tmp = 1/tmp - for _ in range(abs(gv)): - term *= tmp - term *= ZZ(-1) ** m[0] - ct //= (p-1) - resu += term * p**(ct+f*(D + m[0] - m[r])) - else: - teich = p_ring.teichmuller(M / t) - for i in range(p - 3, -1, -1): - sigma = sigma * teich + trcoeffs[i] - resu = ZZ(-1) ** m[0] * sigma / (1 - q) + teich = p_ring.teichmuller(M / t) + for i in range(p - 3, -1, -1): + sigma = sigma * teich + trcoeffs[i] + resu = ZZ(-1) ** m[0] * sigma / (1 - q) return IntegerModRing(p**prec)(resu).lift_centered() trace = padic_H_value @@ -1672,6 +1647,56 @@ 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): + e = t.valuation(p) + t0 = t / p**e + if e%mo: + return 1 + if e > 0: + mul = self.cyclotomic_data()[1].count(mo) + if not mul: + return 1 + elif e < 0: + mul = self.cyclotomic_data()[0].count(mo) + if not mul: + return 1 + else: + return 1 + d = euler_phi(mo) + prec = ceil(d*(self.weight()+1-mul)/2 + log(2*d + 1, p)) + f = IntegerModRing(mo)(p).multiplicative_order() + q = p**f + 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=d, use_fe=False) + @cached_method def euler_factor(self, t, p, deg=None, cache_p=False): """ @@ -1789,14 +1814,25 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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 + -7*T^3 +H 7*T^2 - T + 1 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**-3, 11) + -1331*T^2 + 1 + sage: H.euler_factor(11**-7, 11) + 2357947691*T^6 - 58564*T^3 + 1 - Check error handling for wild and tame primes:: + Check error handling for wild 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: try: @@ -1804,11 +1840,6 @@ def euler_factor(self, t, p, deg=None, cache_p=False): ....: 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: @@ -1826,14 +1857,11 @@ def euler_factor(self, t, p, deg=None, cache_p=False): return self._swap.euler_factor(~t, p) if t.numerator() % p == 0 or t.denominator() % p == 0: typ = "tame" - d = 0 - e = t.valuation(p) index = 1 if t.numerator() % p == 0 else 0 + ans = PolynomialRing(ZZ, 'T').one() for m in set(self.cyclotomic_data()[index]): - if e%m == 0: - d += euler_phi(m) - if d == 0: - return PolynomialRing(ZZ, 'T').one() + ans *= self.euler_factor_tame_contribution(t, p, m) + return ans # now p is good, or p is tame and t is a p-adic unit elif (t-1) % p == 0: typ = "mult" From cca28fc12dbe6401b5b3ba3f736268f3c147a566 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:26:12 -0700 Subject: [PATCH 05/19] Update docstrings --- src/sage/modular/hypergeometric_motive.py | 264 +++++++++++++--------- 1 file changed, 153 insertions(+), 111 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 069eedd2aee..b722c375b9a 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -145,11 +145,11 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f 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 @@ -264,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 @@ -293,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 = [] @@ -331,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) @@ -400,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) @@ -440,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] @@ -526,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 @@ -543,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 @@ -559,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)) @@ -572,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)) @@ -584,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)) @@ -596,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) @@ -608,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) @@ -620,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) @@ -638,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) @@ -654,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() @@ -676,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() @@ -699,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] @@ -724,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 @@ -774,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 @@ -798,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] @@ -849,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 """ @@ -877,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) @@ -909,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)] """ @@ -942,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 @@ -1009,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 @@ -1030,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 @@ -1050,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 @@ -1069,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: @@ -1092,7 +1092,7 @@ def lfunction(self, t, prec=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) @@ -1112,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() @@ -1204,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] @@ -1224,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] """ @@ -1242,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 @@ -1264,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]) """ @@ -1294,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)] @@ -1302,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 @@ -1349,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)] @@ -1370,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)] @@ -1379,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: @@ -1394,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: @@ -1408,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 @@ -1502,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)] @@ -1521,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)] @@ -1532,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: @@ -1541,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: @@ -1613,13 +1613,13 @@ def sign(self, t, p): 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 @@ -1628,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] @@ -1648,6 +1648,42 @@ def sign(self, t, p): return sign def euler_factor_tame_contribution(self, t, p, mo): + """ + Return a contribution to the Euler factor of the motive `H_t` at a tame prime. + + INPUT: + + - `t` -- rational number, not 0 or 1 + + - `p` -- prime number of good reduction + + - `mo` -- integer + + OUTPUT: + + a polynomial + + 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, 5) + 1771561*T^4 + 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%mo: @@ -1722,14 +1758,13 @@ def euler_factor(self, t, p, deg=None, cache_p=False): If ``deg`` is specified, then the polynomial is only computed up to degree ``deg`` (inclusive). - The prime `p` may not be wild. It may be tame if `t` is a `p`-adic unit. - When `v_p(t-1)` is nonzero and even, the Euler factor includes a linear term - described in 11.2 of [Watkins]_. + 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 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 @@ -1745,7 +1780,7 @@ def euler_factor(self, t, p, deg=None, 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]] @@ -1756,7 +1791,7 @@ def euler_factor(self, t, p, deg=None, 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 @@ -1778,30 +1813,44 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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: @@ -1810,12 +1859,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): Check handling of some tame cases:: - 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 +H 7*T^2 - T + 1 - sage: H = Hyp(cyclotomic=[[4,2,2,2],[3,1,1,1]]) + 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) @@ -1834,7 +1878,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): Check error handling for wild 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.euler_factor(2, 5)) ....: except NotImplementedError as s: @@ -1856,10 +1900,8 @@ def euler_factor(self, t, p, deg=None, cache_p=False): if 0 in self._alpha: return self._swap.euler_factor(~t, p) if t.numerator() % p == 0 or t.denominator() % p == 0: - typ = "tame" - index = 1 if t.numerator() % p == 0 else 0 ans = PolynomialRing(ZZ, 'T').one() - for m in set(self.cyclotomic_data()[index]): + for m in set(flatten(self.cyclotomic_data())): ans *= self.euler_factor_tame_contribution(t, p, m) return ans # now p is good, or p is tame and t is a p-adic unit From a318b87689280f598c6bd1664d016ac2809dc6ce Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:30:24 -0700 Subject: [PATCH 06/19] More docstring updates --- src/sage/modular/hypergeometric_motive.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index b722c375b9a..afa5b831162 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` -for which `v_p(t) = 0`. +of good or tame reduction. AUTHORS: @@ -1650,7 +1650,11 @@ def sign(self, t, p): def euler_factor_tame_contribution(self, t, p, mo): """ 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 recipe is described in section 11.4.1 of [Watkins]_. + + INPUT: - `t` -- rational number, not 0 or 1 @@ -1744,7 +1748,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): - `p` -- prime number of good reduction - - `deg` -- integer or None + - ``deg`` -- integer or ``None`` OUTPUT: @@ -1753,7 +1757,7 @@ def euler_factor(self, t, p, deg=None, 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). @@ -1927,7 +1931,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg, use_fe=use_fe) # Add an extra factor when w is even and t-1 has nonzero even valuation. - if (w % 2 == 0 and typ == "mult" and (t-1).valuation(p) % 2 == 0): + if w % 2 == 0 and typ == "mult" and (t-1).valuation(p) % 2 == 0: m1 = self.cyclotomic_data()[1].count(1) K = (-1)**((m1-1)//2)*2*prod(abs(x) for x in self.gamma_list()) t0 = (~t-1)/p**((t-1).valuation(p)) From 40eb45119d646476c8665af41bee48238aca6a6d Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:45:28 -0700 Subject: [PATCH 07/19] Fix some doctest failures --- src/sage/modular/hypergeometric_motive.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index afa5b831162..e557da38a56 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1872,11 +1872,11 @@ def euler_factor(self, t, p, deg=None, cache_p=False): sage: H.euler_factor(11, 11) 1 sage: H.euler_factor(11**4, 11) - -1331*T^2 + 1 + 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**-3, 11) - -1331*T^2 + 1 + 1331*T^2 + 1 sage: H.euler_factor(11**-7, 11) 2357947691*T^6 - 58564*T^3 + 1 @@ -1905,7 +1905,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): return self._swap.euler_factor(~t, p) if t.numerator() % p == 0 or t.denominator() % p == 0: ans = PolynomialRing(ZZ, 'T').one() - for m in set(flatten(self.cyclotomic_data())): + for m in set(j for i in self.cyclotomic_data() for j in i): ans *= self.euler_factor_tame_contribution(t, p, m) return ans # now p is good, or p is tame and t is a p-adic unit From 7babaa8e26a4b2e4e10f75bccb3686bc7e96d2b0 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:48:09 -0700 Subject: [PATCH 08/19] Fix some whitespace issues raised by lint --- src/sage/modular/hypergeometric_motive.py | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index e557da38a56..a259e444903 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -114,17 +114,17 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f - ``i`` -- integer, the weight in the motivic sense - ``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:: @@ -1660,7 +1660,7 @@ def euler_factor_tame_contribution(self, t, p, mo): - `t` -- rational number, not 0 or 1 - `p` -- prime number of good reduction - + - `mo` -- integer OUTPUT: @@ -1668,7 +1668,7 @@ def euler_factor_tame_contribution(self, t, p, mo): a polynomial 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) @@ -1690,7 +1690,7 @@ def euler_factor_tame_contribution(self, t, p, mo): e = t.valuation(p) t0 = t / p**e - if e%mo: + if e % mo: return 1 if e > 0: mul = self.cyclotomic_data()[1].count(mo) @@ -1725,14 +1725,14 @@ def euler_factor_tame_contribution(self, t, p, mo): term = teich**r * ZZ(-1)**m[0] ct = 0 for v, gv in gamma.items(): - r1 = v*r%(q-1) + 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)] + 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=d, use_fe=False) @@ -1747,7 +1747,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): - `t` -- rational number, not 0 or 1 - `p` -- prime number of good reduction - + - ``deg`` -- integer or ``None`` OUTPUT: @@ -1758,10 +1758,10 @@ def euler_factor(self, t, p, deg=None, cache_p=False): For odd weight, the sign of the functional equation is +1. For even 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 11.2 of [Watkins]_. @@ -1862,7 +1862,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): p^f cannot exceed 2^31 Check handling of some tame cases:: - + 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 @@ -1919,7 +1919,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): bound = d // 2 if use_fe else d if deg is not None: bound = min(deg, bound) - + if p ** bound > 2 ** 31: raise ValueError("p^f cannot exceed 2^31") From f93b4f4b232139c6faae5f1dc700d010936367f2 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:57:53 -0700 Subject: [PATCH 09/19] Add colon after "default" in docstrings --- src/sage/modular/hypergeometric_motive.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index a259e444903..88e037057a2 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -117,7 +117,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f - ``deg`` -- an integer or None - - ``use_fe`` -- a boolean (default True) + - ``use_fe`` -- a boolean (default: ``True``) OUTPUT: @@ -932,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: @@ -1087,7 +1087,7 @@ def lfunction(self, t, prec=53): INPUT: - - ``prec`` -- precision (default 53) + - ``prec`` -- precision (default: 53) EXAMPLES:: @@ -1481,7 +1481,7 @@ def H_value(self, p, f, t, ring=None): - `t` -- a rational parameter - - ``ring`` -- optional (default :class:`UniversalCyclotomicfield`) + - ``ring`` -- optional (default: :class:`UniversalCyclotomicfield`) The ring could be also ``ComplexField(n)`` or ``QQbar``. From a119d846cb9e5e4ea6f39c95709a38a6ce6930e8 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:59:18 -0700 Subject: [PATCH 10/19] More docstring cleanup --- src/sage/modular/hypergeometric_motive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 88e037057a2..97053e243aa 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -115,7 +115,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f - ``sign`` -- integer, the sign - - ``deg`` -- an integer or None + - ``deg`` -- an integer or ``None`` - ``use_fe`` -- a boolean (default: ``True``) @@ -183,7 +183,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f coeffs += [0] * max(0, bound + 1 - len(coeffs)) fulldeg = d if deg is None else deg - data = [0 for _ in range(fulldeg + 1)] + data = [0] * fulldeg for k in range(bound + 1): data[k] = coeffs[k] for k in range(bound + 1, fulldeg + 1): From 14ad47bfd31325fb2ec08698198677704ec2ea89 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sat, 4 May 2024 18:59:55 -0700 Subject: [PATCH 11/19] One more docstring fix --- src/sage/modular/hypergeometric_motive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 97053e243aa..9b94a783766 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -125,7 +125,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f 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. + If ``use_fe`` is ``False``, we ignore the local functional equation. EXAMPLES:: From 76dd8eeea424832433424066a93bab153c3f867c Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sun, 5 May 2024 07:44:45 -0700 Subject: [PATCH 12/19] Handle truncation in euler_factor_tame_contribution; fix doctest failures --- src/sage/modular/hypergeometric_motive.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 9b94a783766..d90750968ba 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -183,7 +183,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f coeffs += [0] * max(0, bound + 1 - len(coeffs)) fulldeg = d if deg is None else deg - data = [0] * fulldeg + data = [0] * (fulldeg+1) for k in range(bound + 1): data[k] = coeffs[k] for k in range(bound + 1, fulldeg + 1): @@ -1647,14 +1647,13 @@ 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): + 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 recipe is described in section 11.4.1 of [Watkins]_. - INPUT: - `t` -- rational number, not 0 or 1 @@ -1662,6 +1661,8 @@ def euler_factor_tame_contribution(self, t, p, mo): - `p` -- prime number of good reduction - `mo` -- integer + + - `deg` -- integer or ``None`` (default: ``None``) OUTPUT: @@ -1675,6 +1676,8 @@ def euler_factor_tame_contribution(self, t, p, mo): 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, 6) @@ -1703,9 +1706,11 @@ def euler_factor_tame_contribution(self, t, p, mo): else: return 1 d = euler_phi(mo) - prec = ceil(d*(self.weight()+1-mul)/2 + log(2*d + 1, p)) f = IntegerModRing(mo)(p).multiplicative_order() + if deg is not None and deg < f: + return 1 q = p**f + prec = ceil(d*(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) @@ -1868,13 +1873,15 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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 = 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) @@ -1906,7 +1913,9 @@ def euler_factor(self, t, p, deg=None, cache_p=False): if t.numerator() % p == 0 or t.denominator() % p == 0: ans = PolynomialRing(ZZ, 'T').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) + 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: From 549e1b9b5ad0ad53c581beb71ff3beec7ed6d7db Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sun, 5 May 2024 07:48:28 -0700 Subject: [PATCH 13/19] Add some doctests for p=2 --- src/sage/modular/hypergeometric_motive.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index d90750968ba..41019372c06 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1886,6 +1886,13 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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 Check error handling for wild primes:: From 5d34fde0fa97a1f79ff1e374303f499aa130da30 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sun, 5 May 2024 09:41:29 -0700 Subject: [PATCH 14/19] Update some docstring formatting --- src/sage/modular/hypergeometric_motive.py | 38 ++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 41019372c06..36966f63059 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -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 @@ -1081,9 +1081,9 @@ 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: @@ -1330,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) @@ -1475,11 +1475,11 @@ 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`) @@ -1656,13 +1656,13 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): 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 - - `mo` -- integer + - ``mo`` -- integer - - `deg` -- integer or ``None`` (default: ``None``) + - ``deg`` -- integer or ``None`` (default: ``None``) OUTPUT: @@ -1707,10 +1707,12 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): return 1 d = euler_phi(mo) f = IntegerModRing(mo)(p).multiplicative_order() - if deg is not None and deg < f: + if deg is None: + deg = d + if deg < f: return 1 q = p**f - prec = ceil(d*(self.weight()+1-mul)/2 + log(2*d + 1, p)) + 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) @@ -1749,9 +1751,9 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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`` From 4f799e39f1a822d1617e2f20654aeefdfaeed53f Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sun, 5 May 2024 13:43:48 -0700 Subject: [PATCH 15/19] Remove whitespace from one empty line --- src/sage/modular/hypergeometric_motive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 36966f63059..1af76717ca6 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1661,7 +1661,7 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): - ``p`` -- prime number of good reduction - ``mo`` -- integer - + - ``deg`` -- integer or ``None`` (default: ``None``) OUTPUT: From 57bed93ef2568c43bac5b3348f28eab182913195 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Sun, 5 May 2024 18:45:25 -0700 Subject: [PATCH 16/19] Use local functional equation at tame primes --- src/sage/modular/hypergeometric_motive.py | 90 +++++++++++++++++++---- 1 file changed, 74 insertions(+), 16 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 1af76717ca6..8c1870d1370 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1662,11 +1662,13 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): - ``mo`` -- integer - - ``deg`` -- integer or ``None`` (default: ``None``) + - ``deg`` -- integer (optional) OUTPUT: a polynomial + + If ``deg`` is specified, the output is truncated to that degree (inclusive). EXAMPLES:: @@ -1837,7 +1839,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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])) @@ -1896,6 +1898,22 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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]]) @@ -1919,8 +1937,9 @@ def euler_factor(self, t, p, deg=None, cache_p=False): raise NotImplementedError('p is wild') 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 = PolynomialRing(ZZ, 'T').one() + 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: @@ -1929,12 +1948,11 @@ def euler_factor(self, t, p, deg=None, cache_p=False): # 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 + d = (self.degree() - 1) // 2 * 2 else: typ = "good" d = self.degree() - use_fe = (typ == "good") - bound = d // 2 if use_fe else d + bound = d // 2 if deg is not None: bound = min(deg, bound) @@ -1945,14 +1963,54 @@ def euler_factor(self, t, p, deg=None, cache_p=False): for i in range(bound)] w = self.weight() - sign = self.sign(t, p) - ans = characteristic_polynomial_from_traces(traces, d, p, w, sign, deg=deg, use_fe=use_fe) - - # Add an extra factor when w is even and t-1 has nonzero even valuation. - if w % 2 == 0 and typ == "mult" and (t-1).valuation(p) % 2 == 0: - m1 = self.cyclotomic_data()[1].count(1) - 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*ans.parent().gen() + 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) + for j in range(len(traces)): + traces[j] -= c ** (j+1) + 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 + From fd1726d7798edc08bd35f3fd8bf5f6e5379b82c1 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Mon, 6 May 2024 06:46:46 -0700 Subject: [PATCH 17/19] Correct some whitespace --- src/sage/modular/hypergeometric_motive.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 8c1870d1370..3638fa12416 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1667,7 +1667,7 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): OUTPUT: a polynomial - + If ``deg`` is specified, the output is truncated to that degree (inclusive). EXAMPLES:: @@ -1839,7 +1839,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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])) @@ -2013,4 +2013,3 @@ def euler_factor(self, t, p, deg=None, cache_p=False): if deg is not None: ans = ans.truncate(deg+1) return ans - From a3d650d4f92aeb9e11815007593937a14a7904c7 Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Mon, 6 May 2024 18:13:31 -0700 Subject: [PATCH 18/19] Docstring edits; add truncation doctest --- src/sage/modular/hypergeometric_motive.py | 40 +++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 3638fa12416..c0bd3338387 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1607,7 +1607,7 @@ 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:: @@ -1652,7 +1652,7 @@ 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 recipe is described in section 11.4.1 of [Watkins]_. + The algorithm is described in Section 11.4.1 of [Watkins]_. INPUT: @@ -1673,7 +1673,7 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: H = Hyp(cyclotomic=[[3,7],[4,5,6]]) + 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) @@ -1682,6 +1682,8 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): 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 """ @@ -1695,27 +1697,23 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): e = t.valuation(p) t0 = t / p**e - if e % mo: - return 1 if e > 0: mul = self.cyclotomic_data()[1].count(mo) - if not mul: - return 1 elif e < 0: mul = self.cyclotomic_data()[0].count(mo) - if not mul: - return 1 else: - return 1 + 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 1 - q = p**f + return ZZ.one() + q = p ** f prec = ceil(deg*(self.weight()+1-mul)/2 + log(2*d + 1, p)) - k = (q-1)//mo + k = (q-1) // mo flip = (f == 1 and prec == 1) gtab_prec, gtab = self.gauss_table(p, f, prec) try: @@ -1730,21 +1728,21 @@ def euler_factor_tame_contribution(self, t, p, mo, deg=None): l = [] for j in range(mo): if gcd(j, mo) == 1: - r = j*k + 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 += 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)] + 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=d, use_fe=False) + return characteristic_polynomial_from_traces(traces, d, p, 0, 1, deg, use_fe=False) @cached_method def euler_factor(self, t, p, deg=None, cache_p=False): @@ -1766,7 +1764,7 @@ def euler_factor(self, t, p, deg=None, 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 section 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). From 1de49bef71753671f431b96002bf0fd6922a9c8b Mon Sep 17 00:00:00 2001 From: "Kiran S. Kedlaya" Date: Thu, 9 May 2024 08:24:00 -0700 Subject: [PATCH 19/19] Review edits --- src/sage/modular/hypergeometric_motive.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index c0bd3338387..d79307c7aa4 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -183,7 +183,7 @@ def characteristic_polynomial_from_traces(traces, d, q, i, sign, deg=None, use_f coeffs += [0] * max(0, bound + 1 - len(coeffs)) fulldeg = d if deg is None else deg - data = [0] * (fulldeg+1) + data = [0] * (fulldeg + 1) for k in range(bound + 1): data[k] = coeffs[k] for k in range(bound + 1, fulldeg + 1): @@ -1770,7 +1770,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): ``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 11.2 of [Watkins]_. + the Euler factor includes a linear term described in Section 11.2 of [Watkins]_. EXAMPLES:: @@ -1941,12 +1941,14 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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) + 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) // 2 * 2 + d = self.degree() - 1 + if d % 2: + d -= 1 else: typ = "good" d = self.degree() @@ -1971,7 +1973,7 @@ def euler_factor(self, t, p, deg=None, cache_p=False): if w % 2: assert m1 % 2 == 0 u = (-1) ** (m1//2) - u *= prod(v**gv for v, gv in self.gamma_array().items()) + 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) @@ -1982,8 +1984,10 @@ def euler_factor(self, t, p, deg=None, cache_p=False): 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] -= c ** (j+1) + traces[j] -= cpow + cpow *= c tmp = 1 - c*P.gen() else: u = (-1) ** (1+(self.degree()-1)//2) @@ -2009,5 +2013,5 @@ def euler_factor(self, t, p, deg=None, cache_p=False): c = kronecker_symbol(K*t0, p) * p**(w//2) ans *= 1 - c*P.gen() if deg is not None: - ans = ans.truncate(deg+1) + ans = ans.truncate(deg + 1) return ans