def padic_gauss_sum(a, p, f, prec=20, factored=False, algorithm='pari', parent=None): # Copied from Sage from sage.rings.padics.factory import Zp from sage.rings.all import PolynomialRing q = p**f a = a % (q - 1) if parent is None: R = Zp(p, prec) else: R = parent out = -R.one() if a != 0: t = R(1 / (q - 1)) for i in range(f): out *= (a * t).gamma(algorithm) a = (a * p) % (q - 1) s = sum(a.digits(base=p)) if factored: return (s, out) X = PolynomialRing(R, name='X').gen() pi = R.ext(X**(p - 1) + p, names='pi').gen() out *= pi**s return out
def integer_ring(self, print_mode=None): r""" Returns the integer ring of ``self``, possibly with ``print_mode`` changed. INPUT: - ``print_mode`` - a dictionary containing print options. Defaults to the same options as this ring. OUTPUT: - The ring of integral elements in ``self``. EXAMPLES:: sage: K = Qp(5, print_mode='digits') sage: R = K.integer_ring(); repr(R(1/3))[3:] '31313131313131313132' sage: S = K.integer_ring({'max_ram_terms':4}); repr(S(1/3))[3:] '3132' """ if not self.is_field() and print_mode is None: return self from sage.rings.padics.factory import Zp return Zp(self.prime(), self.precision_cap(), self._prec_type(), print_mode=self._modified_print_mode(print_mode), names=self._uniformizer_print())
def logp_gam(p, p_prec): """ Returns the (integral) power series log_p(1+z) / log_p(1+p)) where the denominator is computed with some accuracy. """ L = logp(p, p_prec) ZZp = Zp(p, 2 * p_prec) loggam = ZZ(ZZp(1 + p).log(0)) return ps_normalize(L / loggam, p, p_prec)
def _torsion_poly(ell, P=None): """ Computes the ell-th gauss period. If `P` is given, it must be a polynomial ring into which te result is coerced. This is my favourite equality: sage: all(_torsion_poly(n)(I) == I^n*lucas_number2(n,1,-1) for n in range(1,10)) True """ if P is None: P, R, = PolynomialRing(ZZ, 'x'), ZZ, elif P.characteristic() == 0: R = ZZ else: R = Zp(P.characteristic(), prec=1, type='capped-rel') t = [1, 0] for k in range(1, ell/2 + 1): m = R(ell - 2*k + 2) * R(ell - 2*k + 1) / (R(ell - k) * R(k)) t.append(-t[-2] * m) t.append(0) return P(list(reversed(t))).shift(ell % 2 - 1)
def gauss_sum(a, p, f, prec=20, factored=False, algorithm='pari', parent=None): r""" Return the Gauss sum `g_q(a)` as a `p`-adic number. The Gauss sum `g_q(a)` is defined by .. MATH:: g_q(a)= \sum_{u\in F_q^*} \omega(u)^{-a} \zeta_q^u, where `q = p^f`, `\omega` is the Teichmüller character and `\zeta_q` is some arbitrary choice of primitive `q`-th root of unity. The computation is adapted from the main theorem in Alain Robert's paper *The Gross-Koblitz formula revisited*, Rend. Sem. Mat. Univ. Padova 105 (2001), 157--170. Let `p` be a prime, `f` a positive integer, `q=p^f`, and `\pi` be the unique root of `f(x) = x^{p-1}+p` congruent to `\zeta_p - 1` modulo `(\zeta_p - 1)^2`. Let `0\leq a < q-1`. Then the Gross-Koblitz formula gives us the value of the Gauss sum `g_q(a)` as a product of `p`-adic Gamma functions as follows: .. MATH:: g_q(a) = -\pi^s \prod_{0\leq i < f} \Gamma_p(a^{(i)}/(q-1)), where `s` is the sum of the digits of `a` in base `p` and the `a^{(i)}` have `p`-adic expansions obtained from cyclic permutations of that of `a`. INPUT: - ``a`` -- integer - ``p`` -- prime - ``f`` -- positive integer - ``prec`` -- positive integer (optional, 20 by default) - ``factored`` - boolean (optional, False by default) - ``algorithm`` - flag passed to p-adic Gamma function (optional, "pari" by default) OUTPUT: If ``factored`` is ``False``, returns a `p`-adic number in an Eisenstein extension of `\QQ_p`. This number has the form `pi^e * z` where `pi` is as above, `e` is some nonnegative integer, and `z` is an element of `\ZZ_p`; if ``factored`` is ``True``, the pair `(e,z)` is returned instead, and the Eisenstein extension is not formed. .. NOTE:: This is based on GP code written by Adriana Salerno. EXAMPLES: In this example, we verify that `g_3(0) = -1`:: sage: from sage.rings.padics.misc import gauss_sum sage: -gauss_sum(0,3,1) 1 + O(pi^40) Next, we verify that `g_5(a) g_5(-a) = 5 (-1)^a`:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,5,1)^2-5 O(pi^84) sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 O(pi^84) Finally, we compute a non-trivial value:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,13,2) 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) sage: gauss_sum(2,13,2,prec=5,factored=True) (2, 6 + 6*13 + 10*13^2 + O(13^5)) .. SEEALSO:: - :func:`sage.arith.misc.gauss_sum` for general finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum` for prime finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum_numerical` for prime finite fields """ from sage.rings.padics.factory import Zp from sage.rings.all import PolynomialRing q = p**f a = a % (q - 1) if parent is None: R = Zp(p, prec) else: R = parent out = -R.one() if a != 0: t = R(1 / (q - 1)) for i in range(f): out *= (a * t).gamma(algorithm) a = (a * p) % (q - 1) s = sum(a.digits(base=p)) if factored: return s, out X = PolynomialRing(R, name='X').gen() pi = R.ext(X**(p - 1) + p, names='pi').gen() out *= pi**s return out
def _init_frob(self, desired_prec=None): """ Initialise everything for Frobenius polynomial computation. TESTS:: sage: p = 4999 sage: x = PolynomialRing(GF(p),"x").gen() sage: C = CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1) sage: C._init_frob() sage: C._init_frobQ True sage: C._plarge True sage: C._sqrtp True """ def _N0_RH(): return ceil( log(2 * binomial(2 * self._genus, self._genus), self._p) + self._genus * self._n / ZZ(2)) def _find_N0(): if self._nodenominators: return _N0_nodenominators(self._p, self._genus, self._n) else: return _N0_RH() + self._extraprec def _find_N_43(): """ Find the precision used for thm 4.3 in Goncalves for p >> 0, N = N0 + 2 """ p = self._p r = self._r d = self._d delta = self._delta N0 = self._N0 left_side = N0 + floor(log((d * p * (r - 1) + r) / delta) / log(p)) def right_side_log(n): return floor(log(p * (r * n - 1) - r) / log(p)) n = left_side while n <= left_side + right_side_log(n): n += 1 return n if not self._init_frobQ or self._N0 != desired_prec: if self._r < 2 or self._d < 2: raise NotImplementedError( "Only implemented for r, f.degree() >= 2") self._init_frobQ = True self._Fq = self._f.base_ring() self._p = self._Fq.characteristic() self._q = self._Fq.cardinality() self._n = self._Fq.degree() self._epsilon = 0 if self._delta == 1 else 1 # our basis choice doesn't always give an integral matrix if self._epsilon == 0: self._extraprec = floor( log(self._r, self._p) + log((2 * self._genus + (self._delta - 2)) / self._delta, self._p)) else: self._extraprec = floor(log(self._r * 2 - 1, self._p)) self._nodenominators = self._extraprec == 0 if desired_prec is None: self._N0 = _find_N0() else: self._N0 = desired_prec self._plarge = self._p > self._d * self._r * (self._N0 + self._epsilon) # working prec if self._plarge: self._N = self._N0 + 1 else: self._N = _find_N_43() # we will use the sqrt(p) version? self._sqrtp = self._plarge and self._p == self._q self._extraworkingprec = self._extraprec if not self._plarge: # we might have some denominators showing up during horizontal # and vertical reductions self._extraworkingprec += 2 * ceil( log(self._d * self._r * (self._N0 + self._epsilon), self._p)) # Rings if self._plarge and self._nodenominators: if self._n == 1: # IntegerModRing is significantly faster than Zq self._Zq = IntegerModRing(self._p**self._N) if self._sqrtp: self._Zq0 = IntegerModRing(self._p**(self._N - 1)) self._Qq = Qq(self._p, prec=self._N, type="capped-rel") self._w = 1 else: self._Zq = Zq( self._q, names="w", modulus=self._Fq.polynomial(), prec=self._N, type="capped-abs", ) self._w = self._Zq.gen() self._Qq = self._Zq.fraction_field() else: self._Zq = Qq( self._q, names="w", modulus=self._Fq.polynomial(), prec=self._N + self._extraworkingprec, ) self._w = self._Zq.gen() self._Qq = self._Zq self._Zp = Zp(self._p, prec=self._N + self._extraworkingprec) self._Zqx = PolynomialRing(self._Zq, "x") # Want to take a lift of f from Fq to Zq if self._n == 1: # When n = 1, can lift from Fp[x] to Z[x] and then to Zp[x] self._flift = self._Zqx([elt.lift() for elt in self._f.list()]) self._frobf = self._Zqx(self._flift.list()) else: # When n > 1, need to be more careful with the lift self._flift = self._Zqx([ elt.polynomial().change_ring(ZZ)(self._Zq.gen()) for elt in self._f.list() ]) self._frobf = self._Zqx( [elt.frobenius() for elt in self._flift.list()]) self._dflift = self._flift.derivative() # Set up local cache for Frob(f)^s # This variable will store the powers of frob(f) frobpow = [None] * (self._N0 + 2) frobpow[0] = self._Zqx(1) for k in range(self._N0 + 1): frobpow[k + 1] = self._frobf * frobpow[k] # We don't make it a polynomials as we need to keep track that the # ith coefficient represents (i*p)-th self._frobpow_list = [elt.list() for elt in frobpow] if self._sqrtp: # precision of self._Zq0 N = self._N - 1 vandermonde = matrix(self._Zq0, N, N) for i in range(N): vandermonde[i, 0] = 1 for j in range(1, N): vandermonde[i, j] = vandermonde[i, j - 1] * (i + 1) self._vandermonde = vandermonde.inverse() self._horizontal_fat_s = {} self._vertical_fat_s = {}
def gauss_sum(a, p, f, prec=20, factored=False, algorithm='pari', parent=None): r""" Return the Gauss sum `g_q(a)` as a `p`-adic number. The Gauss sum `g_q(a)` is defined by .. MATH:: g_q(a)= \sum_{u\in F_q^*} \omega(u)^{-a} \zeta_q^u, where `q = p^f`, `\omega` is the Teichmüller character and `\zeta_q` is some arbitrary choice of primitive `q`-th root of unity. The computation is adapted from the main theorem in Alain Robert's paper *The Gross-Koblitz formula revisited*, Rend. Sem. Mat. Univ. Padova 105 (2001), 157--170. Let `p` be a prime, `f` a positive integer, `q=p^f`, and `\pi` be the unique root of `f(x) = x^{p-1}+p` congruent to `\zeta_p - 1` modulo `(\zeta_p - 1)^2`. Let `0\leq a < q-1`. Then the Gross-Koblitz formula gives us the value of the Gauss sum `g_q(a)` as a product of `p`-adic Gamma functions as follows: .. MATH:: g_q(a) = -\pi^s \prod_{0\leq i < f} \Gamma_p(a^{(i)}/(q-1)), where `s` is the sum of the digits of `a` in base `p` and the `a^{(i)}` have `p`-adic expansions obtained from cyclic permutations of that of `a`. INPUT: - ``a`` -- integer - ``p`` -- prime - ``f`` -- positive integer - ``prec`` -- positive integer (optional, 20 by default) - ``factored`` - boolean (optional, False by default) - ``algorithm`` - flag passed to p-adic Gamma function (optional, "pari" by default) OUTPUT: If ``factored`` is ``False``, returns a `p`-adic number in an Eisenstein extension of `\QQ_p`. This number has the form `pi^e * z` where `pi` is as above, `e` is some nonnegative integer, and `z` is an element of `\ZZ_p`; if ``factored`` is ``True``, the pair `(e,z)` is returned instead, and the Eisenstein extension is not formed. .. NOTE:: This is based on GP code written by Adriana Salerno. EXAMPLES: In this example, we verify that `g_3(0) = -1`:: sage: from sage.rings.padics.misc import gauss_sum sage: -gauss_sum(0,3,1) 1 + O(pi^40) Next, we verify that `g_5(a) g_5(-a) = 5 (-1)^a`:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,5,1)^2-5 O(pi^84) sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 O(pi^84) Finally, we compute a non-trivial value:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,13,2) 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) sage: gauss_sum(2,13,2,prec=5,factored=True) (2, 6 + 6*13 + 10*13^2 + O(13^5)) .. SEEALSO:: - :func:`sage.arith.misc.gauss_sum` for general finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum` for prime finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum_numerical` for prime finite fields """ from sage.rings.padics.factory import Zp from sage.rings.all import PolynomialRing q = p**f a = a % (q-1) if parent is None: R = Zp(p, prec) else: R = parent out = -R.one() if a != 0: t = R(1/(q-1)) for i in range(f): out *= (a*t).gamma(algorithm) a = (a*p) % (q-1) s = sum(a.digits(base=p)) if factored: return(s, out) X = PolynomialRing(R, name='X').gen() pi = R.ext(X**(p - 1) + p, names='pi').gen() out *= pi**s return out
def padic_H_value(self, p, f, t, prec=20): """ Return the `p`-adic trace of Frobenius, computed using the Gross-Koblitz formula. INPUT: - `p` -- a prime number - `f` -- an integer such that `q = p^f` - `t` -- a rational parameter - ``prec`` -- precision (optional, default 20) OUTPUT: an integer EXAMPLES: 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.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)] [-4, 276] sage: [H.padic_H_value(7,i,-1) for i in range(1,3)] [0, -476] sage: [H.padic_H_value(11,i,-1) for i in range(1,3)] [0, -4972] From [Roberts2015]_ (but note conventions regarding `t`):: sage: H = Hyp(gamma_list=[-6,-1,4,3]) sage: t = 189/125 sage: H.padic_H_value(13,1,1/t) 0 REFERENCES: - [MagmaHGM]_ """ alpha = self._alpha beta = self._beta if 0 in alpha: H = self.swap_alpha_beta() return (H.padic_H_value(p, f, ~t, prec)) t = QQ(t) gamma = self.gamma_array() q = p**f m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)} M = self.M_value() D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 gauss_table = [ padic_gauss_sum(r, p, f, prec, factored=True) for r in range(q - 1) ] p_ring = Zp(p, prec=prec) teich = p_ring.teichmuller(M / t) sigma = sum(q**(D + m[0] - m[r]) * (-p)**(sum(gauss_table[(v * r) % (q - 1)][0] * gv for v, gv in gamma.items()) // (p - 1)) * prod(gauss_table[(v * r) % (q - 1)][1]**gv for v, gv in gamma.items()) * teich**r for r in range(q - 1)) resu = ZZ(-1)**m[0] / (1 - q) * sigma return IntegerModRing(p**prec)(resu).lift_centered()