Path: blob/master/sage/schemes/elliptic_curves/monsky_washnitzer.py
4159 views
r"""1Computation of Frobenius matrix on Monsky-Washnitzer cohomology23The most interesting functions to be exported here are4:func:`matrix_of_frobenius` and :func:`adjusted_prec`.56Currently this code is limited to the case `p \geq 5` (no7`GF(p^n)` for `n > 1`), and only handles the8elliptic curve case (not more general hyperelliptic curves).910REFERENCES:1112- Kedlaya, K., "Counting points on hyperelliptic curves using13Monsky-Washnitzer cohomology", J. Ramanujan Math. Soc. 16 (2001) no144, 323-3381516- Edixhoven, B., "Point counting after Kedlaya", EIDMA-Stieltjes17graduate course, Lieden (lecture notes?).1819AUTHORS:2021- David Harvey and Robert Bradshaw: initial code developed at the 200622MSRI graduate workshop, working with Jennifer Balakrishnan and Liang23Xiao2425- David Harvey (2006-08): cleaned up, rewrote some chunks, lots more26documentation, added Newton iteration method, added more complete27'trace trick', integrated better into Sage.2829- David Harvey (2007-02): added algorithm with sqrt(p) complexity30(removed in May 2007 due to better C++ implementation)3132- Robert Bradshaw (2007-03): keep track of exact form in reduction33algorithms3435- Robert Bradshaw (2007-04): generalization to hyperelliptic curves36"""3738#*****************************************************************************39# Copyright (C) 2006 William Stein <[email protected]>40# 2006 Robert Bradshaw <[email protected]>41# 2006 David Harvey <[email protected]>42#43# Distributed under the terms of the GNU General Public License (GPL)44# http://www.gnu.org/licenses/45#*****************************************************************************464748from sage.rings.all import Integers, Integer, PolynomialRing, is_Polynomial, PowerSeriesRing, Rationals, Rational, LaurentSeriesRing49from sage.modules.module import Module50from sage.structure.element import ModuleElement51from sage.matrix.all import matrix52from sage.modules.all import vector53from sage.rings.ring import CommutativeAlgebra54from sage.structure.element import CommutativeAlgebraElement5556from sage.rings.arith import binomial, integer_ceil as ceil57from sage.misc.functional import log58from sage.misc.misc import newton_method_sizes5960from ell_generic import is_EllipticCurve61from constructor import EllipticCurve626364class SpecialCubicQuotientRing(CommutativeAlgebra):65r"""66Specialised class for representing the quotient ring67`R[x,T]/(T - x^3 - ax - b)`, where `R` is an68arbitrary commutative base ring (in which 2 and 3 are invertible),69`a` and `b` are elements of that ring.7071Polynomials are represented internally in the form72`p_0 + p_1 x + p_2 x^2` where the `p_i` are73polynomials in `T`. Multiplication of polynomials always74reduces high powers of `x` (i.e. beyond `x^2`) to75powers of `T`.7677Hopefully this ring is faster than a general quotient ring because78it uses the special structure of this ring to speed multiplication79(which is the dominant operation in the frobenius matrix80calculation). I haven't actually tested this theory though...8182TODO: - Eventually we will want to run this in characteristic 3, so83we need to: (a) Allow `Q(x)` to contain an `x^2` term, and84(b) Remove the requirement that 3 be invertible. Currently this is85used in the Toom-Cook algorithm to speed multiplication.8687EXAMPLES::8889sage: B.<t> = PolynomialRing(Integers(125))90sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))91sage: R92SpecialCubicQuotientRing over Ring of integers modulo 125 with polynomial T = x^3 + 124*x + 949394Get generators::9596sage: x, T = R.gens()97sage: x98(0) + (1)*x + (0)*x^299sage: T100(T) + (0)*x + (0)*x^2101102Coercions::103104sage: R(7)105(7) + (0)*x + (0)*x^2106107Create elements directly from polynomials::108109sage: A, z = R.poly_ring().objgen()110sage: A111Univariate Polynomial Ring in T over Ring of integers modulo 125112sage: R.create_element(z^2, z+1, 3)113(T^2) + (T + 1)*x + (3)*x^2114115Some arithmetic::116117sage: x^3118(T + 31) + (1)*x + (0)*x^2119sage: 3 * x**15 * T**2 + x - T120(3*T^7 + 90*T^6 + 110*T^5 + 20*T^4 + 58*T^3 + 26*T^2 + 124*T) + (15*T^6 + 110*T^5 + 35*T^4 + 63*T^2 + 1)*x + (30*T^5 + 40*T^4 + 8*T^3 + 38*T^2)*x^2121122Retrieve coefficients (output is zero-padded)::123124sage: x^10125(3*T^2 + 61*T + 8) + (T^3 + 93*T^2 + 12*T + 40)*x + (3*T^2 + 61*T + 9)*x^2126sage: (x^10).coeffs()127[[8, 61, 3, 0], [40, 12, 93, 1], [9, 61, 3, 0]]128129TODO: write an example checking multiplication of these polynomials130against Sage's ordinary quotient ring arithmetic. I can't seem to131get the quotient ring stuff happening right now...132"""133134def __init__(self, Q, laurent_series = False):135"""136Constructor.137138INPUT:139140141- ``Q`` - a polynomial of the form142`Q(x) = x^3 + ax + b`, where `a`, `b` belong to a ring in which1432, 3 are invertible.144145- ``laurent_series`` - whether or not to allow146negative powers of `T` (default=False)147148149EXAMPLES::150151sage: B.<t> = PolynomialRing(Integers(125))152sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))153sage: R154SpecialCubicQuotientRing over Ring of integers modulo 125 with polynomial T = x^3 + 124*x + 94155156::157158sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 + 2*t^2 - t + B(1/4))159Traceback (most recent call last):160...161ValueError: Q (=t^3 + 2*t^2 + 124*t + 94) must be of the form x^3 + ax + b162163::164165sage: B.<t> = PolynomialRing(Integers(10))166sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + 1)167Traceback (most recent call last):168...169ArithmeticError: 2 and 3 must be invertible in the coefficient ring (=Ring of integers modulo 10) of Q170"""171if not is_Polynomial(Q):172raise TypeError, "Q (=%s) must be a polynomial" % Q173174if Q.degree() != 3 or not Q[2].is_zero():175raise ValueError, "Q (=%s) must be of the form x^3 + ax + b" % Q176177base_ring = Q.parent().base_ring()178179if not base_ring(6).is_unit():180raise ArithmeticError, \181"2 and 3 must be invertible in the coefficient ring (=%s) of Q" % \182base_ring183184# CommutativeAlgebra.__init__ tries to establish a coercion185# from the base ring, by trac ticket #9138. The corresponding186# hom set is cached. In order to use self as cache key, its187# string representation is used. In otder to get the string188# representation, we need to know the attributes _a and189# _b. Hence, in #9138, we have to move CommutativeAlgebra.__init__190# further down:191self._a = Q[1]192self._b = Q[0]193if laurent_series:194self._poly_ring = LaurentSeriesRing(base_ring, 'T') # R[T]195else:196self._poly_ring = PolynomialRing(base_ring, 'T') # R[T]197self._poly_generator = self._poly_ring.gen(0) # the generator T198CommutativeAlgebra.__init__(self, base_ring)199200# Precompute a matrix that is used in the Toom-Cook multiplication.201# This is where we need 2 and 3 invertible.202203# (a good description of Toom-Cook is online at:204# http://www.gnu.org/software/gmp/manual/html_node/Toom-Cook-3-Way-Multiplication.html )205206self._speedup_matrix = \207(matrix(Integers(), 3, 3, [2, 4, 8,2081, 1, 1,2098, 4, 2])**(-1)210).change_ring(base_ring).list()211212213def __repr__(self):214"""215EXAMPLES::216217sage: B.<t> = PolynomialRing(Integers(125))218sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))219sage: print R220SpecialCubicQuotientRing over Ring of integers modulo 125 with polynomial T = x^3 + 124*x + 94221"""222return "SpecialCubicQuotientRing over %s with polynomial T = %s" % \223(self.base_ring(), PolynomialRing(self.base_ring(), 'x')(224[self._b, self._a, 0, 1]))225226def poly_ring(self):227"""228Return the underlying polynomial ring in `T`.229230EXAMPLES::231232sage: B.<t> = PolynomialRing(Integers(125))233sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))234sage: R.poly_ring()235Univariate Polynomial Ring in T over Ring of integers modulo 125236"""237return self._poly_ring238239def gens(self):240"""241Return a list [x, T] where x and T are the generators of the ring242(as element *of this ring*).243244.. note::245246I have no idea if this is compatible with the usual Sage247'gens' interface.248249EXAMPLES::250251sage: B.<t> = PolynomialRing(Integers(125))252sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))253sage: x, T = R.gens()254sage: x255(0) + (1)*x + (0)*x^2256sage: T257(T) + (0)*x + (0)*x^2258"""259return [SpecialCubicQuotientRingElement(self,260self._poly_ring(0), self._poly_ring(1), self._poly_ring(0),261check=False),262SpecialCubicQuotientRingElement(self,263self._poly_generator, self._poly_ring(0), self._poly_ring(0),264check=False)]265266def create_element(self, p0, p1, p2, check=True):267"""268Creates the element `p_0 + p_1*x + p_2*x^2`, where the `p_i`269are polynomials in `T`.270271INPUT:272273274- ``p0, p1, p2`` - coefficients; must be coercible275into poly_ring()276277- ``check`` - bool (default True): whether to carry278out coercion279280281EXAMPLES::282283sage: B.<t> = PolynomialRing(Integers(125))284sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))285sage: A, z = R.poly_ring().objgen()286sage: R.create_element(z^2, z+1, 3)287(T^2) + (T + 1)*x + (3)*x^2288"""289return SpecialCubicQuotientRingElement(self, p0, p1, p2, check)290291292def __call__(self, value):293"""294EXAMPLES::295296sage: B.<t> = PolynomialRing(Integers(125))297sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))298sage: R(3)299(3) + (0)*x + (0)*x^2300"""301return self._coerce_(value)302303304def _coerce_impl(self, value):305"""306EXAMPLES::307308sage: B.<t> = PolynomialRing(Integers(125))309sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))310sage: R._coerce_impl(3)311(3) + (0)*x + (0)*x^2312"""313# coerce to underlying polynomial ring (possibly via base ring):314value = self._poly_ring._coerce_(value)315316return SpecialCubicQuotientRingElement(self,317value, self._poly_ring(0), self._poly_ring(0), check=False)318319320321322class SpecialCubicQuotientRingElement(CommutativeAlgebraElement):323"""324An element of a SpecialCubicQuotientRing.325"""326327def __init__(self, parent, p0, p1, p2, check=True):328"""329Constructs the element `p_0 + p_1*x + p_2*x^2`, where330the `p_i` are polynomials in `T`.331332INPUT:333334335- ``parent`` - a SpecialCubicQuotientRing336337- ``p0, p1, p2`` - coefficients; must be coercible338into parent.poly_ring()339340- ``check`` - bool (default True): whether to carry341out coercion342343344EXAMPLES::345346sage: B.<t> = PolynomialRing(Integers(125))347sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))348sage: from sage.schemes.elliptic_curves.monsky_washnitzer import SpecialCubicQuotientRingElement349sage: SpecialCubicQuotientRingElement(R, 2, 3, 4)350(2) + (3)*x + (4)*x^2351"""352if not isinstance(parent, SpecialCubicQuotientRing):353raise TypeError, \354"parent (=%s) must be a SpecialCubicQuotientRing" % parent355356CommutativeAlgebraElement.__init__(self, parent)357358if check:359poly_ring = parent.poly_ring()360p0 = poly_ring(p0)361p1 = poly_ring(p1)362p2 = poly_ring(p2)363364self._triple = (p0, p1, p2)365366def coeffs(self):367"""368Returns list of three lists of coefficients, corresponding to the369`x^0`, `x^1`, `x^2` coefficients. The lists370are zero padded to the same length. The list entries belong to the371base ring.372373EXAMPLES::374375sage: B.<t> = PolynomialRing(Integers(125))376sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))377sage: p = R.create_element(t, t^2 - 2, 3)378sage: p.coeffs()379[[0, 1, 0], [123, 0, 1], [3, 0, 0]]380"""381coeffs = [column.coeffs() for column in self._triple]382degree = max([len(x) for x in coeffs])383base_ring = self.parent().base_ring()384for column in coeffs:385column.extend([base_ring(0)] * (degree - len(column)))386return coeffs387388def __nonzero__(self):389"""390EXAMPLES::391392sage: B.<t> = PolynomialRing(Integers(125))393sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))394sage: x, T = R.gens()395sage: not x396False397sage: not T398False399sage: not R.create_element(0, 0, 0)400True401"""402return not not self._triple[0] or not not self._triple[1] or not not self._triple[2]403404def __cmp__(self, other):405"""406EXAMPLES::407408sage: B.<t> = PolynomialRing(Integers(125))409sage: x, t = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4)).gens()410sage: x == t411False412sage: x == x413True414sage: x == x + x - x415True416"""417return cmp(self._triple, other._triple)418419def _repr_(self):420"""421EXAMPLES::422423sage: B.<t> = PolynomialRing(Integers(125))424sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))425sage: x, T = R.gens()426sage: x + T*x - 2*T^2427(123*T^2) + (T + 1)*x + (0)*x^2428"""429return "(%s) + (%s)*x + (%s)*x^2" % self._triple430431432def _latex_(self):433"""434EXAMPLES::435436sage: B.<t> = PolynomialRing(Integers(125))437sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))438sage: x, T = R.gens()439sage: f = x + T*x - 2*T^2440sage: latex(f)441(123 T^{2}) + (T + 1)x + (0)x^2442"""443return "(%s) + (%s)x + (%s)x^2" % \444tuple([column._latex_() for column in self._triple])445446447def _add_(self, other):448"""449EXAMPLES::450451sage: B.<t> = PolynomialRing(Integers(125))452sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))453sage: f = R.create_element(2, t, t^2 - 3)454sage: g = R.create_element(3 + t, -t, t)455sage: f + g456(T + 5) + (0)*x + (T^2 + T + 122)*x^2457"""458return SpecialCubicQuotientRingElement(self.parent(),459self._triple[0] + other._triple[0],460self._triple[1] + other._triple[1],461self._triple[2] + other._triple[2],462check=False)463464465def _sub_(self, other):466"""467EXAMPLES::468469sage: B.<t> = PolynomialRing(Integers(125))470sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))471sage: f = R.create_element(2, t, t^2 - 3)472sage: g = R.create_element(3 + t, -t, t)473sage: f - g474(124*T + 124) + (2*T)*x + (T^2 + 124*T + 122)*x^2475"""476return SpecialCubicQuotientRingElement(self.parent(),477self._triple[0] - other._triple[0],478self._triple[1] - other._triple[1],479self._triple[2] - other._triple[2],480check=False)481482483def shift(self, n):484"""485Returns this element multiplied by `T^n`.486487EXAMPLES::488489sage: B.<t> = PolynomialRing(Integers(125))490sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))491sage: f = R.create_element(2, t, t^2 - 3)492sage: f493(2) + (T)*x + (T^2 + 122)*x^2494sage: f.shift(1)495(2*T) + (T^2)*x + (T^3 + 122*T)*x^2496sage: f.shift(2)497(2*T^2) + (T^3)*x + (T^4 + 122*T^2)*x^2498"""499return SpecialCubicQuotientRingElement(self.parent(),500self._triple[0].shift(n),501self._triple[1].shift(n),502self._triple[2].shift(n),503check=False)504505506def scalar_multiply(self, scalar):507"""508Multiplies this element by a scalar, i.e. just multiply each509coefficient of `x^j` by the scalar.510511INPUT:512513514- ``scalar`` - either an element of base_ring, or an515element of poly_ring.516517518EXAMPLES::519520sage: B.<t> = PolynomialRing(Integers(125))521sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))522sage: x, T = R.gens()523sage: f = R.create_element(2, t, t^2 - 3)524sage: f525(2) + (T)*x + (T^2 + 122)*x^2526sage: f.scalar_multiply(2)527(4) + (2*T)*x + (2*T^2 + 119)*x^2528sage: f.scalar_multiply(t)529(2*T) + (T^2)*x + (T^3 + 122*T)*x^2530"""531scalar = self.parent()._poly_ring(scalar)532return SpecialCubicQuotientRingElement(self.parent(),533scalar * self._triple[0],534scalar * self._triple[1],535scalar * self._triple[2],536check=False)537538def square(self):539"""540EXAMPLES::541542sage: B.<t> = PolynomialRing(Integers(125))543sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))544sage: x, T = R.gens()545546::547548sage: f = R.create_element(1 + 2*t + 3*t^2, 4 + 7*t + 9*t^2, 3 + 5*t + 11*t^2)549sage: f.square()550(73*T^5 + 16*T^4 + 38*T^3 + 39*T^2 + 70*T + 120) + (121*T^5 + 113*T^4 + 73*T^3 + 8*T^2 + 51*T + 61)*x + (18*T^4 + 60*T^3 + 22*T^2 + 108*T + 31)*x^2551"""552return self * self553554555def _mul_(self, other):556"""557EXAMPLES::558559sage: B.<t> = PolynomialRing(Integers(125))560sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))561sage: x, T = R.gens()562563::564565sage: f = R.create_element(1 + 2*t + 3*t^2, 4 + 7*t + 9*t^2, 3 + 5*t + 11*t^2)566sage: g = R.create_element(4 + 3*t + 7*t^2, 2 + 3*t + t^2, 8 + 4*t + 6*t^2)567sage: f * g568(65*T^5 + 27*T^4 + 33*T^3 + 75*T^2 + 120*T + 57) + (66*T^5 + T^4 + 123*T^3 + 95*T^2 + 24*T + 50)*x + (45*T^4 + 75*T^3 + 37*T^2 + 2*T + 52)*x^2569"""570if not isinstance(other, SpecialCubicQuotientRingElement):571return self.scalar_multiply(other)572573# Here we do Toom-Cook three-way multiplication, which reduces the574# naive 9 polynomial multiplications to only 5 polynomial multiplications.575576a0, a1, a2 = self._triple577b0, b1, b2 = other._triple578M = self.parent()._speedup_matrix579580if self is other:581# faster method if we're squaring582p0 = a0 * a0583temp = a0 + 2*a1 + 4*a2584p1 = temp * temp585temp = a0 + a1 + a2586p2 = temp * temp587temp = 4*a0 + 2*a1 + a2588p3 = temp * temp589p4 = a2 * a2590591else:592p0 = a0 * b0593p1 = (a0 + 2*a1 + 4*a2) * (b0 + 2*b1 + 4*b2)594p2 = (a0 + a1 + a2) * (b0 + b1 + b2)595p3 = (4*a0 + 2*a1 + a2) * (4*b0 + 2*b1 + b2)596p4 = a2 * b2597598q1 = p1 - p0 - 16*p4599q2 = p2 - p0 - p4600q3 = p3 - 16*p0 - p4601602c0 = p0603c1 = M[0]*q1 + M[1]*q2 + M[2]*q3604c2 = M[3]*q1 + M[4]*q2 + M[5]*q3605c3 = M[6]*q1 + M[7]*q2 + M[8]*q3606c4 = p4607608# Now the product is c0 + c1 x + c2 x^2 + c3 x^3 + c4 x^4.609# We need to reduce mod y = x^3 + ax + b and return result.610611parent = self.parent()612T = parent._poly_generator613b = parent._b614a = parent._a615616# todo: These lines are necessary to get binop stuff working617# for certain base rings, e.g. when we compute b*c3 in the618# final line. They shouldn't be necessary. Need to fix this619# somewhere else in Sage.620a = parent._poly_ring(a)621b = parent._poly_ring(b)622623return SpecialCubicQuotientRingElement(parent,624-b*c3 + c0 + c3*T,625-b*c4 - a*c3 + c1 + c4*T,626-a*c4 + c2,627check=False)628629def transpose_list(input):630"""631INPUT:632633634- ``input`` - a list of lists, each list of the same635length636637638OUTPUT:639640641- ``output`` - a list of lists such that output[i][j]642= input[j][i]643644645EXAMPLES::646647sage: from sage.schemes.elliptic_curves.monsky_washnitzer import transpose_list648sage: L = [[1, 2], [3, 4], [5, 6]]649sage: transpose_list(L)650[[1, 3, 5], [2, 4, 6]]651"""652653h = len(input)654w = len(input[0])655656output = []657for i in range(w):658row = []659for j in range(h):660row.append(input[j][i])661output.append(row)662return output663664665666def helper_matrix(Q):667"""668Computes the (constant) matrix used to calculate the linear669combinations of the `d(x^i y^j)` needed to eliminate the670negative powers of `y` in the cohomology (i.e. in671reduce_negative()).672673INPUT:674675676- ``Q`` - cubic polynomial677"""678a = Q[1]679b = Q[0]680681# Discriminant (should be invertible for a curve of good reduction)682D = 4*a**3 + 27*b**2683Dinv = D**(-1) # NB do not use 1/D684685# This is the inverse of the matrix686# [ a, -3b, 0 ]687# [ 0, -2a, -3b ]688# [ 3, 0, -2a ]689690M = Dinv * matrix( [ [4*a**2 , -6*b*a , 9*b**2 ],691[-9*b , -2*a**2 , 3*b*a ],692[ 6*a , -9*b , -2*a**2 ] ])693return M694695def lift(x):696r"""697Tries to call x.lift(), presumably from the `p`-adics to ZZ.698699If this fails, it assumes the input is a power series, and tries to700lift it to a power series over QQ.701702This function is just a very kludgy solution to the problem of703trying to make the reduction code (below) work over both Zp and704Zp[[t]].705"""706try:707return x.lift()708except AttributeError:709return PowerSeriesRing(Rationals(), "t")(x.list(), x.prec())710711712713def reduce_negative(Q, p, coeffs, offset, exact_form=None):714"""715Applies cohomology relations to incorporate negative powers of716`y` into the `y^0` term.717718INPUT:719720721- ``p`` - prime722723- ``Q`` - cubic polynomial724725- ``coeffs`` - list of length 3 lists. The726`i^{th}` list [a, b, c] represents727`y^{2(i - offset)} (a + bx + cx^2) dx/y`.728729- ``offset`` - nonnegative integer730731732OUTPUT: The reduction is performed in-place. The output is placed733in coeffs[offset]. Note that coeffs[i] will be meaningless for i734offset after this function is finished.735736EXAMPLE::737738sage: R.<x> = Integers(5^3)['x']739sage: Q = x^3 - x + R(1/4)740sage: coeffs = [[10, 15, 20], [1, 2, 3], [4, 5, 6], [7, 8, 9]]741sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]742sage: monsky_washnitzer.reduce_negative(Q, 5, coeffs, 3)743sage: coeffs[3]744[28, 52, 9]745746::747748sage: R.<x> = Integers(7^3)['x']749sage: Q = x^3 - x + R(1/4)750sage: coeffs = [[7, 14, 21], [1, 2, 3], [4, 5, 6], [7, 8, 9]]751sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]752sage: monsky_washnitzer.reduce_negative(Q, 7, coeffs, 3)753sage: coeffs[3]754[245, 332, 9]755"""756757m = helper_matrix(Q).list()758base_ring = Q.base_ring()759next_a = coeffs[0]760761if exact_form is not None:762x = exact_form.parent().gen(0)763y = exact_form.parent()(exact_form.parent().base_ring().gen(0))764765try:766three_j_plus_5 = 5 - base_ring(6*offset)767three_j_plus_7 = 7 - base_ring(6*offset)768six = base_ring(6)769770for i in range(0, offset):771772j = 2*(i-offset)773a = next_a774next_a = coeffs[i+1]775776# todo: the following divisions will sometimes involve777# a division by (a power of) p. In all cases, we know (from778# Kedlaya's estimates) that the answer should be p-integral.779# However, since we're working over $Z/p^k Z$, we're not allowed780# to "divide by p". So currently we lift to Q, divide, and coerce781# back. Eventually, when pAdicInteger is implemented, and plays782# nicely with pAdicField, we should reimplement this stuff783# using pAdicInteger.784785if (p.divides(j+1)):786# need to lift here to perform the division787a[0] = base_ring(lift(a[0]) / (j+1))788a[1] = base_ring(lift(a[1]) / (j+1))789a[2] = base_ring(lift(a[2]) / (j+1))790else:791j_plus_1_inv = ~base_ring(j+1)792a[0] = a[0] * j_plus_1_inv793a[1] = a[1] * j_plus_1_inv794a[2] = a[2] * j_plus_1_inv795796c1 = m[3]*a[0] + m[4]*a[1] + m[5]*a[2]797c2 = m[6]*a[0] + m[7]*a[1] + m[8]*a[2]798next_a[0] = next_a[0] - three_j_plus_5 * c1799next_a[1] = next_a[1] - three_j_plus_7 * c2800801three_j_plus_7 = three_j_plus_7 + six802three_j_plus_5 = three_j_plus_5 + six803804if exact_form is not None:805c0 = m[0]*a[0] + m[1]*a[1] + m[2]*a[2]806exact_form += (c0 + c1*x + c2 * x**2) * y**(j+1)807808809except NotImplementedError:810raise NotImplementedError, \811"It looks like you've found a non-integral matrix of Frobenius! " \812"(Q=%s, p=%s)\nTime to write a paper." % (Q, p)813814coeffs[int(offset)] = next_a815816return exact_form817818819820def reduce_positive(Q, p, coeffs, offset, exact_form=None):821"""822Applies cohomology relations to incorporate positive powers of823`y` into the `y^0` term.824825INPUT:826827828- ``Q`` - cubic polynomial829830- ``coeffs`` - list of length 3 lists. The831`i^{th}` list [a, b, c] represents832`y^{2(i - offset)} (a + bx + cx^2) dx/y`.833834- ``offset`` - nonnegative integer835836837OUTPUT: The reduction is performed in-place. The output is placed838in coeffs[offset]. Note that coeffs[i] will be meaningless for i839offset after this function is finished.840841EXAMPLE::842843sage: R.<x> = Integers(5^3)['x']844sage: Q = x^3 - x + R(1/4)845846::847848sage: coeffs = [[1, 2, 3], [10, 15, 20]]849sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]850sage: monsky_washnitzer.reduce_positive(Q, 5, coeffs, 0)851sage: coeffs[0]852[16, 102, 88]853854::855856sage: coeffs = [[9, 8, 7], [10, 15, 20]]857sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]858sage: monsky_washnitzer.reduce_positive(Q, 5, coeffs, 0)859sage: coeffs[0]860[24, 108, 92]861"""862863base_ring = Q.base_ring()864next_a = coeffs[len(coeffs) - 1]865866Qa = Q[1]867Qb = Q[0]868869A = 2*Qa870B = 3*Qb871872offset = Integer(offset)873874875if exact_form is not None:876x = exact_form.parent().gen(0)877y = exact_form.parent().base_ring().gen(0)878# y = exact_form.parent()(exact_form.parent().base_ring().gen(0))879880for i in range(len(coeffs)-1, offset, -1):881j = 2*(i-offset) - 2882a = next_a883next_a = coeffs[i-1]884885a[0] = a[0] - Qa*a[2]/3 # subtract d(y^j + 3)886if exact_form is not None:887exact_form += Q.base_ring()(a[2].lift() / (3*j+9)) * y**(j+3)888889# todo: see comments about pAdicInteger in reduceNegative()890891# subtract off c1 of d(x y^j + 1), and892if p.divides(3*j + 5):893c1 = base_ring(lift(a[0]) / (3*j + 5))894else:895c1 = a[0] / (3*j + 5)896897# subtract off c2 of d(x^2 y^j + 1)898if p.divides(3*j + 7):899c2 = base_ring(lift(a[1]) / (3*j + 7))900else:901c2 = a[1] / (3*j + 7)902903next_a[0] = next_a[0] + B*c1*(j+1)904next_a[1] = next_a[1] + A*c1*(j+1) + B*c2*(j+1)905next_a[2] = next_a[2] + A*c2*(j+1)906907if exact_form is not None:908exact_form += (c1*x + c2 * x**2) * y**(j+1)909910coeffs[int(offset)] = next_a911912return exact_form913914915916def reduce_zero(Q, coeffs, offset, exact_form=None):917"""918Applies cohomology relation to incorporate `x^2 y^0` term919into `x^0 y^0` and `x^1 y^0` terms.920921INPUT:922923924- ``Q`` - cubic polynomial925926- ``coeffs`` - list of length 3 lists. The927`i^{th}` list [a, b, c] represents928`y^{2(i - offset)} (a + bx + cx^2) dx/y`.929930- ``offset`` - nonnegative integer931932933OUTPUT: The reduction is performed in-place. The output is placed934in coeffs[offset]. This method completely ignores coeffs[i] for i935!= offset.936937EXAMPLE::938939sage: R.<x> = Integers(5^3)['x']940sage: Q = x^3 - x + R(1/4)941sage: coeffs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]942sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]943sage: monsky_washnitzer.reduce_zero(Q, coeffs, 1)944sage: coeffs[1]945[6, 5, 0]946"""947948a = coeffs[int(offset)]949if a[2] == 0:950return exact_form951952Qa = Q[1]953954a[0] = a[0] - a[2]*Qa/3 # $3x^2 dx/y = -a dx/y$955956coeffs[int(offset)] = a957958if exact_form is not None:959x = exact_form.parent().gen(0)960y = exact_form.parent()(exact_form.parent().base_ring().gen(0))961exact_form += Q.base_ring()(a[2] / 3) * y962963a[2] = 0964965coeffs[int(offset)] = a966return exact_form967968969970def reduce_all(Q, p, coeffs, offset, compute_exact_form=False):971"""972Applies cohomology relations to reduce all terms to a linear973combination of `dx/y` and `x dx/y`.974975INPUT:976977978- ``Q`` - cubic polynomial979980- ``coeffs`` - list of length 3 lists. The981`i^{th}` list [a, b, c] represents982`y^{2(i - offset)} (a + bx + cx^2) dx/y`.983984- ``offset`` - nonnegative integer985986987OUTPUT:988989990- ``A, B`` - pair such that the input differential is991cohomologous to (A + Bx) dx/y.992993994.. note::995996The algorithm operates in-place, so the data in coeffs is997destroyed.998999EXAMPLE::10001001sage: R.<x> = Integers(5^3)['x']1002sage: Q = x^3 - x + R(1/4)1003sage: coeffs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]1004sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]1005sage: monsky_washnitzer.reduce_all(Q, 5, coeffs, 1)1006(21, 106)1007"""10081009R = Q.base_ring()10101011if compute_exact_form:1012# exact_form = SpecialCubicQuotientRing(Q, laurent_series=True)(0)1013exact_form = PolynomialRing(LaurentSeriesRing(Q.base_ring(), 'y'), 'x')(0)1014# t = (Q.base_ring().order().factor())[0]1015# from sage.rings.padics.qp import pAdicField1016# exact_form = PolynomialRing(LaurentSeriesRing(pAdicField(p, t[1]), 'y'), 'x')(0)1017else:1018exact_form = None10191020while len(coeffs) <= offset:1021coeffs.append([R(0), R(0), R(0)])10221023exact_form = reduce_negative(Q, p, coeffs, offset, exact_form)1024exact_form = reduce_positive(Q, p, coeffs, offset, exact_form)1025exact_form = reduce_zero(Q, coeffs, offset, exact_form)10261027if exact_form is None:1028return coeffs[int(offset)][0], coeffs[int(offset)][1]1029else:1030return (coeffs[int(offset)][0], coeffs[int(offset)][1]), exact_form1031103210331034def frobenius_expansion_by_newton(Q, p, M):1035r"""1036Computes the action of Frobenius on `dx/y` and on1037`x dx/y`, using Newton's method (as suggested in Kedlaya's1038paper).10391040(This function does *not* yet use the cohomology relations - that1041happens afterwards in the "reduction" step.)10421043More specifically, it finds `F_0` and `F_1` in1044the quotient ring `R[x, T]/(T - Q(x))`, such that10451046.. math::10471048F( dx/y) = T^{-r} F0 dx/y, \text{\ and\ } F(x dx/y) = T^{-r} F1 dx/y10491050where10511052.. math::10531054r = ( (2M-3)p - 1 )/2.105510561057(Here `T` is `y^2 = z^{-2}`, and `R` is the1058coefficient ring of `Q`.)10591060`F_0` and `F_1` are computed in the1061SpecialCubicQuotientRing associated to `Q`, so all powers1062of `x^j` for `j \geq 3` are reduced to powers of1063`T`.10641065INPUT:106610671068- ``Q`` - cubic polynomial of the form1069`Q(x) = x^3 + ax + b`, whose coefficient ring is a1070`Z/(p^M)Z`-algebra10711072- ``p`` - residue characteristic of the p-adic field10731074- ``M`` - p-adic precision of the coefficient ring1075(this will be used to determine the number of Newton iterations)107610771078OUTPUT:107910801081- ``F0, F1`` - elements of1082SpecialCubicQuotientRing(Q), as described above10831084- ``r`` - non-negative integer, as described above1085"""10861087S = SpecialCubicQuotientRing(Q)1088x, _ = S.gens() # T = y^21089base_ring = S.base_ring()10901091# When we compute Frob(1/y) we actually only need precision M-1, since1092# we're going to multiply by p at the end anyway.1093M = float(M - 1)10941095# Kedlaya sets s = Q(x^p)/T^p = 1 + p T^{-p} E, where1096# E = (Q(x^p) - Q(x)^p) / p (has integral coefficients).1097# Then he computes s^{-1/2} in S, using Newton's method to find1098# successive approximations. We follow this plan, but we normalise our1099# approximations so that we only ever need positive powers of T.11001101# Start by setting r = Q(x^p)/2 = 1/2 T^p s.1102# (The 1/2 is for convenience later on.)1103x_to_p_less_one = x**(p-1)1104x_to_p = x_to_p_less_one * x1105x_to_p_cubed = x_to_p.square() * x_to_p1106r = (base_ring(1) / base_ring(2)) * (x_to_p_cubed + Q[1]*x_to_p + S(Q[0]))11071108# todo: this next loop would be clearer if it used the newton_method_sizes()1109# function11101111# We will start with a hard-coded initial approximation, which we provide1112# up to precision 3. First work out what precision is best to start with.1113if M <= 3:1114initial_precision = M1115elif ceil(log(M/2, 2)) == ceil(log(M/3, 2)):1116# In this case there's no advantage to starting with precision three,1117# because we'll overshoot at the end. E.g. suppose the final precision1118# is 8. If we start with precision 2, we need two iterations to get us1119# to 8. If we start at precision 3, we will still need two iterations,1120# but we do more work along the way. So may as well start with only 2.1121initial_precision = 21122else:1123initial_precision = 311241125# Now compute the first approximation. In the main loop below, X is the1126# normalised approximation, and k is the precision. More specifically,1127# X = T^{p(k-1)} x_i, where x_i is an approximation to s^{-1/2}, and the1128# approximation is correct mod p^k.1129if initial_precision == 1:1130k = 11131X = S(1)1132elif initial_precision == 2:1133# approximation is 3/2 - 1/2 s1134k = 21135X = S(base_ring(3) / base_ring(2)).shift(p) - r1136elif initial_precision == 3:1137# approximation is (15 - 10 s + 3 s^2) / 81138k = 31139X = (base_ring(1) / base_ring(8)) * (1140S(15).shift(2*p) - \1141(base_ring(20) * r).shift(p) + \1142(base_ring(12) * r.square()) \1143)11441145# The key to the following calculation is that the T^{-m} coefficient1146# of every x_i is divisible by p^(ceil(m/p)) (for m >= 0). Therefore if1147# we are only expecting an answer correct mod p^k, we can truncate1148# beyond the T^{-(k-1)p} term without any problems.11491150# todo: what would be really nice is to be able to work in a lower1151# precision *coefficient ring* when we start the iteration, and move up to1152# higher precision rings as the iteration proceeds. This would be feasible1153# over Integers(p**n), but quite complicated (maybe impossible) over a more1154# general base ring. This might give a decent constant factor speedup;1155# or it might not, depending on how much the last iteration dominates the1156# whole runtime. My guess is that it isn't worth the effort.11571158three_halves = base_ring(3) / base_ring(2)11591160# Newton iteration loop1161while k < M:1162# target_k = k' = precision we want our answer to be after this iteration1163target_k = 2*k11641165# This prevents us overshooting. For example if the current precision1166# is 3 and we want to get to 10, we're better off going up to 51167# instead of 6, because it's less work to get from 5 to 10 than it1168# is to get from 6 to 10.1169if ceil(log(M/target_k, 2)) == ceil(log(M/(target_k-1), 2)):1170target_k -= 111711172# temp = T^{p(3k-2)} 1/2 s x_i^31173temp = X.square() * (X * r)11741175# We know that the final result is only going to be correct mod1176# p^(target_k), so we might as well truncate the extraneous terms now.1177# temp = T^{p(k'-1)} 1/2 s x_i^31178temp = temp.shift(-p*(3*k - target_k - 1))11791180# X = T^{p(k'-1)} (3/2 x_i - 1/2 s x_i^3)1181# = T^{p(k'-1)} x_{i+1}1182X = (three_halves * X).shift(p*(target_k - k)) - temp11831184k = target_k11851186# Now k should equal M, since we're up to the correct precision1187assert k == M, "Oops, something went wrong in the iteration"11881189# We should have s^{-1/2} correct to precision M.1190# The following line can be uncommented to verify this.1191# (It's a slow verification though, can double the whole computation time.)11921193#assert (p * X.square() * r * base_ring(2)).coeffs() == \1194# R(p).shift(p*(2*M - 1)).coeffs()11951196# Finally incorporate frobenius of dx and x dx, and choose offset that1197# compensates for our normalisations by powers of T.1198F0 = base_ring(p) * x_to_p_less_one * X1199F1 = F0 * x_to_p1200offset = ((2*k-1)*p - 1)/212011202return F0, F1, offset12031204120512061207def frobenius_expansion_by_series(Q, p, M):1208r"""1209Computes the action of Frobenius on `dx/y` and on `x dx/y`, using a1210series expansion.12111212(This function computes the same thing as1213frobenius_expansion_by_newton(), using a different method.1214Theoretically the Newton method should be asymptotically faster,1215when the precision gets large. However, in practice, this functions1216seems to be marginally faster for moderate precision, so I'm1217keeping it here until I figure out exactly why it's faster.)12181219(This function does *not* yet use the cohomology relations - that1220happens afterwards in the "reduction" step.)12211222More specifically, it finds F0 and F1 in the quotient ring1223`R[x, T]/(T - Q(x))`, such that1224`F( dx/y) = T^{-r} F0 dx/y`, and1225`F(x dx/y) = T^{-r} F1 dx/y` where1226`r = ( (2M-3)p - 1 )/2`. (Here `T` is `y^2 = z^{-2}`,1227and `R` is the coefficient ring of `Q`.)12281229`F_0` and `F_1` are computed in the1230SpecialCubicQuotientRing associated to `Q`, so all powers1231of `x^j` for `j \geq 3` are reduced to powers of1232`T`.12331234It uses the sum12351236.. math::12371238F0 = \sum_{k=0}^{M-2} {-1/2 \choose k} p x^{p-1} E^k T^{(M-2-k)p}12391240and12411242.. math::12431244F1 = x^p F0,12451246where `E = Q(x^p) - Q(x)^p`.12471248INPUT:124912501251- ``Q`` - cubic polynomial of the form1252`Q(x) = x^3 + ax + b`, whose coefficient ring is a1253`\ZZ/(p^M)\ZZ` -algebra12541255- ``p`` - residue characteristic of the `p`-adic field12561257- ``M`` - `p`-adic precision of the coefficient ring1258(this will be used to determine the number of terms in the1259series)12601261OUTPUT:12621263- ``F0, F1`` - elements of1264SpecialCubicQuotientRing(Q), as described above12651266- ``r`` - non-negative integer, as described above1267"""12681269S = SpecialCubicQuotientRing(Q)1270x, _ = S.gens()1271base_ring = S.base_ring()12721273x_to_p_less_1 = x**(p-1)1274x_to_p = x_to_p_less_1 * x12751276# compute frobQ = Q(x^p)1277x_to_p_squared = x_to_p * x_to_p1278x_to_p_cubed = x_to_p_squared * x_to_p1279frobQ = x_to_p_cubed + Q[1]*x_to_p + Q[0]*S(1)1280# anticipating the day when p = 3 is supported:1281# frobQ = x_to_p_cubed + Q[2]*x_to_p_squared + Q[1]*x_to_p + Q[0]*S(1)12821283E = frobQ - S(1).shift(p) # E = Q(x^p) - Q(x)^p12841285offset = int( ((2*M-3)*p-1)/2 )1286term = p * x_to_p_less_11287F0 = term.shift((M-2)*p)12881289# todo: Possible speedup idea, perhaps by a factor of 2, but1290# it requires a lot of work:1291# Note that p divides E, so p^k divides E^k. So when we are1292# working with high powers of E, we're doing a lot more work1293# in the multiplications than we need to. To take advantage of1294# this we would need some protocol for "lowering the precision"1295# of a SpecialCubicQuotientRing. This would be quite messy to1296# do properly over an arbitrary base ring. Perhaps it is1297# feasible to do for the most common case (i.e. Z/p^nZ).1298# (but it probably won't save much time unless p^n is very1299# large, because the machine word size is probably pretty1300# big anyway.)13011302for k in range(int(1), int(M-1)):1303term = term * E1304c = base_ring(binomial(-Integer(1)/2, k))1305F0 += (term * c).shift((M-k-2)*p)13061307return F0, F0 * x_to_p, offset1308130913101311def adjusted_prec(p, prec):1312r"""1313Computes how much precision is required in matrix_of_frobenius to1314get an answer correct to prec `p`-adic digits.13151316The issue is that the algorithm used in matrix_of_frobenius1317sometimes performs divisions by `p`, so precision is lost1318during the algorithm.13191320The estimate returned by this function is based on Kedlaya's result1321(Lemmas 2 and 3 of "Counting Points on Hyperelliptic Curves..."),1322which implies that if we start with `M` `p`-adic1323digits, the total precision loss is at most1324`1 + \lfloor \log_p(2M-3) \rfloor` `p`-adic1325digits. (This estimate is somewhat less than the amount you would1326expect by naively counting the number of divisions by1327`p`.)13281329INPUT:133013311332- ``p`` - a prime = 513331334- ``prec`` - integer, desired output precision, = 1133513361337OUTPUT: adjusted precision (usually slightly more than prec)1338"""13391340# initial estimate:1341if prec <= 2:1342adjusted = 21343else:1344adjusted = prec + int(log(2*prec - 3, p)) - 113451346# increase it until we have enough1347while adjusted - int(log(2*adjusted - 3, p)) - 1 < prec:1348adjusted += 113491350return adjusted1351135213531354def matrix_of_frobenius(Q, p, M, trace=None, compute_exact_forms=False):1355"""1356Computes the matrix of Frobenius on Monsky-Washnitzer cohomology,1357with respect to the basis `(dx/y, x dx/y)`.13581359INPUT:136013611362- ``Q`` - cubic polynomial `Q(x) = x^3 + ax + b`1363defining an elliptic curve `E` by1364`y^2 = Q(x)`. The coefficient ring of `Q` should be a1365`\ZZ/(p^M)\ZZ`-algebra in which the matrix of1366frobenius will be constructed.13671368- ``p`` - prime = 5 for which E has good reduction13691370- ``M`` - integer = 2; `p` -adic precision of1371the coefficient ring13721373- ``trace`` - (optional) the trace of the matrix, if1374known in advance. This is easy to compute because it's just the1375`a_p` of the curve. If the trace is supplied,1376matrix_of_frobenius will use it to speed the computation (i.e. we1377know the determinant is `p`, so we have two conditions, so1378really only column of the matrix needs to be computed. It's1379actually a little more complicated than that, but that's the basic1380idea.) If trace=None, then both columns will be computed1381independently, and you can get a strong indication of correctness1382by verifying the trace afterwards.13831384.. warning::13851386THE RESULT WILL NOT NECESSARILY BE CORRECT TO M p-ADIC1387DIGITS. If you want prec digits of precision, you need to use1388the function adjusted_prec(), and then you need to reduce the1389answer mod `p^{\mathrm{prec}}` at the end.139013911392OUTPUT:139313942x2 matrix of frobenius on Monsky-Washnitzer cohomology,1395with entries in the coefficient ring of Q.13961397EXAMPLES:13981399A simple example::14001401sage: p = 51402sage: prec = 31403sage: M = monsky_washnitzer.adjusted_prec(p, prec)1404sage: M140551406sage: R.<x> = PolynomialRing(Integers(p**M))1407sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)1408sage: A1409[3090 187]1410[2945 408]14111412But the result is only accurate to prec digits::14131414sage: B = A.change_ring(Integers(p**prec))1415sage: B1416[90 62]1417[70 33]14181419Check trace (123 = -2 mod 125) and determinant::14201421sage: B.det()142251423sage: B.trace()14241231425sage: EllipticCurve([-1, 1/4]).ap(5)1426-214271428Try using the trace to speed up the calculation::14291430sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4),1431... p, M, -2)1432sage: A1433[2715 187]1434[1445 408]14351436Hmmm... it looks different, but that's because the trace of our1437first answer was only -2 modulo `5^3`, not -2 modulo1438`5^5`. So the right answer is::14391440sage: A.change_ring(Integers(p**prec))1441[90 62]1442[70 33]14431444Check it works with only one digit of precision::14451446sage: p = 51447sage: prec = 11448sage: M = monsky_washnitzer.adjusted_prec(p, prec)1449sage: R.<x> = PolynomialRing(Integers(p**M))1450sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)1451sage: A.change_ring(Integers(p))1452[0 2]1453[0 3]14541455Here's an example that's particularly badly conditioned for using1456the trace trick::14571458sage: p = 111459sage: prec = 31460sage: M = monsky_washnitzer.adjusted_prec(p, prec)1461sage: R.<x> = PolynomialRing(Integers(p**M))1462sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 + 7*x + 8, p, M)1463sage: A.change_ring(Integers(p**prec))1464[1144 176]1465[ 847 185]14661467The problem here is that the top-right entry is divisible by 11,1468and the bottom-left entry is divisible by `11^2`. So when1469you apply the trace trick, neither `F(dx/y)` nor1470`F(x dx/y)` is enough to compute the whole matrix to the1471desired precision, even if you try increasing the target precision1472by one. Nevertheless, ``matrix_of_frobenius`` knows1473how to get the right answer by evaluating `F((x+1) dx/y)`1474instead::14751476sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 + 7*x + 8, p, M, -2)1477sage: A.change_ring(Integers(p**prec))1478[1144 176]1479[ 847 185]14801481The running time is about ``O(p*prec**2)`` (times1482some logarithmic factors), so it's feasible to run on fairly large1483primes, or precision (or both?!?!)::14841485sage: p = 100071486sage: prec = 21487sage: M = monsky_washnitzer.adjusted_prec(p, prec)1488sage: R.<x> = PolynomialRing(Integers(p**M))1489sage: A = monsky_washnitzer.matrix_of_frobenius( # long time1490... x^3 - x + R(1/4), p, M) # long time1491sage: B = A.change_ring(Integers(p**prec)); B # long time1492[74311982 57996908]1493[95877067 25828133]1494sage: B.det() # long time1495100071496sage: B.trace() # long time1497661498sage: EllipticCurve([-1, 1/4]).ap(10007) # long time14996615001501::15021503sage: p = 51504sage: prec = 3001505sage: M = monsky_washnitzer.adjusted_prec(p, prec)1506sage: R.<x> = PolynomialRing(Integers(p**M))1507sage: A = monsky_washnitzer.matrix_of_frobenius( # long time1508... x^3 - x + R(1/4), p, M) # long time1509sage: B = A.change_ring(Integers(p**prec)) # long time1510sage: B.det() # long time151151512sage: -B.trace() # long time151321514sage: EllipticCurve([-1, 1/4]).ap(5) # long time1515-215161517Let's check consistency of the results for a range of precisions::15181519sage: p = 51520sage: max_prec = 601521sage: M = monsky_washnitzer.adjusted_prec(p, max_prec)1522sage: R.<x> = PolynomialRing(Integers(p**M))1523sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M) # long time1524sage: A = A.change_ring(Integers(p**max_prec)) # long time1525sage: result = [] # long time1526sage: for prec in range(1, max_prec): # long time1527... M = monsky_washnitzer.adjusted_prec(p, prec) # long time1528... R.<x> = PolynomialRing(Integers(p^M),'x') # long time1529... B = monsky_washnitzer.matrix_of_frobenius( # long time1530... x^3 - x + R(1/4), p, M) # long time1531... B = B.change_ring(Integers(p**prec)) # long time1532... result.append(B == A.change_ring( # long time1533... Integers(p**prec))) # long time1534sage: result == [True] * (max_prec - 1) # long time1535True15361537The remaining examples discuss what happens when you take the1538coefficient ring to be a power series ring; i.e. in effect you're1539looking at a family of curves.15401541The code does in fact work...15421543::15441545sage: p = 111546sage: prec = 31547sage: M = monsky_washnitzer.adjusted_prec(p, prec)1548sage: S.<t> = PowerSeriesRing(Integers(p**M), default_prec=4)1549sage: a = 7 + t + 3*t^21550sage: b = 8 - 6*t + 17*t^21551sage: R.<x> = PolynomialRing(S)1552sage: Q = x**3 + a*x + b1553sage: A = monsky_washnitzer.matrix_of_frobenius(Q, p, M) # long time1554sage: B = A.change_ring(PowerSeriesRing(Integers(p**prec), 't', default_prec=4)) # long time1555sage: B # long time1556[1144 + 264*t + 841*t^2 + 1025*t^3 + O(t^4) 176 + 1052*t + 216*t^2 + 523*t^3 + O(t^4)]1557[ 847 + 668*t + 81*t^2 + 424*t^3 + O(t^4) 185 + 341*t + 171*t^2 + 642*t^3 + O(t^4)]15581559The trace trick should work for power series rings too, even in the1560badly- conditioned case. Unfortunately I don't know how to compute1561the trace in advance, so I'm not sure exactly how this would help.1562Also, I suspect the running time will be dominated by the1563expansion, so the trace trick won't really speed things up anyway.1564Another problem is that the determinant is not always p::15651566sage: B.det() # long time156711 + 484*t^2 + 451*t^3 + O(t^4)15681569However, it appears that the determinant always has the property1570that if you substitute t - 11t, you do get the constant series p1571(mod p\*\*prec). Similarly for the trace. And since the parameter1572only really makes sense when it's divisible by p anyway, perhaps1573this isn't a problem after all.1574"""15751576M = int(M)1577if M < 2:1578raise ValueError, "M (=%s) must be at least 2" % M15791580base_ring = Q.base_ring()15811582# Expand out frobenius of dx/y and x dx/y.1583# (You can substitute frobenius_expansion_by_series here, that will work1584# as well. See its docstring for some performance notes.)1585F0, F1, offset = frobenius_expansion_by_newton(Q, p, M)1586#F0, F1, offset = frobenius_expansion_by_series(Q, p, M)15871588if compute_exact_forms:1589# we need to do all the work to get the exact expressions f such that F(x^i dx/y) = df + \sum a_i x^i dx/y1590F0_coeffs = transpose_list(F0.coeffs())1591F0_reduced, f_0 = reduce_all(Q, p, F0_coeffs, offset, True)15921593F1_coeffs = transpose_list(F1.coeffs())1594F1_reduced, f_1 = reduce_all(Q, p, F1_coeffs, offset, True)15951596elif M == 2:1597# This implies that only one digit of precision is valid, so we only need1598# to reduce the second column. Also, the trace doesn't help at all.15991600F0_reduced = [ base_ring(0), base_ring(0) ]16011602F1_coeffs = transpose_list(F1.coeffs())1603F1_reduced = reduce_all(Q, p, F1_coeffs, offset)16041605elif trace is None:1606# No trace provided, just reduce F(dx/y) and F(x dx/y) separately.16071608F0_coeffs = transpose_list(F0.coeffs())1609F0_reduced = reduce_all(Q, p, F0_coeffs, offset)16101611F1_coeffs = transpose_list(F1.coeffs())1612F1_reduced = reduce_all(Q, p, F1_coeffs, offset)16131614else:1615# Trace has been provided.16161617# In most cases this can be used to quickly compute F(dx/y) from1618# F(x dx/y). However, if we're unlucky, the (dx/y)-component of1619# F(x dx/y) (i.e. the top-right corner of the matrix) may be divisible1620# by p, in which case there isn't enough information to get the1621# (x dx/y)-component of F(dx/y) to the desired precision. When this1622# happens, it turns out that F((x+1) dx/y) always *does* give enough1623# information (together with the trace) to get both columns to the1624# desired precision.16251626# First however we need a quick way of telling whether the top-right1627# corner is divisible by p, i.e. we want to compute the second column1628# of the matrix mod p. We could do this by just running the entire1629# algorithm with M = 2 (which assures precision 1). Luckily, we've1630# already done most of the work by computing F1 to high precision; so1631# all we need to do is extract the coefficients that would correspond1632# to the first term of the series, and run the reduction on them.16331634# todo: actually we only need to do this reduction step mod p^2, not1635# mod p^M, which is what the code currently does. If the base ring1636# is Integers(p^M), then it's easy. Otherwise it's tricky to construct1637# the right ring, I don't know how to do it.16381639F1_coeffs = transpose_list(F1.coeffs())1640F1_modp_coeffs = F1_coeffs[int((M-2)*p):]1641# make a copy, because reduce_all will destroy the coefficients:1642F1_modp_coeffs = [[cell for cell in row] for row in F1_modp_coeffs]1643F1_modp_offset = offset - (M-2)*p1644F1_modp_reduced = reduce_all(Q, p, F1_modp_coeffs, F1_modp_offset)16451646if F1_modp_reduced[0].is_unit():1647# If the first entry is invertible mod p, then F(x dx/y) is sufficient1648# to get the whole matrix.16491650F1_reduced = reduce_all(Q, p, F1_coeffs, offset)16511652F0_reduced = [ base_ring(trace) - F1_reduced[1], None ]1653# using that the determinant is p:1654F0_reduced[1] = (F0_reduced[0] * F1_reduced[1] - base_ring(p)) \1655/ F1_reduced[0]16561657else:1658# If the first entry is zero mod p, then F((x+1) dx/y) will be sufficient1659# to get the whole matrix. (Here we are using the fact that the second1660# entry *cannot* be zero mod p. This is guaranteed by some results in1661# section 3.2 of ``Computation of p-adic Heights and Log Convergence''1662# by Mazur, Stein, Tate. But let's quickly check it anyway :-))1663assert F1_modp_reduced[1].is_unit(), \1664"Hey that's impossible! The second entry in the second column " \1665"should be invertible mod p!"16661667G0_coeffs = transpose_list( (F0 + F1).coeffs())1668G0_reduced = reduce_all(Q, p, G0_coeffs, offset)16691670# Now G0_reduced expresses F((x+1) dx/y) in terms of dx/y and x dx/y.1671# Re-express this in terms of (x+1) dx/y and x dx/y.1672H0_reduced = [ G0_reduced[0], G0_reduced[1] - G0_reduced[0] ]16731674# The thing we're about to divide by better be a unit.1675assert H0_reduced[1].is_unit(), \1676"Hey that's impossible! The second entry in this column " \1677"should be invertible mod p!"16781679# Figure out the second column using the trace...1680H1_reduced = [ None, base_ring(trace) - H0_reduced[0] ]1681# ... and using that the determinant is p:1682H1_reduced[0] = (H0_reduced[0] * H1_reduced[1] - base_ring(p)) \1683/ H0_reduced[1]16841685# Finally, change back to the usual basis (dx/y, x dx/y)1686F1_reduced = [ H1_reduced[0], \1687H1_reduced[0] + H1_reduced[1] ]1688F0_reduced = [ H0_reduced[0] - F1_reduced[0],1689H0_reduced[0] + H0_reduced[1] - F1_reduced[1] ]16901691# One more sanity check: our final result should be congruent mod p1692# to the approximation we used earlier.1693assert not (1694(F1_reduced[0] - F1_modp_reduced[0]).is_unit() or \1695(F1_reduced[1] - F1_modp_reduced[1]).is_unit() or \1696F0_reduced[0].is_unit() or F0_reduced[1].is_unit()), \1697"Hey that's impossible! The output matrix is not congruent mod p " \1698"to the approximation found earlier!"16991700if compute_exact_forms:1701return matrix(base_ring, 2, 2, [F0_reduced[0], F1_reduced[0],1702F0_reduced[1], F1_reduced[1]]), f_0, f_11703else:1704return matrix(base_ring, 2, 2, [F0_reduced[0], F1_reduced[0],1705F0_reduced[1], F1_reduced[1]])17061707170817091710#*****************************************************************************1711# This is a generalization of the above functionality for hyperelliptic curves.1712#1713# THIS IS A WORK IN PROGRESS.1714#1715# I tried to embed must stuff into the rings themselves rather than1716# just extract and manipulate lists of coefficients. Hence the implementations1717# below are much less optimized, so are much slower, but should hopefully be1718# easier to follow. (E.g. one can print/make sense of intermediate results.)1719#1720# AUTHOR:1721# -- Robert Bradshaw (2007-04)1722#1723#*****************************************************************************172417251726import weakref17271728from sage.schemes.hyperelliptic_curves.all import is_HyperellipticCurve, HyperellipticCurve1729from sage.rings.padics.all import pAdicField1730from sage.rings.all import QQ, is_LaurentSeriesRing, is_IntegralDomain1731from sage.modules.all import FreeModule, is_FreeModuleElement17321733from sage.misc.profiler import Profiler1734from sage.misc.misc import repr_lincomb17351736def matrix_of_frobenius_hyperelliptic(Q, p=None, prec=None, M=None):1737"""1738Computes the matrix of Frobenius on Monsky-Washnitzer cohomology,1739with respect to the basis `(dx/2y, x dx/2y, ...x^{d-2} dx/2y)`, where1740`d` is the degree of `Q`.17411742INPUT:17431744- ``Q`` - monic polynomial `Q(x)`17451746- ``p`` - prime `\geq 5` for which `E` has good reduction17471748- ``prec`` - (optional) `p`-adic precision of the coefficient ring17491750- ``M`` - (optional) adjusted `p`-adic precision of the coefficint ring17511752OUTPUT:17531754`(d-1)` x `(d-1)` matrix `M` of Frobenius on Monsky-Washnitzer cohomology,1755and list of differentials \{f_i \} such that17561757.. math::17581759\phi^* (x^i dx/2y) = df_i + M[i]*vec(dx/2y, ..., x^{d-2} dx/2y)17601761EXAMPLES::17621763sage: p = 51764sage: prec = 31765sage: R.<x> = QQ['x']1766sage: A,f = monsky_washnitzer.matrix_of_frobenius_hyperelliptic(x^5 - 2*x + 3, p, prec)1767sage: A1768[ 4*5 + O(5^3) 5 + 2*5^2 + O(5^3) 2 + 3*5 + 2*5^2 + O(5^3) 2 + 5 + 5^2 + O(5^3)]1769[ 3*5 + 5^2 + O(5^3) 3*5 + O(5^3) 4*5 + O(5^3) 2 + 5^2 + O(5^3)]1770[ 4*5 + 4*5^2 + O(5^3) 3*5 + 2*5^2 + O(5^3) 5 + 3*5^2 + O(5^3) 2*5 + 2*5^2 + O(5^3)]1771[ 5^2 + O(5^3) 5 + 4*5^2 + O(5^3) 4*5 + 3*5^2 + O(5^3) 2*5 + O(5^3)]17721773"""1774prof = Profiler()1775prof("setup")1776if p is None:1777try:1778K = Q.base_ring()1779p = K.prime()1780prec = K.precision_cap()1781except AttributeError:1782raise ValueError, "p and prec must be specified if Q is not defined over a p-adic ring"1783if M is None:1784M = adjusted_prec(p, prec)1785extra_prec_ring = Integers(p**M)1786# extra_prec_ring = pAdicField(p, M) # SLOW!17871788real_prec_ring = pAdicField(p, prec) # pAdicField(p, prec) # To capped absolute?1789S = SpecialHyperellipticQuotientRing(Q, extra_prec_ring, True)1790MW = S.monsky_washnitzer()1791prof("frob basis elements")1792F = MW.frob_basis_elements(M, p)17931794prof("rationalize")1795# do reduction over Q in case we have non-integral entries (and it's so much faster than padics)1796rational_S = S.change_ring(QQ)1797# this is a hack until pAdics are fast1798# (They are in the latest development bundle, but its not standard and I'd need to merge.1799# (it will periodically cast into this ring to reduce coefficient size)1800rational_S._prec_cap = p**M1801rational_S._p = p1802# S._p = p1803# rational_S(F[0]).reduce_fast()1804# prof("reduce others")18051806# rational_S = S.change_ring(pAdicField(p, M))1807F = [rational_S(F_i) for F_i in F]18081809prof("reduce")1810reduced = [F_i.reduce_fast(True) for F_i in F]1811# reduced = [F_i.reduce() for F_i in F]18121813#print reduced[0][0].diff() - F[0]18141815# but the coeffs are WAY more precision than they need to be1816# print reduced[0][1]18171818prof("make matrix")1819# now take care of precision capping1820M = matrix(real_prec_ring, [a for f, a in reduced])1821for i in range(M.ncols()):1822for j in range(M.nrows()):1823M[i,j] = M[i,j].add_bigoh(prec)1824# print prof1825return M.transpose(), [f for f, a in reduced]18261827182818291830# For uniqueness (as many of the non-trivial calculations are cached along the way).18311832_special_ring_cache = {}1833_mw_cache = {}18341835def SpecialHyperellipticQuotientRing(*args):1836if _special_ring_cache.has_key(args):1837R = _special_ring_cache[args]()1838if R is not None:1839return R1840R = SpecialHyperellipticQuotientRing_class(*args)1841_special_ring_cache[args] = weakref.ref(R)1842return R18431844def MonskyWashnitzerDifferentialRing(base_ring):1845if _mw_cache.has_key(base_ring):1846R = _mw_cache[base_ring]()1847if R is not None:1848return R18491850R = MonskyWashnitzerDifferentialRing_class(base_ring)1851_mw_cache[base_ring] = weakref.ref(R)1852return R18531854class SpecialHyperellipticQuotientRing_class(CommutativeAlgebra):18551856_p = None18571858def __init__(self, Q, R=None, invert_y=True):1859if R is None:1860R = Q.base_ring()18611862# Trac ticket #9138: CommutativeAlgebra.__init__ must not be1863# done so early. It tries to register a coercion, but that1864# requires the hash bein available. But the hash, in its1865# default implementation, relies on the string representation,1866# which is not available at this point.1867#CommutativeAlgebra.__init__(self, R) # moved to below.18681869x = PolynomialRing(R, 'xx').gen(0)1870if is_EllipticCurve(Q):1871E = Q1872if E.a1() != 0 or E.a2() != 0:1873raise NotImplementedError, "Curve must be in Weierstrass normal form."1874Q = -E.change_ring(R).defining_polynomial()(x,0,1)1875self._curve = E18761877elif is_HyperellipticCurve(Q):1878C = Q1879if C.hyperelliptic_polynomials()[1] != 0:1880raise NotImplementedError, "Curve must be of form y^2 = Q(x)."1881Q = C.hyperelliptic_polynomials()[0].change_ring(R)1882self._curve = C18831884if is_Polynomial(Q):1885self._Q = Q.change_ring(R)1886self._coeffs = self._Q.coeffs()1887if self._coeffs.pop() != 1:1888raise NotImplementedError, "Polynomial must be monic."1889if not hasattr(self, '_curve'):1890if self._Q.degree() == 3:1891ainvs = [0, self._Q[2], 0, self._Q[1], self._Q[0]]1892self._curve = EllipticCurve(ainvs)1893else:1894self._curve = HyperellipticCurve(self._Q)18951896else:1897raise NotImplementedError, "Must be an elliptic curve or polynomial Q for y^2 = Q(x)\n(Got element of %s)" % Q.parent()18981899self._n = degree = int(Q.degree())1900self._series_ring = (LaurentSeriesRing if invert_y else PolynomialRing)(R, 'y')1901self._series_ring_y = self._series_ring.gen(0)1902self._series_ring_0 = self._series_ring(0)19031904# Trac ticket #9138: Initialise the commutative algebra here!1905# Below, we do self(self._poly_ring.gen(0)), which requires1906# the initialisation being finished.1907CommutativeAlgebra.__init__(self, R)19081909self._poly_ring = PolynomialRing(self._series_ring, 'x')19101911self._x = self(self._poly_ring.gen(0))1912self._y = self(self._series_ring.gen(0))19131914self._Q_coeffs = Q.change_ring(self._series_ring).list()1915self._dQ = Q.derivative().change_ring(self)(self._x)1916self._monsky_washnitzer = MonskyWashnitzerDifferentialRing(self)19171918self._monomial_diffs = {}1919self._monomial_diff_coeffs = {}19201921def _repr_(self):1922y_inverse = ",y^-1" if is_LaurentSeriesRing(self._series_ring) else ""1923return "SpecialHyperellipticQuotientRing K[x,y%s] / (y^2 = %s) over %s"%(y_inverse, self._Q, self.base_ring())19241925def base_extend(self, R):1926if R.has_coerce_map_from(self.base_ring()):1927return self.change_ring(R)1928else:1929raise TypeError, "no such base extension"19301931def change_ring(self, R):1932return SpecialHyperellipticQuotientRing(self._Q, R, is_LaurentSeriesRing(self._series_ring))19331934def __call__(self, val, offset=0, check=True):1935if isinstance(val, SpecialHyperellipticQuotientElement) and val.parent() is self:1936if offset == 0:1937return val1938else:1939return val << offset1940elif isinstance(val, MonskyWashnitzerDifferential):1941return self._monsky_washnitzer(val)1942return SpecialHyperellipticQuotientElement(self, val, offset, check)19431944def gens(self):1945return self._x, self._y19461947def x(self):1948return self._x19491950def y(self):1951return self._y19521953def monomial(self, i, j, b=None):1954"""1955Returns `b y^j x^i`, computed quickly.1956"""1957i = int(i)1958j = int(j)19591960if 0 < i and i < self._n:1961if b is None:1962by_to_j = self._series_ring_y << (j-1)1963else:1964by_to_j = self._series_ring(b) << j1965v = [self._series_ring_0] * self._n1966v[i] = by_to_j1967return self(v)1968else:1969return (self._x ** i) << j if b is None else self.base_ring()(b) * (self._x ** i) << j19701971def monomial_diff_coeffs(self, i, j):1972r"""1973The key here is that the formula for `d(x^iy^j)` is messy1974in terms of `i`, but varies nicely with `j`.19751976.. math::19771978d(x^iy^j) = y^{j-1} (2ix^{i-1}y^2 + j (A_i(x) + B_i(x)y^2)) \frac{dx}{2y}197919801981Where `A,B` have degree at most `n-1` for each1982`i`. Pre-compute `A_i, B_i` for each `i`1983the "hard" way, and the rest are easy.1984"""1985try:1986return self._monomial_diff_coeffs[i,j]1987except KeyError:1988pass1989if i < self._n:1990try:1991A, B, two_i_x_to_i = self._precomputed_diff_coeffs[i]1992except AttributeError:1993self._precomputed_diff_coeffs = self._precompute_monomial_diffs()1994A, B, two_i_x_to_i = self._precomputed_diff_coeffs[i]1995if i == 0:1996return j*A, j*B1997else:1998return j*A, j*B + two_i_x_to_i1999else:2000dg = self.monomial(i, j).diff()2001coeffs = [dg.extract_pow_y(j-1), dg.extract_pow_y(j+1)]2002self._monomial_diff_coeffs[i,j] = coeffs2003return coeffs20042005def monomial_diff_coeffs_matrices(self):2006self.monomial_diff_coeffs(0, 0) # precompute stuff2007R = self.base_ring()2008mat_1 = matrix(R, self._n, self._n)2009mat_2 = matrix(R, self._n, self._n)2010for i in range(self._n):2011mat_1[i] = self._precomputed_diff_coeffs[i][1]2012mat_2[i] = self._precomputed_diff_coeffs[i][2]2013return mat_1.transpose(), mat_2.transpose()20142015def _precompute_monomial_diffs(self):2016x, y = self.gens()2017R = self.base_ring()2018V = FreeModule(R, self.degree())2019As = []2020for i in range(self.degree()):2021dg = self.monomial(i, 1).diff()2022two_i_x_to_i = R(2*i) * x**(i-1) * y*y if i > 0 else self(0)2023A = dg - self._monsky_washnitzer(two_i_x_to_i)2024As.append( (V(A.extract_pow_y(0)), V(A.extract_pow_y(2)), V(two_i_x_to_i.extract_pow_y(2))) )2025return As202620272028def Q(self):2029return self._Q20302031def curve(self):2032return self._curve20332034def degree(self):2035return self._n20362037def prime(self):2038return self._p20392040def monsky_washnitzer(self):2041return self._monsky_washnitzer20422043def is_field(self, proof = True):2044return False2045204620472048class SpecialHyperellipticQuotientElement(CommutativeAlgebraElement):20492050def __init__(self, parent, val=0, offset=0, check=True):2051CommutativeAlgebraElement.__init__(self, parent)2052if not check:2053self._f = parent._poly_ring(val, check=False)2054return2055if isinstance(val, SpecialHyperellipticQuotientElement):2056R = parent.base_ring()2057self._f = parent._poly_ring([a.change_ring(R) for a in val._f])2058return2059if isinstance(val, tuple):2060val, offset = val2061if isinstance(val, list) and len(val) > 0 and is_FreeModuleElement(val[0]):2062val = transpose_list(val)2063self._f = parent._poly_ring(val)2064if offset != 0:2065self._f = self._f.parent()([a << offset for a in self._f], check=False)20662067def __cmp__(self, other):2068"""2069EXAMPLES:2070"""2071return cmp(self._f, other._f)20722073def change_ring(self, R):2074return self.parent().change_ring(R)(self)20752076def __call__(self, *x):2077return self._f(*x)20782079def __invert__(self):2080"""2081The general element in our ring is not invertible, but `y` may be. We2082do not want to pass to the fraction field.2083"""2084if self._f.degree() == 0 and self._f[0].is_unit():2085return SpecialHyperellipticQuotientElement(self.parent(), ~self._f[0])2086else:2087raise ZeroDivisionError, "Element not invertible"20882089def __nonzero__(self):2090return not not self._f20912092def __eq__(self, other):2093if not isinstance(other, SpecialHyperellipticQuotientElement):2094other = self.parent()(other)2095return self._f == other._f20962097def _add_(self, other):2098return SpecialHyperellipticQuotientElement(self.parent(), self._f + other._f)20992100def _sub_(self, other):2101return SpecialHyperellipticQuotientElement(self.parent(), self._f - other._f)21022103def _mul_(self, other):2104# over laurent series, addition and subtraction can be expensive,2105# and the degree of this poly is small enough that Karatsuba actually hurts2106# significantly in some cases2107if self._f[0].valuation() + other._f[0].valuation() > -200:2108prod = self._f._mul_generic(other._f)2109else:2110prod = self._f * other._f2111v = prod.list()2112parent = self.parent()2113Q_coeffs = parent._Q_coeffs2114n = len(Q_coeffs) - 12115y2 = self.parent()._series_ring_y << 12116for i in range(len(v)-1, n-1, -1):2117for j in range(n):2118v[i-n+j] -= Q_coeffs[j] * v[i]2119v[i-n] += y2 * v[i]2120return SpecialHyperellipticQuotientElement(parent, v[0:n])21212122def _rmul_(self, c):2123coeffs = self._f.list(copy=False)2124return self.parent()([c*a for a in coeffs], check=False)21252126def _lmul_(self, c):2127coeffs = self._f.list(copy=False)2128return self.parent()([a*c for a in coeffs], check=False)21292130def __lshift__(self, k):2131coeffs = self._f.list(copy=False)2132return self.parent()([a << k for a in coeffs], check=False)21332134def __rshift__(self, k):2135coeffs = self._f.list(copy=False)2136return self.parent()([a >> k for a in coeffs], check=False)21372138def truncate_neg(self, n):2139coeffs = self._f.list(copy=False)2140return self.parent()([a.truncate_neg(n) for a in coeffs], check=False)21412142def _repr_(self):2143x = PolynomialRing(QQ, 'x').gen(0)2144coeffs = self._f.list()2145return repr_lincomb([(x**i, coeffs[i]) for i in range(len(coeffs))])21462147def _latex_(self):2148x = PolynomialRing(QQ, 'x').gen(0)2149coeffs = self._f.list()2150return repr_lincomb([(x**i, coeffs[i]) for i in range(len(coeffs))], is_latex=True)21512152def diff(self):21532154# try:2155# return self._diff_x2156# except AttributeError:2157# pass21582159# d(self) = A dx + B dy2160# = (2y A + BQ') dx/2y2161parent = self.parent()2162R = parent.base_ring()2163x, y = parent.gens()2164v = self._f.list()2165n = len(v)2166A = parent([R(i) * v[i] for i in range(1,n)])2167B = parent([a.derivative() for a in v])2168dQ = parent._dQ2169return parent._monsky_washnitzer( (R(2) * A << 1) + dQ * B )2170# self._diff = self.parent()._monsky_washnitzer( two_y * A + dQ * B )2171# return self._diff21722173def extract_pow_y(self, k):2174v = [a[k] for a in self._f.list()]2175while len(v) < self.parent()._n:2176v.append(0)2177return v21782179def min_pow_y(self):2180if self._f.degree() == -1:2181return 02182return min([a.valuation() for a in self._f.list()])21832184def max_pow_y(self):2185if self._f.degree() == -1:2186return 02187return max([a.degree() for a in self._f.list()])21882189def coeffs(self, R=None):2190"""2191Returns the raw coefficients of this element.21922193INPUT:21942195- ``R`` - an (optional) base-ring in which to cast the coefficients21962197OUTPUT:21982199- ``coeffs`` - a list of coefficients of powers of `x` for each power2200of `y`22012202- ``n`` - an offset indicating the power of `y` of the first list2203element22042205EXAMPLES::22062207sage: R.<x> = QQ['x']2208sage: E = HyperellipticCurve(x^5-3*x+1)2209sage: x,y = E.monsky_washnitzer_gens()2210sage: x.coeffs()2211([(0, 1, 0, 0, 0)], 0)2212sage: y.coeffs()2213([(0, 0, 0, 0, 0), (1, 0, 0, 0, 0)], 0)22142215sage: a = sum(n*x^n for n in range(5)); a2216x + 2*x^2 + 3*x^3 + 4*x^42217sage: a.coeffs()2218([(0, 1, 2, 3, 4)], 0)2219sage: a.coeffs(Qp(7))2220([(0, 1 + O(7^20), 2 + O(7^20), 3 + O(7^20), 4 + O(7^20))], 0)2221sage: (a*y).coeffs()2222([(0, 0, 0, 0, 0), (0, 1, 2, 3, 4)], 0)2223sage: (a*y^-2).coeffs()2224([(0, 1, 2, 3, 4), (0, 0, 0, 0, 0), (0, 0, 0, 0, 0)], -2)22252226Note that the coefficient list is transposed compared to how they2227are stored and printed::22282229sage: a*y^-22230(y^-2)*x + (2*y^-2)*x^2 + (3*y^-2)*x^3 + (4*y^-2)*x^422312232A more complicated example::22332234sage: a = x^20*y^-3 - x^11*y^2; a2235(y^-3-4*y^-1+6*y-4*y^3+y^5)*1 + (-12*y^-3+36*y^-1-36*y-y^2+12*y^3+2*y^4-y^6)*x + (54*y^-3-108*y^-1+54*y+6*y^2-6*y^4)*x^2 + (-108*y^-3+108*y^-1-9*y^2)*x^3 + (81*y^-3)*x^42236sage: raw, offset = a.coeffs()2237sage: a.min_pow_y()2238-32239sage: offset2240-32241sage: raw2242[(1, -12, 54, -108, 81),2243(0, 0, 0, 0, 0),2244(-4, 36, -108, 108, 0),2245(0, 0, 0, 0, 0),2246(6, -36, 54, 0, 0),2247(0, -1, 6, -9, 0),2248(-4, 12, 0, 0, 0),2249(0, 2, -6, 0, 0),2250(1, 0, 0, 0, 0),2251(0, -1, 0, 0, 0)]2252sage: sum(c * x^i * y^(j+offset) for j, L in enumerate(raw) for i, c in enumerate(L)) == a2253True22542255Can also be used to construct elements::22562257sage: a.parent()(raw, offset) == a2258True2259"""2260zero = self.base_ring()(0) if R is None else R(0)2261y_offset = min(self.min_pow_y(), 0)2262y_degree = max(self.max_pow_y(), 0)2263coeffs = []2264n = y_degree - y_offset + 12265for a in self._f.list():2266k = a.valuation() - y_offset2267z = a.list()2268coeffs.append( [zero] * k + z + [zero]*(n - len(z) - k))2269while len(coeffs) < self.parent().degree():2270coeffs.append( [zero] * n )2271V = FreeModule(self.base_ring() if R is None else R, self.parent().degree())2272coeffs = transpose_list(coeffs)2273return [V(a) for a in coeffs], y_offset2274227522762277class MonskyWashnitzerDifferentialRing_class(Module):22782279def __init__(self, base_ring):2280r"""2281Class for the ring of Monsky--Washnitzer differentials over a given2282base ring.2283"""2284Module.__init__(self, base_ring)2285self._cache = {}22862287def invariant_differential(self):2288"""2289Returns `dx/2y` as an element of self.22902291EXAMPLES::22922293sage: R.<x> = QQ['x']2294sage: C = HyperellipticCurve(x^5-4*x+4)2295sage: MW = C.invariant_differential().parent()2296sage: MW.invariant_differential()22971 dx/2y2298"""2299return self(1)23002301def __call__(self, val, offset=0):2302return MonskyWashnitzerDifferential(self, val, offset)23032304def base_extend(self, R):2305"""2306Return a new differential ring which is self base-extended to `R`23072308INPUT:23092310- ``R`` - ring23112312OUTPUT:23132314Self, base-extended to `R`.23152316EXAMPLES::23172318sage: R.<x> = QQ['x']2319sage: C = HyperellipticCurve(x^5-4*x+4)2320sage: MW = C.invariant_differential().parent()2321sage: MW.base_ring()2322SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 4*x + 4) over Rational Field2323sage: MW.base_extend(Qp(5,5)).base_ring()2324SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = (1 + O(5^5))*x^5 + (1 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5))*x + (4 + O(5^5))) over 5-adic Field with capped relative precision 52325"""2326return MonskyWashnitzerDifferentialRing(self.base_ring().base_extend(R))23272328def change_ring(self, R):2329"""2330Returns a new differential ring which is self with the coefficient2331ring changed to `R`.23322333INPUT:23342335- ``R`` - ring of coefficients23362337OUTPUT:23382339Self, with the coefficient ring changed to `R`.23402341EXAMPLES::23422343sage: R.<x> = QQ['x']2344sage: C = HyperellipticCurve(x^5-4*x+4)2345sage: MW = C.invariant_differential().parent()2346sage: MW.base_ring()2347SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 4*x + 4) over Rational Field2348sage: MW.change_ring(Qp(5,5)).base_ring()2349SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = (1 + O(5^5))*x^5 + (1 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5))*x + (4 + O(5^5))) over 5-adic Field with capped relative precision 52350"""2351return MonskyWashnitzerDifferentialRing(self.base_ring().change_ring(R))23522353def degree(self):2354"""2355Returns the degree of `Q(x)`, where the model of the underlying2356hyperelliptic curve of self is given by `y^2 = Q(x)`.23572358EXAMPLES::23592360sage: R.<x> = QQ['x']2361sage: C = HyperellipticCurve(x^5-4*x+4)2362sage: MW = C.invariant_differential().parent()2363sage: MW.Q()2364x^5 - 4*x + 42365sage: MW.degree()236652367"""2368return self.base_ring().degree()23692370def dimension(self):2371"""2372Returns the dimension of self.23732374EXAMPLES::23752376sage: R.<x> = QQ['x']2377sage: C = HyperellipticCurve(x^5-4*x+4)2378sage: K = Qp(7,5)2379sage: CK = C.change_ring(K)2380sage: MW = CK.invariant_differential().parent()2381sage: MW.dimension()238242383"""2384return self.base_ring().degree()-123852386def Q(self):2387"""2388Returns `Q(x)` where the model of the underlying hyperelliptic curve2389of self is given by `y^2 = Q(x)`.23902391EXAMPLES::23922393sage: R.<x> = QQ['x']2394sage: C = HyperellipticCurve(x^5-4*x+4)2395sage: MW = C.invariant_differential().parent()2396sage: MW.Q()2397x^5 - 4*x + 42398"""2399return self.base_ring().Q()24002401def x_to_p(self, p):2402"""2403Returns and caches `x^p`, reduced via the relations coming from the2404defining polynomial of the hyperelliptic curve.24052406EXAMPLES::24072408sage: R.<x> = QQ['x']2409sage: C = HyperellipticCurve(x^5-4*x+4)2410sage: MW = C.invariant_differential().parent()2411sage: MW.x_to_p(3)2412x^32413sage: MW.x_to_p(5)2414(-4+y^2)*1 + 4*x2415sage: MW.x_to_p(101) is MW.x_to_p(101)2416True2417"""2418try:2419return self._cache["x_to_p", p]2420except KeyError:2421x_to_p = self.base_ring().x() ** p2422self._cache["x_to_p", p] = x_to_p2423return x_to_p24242425def frob_Q(self, p):2426"""2427Returns and caches `Q(x^p)`, which is used in computing the image of2428`y` under a `p`-power lift of Frobenius to `A^{\dagger}`.24292430EXAMPLES::24312432sage: R.<x> = QQ['x']2433sage: C = HyperellipticCurve(x^5-4*x+4)2434sage: MW = C.invariant_differential().parent()2435sage: MW.frob_Q(3)2436(-60+48*y^2-12*y^4+y^6)*1 + (192-96*y^2+12*y^4)*x + (-192+48*y^2)*x^2 + 60*x^32437sage: MW.Q()(MW.x_to_p(3))2438(-60+48*y^2-12*y^4+y^6)*1 + (192-96*y^2+12*y^4)*x + (-192+48*y^2)*x^2 + 60*x^32439sage: MW.frob_Q(11) is MW.frob_Q(11)2440True2441"""2442try:2443return self._cache["frobQ", p]2444except KeyError:2445x_to_p = self.x_to_p(p)2446frobQ = self.base_ring()._Q.change_ring(self.base_ring())(x_to_p)2447self._cache["frobQ", p] = frobQ2448return frobQ24492450def frob_invariant_differential(self, prec, p):2451r"""2452Kedlaya's algorithm allows us to calculate the action of Frobenius on2453the Monsky-Washnitzer cohomology. First we lift `\phi` to `A^{\dagger}`2454by setting24552456.. math::24572458\phi(x) = x^p24592460\phi(y) = y^p \sqrt{1 + \frac{Q(x^p) - Q(x)^p}{Q(x)^p}}.24612462Pulling back the differential `dx/2y`, we get24632464.. math::24652466\phi^*(dx/2y) = px^{p-1} y(\phi(y))^{-1} dx/2y2467= px^{p-1} y^{1-p} \sqrt{1+ \frac{Q(x^p) - Q(x)^p}{Q(x)^p}} dx/2y24682469Use Newton's method to calculate the square root.24702471EXAMPLES::24722473sage: R.<x> = QQ['x']2474sage: C = HyperellipticCurve(x^5-4*x+4)2475sage: prec = 22476sage: p = 72477sage: MW = C.invariant_differential().parent()2478sage: MW.frob_invariant_differential(prec,p)2479((67894400*y^-20-81198880*y^-18+40140800*y^-16-10035200*y^-14+1254400*y^-12-62720*y^-10)*1 + (-119503944*y^-20+116064242*y^-18-43753472*y^-16+7426048*y^-14-514304*y^-12+12544*y^-10-1568*y^-8+70*y^-6+7*y^-4)*x + (78905288*y^-20-61014016*y^-18+16859136*y^-16-2207744*y^-14+250880*y^-12-37632*y^-10+3136*y^-8-70*y^-6)*x^2 + (-39452448*y^-20+26148752*y^-18-8085490*y^-16+2007040*y^-14-376320*y^-12+37632*y^-10-1568*y^-8)*x^3 + (21102144*y^-20-18120592*y^-18+8028160*y^-16-2007040*y^-14+250880*y^-12-12544*y^-10)*x^4) dx/2y2480"""2481prof = Profiler()2482prof("setup")2483# TODO, would it be useful to be able to take Frobenius of any element? Less efficient?2484x, y = self.base_ring().gens()2485prof("x_to_p")2486x_to_p_less_1 = x**(p-1)2487x_to_p = x*x_to_p_less_124882489# cache for future use2490self._cache["x_to_p", p] = x_to_p24912492prof("frob_Q")2493a = self.frob_Q(p) >> 2*p # frobQ * y^{-2p}24942495prof("sqrt")2496Q = self.base_ring()._Q24972498# three_halves = Q.parent().base_ring()(Rational((3,2)))2499# one_half = Q.parent().base_ring()(Rational((1,2)))2500three_halves = self.base_ring()._series_ring.base_ring()(Rational((3,2)))2501one_half = self.base_ring()._series_ring.base_ring()(Rational((1,2)))2502half_a = a._rmul_(one_half)25032504# We are solving for t = a^{-1/2} = (F_pQ y^{-p})^{-1/2}2505# Newton's method converges because we know the root is in the same residue class as 1.25062507# t = self.base_ring()(1)2508t = self.base_ring()(three_halves) - half_a # first iteration trivial, start with prec 225092510for cur_prec in newton_method_sizes(prec)[2:]: # newton_method_sizes = [1, 2, ...]2511y_prec = -(2*cur_prec-1)*p+1 # binomial expansion is $\sum p^{k+1} y^{-(2k+1)p+1} f(x)$2512# so if we are only correct mod p^prec, can ignore y powers less than y_prec2513t_cube = (t*t*t).truncate_neg(y_prec)2514t = t._rmul_(three_halves) - (half_a * t_cube).truncate_neg(y_prec)# t = (3/2) t - (1/2) a t^32515# print "a =", a2516# print "t =", t25172518# prof("verify")2519# print "a*t^2 =", a * t**225202521prof("compose")2522F_dx_y = (p * x_to_p_less_1 * t) >> (p-1) # px^{p-1} sqrt(a) * y^{-p+1}25232524# print "-----", F_dx_y2525# print "-----", x_to_p * F_dx_y2526prof("done")2527# print prof2528return MonskyWashnitzerDifferential(self, F_dx_y)25292530def frob_basis_elements(self, prec, p):2531"""2532Returns the action of a `p`-power lift of Frobenius on the basis25332534.. math::25352536\{ dx/2y, x dx/2y, ..., x^{d-2} dx/2y \}25372538where `d` is the degree of the underlying hyperelliptic curve.25392540EXAMPLES::25412542sage: R.<x> = QQ['x']2543sage: C = HyperellipticCurve(x^5-4*x+4)2544sage: prec = 12545sage: p = 52546sage: MW = C.invariant_differential().parent()2547sage: MW.frob_basis_elements(prec,p)2548[((92000*y^-14-74200*y^-12+32000*y^-10-8000*y^-8+1000*y^-6-50*y^-4)*1 + (-194400*y^-14+153600*y^-12-57600*y^-10+9600*y^-8-600*y^-6)*x + (204800*y^-14-153600*y^-12+38400*y^-10-3200*y^-8)*x^2 + (-153600*y^-14+76800*y^-12-9600*y^-10)*x^3 + (63950*y^-14-18550*y^-12+1600*y^-10-400*y^-8+50*y^-6+5*y^-4)*x^4) dx/2y, ((-1391200*y^-14+941400*y^-12-302000*y^-10+76800*y^-8-14400*y^-6+1320*y^-4-30*y^-2)*1 + (2168800*y^-14-1402400*y^-12+537600*y^-10-134400*y^-8+16800*y^-6-720*y^-4)*x + (-1596800*y^-14+1433600*y^-12-537600*y^-10+89600*y^-8-5600*y^-6)*x^2 + (1433600*y^-14-1075200*y^-12+268800*y^-10-22400*y^-8)*x^3 + (-870200*y^-14+445350*y^-12-63350*y^-10+3200*y^-8-600*y^-6+30*y^-4+5*y^-2)*x^4) dx/2y, ((19488000*y^-14-15763200*y^-12+4944400*y^-10-913800*y^-8+156800*y^-6-22560*y^-4+1480*y^-2-10)*1 + (-28163200*y^-14+18669600*y^-12-5774400*y^-10+1433600*y^-8-268800*y^-6+25440*y^-4-760*y^-2)*x + (15062400*y^-14-12940800*y^-12+5734400*y^-10-1433600*y^-8+179200*y^-6-8480*y^-4)*x^2 + (-12121600*y^-14+11468800*y^-12-4300800*y^-10+716800*y^-8-44800*y^-6)*x^3 + (9215200*y^-14-6952400*y^-12+1773950*y^-10-165750*y^-8+5600*y^-6-720*y^-4+10*y^-2+5)*x^4) dx/2y, ((-225395200*y^-14+230640000*y^-12-91733600*y^-10+18347400*y^-8-2293600*y^-6+280960*y^-4-31520*y^-2+1480+10*y^2)*1 + (338048000*y^-14-277132800*y^-12+89928000*y^-10-17816000*y^-8+3225600*y^-6-472320*y^-4+34560*y^-2-720)*x + (-172902400*y^-14+141504000*y^-12-58976000*y^-10+17203200*y^-8-3225600*y^-6+314880*y^-4-11520*y^-2)*x^2 + (108736000*y^-14-109760000*y^-12+51609600*y^-10-12902400*y^-8+1612800*y^-6-78720*y^-4)*x^3 + (-85347200*y^-14+82900000*y^-12-31251400*y^-10+5304150*y^-8-367350*y^-6+8480*y^-4-760*y^-2-10+5*y^2)*x^4) dx/2y]2549"""2550F_i = self.frob_invariant_differential(prec, p)2551x_to_p = self.x_to_p(p)2552F = [F_i]2553for i in range(1, self.degree()-1):2554F_i *= x_to_p2555F.append(F_i)2556return F25572558def helper_matrix(self):2559"""2560We use this to solve for the linear combination of2561`x^i y^j` needed to clear all terms with2562`y^{j-1}`.2563"""2564try:2565return self._helper_matrix2566except:2567AttributeError2568# The smallest y term of (1/j) d(x^i y^j) is constant for all j.2569L = []2570x, y = self.base_ring().gens()2571n = self.degree()2572for i in range(n):2573L.append( (y*x**i).diff().extract_pow_y(0) )2574A = matrix(L).transpose()2575if not is_IntegralDomain(A.base_ring()):2576# must be using integer_mod or something to approximate2577self._helper_matrix = (~A.change_ring(QQ)).change_ring(A.base_ring())2578else:2579self._helper_matrix = ~A2580return self._helper_matrix258125822583class MonskyWashnitzerDifferential(ModuleElement):25842585def __init__(self, parent, val=0, offset=0):2586r"""2587Create an element of the Monsky-Washnitzer ring of differentials, of2588the form `F dx/2y`.25892590INPUT:25912592- ``parent`` - Monsky-Washnitzer differential ring (instance of class2593:class:`~MonskyWashnitzerDifferentialRing_class`25942595- ``val`` - element of the base ring, or list of coefficients25962597- ``offset`` - if non-zero, shift val by `y^\text{offset}` (default 0)25982599EXAMPLES::26002601sage: R.<x> = QQ['x']2602sage: C = HyperellipticCurve(x^5 - 4*x + 4)2603sage: x,y = C.monsky_washnitzer_gens()2604sage: MW = C.invariant_differential().parent()2605sage: sage.schemes.elliptic_curves.monsky_washnitzer.MonskyWashnitzerDifferential(MW, x)2606x dx/2y2607sage: sage.schemes.elliptic_curves.monsky_washnitzer.MonskyWashnitzerDifferential(MW, y)2608y*1 dx/2y2609sage: sage.schemes.elliptic_curves.monsky_washnitzer.MonskyWashnitzerDifferential(MW, x, 10)2610y^10*x dx/2y2611"""2612ModuleElement.__init__(self, parent)2613if isinstance(val, MonskyWashnitzerDifferential):2614val = val._coeff2615self._coeff = self.parent().base_ring()(val, offset)26162617def _add_(left, right):2618"""2619Returns the sum of left and right, both elements of the2620Monsky-Washnitzer ring of differentials.26212622EXAMPLES::26232624sage: R.<x> = QQ['x']2625sage: C = HyperellipticCurve(x^5-4*x + 4)2626sage: x,y = C.monsky_washnitzer_gens()2627sage: w = C.invariant_differential()2628sage: w + w26292*1 dx/2y2630sage: x*w + w2631(1 + x) dx/2y2632sage: x*w + y*w2633(y*1 + x) dx/2y2634"""2635return MonskyWashnitzerDifferential(left.parent(),2636left._coeff + right._coeff)26372638def _sub_(left, right):2639"""2640Returns the difference of left and right, both elements of the2641Monsky-Washnitzer ring of differentials.26422643EXAMPLES::26442645sage: R.<x> = QQ['x']2646sage: C = HyperellipticCurve(x^5-4*x+4)2647sage: x,y = C.monsky_washnitzer_gens()2648sage: w = C.invariant_differential()2649sage: w-w26500 dx/2y2651sage: x*w-w2652((-1)*1 + x) dx/2y2653sage: w - x*w - y*w2654((1-y)*1 + (-1)*x) dx/2y2655"""2656return MonskyWashnitzerDifferential(left.parent(),2657left._coeff - right._coeff)26582659def __neg__(self):2660"""2661Returns the additive inverse of self.26622663EXAMPLES::26642665sage: R.<x> = QQ['x']2666sage: C = HyperellipticCurve(x^5-4*x+4)2667sage: x,y = C.monsky_washnitzer_gens()2668sage: w = C.invariant_differential()2669sage: -w2670((-1)*1) dx/2y2671sage: -((y-x)*w)2672((-y)*1 + x) dx/2y2673"""2674return MonskyWashnitzerDifferential(self.parent(), -self._coeff)26752676def _lmul_(self, a):2677"""2678Returns `self * a`.26792680EXAMPLES::26812682sage: R.<x> = QQ['x']2683sage: C = HyperellipticCurve(x^5-4*x+4)2684sage: x,y = C.monsky_washnitzer_gens()2685sage: w = C.invariant_differential()2686sage: w*x2687x dx/2y2688sage: (w*x)*226892*x dx/2y2690sage: w*y2691y*1 dx/2y2692sage: w*(x+y)2693(y*1 + x) dx/2y2694"""2695return MonskyWashnitzerDifferential(self.parent(), self._coeff * a)26962697def _rmul_(self, a):2698"""2699Returns `a * self`.27002701EXAMPLES::27022703sage: R.<x> = QQ['x']2704sage: C = HyperellipticCurve(x^5-4*x+4)2705sage: x,y = C.monsky_washnitzer_gens()2706sage: w = C.invariant_differential()2707sage: x*w2708x dx/2y2709sage: 2*(x*w)27102*x dx/2y2711sage: y*w2712y*1 dx/2y2713sage: (x+y)*w2714(y*1 + x) dx/2y2715"""2716return MonskyWashnitzerDifferential(self.parent(), a * self._coeff)27172718def coeff(self):2719r"""2720Returns `A`, where this element is `A dx/2y`.27212722EXAMPLES::27232724sage: R.<x> = QQ['x']2725sage: C = HyperellipticCurve(x^5-4*x+4)2726sage: x,y = C.monsky_washnitzer_gens()2727sage: w = C.invariant_differential()2728sage: w27291 dx/2y2730sage: w.coeff()273112732sage: (x*y*w).coeff()2733y*x2734"""2735return self._coeff27362737def __nonzero__(self):2738"""2739EXAMPLES::27402741sage: R.<x> = QQ['x']2742sage: C = HyperellipticCurve(x^5-4*x+4)2743sage: x,y = C.monsky_washnitzer_gens()2744sage: w = C.invariant_differential()2745sage: not w2746False2747sage: not 0*w2748True2749sage: not x*y*w2750False2751"""2752return not not self._coeff27532754def _repr_(self):2755"""2756EXAMPLES::27572758sage: R.<x> = QQ['x']2759sage: C = HyperellipticCurve(x^5-4*x+4)2760sage: x,y = C.monsky_washnitzer_gens()2761sage: w = C.invariant_differential()2762sage: w27631 dx/2y2764sage: (2*x+y)*w2765(y*1 + 2*x) dx/2y2766"""2767s = self._coeff._repr_()2768if s.find("+") != -1 or s.find("-") > 0:2769s = "(%s)"%s2770return s + " dx/2y"27712772def _latex_(self):2773"""2774Returns the latex representation of self.27752776EXAMPLES::27772778sage: R.<x> = QQ['x']2779sage: C = HyperellipticCurve(x^5-4*x+4)2780sage: x,y = C.monsky_washnitzer_gens()2781sage: w = C.invariant_differential()2782sage: latex(w)27831 \frac{dx}{2y}2784sage: latex(x*w)2785x \frac{dx}{2y}2786"""2787s = self._coeff._latex_()2788if s.find("+") != -1 or s.find("-") > 0:2789s = "\\left(%s\\right)"%s2790return s + " \\frac{dx}{2y}"27912792def extract_pow_y(self, k):2793"""2794Returns the power of `y` in `A` where self is `A dx/2y`.27952796EXAMPLES::27972798sage: R.<x> = QQ['x']2799sage: C = HyperellipticCurve(x^5-3*x+1)2800sage: x,y = C.monsky_washnitzer_gens()2801sage: A = y^5 - x*y^32802sage: A.extract_pow_y(5)2803[1, 0, 0, 0, 0]2804sage: (A * C.invariant_differential()).extract_pow_y(5)2805[1, 0, 0, 0, 0]2806"""2807return self._coeff.extract_pow_y(k)28082809def min_pow_y(self):2810"""2811Returns the minimum power of `y` in `A` where self is `A dx/2y`.28122813EXAMPLES::28142815sage: R.<x> = QQ['x']2816sage: C = HyperellipticCurve(x^5-3*x+1)2817sage: x,y = C.monsky_washnitzer_gens()2818sage: w = y^5 * C.invariant_differential()2819sage: w.min_pow_y()282052821sage: w = (x^2*y^4 + y^5) * C.invariant_differential()2822sage: w.min_pow_y()282342824"""2825return self._coeff.min_pow_y()28262827def max_pow_y(self):2828"""2829Returns the maximum power of `y` in `A` where self is `A dx/2y`.28302831EXAMPLES::28322833sage: R.<x> = QQ['x']2834sage: C = HyperellipticCurve(x^5-3*x+1)2835sage: x,y = C.monsky_washnitzer_gens()2836sage: w = y^5 * C.invariant_differential()2837sage: w.max_pow_y()283852839sage: w = (x^2*y^4 + y^5) * C.invariant_differential()2840sage: w.max_pow_y()284152842"""2843return self._coeff.max_pow_y()28442845def reduce_neg_y(self):2846"""2847Use homology relations to eliminate negative powers of `y`.28482849EXAMPLES::28502851sage: R.<x> = QQ['x']2852sage: C = HyperellipticCurve(x^5-3*x+1)2853sage: x,y = C.monsky_washnitzer_gens()2854sage: (y^-1).diff().reduce_neg_y()2855((y^-1)*1, 0 dx/2y)2856sage: (y^-5*x^2+y^-1*x).diff().reduce_neg_y()2857((y^-1)*x + (y^-5)*x^2, 0 dx/2y)2858"""2859S = self.parent().base_ring()2860R = S.base_ring()2861M = self.parent().helper_matrix()2862p = S._p2863n = S.degree()2864x, y = S.gens()2865f = S(0)2866reduced = self2867for j in range(self.min_pow_y()+1, 0):2868if p is not None and p.divides(j):2869cs = [a/j for a in reduced.extract_pow_y(j-1)]2870else:2871j_inverse = ~R(j)2872cs = [a*j_inverse for a in reduced.extract_pow_y(j-1)]2873lin_comb = M * vector(M.base_ring(), cs)2874# print "j =", j, "b =", cs, "lin_comb =", lin_comb2875g = self.parent().base_ring()(0)2876if not lin_comb.is_zero():2877for i in range(n):2878if lin_comb[i] != 0:2879g += S.monomial(i, j, lin_comb[i])2880if not g.is_zero():2881f += g2882reduced -= g.diff()2883# print g, g.diff()2884# print "reduced", reduced28852886return f, reduced28872888def reduce_neg_y_fast(self, even_degree_only=False):2889"""2890Use homology relations to eliminate negative powers of `y`.28912892EXAMPLES::28932894sage: R.<x> = QQ['x']2895sage: E = HyperellipticCurve(x^5-3*x+1)2896sage: x, y = E.monsky_washnitzer_gens()2897sage: (y^-1).diff().reduce_neg_y_fast()2898((y^-1)*1, 0 dx/2y)2899sage: (y^-5*x^2+y^-1*x).diff().reduce_neg_y_fast()2900((y^-1)*x + (y^-5)*x^2, 0 dx/2y)29012902It leaves non-negative powers of `y` alone::29032904sage: y.diff()2905((-3)*1 + 5*x^4) dx/2y2906sage: y.diff().reduce_neg_y_fast()2907(0, ((-3)*1 + 5*x^4) dx/2y)2908"""2909# prof = Profiler()2910# prof("reduce setup")2911S = self.parent().base_ring()2912R = S.base_ring()2913M = self.parent().helper_matrix()29142915# prof("extract coeffs")2916coeffs, offset = self.coeffs(R)2917V = coeffs[0].parent()29182919if offset == 0:2920return S(0), self29212922# prof("loop %s"%self.min_pow_y())2923forms = []2924p = S._p2925for j in range(self.min_pow_y()+1, 0):2926if (even_degree_only and j % 2 == 0) or coeffs[j-offset-1].is_zero():2927forms.append(V(0))2928else:2929# this is a total hack to deal with the fact that we're using2930# rational numbers to approximate fixed precision p-adics2931if p is not None and j % 3 == 1:2932try:2933v = coeffs[j-offset-1]2934for kk in range(len(v)):2935a = v[kk]2936ppow = p**max(-a.valuation(S._p), 0)2937v[kk] = ((a * ppow) % S._prec_cap) / ppow2938except AttributeError:2939pass2940lin_comb = ~R(j) * (M * coeffs[j-offset-1])2941forms.append(lin_comb)2942for i in lin_comb.nonzero_positions():2943# g = lin_comb[i] x^i y^j2944# self -= dg2945coeffs[j-offset+1] -= lin_comb[i] * S.monomial_diff_coeffs(i, j)[1]29462947# prof("recreate forms")2948f = S(forms, offset+1)2949reduced = S._monsky_washnitzer(coeffs[-1-offset:], -1)2950# print self - f.diff() - reduced2951# prof("done")2952# print prof2953return f, reduced29542955def reduce_neg_y_faster(self, even_degree_only=False):2956"""2957Use homology relations to eliminate negative powers of `y`.2958"""2959# Timings indicate this isn't any faster after all...29602961S = self.parent().base_ring()2962R = S.base_ring()2963M = self.parent().helper_matrix()29642965coeffs, offset = self.coeffs(R)2966V = coeffs[0].parent()2967zeroV = V(0)29682969if offset == 0:2970return S(0), self29712972# See monomial_diff_coeffs2973# this is the B_i and x_to_i contributions respectively for all i2974d_mat_1, d_mat_2 = S.monomial_diff_coeffs_matrices()29752976forms = []2977for j in range(self.min_pow_y()+1, 0):2978if coeffs[j-offset-1].is_zero():2979forms.append(zeroV)2980else:2981# this is a total hack to deal with the fact that we're using2982# rational numbers to approximate fixed precision p-adics2983if j % 3 == 0:2984try:2985v = coeffs[j-offset-1]2986for kk in range(len(v)):2987a = v[kk]2988ppow = S._p**max(-a.valuation(S._p), 0)2989v[kk] = ((a * ppow) % S._prec_cap) / ppow2990except AttributeError:2991pass2992j_inverse = ~R(j)2993lin_comb = (M * coeffs[j-offset-1])2994forms.append(j_inverse * lin_comb)2995coeffs[j-offset+1] -= (d_mat_1 + j_inverse * d_mat_2) * lin_comb29962997f = S(forms, offset+1)2998reduced = S._monsky_washnitzer(coeffs[-1-offset:], -1)2999# reduced = self - f.diff()3000return f, reduced300130023003def reduce_pos_y(self):3004"""3005Use homology relations to eliminate positive powers of `y`.30063007EXAMPLES::30083009sage: R.<x> = QQ['x']3010sage: C = HyperellipticCurve(x^3-4*x+4)3011sage: x,y = C.monsky_washnitzer_gens()3012sage: (y^2).diff().reduce_pos_y()3013(y^2*1, 0 dx/2y)3014sage: (y^2*x).diff().reduce_pos_y()3015(y^2*x, 0 dx/2y)3016sage: (y^92*x).diff().reduce_pos_y()3017(y^92*x, 0 dx/2y)3018sage: w = (y^3 + x).diff()3019sage: w += w.parent()(x)3020sage: w.reduce_pos_y_fast()3021(y^3*1 + x, x dx/2y)3022"""3023S = self.parent().base_ring()3024series = S.base_ring()3025n = S.Q().degree()3026p = S._p3027x, y = S.gens()3028f = S(0)3029reduced = self3030for j in range(self.max_pow_y(), 0, -1):3031for i in range(n-1, -1, -1):3032c = reduced.extract_pow_y(j)[i]3033# print "x^%s y^%s"%(i,j), c3034if c != 0:3035g = S.monomial(0, j+1) if i == n-1 else S.monomial(i+1, j-1)3036dg = g.diff()3037# print reduced, " - ", dg3038denom = dg.extract_pow_y(j)[i]3039c /= denom3040c = g.parent()(c)3041f += c * g3042reduced -= c * dg30433044return f, reduced304530463047def reduce_pos_y_fast(self, even_degree_only=False):3048"""3049Use homology relations to eliminate positive powers of `y`.30503051EXAMPLES::30523053sage: R.<x> = QQ['x']3054sage: E = HyperellipticCurve(x^3-4*x+4)3055sage: x, y = E.monsky_washnitzer_gens()3056sage: y.diff().reduce_pos_y_fast()3057(y*1, 0 dx/2y)3058sage: (y^2).diff().reduce_pos_y_fast()3059(y^2*1, 0 dx/2y)3060sage: (y^2*x).diff().reduce_pos_y_fast()3061(y^2*x, 0 dx/2y)3062sage: (y^92*x).diff().reduce_pos_y_fast()3063(y^92*x, 0 dx/2y)3064sage: w = (y^3 + x).diff()3065sage: w += w.parent()(x)3066sage: w.reduce_pos_y_fast()3067(y^3*1 + x, x dx/2y)3068"""3069S = self.parent().base_ring()3070R = S.base_ring()3071n = S.Q().degree()30723073coeffs, offset = self.coeffs(R)3074V = coeffs[0].parent()3075zeroV = V(0)3076forms = [V(0), V(0)]30773078for j in range(self.max_pow_y(), -1, -1):30793080if (even_degree_only and j % 2 == 1) or (j > 0 and coeffs[j-offset].is_zero()):3081forms.append(zeroV)3082continue30833084form = V(0)3085i = n-13086c = coeffs[j-offset][i]3087if c != 0:3088dg_coeffs = S.monomial_diff_coeffs(0, j+1)[0]3089c /= dg_coeffs[i]3090forms[len(forms)-2][0] = c3091# self -= c d(y^{j+1})3092coeffs[j-offset] -= c*dg_coeffs30933094if j == 0:3095# the others are basis elements3096break30973098for i in range(n-2, -1, -1):3099c = coeffs[j-offset][i]3100if c != 0:3101dg_coeffs = S.monomial_diff_coeffs(i+1, j-1)3102denom = dg_coeffs[1][i]3103c /= denom3104form[i+1] = c3105# self -= c d(x^{i+1} y^{j-1})3106coeffs[j-offset] -= c*dg_coeffs[1]3107coeffs[j-offset-2] -= c*dg_coeffs[0]3108forms.append(form)31093110forms.reverse()3111f = S(forms)3112reduced = self.parent()(coeffs[:1-offset], offset)3113return f, reduced31143115def reduce(self):3116"""3117Use homology relations to find `a` and `f` such that this element is3118equal to `a + df`, where `a` is given in terms of the `x^i dx/2y`.31193120EXAMPLES::31213122sage: R.<x> = QQ['x']3123sage: C = HyperellipticCurve(x^5-4*x+4)3124sage: x,y = C.monsky_washnitzer_gens()3125sage: w = (y*x).diff()3126sage: w.reduce()3127(y*x, 0 dx/2y)31283129sage: w = x^4 * C.invariant_differential()3130sage: w.reduce()3131(1/5*y*1, 4/5*1 dx/2y)31323133sage: w = sum(QQ.random_element() * x^i * y^j for i in [0..4] for j in [-3..3]) * C.invariant_differential()3134sage: f, a = w.reduce()3135sage: f.diff() + a - w31360 dx/2y3137"""3138# print "max_pow_y = ", self.max_pow_y(), "min_pow_y = ", self.min_pow_y()3139n = self.parent().base_ring().Q().degree()3140f1, a = self.reduce_neg_y()3141f2, a = a.reduce_pos_y()3142f = f1 + f231433144c = a.extract_pow_y(0)[n-1]3145if c != 0:3146x, y = self.parent().base_ring().gens()3147g = y3148dg = g.diff()3149c = g.parent()(c/dg.extract_pow_y(0)[n-1])3150f += c * g3151a -= c * dg3152# print g, dg31533154return f, a31553156def reduce_fast(self, even_degree_only=False):3157"""3158Use homology relations to find `a` and `f` such that this element is3159equal to `a + df`, where `a` is given in terms of the `x^i dx/2y`.31603161EXAMPLES::31623163sage: R.<x> = QQ['x']3164sage: E = HyperellipticCurve(x^3-4*x+4)3165sage: x, y = E.monsky_washnitzer_gens()3166sage: x.diff().reduce_fast()3167(x, (0, 0))3168sage: y.diff().reduce_fast()3169(y*1, (0, 0))3170sage: (y^-1).diff().reduce_fast()3171((y^-1)*1, (0, 0))3172sage: (y^-11).diff().reduce_fast()3173((y^-11)*1, (0, 0))3174sage: (x*y^2).diff().reduce_fast()3175(y^2*x, (0, 0))3176"""3177# print "max_pow_y = ", self.max_pow_y(), "min_pow_y = ", self.min_pow_y()31783179f1, reduced = self.reduce_neg_y_fast(even_degree_only)3180f2, reduced = reduced.reduce_pos_y_fast(even_degree_only)3181# f1, reduced = self.reduce_neg_y()3182# f2, reduced = reduced.reduce_pos_y()3183v = reduced.extract_pow_y(0)3184v.pop()3185V = FreeModule(self.base_ring().base_ring(), len(v))3186return f1+f2, V(v)31873188def coeffs(self, R=None):3189"""3190Used to obtain the raw coefficients of a differential, see3191:meth:`SpecialHyperellipticQuotientElement.coeffs`31923193INPUT:31943195- R - An (optional) base ring in which to cast the coefficients31963197OUTPUT:31983199The raw coefficients of $A$ where self is $A dx/2y$.32003201EXAMPLES::32023203sage: R.<x> = QQ['x']3204sage: C = HyperellipticCurve(x^5-4*x+4)3205sage: x,y = C.monsky_washnitzer_gens()3206sage: w = C.invariant_differential()3207sage: w.coeffs()3208([(1, 0, 0, 0, 0)], 0)3209sage: (x*w).coeffs()3210([(0, 1, 0, 0, 0)], 0)3211sage: (y*w).coeffs()3212([(0, 0, 0, 0, 0), (1, 0, 0, 0, 0)], 0)3213sage: (y^-2*w).coeffs()3214([(1, 0, 0, 0, 0), (0, 0, 0, 0, 0), (0, 0, 0, 0, 0)], -2)3215"""3216return self._coeff.coeffs(R)32173218def coleman_integral(self, P, Q):3219r"""3220Computes the definite integral of self from $P$ to $Q$.32213222INPUT:32233224- P, Q - Two points on the underlying curve32253226OUTPUT:32273228`\int_P^Q \text{self}`32293230EXAMPLES::32313232sage: K = pAdicField(5,7)3233sage: E = EllipticCurve(K,[-31/3,-2501/108]) #11a3234sage: P = E(K(14/3), K(11/2))3235sage: w = E.invariant_differential()3236sage: w.coleman_integral(P,2*P)3237O(5^6)32383239sage: Q = E.lift_x(3)3240sage: w.coleman_integral(P,Q)32412*5 + 4*5^2 + 3*5^3 + 4*5^4 + 3*5^5 + O(5^6)3242sage: w.coleman_integral(2*P,Q)32432*5 + 4*5^2 + 3*5^3 + 4*5^4 + 3*5^5 + O(5^6)3244sage: (2*w).coleman_integral(P, Q) == 2*(w.coleman_integral(P, Q))3245True3246"""32473248return self.parent().base_ring().curve().coleman_integral(self, P, Q)32493250integrate = coleman_integral32513252### end of file325332543255