Path: blob/master/src/sage/schemes/projective/projective_morphism.py
8820 views
r"""1Morphisms on projective varieties23A morphism of schemes determined by rational functions that define4what the morphism does on points in the ambient projective space.567AUTHORS:89- David Kohel, William Stein1011- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as12a projective point.1314- Volker Braun (2011-08-08): Renamed classes, more documentation, misc15cleanups.1617- Ben Hutz (2013-03) iteration functionality and new directory structure18for affine/projective, height functionality1920- Brian Stout, Ben Hutz (Nov 2013) - added minimal model functionality2122"""2324# Historical note: in trac #11599, V.B. renamed25# * _point_morphism_class -> _morphism26# * _homset_class -> _point_homset2728#*****************************************************************************29# Copyright (C) 2011 Volker Braun <[email protected]>30# Copyright (C) 2006 David Kohel <[email protected]>31# Copyright (C) 2006 William Stein <[email protected]>32#33# Distributed under the terms of the GNU General Public License (GPL)34# as published by the Free Software Foundation; either version 2 of35# the License, or (at your option) any later version.36# http://www.gnu.org/licenses/37#*****************************************************************************3839from sage.categories.homset import Hom40from sage.functions.all import sqrt41from sage.matrix.constructor import matrix, identity_matrix42from sage.misc.cachefunc import cached_method43from sage.misc.misc import subsets44from sage.misc.mrange import xmrange45from sage.modules.free_module_element import vector46from sage.rings.all import Integer, moebius47from sage.rings.arith import gcd, lcm, next_prime, binomial, primes48from sage.rings.complex_field import ComplexField49from sage.rings.finite_rings.constructor import GF, is_PrimeFiniteField50from sage.rings.finite_rings.integer_mod_ring import Zmod51from sage.rings.fraction_field import FractionField52from sage.rings.integer_ring import ZZ53from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing54from sage.rings.quotient_ring import QuotientRing_generic55from sage.rings.rational_field import QQ56from sage.rings.real_mpfr import RealField57from sage.schemes.generic.morphism import SchemeMorphism_polynomial58from sage.symbolic.constants import e59from copy import copy606162class SchemeMorphism_polynomial_projective_space(SchemeMorphism_polynomial):63"""64A morphism of schemes determined by rational functions that define65what the morphism does on points in the ambient projective space.6667EXAMPLES::6869sage: R.<x,y> = QQ[]70sage: P1 = ProjectiveSpace(R)71sage: H = P1.Hom(P1)72sage: H([y,2*x])73Scheme endomorphism of Projective Space of dimension 1 over Rational Field74Defn: Defined on coordinates by sending (x : y) to75(y : 2*x)7677An example of a morphism between projective plane curves (see :trac:`10297`)::7879sage: P2.<x,y,z> = ProjectiveSpace(QQ,2)80sage: f = x^3+y^3+60*z^381sage: g = y^2*z-( x^3 - 6400*z^3/3)82sage: C = Curve(f)83sage: E = Curve(g)84sage: xbar,ybar,zbar = C.coordinate_ring().gens()85sage: H = C.Hom(E)86sage: H([zbar,xbar-ybar,-(xbar+ybar)/80])87Scheme morphism:88From: Projective Curve over Rational Field defined by x^3 + y^3 + 60*z^389To: Projective Curve over Rational Field defined by -x^3 + y^2*z + 6400/3*z^390Defn: Defined on coordinates by sending (x : y : z) to91(z : x - y : -1/80*x - 1/80*y)9293A more complicated example::9495sage: P2.<x,y,z> = ProjectiveSpace(2,QQ)96sage: P1 = P2.subscheme(x-y)97sage: H12 = P1.Hom(P2)98sage: H12([x^2,x*z, z^2])99Scheme morphism:100From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:101x - y102To: Projective Space of dimension 2 over Rational Field103Defn: Defined on coordinates by sending (x : y : z) to104(y^2 : y*z : z^2)105106We illustrate some error checking::107108sage: R.<x,y> = QQ[]109sage: P1 = ProjectiveSpace(R)110sage: H = P1.Hom(P1)111sage: f = H([x-y, x*y])112Traceback (most recent call last):113...114ValueError: polys (=[x - y, x*y]) must be of the same degree115116sage: H([x-1, x*y+x])117Traceback (most recent call last):118...119ValueError: polys (=[x - 1, x*y + x]) must be homogeneous120121sage: H([exp(x),exp(y)])122Traceback (most recent call last):123...124TypeError: polys (=[e^x, e^y]) must be elements of125Multivariate Polynomial Ring in x, y over Rational Field126"""127128def __init__(self, parent, polys, check=True):129"""130The Python constructor.131132See :class:`SchemeMorphism_polynomial` for details.133134EXAMPLES::135136sage: P1.<x,y> = ProjectiveSpace(QQ,1)137sage: H = P1.Hom(P1)138sage: H([y,2*x])139Scheme endomorphism of Projective Space of dimension 1 over Rational Field140Defn: Defined on coordinates by sending (x : y) to141(y : 2*x)142"""143SchemeMorphism_polynomial.__init__(self, parent, polys, check)144if check:145# morphisms from projective space are always given by146# homogeneous polynomials of the same degree147try:148d = polys[0].degree()149except AttributeError:150polys = [f.lift() for f in polys]151if not all([f.is_homogeneous() for f in polys]):152raise ValueError("polys (=%s) must be homogeneous"%polys)153degs = [f.degree() for f in polys]154if not all([d == degs[0] for d in degs[1:]]):155raise ValueError("polys (=%s) must be of the same degree"%polys)156157def __eq__(self, right):158"""159Tests the equality of two projective spaces.160161INPUT:162163- ``right`` - a map on projective space164165OUTPUT:166167- Boolean - True if ``self`` and ``right`` define the same projective map. False otherwise.168169EXAMPLES::170171sage: P1.<x,y> = ProjectiveSpace(RR,1)172sage: P2.<x,y> = ProjectiveSpace(QQ,1)173sage: P1==P2174False175176::177178sage: R.<x,y> = QQ[]179sage: P1 = ProjectiveSpace(R)180sage: P2.<x,y> = ProjectiveSpace(QQ,1)181sage: P1==P2182True183"""184if not isinstance(right, SchemeMorphism_polynomial):185return False186else:187n = len(self._polys)188for i in range(0,n):189for j in range(i+1,n):190if self._polys[i]*right._polys[j] != self._polys[j]*right._polys[i]:191return False192return True193194def __ne__(self, right):195"""196Tests the inequality of two projective spaces.197198INPUT:199200- ``right`` -- a map on projective space201202OUTPUT:203204- Boolean -- True if ``self`` and ``right`` define different projective maps. False otherwise.205206EXAMPLES::207208sage: P1.<x,y> = ProjectiveSpace(RR,1)209sage: P2.<x,y> = ProjectiveSpace(QQ,1)210sage: P1!=P2211True212213::214215sage: R.<x,y> = QQ[]216sage: P1 = ProjectiveSpace(R)217sage: P2.<x,y> = ProjectiveSpace(QQ,1)218sage: P1!=P2219False220"""221if not isinstance(right, SchemeMorphism_polynomial):222return True223else:224n=len(self._polys)225for i in range(0,n):226for j in range(i+1,n):227if self._polys[i]*right._polys[j] != self._polys[j]*right._polys[i]:228return True229return False230231def scale_by(self, t):232"""233Scales each coordinates by a factor of `t`.234235A ``TypeError`` occurs if the point is not in the coordinate_ring236of the parent after scaling.237238INPUT:239240- ``t`` -- a ring element241242OUTPUT:243244- None.245246EXAMPLES::247248sage: A.<x,y> = ProjectiveSpace(QQ,1)249sage: H = Hom(A,A)250sage: f = H([x^3-2*x*y^2,x^2*y])251sage: f.scale_by(1/x)252sage: f253Scheme endomorphism of Projective Space of dimension 1 over Rational254Field255Defn: Defined on coordinates by sending (x : y) to256(x^2 - 2*y^2 : x*y)257258::259260sage: R.<t> = PolynomialRing(QQ)261sage: P.<x,y> = ProjectiveSpace(R,1)262sage: H = Hom(P,P)263sage: f = H([3/5*x^2,6*y^2])264sage: f.scale_by(5/3*t); f265Scheme endomorphism of Projective Space of dimension 1 over Univariate266Polynomial Ring in t over Rational Field267Defn: Defined on coordinates by sending (x : y) to268(t*x^2 : 10*t*y^2)269270::271272sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)273sage: X = P.subscheme(x^2-y^2)274sage: H = Hom(X,X)275sage: f = H([x^2,y^2,z^2])276sage: f.scale_by(x-y);f277Scheme endomorphism of Closed subscheme of Projective Space of dimension2782 over Finite Field of size 7 defined by:279x^2 - y^2280Defn: Defined on coordinates by sending (x : y : z) to281(x*y^2 - y^3 : x*y^2 - y^3 : x*z^2 - y*z^2)282"""283if t == 0:284raise ValueError("Cannot scale by 0")285R=self.domain().coordinate_ring()286if isinstance(R, QuotientRing_generic):287phi=R.coerce_map_from(self.domain().ambient_space().coordinate_ring())288for i in range(self.codomain().ambient_space().dimension_relative()+1):289self._polys[i]=phi(self._polys[i]*t).lift()290else:291for i in range(self.codomain().ambient_space().dimension_relative()+1):292self._polys[i]=R(self._polys[i]*t)293294def normalize_coordinates(self):295"""296Scales by 1/gcd of the coordinate functions. Also, scales to clear any denominators from the coefficients.297This is done in place.298299OUTPUT:300301- None.302303EXAMPLES::304305sage: P.<x,y> = ProjectiveSpace(QQ,1)306sage: H = Hom(P,P)307sage: f = H([5/4*x^3,5*x*y^2])308sage: f.normalize_coordinates(); f309Scheme endomorphism of Projective Space of dimension 1 over Rational310Field311Defn: Defined on coordinates by sending (x : y) to312(x^2 : 4*y^2)313314::315316sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)317sage: X = P.subscheme(x^2-y^2)318sage: H = Hom(X,X)319sage: f = H([x^3+x*y^2,x*y^2,x*z^2])320sage: f.normalize_coordinates(); f321Scheme endomorphism of Closed subscheme of Projective Space of dimension3222 over Finite Field of size 7 defined by:323x^2 - y^2324Defn: Defined on coordinates by sending (x : y : z) to325(2*y^2 : y^2 : z^2)326327.. NOTE:: gcd raises an error if the base_ring does not support gcds.328"""329GCD = gcd(self[0],self[1])330index=2331if self[0].lc()>0 or self[1].lc() >0:332neg=0333else:334neg=1335N=self.codomain().ambient_space().dimension_relative()+1336while GCD!=1 and index < N:337if self[index].lc()>0:338neg=0339GCD=gcd(GCD,self[index])340index+=+1341342if GCD != 1:343R=self.domain().base_ring()344if neg == 1:345self.scale_by(R(-1)/GCD)346else:347self.scale_by(R(1)/GCD)348else:349if neg == 1:350self.scale_by(-1)351352#clears any denominators from the coefficients353LCM = lcm([self[i].denominator() for i in range(N)])354self.scale_by(LCM)355356#scales by 1/gcd of the coefficients.357GCD = gcd([self[i].content() for i in range(N)])358if GCD!=1:359self.scale_by(1/GCD)360361def dynatomic_polynomial(self, period):362r"""363For a map `f:\mathbb{P}^1 \to \mathbb{P}^1` this function computes the dynatomic polynomial.364365The dynatomic polynomial is the analog of the cyclotomic366polynomial and its roots are the points of formal period `n`.367368ALGORITHM:369370For a positive integer `n`, let `[F_n,G_n]` be the coordinates of the `nth` iterate of `f`.371Then construct372373.. MATH::374375\Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)}376377where `\mu` is the Moebius function.378379For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute380381.. MATH::382383\Phi^{\ast}_{m,n}(f)(x,y) = \Phi^{\ast}_n(f)(F_m,G_m)/\Phi^{\ast}_n(f)(F_{m-1},G_{m-1})384385REFERENCES:386387.. [Hutz] B. Hutz. Efficient determination of rational preperiodic388points for endomorphisms of projective space.389:arxiv:`1210.6246`, 2012.390391.. [MoPa] P. Morton and P. Patel. The Galois theory of periodic points392of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263.393394INPUT:395396- ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period397398OUTPUT:399400- If possible, a two variable polynomial in the coordinate ring of ``self``.401Otherwise a fraction field element of the coordinate ring of ``self``402403.. TODO::404405Do the division when the base ring is p-adic or a function field406so that the output is a polynomial.407408EXAMPLES::409410sage: P.<x,y> = ProjectiveSpace(QQ,1)411sage: H = Hom(P,P)412sage: f = H([x^2+y^2,y^2])413sage: f.dynatomic_polynomial(2)414x^2 + x*y + 2*y^2415416::417418sage: P.<x,y> = ProjectiveSpace(ZZ,1)419sage: H = Hom(P,P)420sage: f = H([x^2+y^2,x*y])421sage: f.dynatomic_polynomial(4)4222*x^12 + 18*x^10*y^2 + 57*x^8*y^4 + 79*x^6*y^6 + 48*x^4*y^8 + 12*x^2*y^10 + y^12423424::425426sage: P.<x,y> = ProjectiveSpace(CC,1)427sage: H = Hom(P,P)428sage: f = H([x^2+y^2,3*x*y])429sage: f.dynatomic_polynomial(3)43013.0000000000000*x^6 + 117.000000000000*x^4*y^2 +43178.0000000000000*x^2*y^4 + y^6432433::434435sage: P.<x,y> = ProjectiveSpace(QQ,1)436sage: H = Hom(P,P)437sage: f = H([x^2-10/9*y^2,y^2])438sage: f.dynatomic_polynomial([2,1])439x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6440441::442443sage: P.<x,y> = ProjectiveSpace(QQ,1)444sage: H = Hom(P,P)445sage: f = H([x^2-29/16*y^2,y^2])446sage: f.dynatomic_polynomial([2,3])447x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 +4488198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 +449172692729/16777216*y^12450451::452453sage: P.<x,y> = ProjectiveSpace(ZZ,1)454sage: H = Hom(P,P)455sage: f = H([x^2-y^2,y^2])456sage: f.dynatomic_polynomial([1,2])457x^2 - x*y458459::460461sage: P.<x,y> = ProjectiveSpace(QQ,1)462sage: H = Hom(P,P)463sage: f = H([x^3-y^3,3*x*y^2])464sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4)465True466467::468469sage: P.<x,y,z> = ProjectiveSpace(QQ,2)470sage: H = Hom(P,P)471sage: f = H([x^2+y^2,x*y,z^2])472sage: f.dynatomic_polynomial(2)473Traceback (most recent call last):474...475TypeError: Does not make sense in dimension >1476477::478479sage: P.<x,y> = ProjectiveSpace(Qp(5),1)480sage: H = Hom(P,P)481sage: f = H([x^2+y^2,y^2])482sage: f.dynatomic_polynomial(2)483(x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y -484x*y^2 + y^3)485486.. TODO:: It would be nice to get this to actually be a polynomial.487488::489490sage: L.<t> = PolynomialRing(QQ)491sage: P.<x,y> = ProjectiveSpace(L,1)492sage: H = Hom(P,P)493sage: f = H([x^2+t*y^2,y^2])494sage: f.dynatomic_polynomial(2)495x^2 + x*y + (t + 1)*y^2496497::498499sage: K.<c> = PolynomialRing(ZZ)500sage: P.<x,y> = ProjectiveSpace(K,1)501sage: H = Hom(P,P)502sage: f = H([x^2+ c*y^2,y^2])503sage: f.dynatomic_polynomial([1,2])504x^2 - x*y + (c + 1)*y^2505"""506if self.domain() != self.codomain():507raise TypeError("Must have same domain and codomain to iterate")508from sage.schemes.projective.projective_space import is_ProjectiveSpace509if is_ProjectiveSpace(self.domain()) is False:510raise NotImplementedError("Not implemented for subschemes")511if self.domain().dimension_relative()>1:512raise TypeError("Does not make sense in dimension >1")513514if (isinstance(period,(list,tuple)) is False):515period=[0,period]516try:517period[0]=ZZ(period[0])518period[1]=ZZ(period[1])519except TypeError:520raise TypeError("Period and preperiod must be integers")521if period[1]<=0:522raise AttributeError("Period must be at least 1")523524if period[0]!=0:525m=period[0]526fm=self.nth_iterate_map(m)527fm1=self.nth_iterate_map(m-1)528n=period[1]529PHI=1;530x=self.domain().gen(0)531y=self.domain().gen(1)532F=self._polys533f=F534for d in range(1,n+1):535if n%d == 0:536PHI=PHI*((y*F[0]-x*F[1])**moebius(n/d))537if d !=n: #avoid extra iteration538F=[f[0](F[0],F[1]),f[1](F[0],F[1])]539if m!=0:540PHI=PHI(fm._polys)/PHI(fm1._polys)541else:542PHI=1;543x=self.domain().gen(0)544y=self.domain().gen(1)545F=self._polys546f=F547for d in range(1,period[1]+1):548if period[1]%d == 0:549PHI=PHI*((y*F[0]-x*F[1])**moebius(period[1]/d))550if d !=period[1]: #avoid extra iteration551F=[f[0](F[0],F[1]),f[1](F[0],F[1])]552from sage.rings.polynomial.polynomial_ring import PolynomialRing_general553from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic554if (self.domain().base_ring() == RealField()555or self.domain().base_ring() == ComplexField()):556PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()557elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)):558from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing559from sage.rings.function_field.function_field import is_FunctionField560BR=self.domain().base_ring().base_ring()561if is_pAdicField(BR) or is_pAdicRing(BR) or is_FunctionField(BR):562raise NotImplementedError("Not implemented")563PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()564#do it again to divide out by denominators of coefficients565PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()566if PHI.denominator() == 1:567PHI=self.coordinate_ring()(PHI)568return(PHI)569570def nth_iterate_map(self, n):571r"""572For a map ``self`` this function returns the nth iterate of ``self`` as a573function on ``self.domain()``574575ALGORITHM:576577Uses a form of successive squaring to reducing computations.578579580.. TODO:: This could be improved.581582INPUT:583584- ``n`` -- a positive integer.585586OUTPUT:587588- A map between projective spaces589590EXAMPLES::591592sage: P.<x,y> = ProjectiveSpace(QQ,1)593sage: H = Hom(P,P)594sage: f = H([x^2+y^2,y^2])595sage: f.nth_iterate_map(2)596Scheme endomorphism of Projective Space of dimension 1 over Rational597Field598Defn: Defined on coordinates by sending (x : y) to599(x^4 + 2*x^2*y^2 + 2*y^4 : y^4)600601::602603sage: P.<x,y> = ProjectiveSpace(CC,1)604sage: H = Hom(P,P)605sage: f = H([x^2-y^2,x*y])606sage: f.nth_iterate_map(3)607Scheme endomorphism of Projective Space of dimension 1 over Complex608Field with 53 bits of precision609Defn: Defined on coordinates by sending (x : y) to610(x^8 + (-7.00000000000000)*x^6*y^2 + 13.0000000000000*x^4*y^4 +611(-7.00000000000000)*x^2*y^6 + y^8 : x^7*y + (-4.00000000000000)*x^5*y^3612+ 4.00000000000000*x^3*y^5 - x*y^7)613614::615616sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)617sage: H = Hom(P,P)618sage: f = H([x^2-y^2,x*y,z^2+x^2])619sage: f.nth_iterate_map(2)620Scheme endomorphism of Projective Space of dimension 2 over Integer Ring621Defn: Defined on coordinates by sending (x : y : z) to622(x^4 - 3*x^2*y^2 + y^4 : x^3*y - x*y^3 : 2*x^4 - 2*x^2*y^2 + y^4623+ 2*x^2*z^2 + z^4)624625::626627sage: P.<x,y,z> = ProjectiveSpace(QQ,2)628sage: X = P.subscheme(x*z-y^2)629sage: H = Hom(X,X)630sage: f = H([x^2,x*z,z^2])631sage: f.nth_iterate_map(2)632Scheme endomorphism of Closed subscheme of Projective Space of dimension6332 over Rational Field defined by:634-y^2 + x*z635Defn: Defined on coordinates by sending (x : y : z) to636(x^4 : x^2*z^2 : z^4)637"""638if self.domain() != self.codomain():639raise TypeError("Domain and Codomain of function not equal")640if n <0:641raise TypeError("Iterate number must be a positive integer")642N=self.codomain().ambient_space().dimension_relative()+1643F=list(self._polys)644D=Integer(n).digits(2) #need base 2645Coord_ring=self.codomain().coordinate_ring()646if isinstance(Coord_ring, QuotientRing_generic):647PHI=[Coord_ring.gen(i).lift() for i in range(N)]648else:649PHI=[Coord_ring.gen(i) for i in range(N)]650for i in range(len(D)):651T=tuple([F[j] for j in range(N)])652for k in range(D[i]):653PHI=[PHI[j](T) for j in range(N)]654if i!=len(D)-1: #avoid extra iterate655F=[F[j](T) for j in range(N)] #'square'656H=Hom(self.domain(),self.codomain())657return(H(PHI))658659def nth_iterate(self, P, n, normalize=False):660r"""661For a map ``self`` and a point `P` in ``self.domain()``662this function returns the nth iterate of `P` by ``self``.663664If ``normalize`` is ``True``, then the coordinates are665automatically normalized.666667.. TODO:: Is there a more efficient way to do this?668669INPUT:670671- ``P`` -- a point in ``self.domain()``672673- ``n`` -- a positive integer.674675- ``normalize`` - Boolean (optional Default: ``False``)676677OUTPUT:678679- A point in ``self.codomain()``680681EXAMPLES::682683sage: P.<x,y> = ProjectiveSpace(ZZ,1)684sage: H = Hom(P,P)685sage: f = H([x^2+y^2,2*y^2])686sage: Q = P(1,1)687sage: f.nth_iterate(Q,4)688(32768 : 32768)689690::691692sage: P.<x,y> = ProjectiveSpace(ZZ,1)693sage: H = Hom(P,P)694sage: f = H([x^2+y^2,2*y^2])695sage: Q = P(1,1)696sage: f.nth_iterate(Q,4,1)697(1 : 1)698699Is this the right behavior? ::700701sage: P.<x,y,z> = ProjectiveSpace(QQ,2)702sage: H = Hom(P,P)703sage: f = H([x^2,2*y^2,z^2-x^2])704sage: Q = P(2,7,1)705sage: f.nth_iterate(Q,2)706(-16/7 : -2744 : 1)707708::709710sage: R.<t> = PolynomialRing(QQ)711sage: P.<x,y,z> = ProjectiveSpace(R,2)712sage: H = Hom(P,P)713sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2])714sage: Q = P(2+t,7,t)715sage: f.nth_iterate(Q,2)716(t^4 + 2507*t^3 - 6787*t^2 + 10028*t + 16 : -2401*t^3 + 14406*t^2 -71728812*t + 19208 : t^4)718719::720721sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)722sage: X = P.subscheme(x^2-y^2)723sage: H = Hom(X,X)724sage: f = H([x^2,y^2,z^2])725sage: f.nth_iterate(X(2,2,3),3)726(256 : 256 : 6561)727728::729730sage: K.<c> = FunctionField(QQ)731sage: P.<x,y> = ProjectiveSpace(K,1)732sage: H = Hom(P,P)733sage: f = H([x^3-2*x*y^2 - c*y^3,x*y^2])734sage: f.nth_iterate(P(c,1),2)735((c^6 - 9*c^4 + 25*c^2 - c - 21)/(c^2 - 3) : 1)736"""737return(P.nth_iterate(self,n,normalize))738739def degree(self):740r"""741This function returns the degree of ``self``.742743The degree is defined as the degree of the homogeneous744polynomials that are the coordinates of ``self``.745746OUTPUT:747748- A positive integer749750EXAMPLES::751752sage: P.<x,y> = ProjectiveSpace(QQ,1)753sage: H = Hom(P,P)754sage: f = H([x^2+y^2,y^2])755sage: f.degree()7562757758::759760sage: P.<x,y,z> = ProjectiveSpace(CC,2)761sage: H = Hom(P,P)762sage: f = H([x^3+y^3,y^2*z,z*x*y])763sage: f.degree()7643765766::767768sage: R.<t> = PolynomialRing(QQ)769sage: P.<x,y,z> = ProjectiveSpace(R,2)770sage: H = Hom(P,P)771sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2])772sage: f.degree()7732774775::776777sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)778sage: X = P.subscheme(x^2-y^2)779sage: H = Hom(X,X)780sage: f = H([x^2,y^2,z^2])781sage: f.degree()7822783"""784return(self._polys[0].degree())785786def dehomogenize(self, n):787r"""788Returns the standard dehomogenization at the nth coordinate `(\frac{self[0]}{self[n]},\frac{self[1]}{self[n]},...)`.789790Note that the new function is defined over the fraction field791of the base ring of ``self``.792793INPUT:794795- ``n`` -- a nonnegative integer796797OUTPUT:798799- :class:`SchemeMorphism_polynomial_affine_space` (on nth affine patch)800801EXAMPLES::802803sage: P.<x,y> = ProjectiveSpace(ZZ,1)804sage: H = Hom(P,P)805sage: f = H([x^2+y^2,y^2])806sage: f.dehomogenize(0)807Scheme endomorphism of Affine Space of dimension 1 over Integer Ring808Defn: Defined on coordinates by sending (x) to809(x^2/(x^2 + 1))810811::812813sage: P.<x,y,z> = ProjectiveSpace(QQ,2)814sage: H = Hom(P,P)815sage: f = H([x^2+y^2,y^2-z^2,2*z^2])816sage: f.dehomogenize(2)817Scheme endomorphism of Affine Space of dimension 2 over Rational Field818Defn: Defined on coordinates by sending (x0, x1) to819(1/2*x0^2 + 1/2*x1^2, 1/2*x1^2 - 1/2)820821::822823sage: R.<t> = PolynomialRing(QQ)824sage: P.<x,y,z> = ProjectiveSpace(FractionField(R),2)825sage: H = Hom(P,P)826sage: f = H([x^2+t*y^2,t*y^2-z^2,t*z^2])827sage: f.dehomogenize(2)828Scheme endomorphism of Affine Space of dimension 2 over Fraction Field829of Univariate Polynomial Ring in t over Rational Field830Defn: Defined on coordinates by sending (x0, x1) to831(1/t*x0^2 + x1^2, x1^2 - 1/t)832833::834835sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)836sage: X = P.subscheme(x^2-y^2)837sage: H = Hom(X,X)838sage: f = H([x^2,y^2,x*z])839sage: f.dehomogenize(2)840Scheme endomorphism of Closed subscheme of Affine Space of dimension 2841over Integer Ring defined by:842x0^2 - x1^2843Defn: Defined on coordinates by sending (x0, x1) to844(x1^2/x0, x1^2/x0)845"""846PS=self.domain()847A=PS.ambient_space()848if self._polys[n].substitute({A.gen(n):1}) == 0:849raise ValueError("Can't dehomogenize at 0 coordinate.")850else:851Aff=PS.affine_patch(n)852S=Aff.ambient_space().coordinate_ring()853R=A.coordinate_ring()854phi=R.hom([S.gen(j) for j in range(0,n)] + [1] + [S.gen(j) for j in range(n,A.dimension_relative())],S)855F=[]856for i in range(0,A.dimension_relative()+1):857if i !=n:858F.append(phi(self._polys[i])/phi(self._polys[n]))859H=Hom(Aff,Aff)860return(H(F))861862def orbit(self, P, N, **kwds):863r"""864Returns the orbit of `P` by ``self``. If `n` is an integer it returns `[P,self(P),\ldots,self^n(P)]`.865If `n` is a list or tuple `n=[m,k]` it returns `[self^m(P),\ldots,self^k(P)]`.866Automatically normalize the points if ``normalize=True``. Perform the checks on point initialize if867``check=True``868869INPUT:870871- ``P`` -- a point in ``self.domain()``872873- ``n`` -- a non-negative integer or list or tuple of two non-negative integers874875kwds:876877- ``check`` -- boolean (optional - default: ``True``)878879- ``normalize`` -- boolean (optional - default: ``False``)880881882OUTPUT:883884- a list of points in ``self.codomain()``885886EXAMPLES::887888sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)889sage: H = Hom(P,P)890sage: f = H([x^2+y^2,y^2-z^2,2*z^2])891sage: f.orbit(P(1,2,1),3)892[(1 : 2 : 1), (5 : 3 : 2), (34 : 5 : 8), (1181 : -39 : 128)]893894::895896sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)897sage: H = Hom(P,P)898sage: f = H([x^2+y^2,y^2-z^2,2*z^2])899sage: f.orbit(P(1,2,1),[2,4])900[(34 : 5 : 8), (1181 : -39 : 128), (1396282 : -14863 : 32768)]901902::903904sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)905sage: X = P.subscheme(x^2-y^2)906sage: H = Hom(X,X)907sage: f = H([x^2,y^2,x*z])908sage: f.orbit(X(2,2,3),3,normalize=True)909[(2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3)]910911::912913sage: P.<x,y> = ProjectiveSpace(QQ,1)914sage: H = Hom(P,P)915sage: f = H([x^2+y^2,y^2])916sage: f.orbit(P.point([1,2],False),4,check=False)917[(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)]918919::920921sage: K.<c> = FunctionField(QQ)922sage: P.<x,y> = ProjectiveSpace(K,1)923sage: H = Hom(P,P)924sage: f = H([x^2+c*y^2,y^2])925sage: f.orbit(P(0,1),3)926[(0 : 1), (c : 1), (c^2 + c : 1), (c^4 + 2*c^3 + c^2 + c : 1)]927"""928return(P.orbit(self,N,**kwds))929930@cached_method931def is_morphism(self):932r"""933returns ``True`` if self is a morphism (no common zero of defining polynomials).934935The map is a morphism if and only if the ideal generated by936the defining polynomials is the unit ideal.937938OUTPUT:939940- Boolean941942EXAMPLES::943944sage: P.<x,y> = ProjectiveSpace(QQ,1)945sage: H = Hom(P,P)946sage: f = H([x^2+y^2,y^2])947sage: f.is_morphism()948True949950::951952sage: P.<x,y,z> = ProjectiveSpace(RR,2)953sage: H = Hom(P,P)954sage: f = H([x*z-y*z,x^2-y^2,z^2])955sage: f.is_morphism()956False957958::959960sage: R.<t> = PolynomialRing(GF(5))961sage: P.<x,y,z> = ProjectiveSpace(R,2)962sage: H = Hom(P,P)963sage: f = H([x*z-t*y^2,x^2-y^2,t*z^2])964sage: f.is_morphism()965True966"""967from sage.schemes.projective.projective_space import is_ProjectiveSpace968if is_ProjectiveSpace(self.domain()) is False or is_ProjectiveSpace(self.codomain()) is False:969raise NotImplementedError970R=self.coordinate_ring()971F=self._polys972if R.base_ring().is_field():973J=R.ideal(F)974else:975S=PolynomialRing(R.base_ring().fraction_field(),R.gens(),R.ngens())976J=S.ideal([S.coerce(F[i]) for i in range(R.ngens())])977if J.dimension()>0:978return False979else:980return True981982def resultant(self, normalize=False):983r"""984Computes the resultant of the defining polynomials of ``self`` if ``self`` is a map on the projective line.985986If ``normalize`` is ``True``, then first normalize the coordinate987functions with :meth:`normalize_coordinates`.988989INPUT:990991- ``normalize`` -- Boolean (optional - default: ``False``)992993OUTPUT:994995- an element of ``self.codomain().base_ring()``996997EXAMPLES::998999sage: P.<x,y> = ProjectiveSpace(QQ,1)1000sage: H = Hom(P,P)1001sage: f = H([x^2+y^2,6*y^2])1002sage: f.resultant()10033610041005::10061007sage: R.<t> = PolynomialRing(GF(17))1008sage: P.<x,y> = ProjectiveSpace(R,1)1009sage: H = Hom(P,P)1010sage: f = H([t*x^2+t*y^2,6*y^2])1011sage: f.resultant()10122*t^21013"""1014if self.domain().dimension_relative() > 1:1015raise TypeError("Only for dimension 1, use self.primes_of_bad_reduction() to get bad primes")1016if normalize is True:1017F=copy(self)1018F.normalize_coordinates()1019else:1020F=self1021x=self.domain().gen(0)1022y=self.domain().gen(1)1023d=self.degree()1024f=F[0].substitute({y:1})1025g=F[1].substitute({y:1})1026res=(f.lc()**(d-g.degree())*g.lc()**(d-f.degree())*f._pari_().polresultant(g, x))1027return(self.codomain().base_ring()(res))10281029@cached_method1030def primes_of_bad_reduction(self, check=True):1031r"""1032Determines the primes of bad reduction for a map `self: \mathbb{P}^N \to \mathbb{P}^N`1033defined over `\ZZ` or `\QQ`.10341035If ``check`` is ``True``, each prime is verified to be of bad reduction.10361037ALGORITHM:10381039`p` is a prime of bad reduction if and only if the defining1040polynomials of self have a common zero. Or stated another way,1041`p` is a prime of bad reducion if and only if the radical of1042the ideal defined by the defining polynomials of self is not1043`(x_0,x_1,\ldots,x_N)`. This happens if and only if some1044power of each `x_i` is not in the ideal defined by the1045defining polynomials of self. This last condition is what is1046checked. The lcm of the coefficients of the monomials `x_i` in1047a groebner basis is computed. This may return extra primes.10481049INPUT:10501051- ``check`` -- Boolean (optional - default: ``True``)10521053OUTPUT:10541055- a list of integer primes.10561057EXAMPLES::10581059sage: P.<x,y> = ProjectiveSpace(QQ,1)1060sage: H = Hom(P,P)1061sage: f = H([1/3*x^2+1/2*y^2,y^2])1062sage: print f.primes_of_bad_reduction()1063[2, 3]10641065::10661067sage: P.<x,y,z,w> = ProjectiveSpace(QQ,3)1068sage: H = Hom(P,P)1069sage: f = H([12*x*z-7*y^2,31*x^2-y^2,26*z^2,3*w^2-z*w])1070sage: f.primes_of_bad_reduction()1071[2, 3, 7, 13, 31]10721073This is an example where check=False returns extra primes::10741075sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)1076sage: H = Hom(P,P)1077sage: f = H([3*x*y^2 + 7*y^3 - 4*y^2*z + 5*z^3, -5*x^3 + x^2*y + y^3 + 2*x^2*z, -2*x^2*y + x*y^2 + y^3 - 4*y^2*z + x*z^2])1078sage: f.primes_of_bad_reduction(False)1079[2, 5, 37, 2239, 304432717]1080sage: f.primes_of_bad_reduction()1081[5, 37, 2239, 304432717]1082"""1083if self.base_ring() != ZZ and self.base_ring() != QQ:1084raise TypeError("Must be ZZ or QQ")1085from sage.schemes.projective.projective_space import is_ProjectiveSpace1086if is_ProjectiveSpace(self.domain()) is False or is_ProjectiveSpace(self.codomain()) is False:1087raise NotImplementedError1088R=self.coordinate_ring()1089F=self._polys1090if R.base_ring().is_field():1091J=R.ideal(F)1092else:1093S=PolynomialRing(R.base_ring().fraction_field(),R.gens(),R.ngens())1094J=S.ideal([S.coerce(F[i]) for i in range(R.ngens())])1095if J.dimension()>0:1096raise TypeError("Not a morphism.")1097#normalize to coefficients in the ring not the fraction field.1098F=[F[i]*lcm([F[j].denominator() for j in range(len(F))]) for i in range(len(F))]10991100#move the ideal to the ring of integers1101if R.base_ring().is_field():1102S=PolynomialRing(R.base_ring().ring_of_integers(),R.gens(),R.ngens())1103F=[F[i].change_ring(R.base_ring().ring_of_integers()) for i in range(len(F))]1104J=S.ideal(F)1105else:1106J=R.ideal(F)1107GB=J.groebner_basis()1108badprimes=[]11091110#get the primes dividing the coefficients of the monomials x_i^k_i1111for i in range(len(GB)):1112LT=GB[i].lt().degrees()1113power=01114for j in range(R.ngens()):1115if LT[j]!=0:1116power+=11117if power == 1:1118badprimes=badprimes+GB[i].lt().coefficients()[0].support()1119badprimes=list(set(badprimes))1120badprimes.sort()11211122#check to return only the truly bad primes1123if check == True:1124index=01125while index < len(badprimes): #figure out which primes are really bad primes...1126S=PolynomialRing(GF(badprimes[index]),R.gens(),R.ngens())1127J=S.ideal([S.coerce(F[j]) for j in range(R.ngens())])1128if J.dimension() == 0:1129badprimes.pop(index)1130else:1131index+=11132return(badprimes)11331134def conjugate(self, M):1135r"""1136Conjugates ``self`` by ``M``, i.e. `M^{-1} \circ f \circ M`.11371138If possible the map will be defined over the same space as1139``self``. Otherwise, will try to coerce to the base_ring of1140``M``.11411142INPUT:11431144- ``M`` -- a square invertible matrix11451146OUTPUT:11471148- a map from ``self.domain()`` to ``self.codomain()``.11491150EXAMPLES::11511152sage: P.<x,y> = ProjectiveSpace(ZZ,1)1153sage: H = Hom(P,P)1154sage: f = H([x^2+y^2,y^2])1155sage: f.conjugate(matrix([[1,2],[0,1]]))1156Scheme endomorphism of Projective Space of dimension 1 over Integer Ring1157Defn: Defined on coordinates by sending (x : y) to1158(x^2 + 4*x*y + 3*y^2 : y^2)11591160::11611162sage: R.<x> = PolynomialRing(QQ)1163sage: K.<i> = NumberField(x^2+1)1164sage: P.<x,y> = ProjectiveSpace(ZZ,1)1165sage: H = Hom(P,P)1166sage: f = H([x^3+y^3,y^3])1167sage: f.conjugate(matrix([[i,0],[0,-i]]))1168Scheme endomorphism of Projective Space of dimension 1 over Integer Ring1169Defn: Defined on coordinates by sending (x : y) to1170(-x^3 + y^3 : -y^3)11711172::11731174sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)1175sage: H = Hom(P,P)1176sage: f = H([x^2+y^2,y^2,y*z])1177sage: f.conjugate(matrix([[1,2,3],[0,1,2],[0,0,1]]))1178Scheme endomorphism of Projective Space of dimension 2 over Integer Ring1179Defn: Defined on coordinates by sending (x : y : z) to1180(x^2 + 4*x*y + 3*y^2 + 6*x*z + 9*y*z + 7*z^2 : y^2 + 2*y*z : y*z + 2*z^2)11811182::11831184sage: P.<x,y> = ProjectiveSpace(ZZ,1)1185sage: H = Hom(P,P)1186sage: f = H([x^2+y^2,y^2])1187sage: f.conjugate(matrix([[2,0],[0,1/2]]))1188Scheme endomorphism of Projective Space of dimension 1 over Multivariate1189Polynomial Ring in x, y over Rational Field1190Defn: Defined on coordinates by sending (x : y) to1191(2*x^2 + 1/8*y^2 : 1/2*y^2)11921193::11941195sage: R.<x> = PolynomialRing(QQ)1196sage: K.<i> = NumberField(x^2+1)1197sage: P.<x,y> = ProjectiveSpace(QQ,1)1198sage: H = Hom(P,P)1199sage: f = H([1/3*x^2+1/2*y^2,y^2])1200sage: f.conjugate(matrix([[i,0],[0,-i]]))1201Scheme endomorphism of Projective Space of dimension 1 over Multivariate1202Polynomial Ring in x, y over Number Field in i with defining polynomial1203x^2 + 11204Defn: Defined on coordinates by sending (x : y) to1205((1/3*i)*x^2 + (1/2*i)*y^2 : (-i)*y^2)1206"""12071208if M.is_square() == 1 and M.determinant() != 0 and M.ncols() == self.domain().ambient_space().dimension_relative()+1:1209X=M*vector(self[0].parent().gens())1210F=vector(self._polys)1211F=F(list(X))1212N=M.inverse()1213F=N*F1214R=self.codomain().ambient_space().coordinate_ring()1215try:1216F = [R(f) for f in F]1217PS=self.codomain()1218except TypeError: #no longer defined over same ring1219R=R.change_ring(M.base_ring())1220F = [R(f) for f in F]1221PS=self.codomain().change_ring(R)1222H=Hom(PS,PS)1223return(H(F))1224else:1225raise TypeError("matrix must be invertible and size dimension +1 ")12261227def green_function(self, P, v, **kwds):1228r"""1229Evaluates the local Green's function at the place ``v`` for ``P`` with ``N`` terms of the1230series or, in dimension 1, to within a given error bound.12311232Use ``v=0`` for the archimedean place. Must be over `\ZZ` or `\QQ`.12331234ALGORITHM:12351236See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007.12371238INPUT:12391240- ``P`` - a projective point12411242- ``v`` - non-negative integer. a place, use v=0 for the archimedean place12431244kwds:12451246- ``N`` - positive integer. number of terms of the series to use12471248- ``prec`` - positive integer, float point or p-adic precision, default: 10012491250- ``error_bound`` - a positive real number12511252OUTPUT:12531254- a real number12551256EXAMPLES::12571258sage: P.<x,y> = ProjectiveSpace(QQ,1)1259sage: H = Hom(P,P)1260sage: f = H([x^2+y^2,x*y])1261sage: f.green_function(P.point([5,2],False),0,N=30)12621.73154518447774079920855120001263sage: f.green_function(P.point([2,1],False),0,N=30)12640.865772592231810883252262099261265sage: f.green_function(P.point([1,1],False),0,N=30)12660.432886296108623386127001460981267"""1268if self.base_ring() != ZZ and self.base_ring() != QQ:1269raise TypeError("Must be ZZ or QQ")1270return(P.green_function(self,v,**kwds))12711272def canonical_height(self, P, **kwds):1273r"""1274Evaluates the canonical height of ``P`` with respect to ``self``. Must be over `\ZZ` or `\QQ`.12751276Specify either the number of terms of the series to evaluate1277or, in dimension 1, the error bound required.12781279ALGORITHM:12801281The sum of the Green's function at the archimedean place and the places of bad reduction.12821283INPUT:12841285- ``P`` -- a projective point12861287kwds:12881289- ``badprimes`` - a list of primes of bad reduction12901291- ``N`` - positive integer. number of terms of the series to use in the local green functions12921293- ``prec`` - positive integer, float point or p-adic precision, default: 10012941295- ``error_bound`` - a positive real number12961297OUTPUT:12981299- a real number13001301EXAMPLES::13021303sage: P.<x,y> = ProjectiveSpace(ZZ,1)1304sage: H = Hom(P,P)1305sage: f = H([x^2+y^2,2*x*y]);1306sage: f.canonical_height(P.point([5,4]),error_bound=0.001)13072.19705535195034048989268353241308sage: f.canonical_height(P.point([2,1]),error_bound=0.001)13091.098443063282230798497438295513101311Notice that preperiodic points may not be exactly 0::13121313sage: P.<x,y> = ProjectiveSpace(QQ,1)1314sage: H = Hom(P,P)1315sage: f = H([x^2-29/16*y^2,y^2]);1316sage: f.canonical_height(P.point([1,4]),N=60)13171.2024186864216154694752186858e-1813181319::13201321sage: P.<x,y,z> = ProjectiveSpace(QQ,2)1322sage: X = P.subscheme(x^2-y^2);1323sage: H = Hom(X,X)1324sage: f = H([x^2,y^2,4*z^2]);1325sage: Q = X([4,4,1])1326sage: f.canonical_height(Q,badprimes=[2])13270.00135380308703114318245553148821328"""1329if self.base_ring() != ZZ and self.base_ring() != QQ:1330raise TypeError("Must be ZZ or QQ")1331return(P.canonical_height(self,**kwds))13321333def global_height(self, prec=None):1334r"""1335Returns the maximum of the heights of the coefficients in any of the coordinate functions of ``self``.13361337INPUT:13381339- ``prec`` -- desired floating point precision (default:1340default RealField precision).13411342OUTPUT:13431344- a real number13451346EXAMPLES::13471348sage: P.<x,y> = ProjectiveSpace(QQ,1)1349sage: H = Hom(P,P)1350sage: f = H([1/1331*x^2+1/4000*y^2,210*x*y]);1351sage: f.global_height()13528.2940496401020313531354This function does not automatically normalize::13551356sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)1357sage: H = Hom(P,P)1358sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]);1359sage: f.global_height()13609.210340371976181361sage: f.normalize_coordinates()1362sage: f.global_height()13638.5171931914162413641365::13661367sage: R.<z> = PolynomialRing(QQ)1368sage: K.<w> = NumberField(z^2-2)1369sage: O = K.maximal_order()1370sage: P.<x,y> = ProjectiveSpace(O,1)1371sage: H = Hom(P,P)1372sage: f = H([2*x^2 + 3*O(w)*y^2,O(w)*y^2])1373sage: f.global_height()13741.4451858789480813751376.. TODO:: add heights to integer.pyx and remove special case1377"""1378if self.domain().base_ring() == ZZ:1379if prec is None:1380R = RealField()1381else:1382R = RealField(prec)1383H=R(0)1384for i in range(self.domain().ambient_space().dimension_relative()+1):1385C=self[i].coefficients()1386h=max([c.abs() for c in C])1387H=max(H,R(h).log())1388return(H)1389H=01390for i in range(self.domain().ambient_space().dimension_relative()+1):1391C=self[i].coefficients()1392h=max([c.global_height(prec) for c in C])1393H=max(H,h)1394return(H)13951396def height_difference_bound(self, prec=None):1397r"""1398Returns an upper bound on the different bewtween the canonical height of a point with1399respect to ``self`` and the height of the point. ``self`` must be a morphism.14001401ALGORITHM:14021403Uses a Nullstellensatz argument to compute the constant.1404For details: B. Hutz, Efficient determination of rational preperiodic points for endomorphisms of projective1405space, arxiv:1210.6246 (2012).14061407INPUT:14081409- ``prec`` - positive integer, float point, default: RealField default14101411OUTPUT:14121413- a real number14141415EXAMPLES::14161417sage: P.<x,y> = ProjectiveSpace(QQ,1)1418sage: H = End(P)1419sage: f = H([x^2+y^2,x*y]);1420sage: f.height_difference_bound()14211.3862943611198914221423This function does not automatically normalize. ::14241425sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)1426sage: H = End(P)1427sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]);1428sage: f.height_difference_bound()142911.00209984120421430sage: f.normalize_coordinates()1431sage: f.height_difference_bound()143210.76320793292191433"""1434if self.domain().base_ring() != ZZ and self.domain().base_ring() != QQ:1435raise NotImplementedError("Must be over ZZ or QQ")1436if not self.is_endomorphism():1437raise NotImplementedError("Must be an endomorphism of projective space")1438if prec is None:1439R=RealField()1440else:1441R=RealField(prec)1442N=self.domain().dimension_relative()1443d=self.degree()1444D=(N+1)*(d-1) +11445#compute upper bound1446U = self.global_height(prec) + R(binomial(N+d,d)).log()1447#compute lower bound - from explicit polynomials of Nullstellensatz1448CR=self.domain().coordinate_ring()1449CR=CR.change_ring(QQ) #.lift() only works over fields1450I=CR.ideal(self.defining_polynomials())1451MCP=[]1452for k in range(N+1):1453CoeffPolys=(CR.gen(k)**D).lift(I)1454Res=11455h=11456for j in range(len(CoeffPolys)):1457if CoeffPolys[j]!=0:1458for i in range(len(CoeffPolys[j].coefficients())):1459Res=lcm(Res,abs(CoeffPolys[j].coefficients()[i].denominator()))1460h=max(h,abs(CoeffPolys[j].coefficients()[i].numerator()))1461MCP.append([Res,Res*h]) #since we need to clear denominators1462maxh=11463gcdRes=01464for k in range(len(MCP)):1465gcdRes=gcd(gcdRes,MCP[k][0])1466maxh=max(maxh,MCP[k][1])1467L=abs(R(gcdRes/((N+1)*binomial(N + D-d,D-d)*maxh)).log())14681469C=max(U,L) #height difference dh(P) - L <= h(f(P)) <= dh(P) +U1470return(C/(d-1))14711472def multiplier(self,P,n, check=True):1473r"""1474Returns the multiplier of ``self`` at the `QQ`-rational point ``P`` of period ``n``.1475``self`` must be an endomorphism of projective space14761477INPUT:14781479- ``P`` - a point on domain of ``self``14801481- ``n`` - a positive integer, the period of ``P``14821483- ``check`` -- verify that ``P`` has period ``n``, Default:True14841485OUTPUT:14861487- a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of ``self``14881489EXAMPLES::14901491sage: P.<x,y,z> = ProjectiveSpace(QQ,2)1492sage: H = End(P)1493sage: f = H([x^2,y^2,4*z^2]);1494sage: Q = P.point([4,4,1],False);1495sage: f.multiplier(Q,1)1496[2 0]1497[0 2]14981499::15001501sage: P.<x,y> = ProjectiveSpace(QQ,1)1502sage: H = End(P)1503sage: f = H([7*x^2 - 28*y^2,24*x*y])1504sage: f.multiplier(P(2,5),4)1505[231361/20736]15061507::15081509sage: P.<x,y> = ProjectiveSpace(CC,1)1510sage: H = End(P)1511sage: f = H([x^3 - 25*x*y^2 + 12*y^3,12*y^3])1512sage: f.multiplier(P(1,1),5)1513[0.389017489711935]15141515::15161517sage: P.<x,y> = ProjectiveSpace(RR,1)1518sage: H = End(P)1519sage: f = H([x^2-2*y^2,y^2])1520sage: f.multiplier(P(2,1),1)1521[4.00000000000000]15221523::15241525sage: P.<x,y> = ProjectiveSpace(Qp(13),1)1526sage: H = End(P)1527sage: f = H([x^2-29/16*y^2,y^2])1528sage: f.multiplier(P(5,4),3)1529[6 + 8*13 + 13^2 + 8*13^3 + 13^4 + 8*13^5 + 13^6 + 8*13^7 + 13^8 +15308*13^9 + 13^10 + 8*13^11 + 13^12 + 8*13^13 + 13^14 + 8*13^15 + 13^16 +15318*13^17 + 13^18 + 8*13^19 + O(13^20)]15321533::15341535sage: P.<x,y> = ProjectiveSpace(QQ,1)1536sage: H = End(P)1537sage: f = H([x^2-y^2,y^2])1538sage: f.multiplier(P(0,1),1)1539Traceback (most recent call last):1540...1541ValueError: (0 : 1) is not periodic of period 115421543.. TODO:: would be better to keep the dehomogenizations for reuse1544"""1545if not self.is_endomorphism():1546raise NotImplementedError("Must be an endomorphism of projective space")1547if check:1548if self.nth_iterate(P,n)!=P:1549raise ValueError("%s is not periodic of period %s"% (P,n))1550N=self.domain().dimension_relative()1551l=identity_matrix(FractionField(self.codomain().base_ring()),N,N)1552Q=P1553Q.normalize_coordinates()1554index=N1555indexlist=[]1556while Q[index]==0:1557index-=11558indexlist.append(index)1559for i in range(0,n):1560F=[]1561R=self(Q)1562R.normalize_coordinates()1563index=N1564while R[index]==0:1565index-=11566indexlist.append(index)1567S=PolynomialRing(FractionField(self.codomain().base_ring()),N,'x')1568CR=self.coordinate_ring()1569map_vars=list(S.gens())1570map_vars.insert(indexlist[i],1)1571phi=CR.hom(map_vars,S)1572#make map between correct affine patches1573for j in range(N+1):1574if j != indexlist[i+1]:1575F.append(phi(self._polys[j])/phi(self._polys[indexlist[i+1]]))1576J = matrix(FractionField(S),N,N)1577for j1 in range(0,N):1578for j2 in range(0,N):1579J[j1,j2]=F[j1].derivative(S.gen(j2))1580l=J(tuple(Q.dehomogenize(indexlist[i])))*l #get the correct order for chain rule matrix multiplication1581Q=R1582return l15831584def _multipliermod(self,P,n,p,k):1585r"""1586Returns the multiplier of ``self`` at the point ``P`` of period ``n`` modulo `p^k`.1587self must be an endomorphism of projective space defined over `\QQ` or '\ZZ'.1588This function should not be used at the top level as it does not perform input checks.1589It is used primarily for the rational preperiodic and periodic point algorithms.15901591INPUT:15921593- ``P`` - a point on domain of ``self``15941595- ``n`` - a positive integer, the period of ``P``15961597- ``p`` - a positive integer15981599- ``k`` - a positive integer16001601OUTPUT:16021603- a square matrix of size ``self.codomain().dimension_relative()`` in `Zmod(p^k)`.16041605EXAMPLES::16061607sage: P.<x,y> = ProjectiveSpace(QQ,1)1608sage: H = End(P)1609sage: f = H([x^2-29/16*y^2,y^2])1610sage: f._multipliermod(P(5,4),3,11,1)1611[3]16121613::16141615sage: P.<x,y> = ProjectiveSpace(QQ,1)1616sage: H = End(P)1617sage: f = H([x^2-29/16*y^2,y^2])1618sage: f._multipliermod(P(5,4),3,11,2)1619[80]16201621.. TODO:: would be better to keep the dehomogenizations for reuse1622"""1623N=self.domain().dimension_relative()1624BR=FractionField(self.codomain().base_ring())1625l=identity_matrix(BR,N,N)1626Q=copy(P)1627g=gcd(Q._coords) #we can't use normalize_coordinates since it can cause denominators1628Q.scale_by(1/g)1629index=N1630indexlist=[]1631while Q[index]%p==0:1632index-=11633indexlist.append(index)1634for i in range(0,n):1635F=[]1636R=self(Q,False)1637g=gcd(R._coords)1638R.scale_by(1/g)1639for index in range(N+1):1640R._coords[index]=R._coords[index]%(p**k)1641index=N1642while R[index]%p==0:1643index-=11644indexlist.append(index)1645S=PolynomialRing(BR,N,'x')1646CR=self.coordinate_ring()1647map_vars=list(S.gens())1648map_vars.insert(indexlist[i],1)1649phi=CR.hom(map_vars,S)1650for j in range(N+1):1651if j != indexlist[i+1]:1652F.append(phi(self._polys[j])/phi(self._polys[indexlist[i+1]]))1653J = matrix(FractionField(S),N,N)1654for j1 in range(0,N):1655for j2 in range(0,N):1656J[j1,j2]=F[j1].derivative(S.gen(j2))1657l=(J(tuple(Q.dehomogenize(indexlist[i])))*l)%(p**k)1658Q=R1659return(l)16601661def possible_periods(self,**kwds):1662r"""1663Returns the set of possible periods for rational periodic points of self.1664Must be defined over `\ZZ` or `\QQ`.16651666ALGORITHM:1667Calls ``self.possible_periods()`` modulo all primes of good reduction in range ``prime_bound``.1668Returns the intersection of those lists.16691670INPUT:16711672kwds:16731674- ``prime_bound`` - a list or tuple of two positive integers. Or an integer for the upper bound. (optional)1675default: [1,20].16761677- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)16781679OUTPUT:16801681- a list of positive integers.16821683EXAMPLES::16841685sage: P.<x,y> = ProjectiveSpace(QQ,1)1686sage: H = End(P)1687sage: f = H([x^2-29/16*y^2,y^2])1688sage: f.possible_periods()1689[1, 3]16901691::16921693sage: PS.<x,y> = ProjectiveSpace(1,QQ)1694sage: H = End(PS)1695sage: f = H([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3])1696sage: f.possible_periods(prime_bound=[1,5])1697Traceback (most recent call last):1698...1699ValueError: No primes of good reduction in that range1700sage: f.possible_periods(prime_bound=[1,10])1701[1, 4, 12]1702sage: f.possible_periods(prime_bound=[1,20])1703[1, 4]17041705::17061707sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)1708sage: H = End(P)1709sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3])1710sage: f.possible_periods(prime_bound=10)1711[1, 2, 6, 20, 42, 60, 140, 420]1712sage: f.possible_periods(prime_bound=20) # long time1713[1, 20]1714"""1715if not self.is_endomorphism():1716raise NotImplementedError("Must be an endomorphism of projective space")1717if self.domain().base_ring()!=ZZ and self.domain().base_ring()!=QQ:1718raise NotImplementedError("Must be ZZ or QQ")171917201721primebound = kwds.pop("prime_bound",[1,20])1722badprimes = kwds.pop("bad_primes",None)17231724if (isinstance(primebound,(list,tuple))==False):1725try:1726primebound=[1,ZZ(primebound)]1727except TypeError:1728raise TypeError("Bound on primes must be an integer")1729else:1730try:1731primebound[0]=ZZ(primebound[0])1732primebound[1]=ZZ(primebound[1])1733except TypeError:1734raise TypeError("Prime bounds must be integers")17351736if badprimes==None:1737badprimes=self.primes_of_bad_reduction()1738firstgood=01739for q in primes(primebound[0],primebound[1]+1):1740if q in badprimes: #bad reduction1741t=01742#print "Bad Reduction at ", q1743elif firstgood==0:1744F=self.change_ring(GF(q))1745periods=set(F.possible_periods())1746firstgood=11747else:1748F=self.change_ring(GF(q))1749periodsq=set(F.possible_periods())1750periods=periods.intersection(periodsq)1751if firstgood==0:1752raise ValueError("No primes of good reduction in that range")1753else:1754return(sorted(periods))17551756def _preperiodic_points_to_cyclegraph(self,preper):1757r"""1758Given the complete set of periodic or preperiodic points returns the1759digraph representing the orbit. If it is not the complete set, this function1760will not fill in the gaps.176117621763INPUT:17641765- ``preper`` - a list or tuple of projective points. The complete set of rational periodic or preperiodic points.17661767OUTPUT:17681769- a digraph representing the orbit the rational preperiodic points ``preper`` in projective space.17701771Examples::17721773sage: P.<x,y> = ProjectiveSpace(QQ,1)1774sage: H = End(P)1775sage: f = H([x^2-2*y^2,y^2])1776sage: preper = [P(-2, 1), P(1, 0), P(0, 1), P(1, 1), P(2, 1), P(-1, 1)]1777sage: f._preperiodic_points_to_cyclegraph(preper)1778Looped digraph on 6 vertices1779"""1780V=[]1781E=[]1782for i in range(0,len(preper)):1783V.append(str(preper[i]))1784Q=self(preper[i])1785Q.normalize_coordinates()1786E.append([str(Q)])1787from sage.graphs.digraph import DiGraph1788g=DiGraph(dict(zip(V,E)), loops=True)1789return(g)17901791def is_PGL_minimal(self, prime_list=None):1792r"""1793Checks if ``self`` is a minimal model in its conjugacy class. See [Bruin-Molnar]1794and [Molnar] for a description of the algorithm.17951796INPUT:17971798- ``prime_list`` -- list of primes to check minimality, if None, check all places17991800OUTPUT:18011802- Boolean - True if ``self`` is minimal, False otherwise.18031804EXAMPLES::18051806sage: PS.<X,Y> = ProjectiveSpace(QQ,1)1807sage: H = End(PS)1808sage: f = H([X^2+3*Y^2,X*Y])1809sage: f.is_PGL_minimal()1810True18111812::18131814sage: PS.<x,y> = ProjectiveSpace(QQ,1)1815sage: H = End(PS)1816sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y])1817sage: f.is_PGL_minimal()1818False18191820::18211822sage: PS.<x,y> = ProjectiveSpace(QQ,1)1823sage: H = End(PS)1824sage: f = H([6*x^2+12*x*y+7*y^2,y^2])1825sage: f.is_PGL_minimal()1826Traceback (most recent call last):1827...1828TypeError: Affine minimality is only considered for maps not of the form1829f or 1/f for a polynomial f.1830"""1831if self.base_ring()!=QQ and self.base_ring()!=ZZ:1832raise NotImplementedError("Minimal models only implemented over ZZ or QQ")1833if not self.is_morphism():1834raise TypeError("The function is not a morphism")1835if self.degree()==1:1836raise NotImplementedError("Minimality is only for degree 2 or higher")18371838from endPN_minimal_model import affine_minimal1839return(affine_minimal(self, False ,prime_list ,True))18401841def minimal_model(self, return_transformation = False,prime_list=None):1842r"""1843Given ``self`` a scheme morphism on the projective line over the rationals,1844determine if ``self`` is minimal. In particular, determine1845if ``self`` is affine minimal, which is enough to decide if it is minimal1846or not. See Proposition 2.10 in [Bruin-Molnar].18471848REFERENCES:18491850.. [Bruin-Molnar] N. Bruin and A. Molnar, Minimal models for rational1851functions in a dynamical setting1852LMS Journal of Computation and Mathematics, Volume 15 (2012), pp 400-417.18531854.. [Molnar] A. Molnar, Fractional Linear Minimal Models of Rational Functions,1855M.Sc. Thesis.18561857INPUT:18581859- ``self`` -- scheme morphism on the projective line defined over `QQ`.18601861- ``return_transformation`` -- a boolean value, default value True. This1862signals a return of the ``PGL_2`` transformation1863to conjugate ``self`` to the calculated minimal1864model. default: False18651866- ``prime_list`` -- a list of primes, in case one only wants to determine minimality1867at those specific primes.18681869OUTPUT:18701871- a scheme morphism on the projective line which is a minimal model of ``self``.18721873- a `PGL(2,QQ)` element which conjugates ``self`` to a minimal model18741875EXAMPLES::18761877sage: PS.<X,Y> = ProjectiveSpace(QQ,1)1878sage: H = End(PS)1879sage: f = H([X^2+3*Y^2,X*Y])1880sage: f.minimal_model(return_transformation=True)1881(1882Scheme endomorphism of Projective Space of dimension 1 over Rational1883Field1884Defn: Defined on coordinates by sending (X : Y) to1885(X^2 + 3*Y^2 : X*Y)1886,1887[1 0]1888[0 1]1889)18901891::18921893sage: PS.<X,Y> = ProjectiveSpace(QQ,1)1894sage: H = End(PS)1895sage: f = H([7365/2*X^4 + 6282*X^3*Y + 4023*X^2*Y^2 + 1146*X*Y^3 + 245/2*Y^4, -12329/2*X^4 - 10506*X^3*Y - 6723*X^2*Y^2 - 1914*X*Y^3 - 409/2*Y^4])1896sage: f.minimal_model(return_transformation=True)1897(1898Scheme endomorphism of Projective Space of dimension 1 over Rational1899Field1900Defn: Defined on coordinates by sending (X : Y) to1901(22176*X^4 + 151956*X^3*Y + 390474*X^2*Y^2 + 445956*X*Y^3 +1902190999*Y^4 : -12329*X^4 - 84480*X^3*Y - 217080*X^2*Y^2 - 247920*X*Y^3 -1903106180*Y^4),1904[2 3]1905[0 1]1906)19071908::19091910sage: PS.<x,y> = ProjectiveSpace(QQ,1)1911sage: H = End(PS)1912sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y])1913sage: f.minimal_model()1914Scheme endomorphism of Projective Space of dimension 1 over Rational1915Field1916Defn: Defined on coordinates by sending (x : y) to1917(x^2 + 12*x*y + 42*y^2 : 2*x*y)19181919::19201921sage: PS.<x,y> = ProjectiveSpace(ZZ,1)1922sage: H = End(PS)1923sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y + 42*y^2])1924sage: g,M=f.minimal_model(return_transformation=True)1925sage: f.conjugate(M)==g1926True19271928::19291930sage: PS.<X,Y> = ProjectiveSpace(QQ,1)1931sage: H = End(PS)1932sage: f = H([X+Y,X-3*Y])1933sage: f.minimal_model()1934Traceback (most recent call last):1935...1936NotImplementedError: Minimality is only for degree 2 or higher19371938::19391940sage: PS.<X,Y> = ProjectiveSpace(QQ,1)1941sage: H = End(PS)1942sage: f = H([X^2-Y^2,X^2+X*Y])1943sage: f.minimal_model()1944Traceback (most recent call last):1945...1946TypeError: The function is not a morphism19471948"""1949if self.base_ring()!=QQ and self.base_ring()!=ZZ:1950raise NotImplementedError("Minimal models only implemented over ZZ or QQ")1951if not self.is_morphism():1952raise TypeError("The function is not a morphism")1953if self.degree()==1:1954raise NotImplementedError("Minimality is only for degree 2 or higher")19551956from endPN_minimal_model import affine_minimal1957return(affine_minimal(self, return_transformation,prime_list,False))19581959class SchemeMorphism_polynomial_projective_space_field(SchemeMorphism_polynomial_projective_space):19601961def lift_to_rational_periodic(self,points_modp,B=None):1962r"""1963Given a list of points in projective space over `GF(p)`, determine if they lift to1964`\QQ`-rational periodic points. ``self`` must be an endomorphism of projective1965space defined over `\QQ`19661967ALGORITHM:1968Use Hensel lifting to find a `p`-adic approximation for that rational point. The accuracy needed1969is determined by the height bound `B`. Then apply the the LLL algorithm to determine if the lift1970corresponds to a rational point.19711972If the point is a point of high multiplicity (multiplier 1) then procedure can be very slow.197319741975INPUT:19761977- ``points_modp`` - a list or tuple of pairs containing a point in projective space1978over `GF(p)` and the possible period.19791980- ``B`` - a positive integer - the height bound for a rational preperiodic point. (optional)19811982OUTPUT:19831984- a list of projective points.19851986EXAMPLES::19871988sage: P.<x,y> = ProjectiveSpace(QQ,1)1989sage: H = End(P)1990sage: f = H([x^2 - y^2,y^2])1991sage: f.lift_to_rational_periodic([[P(0,1).change_ring(GF(7)),4]])1992[[(0 : 1), 2]]19931994::19951996There may be multiple points in the lift.1997sage: P.<x,y> = ProjectiveSpace(QQ,1)1998sage: H = End(P)1999sage: f = H([-5*x^2 + 4*y^2,4*x*y])2000sage: f.lift_to_rational_periodic([[P(1,0).change_ring(GF(3)),1]]) # long time2001[[(1 : 0), 1], [(2/3 : 1), 1], [(-2/3 : 1), 1]]20022003::20042005sage: P.<x,y> = ProjectiveSpace(QQ,1)2006sage: H = End(P)2007sage: f = H([16*x^2 - 29*y^2,16*y^2])2008sage: f.lift_to_rational_periodic([[P(3,1).change_ring(GF(13)), 3]])2009[[(-1/4 : 1), 3]]20102011::20122013sage: P.<x,y,z> = ProjectiveSpace(QQ,2)2014sage: H = End(P)2015sage: f = H([76*x^2 - 180*x*y + 45*y^2 + 14*x*z + 45*y*z - 90*z^2, 67*x^2 - 180*x*y - 157*x*z + 90*y*z, -90*z^2])2016sage: f.lift_to_rational_periodic([[P(14,19,1).change_ring(GF(23)), 9]]) # long time2017[[(-9 : -4 : 1), 9]]2018"""2019if points_modp==[]:2020return([])2021else:2022if B==None:2023B=e**self.height_difference_bound()20242025p=points_modp[0][0].codomain().base_ring().characteristic()2026if p==0:2027raise TypeError("Must be positive characteristic")2028PS=self.domain()2029N=PS.dimension_relative()2030R=RealField()2031#compute the maximum p-adic precision needed to conclusively determine2032#if the rational point exists2033L=R((R(2**(N/2+1)*sqrt(N+1)*B**2).log())/R(p).log()+1).trunc()20342035points=[]2036for i in range(len(points_modp)):2037#[point mod p, period, current p-adic precision]2038points.append([points_modp[i][0].change_ring(QQ,False),points_modp[i][1],1])2039good_points=[]2040#shifts is used in non-Hensel lifting2041shifts=None2042#While there are still points to consider try to lift to next precision2043while points!=[]:2044q=points.pop()2045qindex=N2046#Find the last non-zero coordinate to use for normalizations2047while q[0][qindex]%p==0:2048qindex-=12049T=q[0]2050n=q[1]2051k=q[2]2052T.scale_by(1/T[qindex]) #normalize2053bad=02054#stop where we reach the needed precision or the point is bad2055while k< L and bad==0:2056l=self._multipliermod(T,n,p,2*k)2057l-=l.parent().one() #f^n(x) - x2058lp=l.change_ring(Zmod(p**k))2059ldet=lp.determinant()2060# if the matrix is invertible then we can Hensel lift2061if ldet%p!=0:2062RQ=ZZ.quo(p**(2*k))2063T.clear_denominators()2064newT = T.change_ring(RQ,False)2065fp=self.change_ring(RQ,False)2066S=newT.nth_iterate(fp,n,False).change_ring(QQ,False)2067T.scale_by(1/T[qindex])2068S.scale_by(1/S[qindex])2069for i in range(N+1):2070S._coords[i]=S._coords[i]-T._coords[i]2071if S[i]%(p**k) !=0 and i!=N:2072bad=12073break2074if bad==1:2075break2076S.scale_by(-1/p**k)2077vecs=[Zmod(p**k)(S._coords[iS]) for iS in range(N+1)]2078vecs.pop(qindex)2079newvecs=list((lp.inverse())*vector(vecs)) #l.inverse should be mod p^k!!2080newS=[]2081[newS.append(QQ(newvecs[i])) for i in range(qindex)]2082newS.append(0)2083[newS.append(QQ(newvecs[i])) for i in range(qindex,N)]2084S=PS.point(newS, False) #don't check for [0,...,0]2085for i in range(N+1):2086S._coords[i]=S._coords[i]%(p**k)2087for i in range(N+1):2088T._coords[i]+=S._coords[i]*(p**k)2089T.normalize_coordinates()2090#Hensel gives us 2k for the newprecision2091k=min(2*k,L)2092else:2093#we are unable to Hensel Lift so must try all possible lifts2094#to the next precision (k+1)2095first=02096newq=[]2097RQ=Zmod(p**(k+1))2098fp=self.change_ring(RQ,False)2099if shifts is None:2100shifts=xmrange([p for i in range(N)])2101for shift in shifts:2102newT=T.change_ring(RQ,False)2103shiftindex=02104for i in range(N+1):2105if i != qindex:2106newT._coords[i]=newT[i]+shift[shiftindex]*p**k2107shiftindex+=12108TT=fp.nth_iterate(newT,n,False)2109if TT== newT:2110if first==0:2111newq.append(newT.change_ring(QQ,False))2112newq.append(n)2113newq.append(k+1)2114first=12115else:2116points.append([newT.change_ring(QQ,False),n,k+1])2117if newq==[]:2118bad=12119break2120else:2121T=newq[0]2122k+=12123#given a p-adic lift of appropriate precision2124#perform LLL to find the "smallest" rational approximation2125#If this height is small enough, then it is a valid rational point2126if bad==0:2127M=matrix(N+2,N+1)2128T.clear_denominators()2129for i in range(N+1):2130M[0,i]=T[i]2131M[i+1,i]=p**L2132M[N+1,N]=p**L2133M=M.LLL()2134Q=[]2135[Q.append(M[1,i]) for i in range(N+1)]2136g=gcd(Q)2137#remove gcds since this is a projective point2138newB=B*g2139for i in range(N+1):2140if abs(Q[i]) >newB:2141#height too big, so not a valid point2142bad=12143break2144if bad==0:2145P=PS.point(Q,False)2146#check that it is actually periodic2147newP=copy(P)2148k=12149done=False2150while done==False and k <= n:2151newP=self(newP)2152if newP==P:2153if ([P,k] in good_points)==False:2154good_points.append([newP,k])2155done=True2156k+=121572158return(good_points)21592160def rational_periodic_points(self,**kwds):2161r"""2162Determine the set of rational periodic points for self an endomorphism of projective space.2163Must be defined over `\QQ`.21642165The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having2166trouble getting a partiuclar map to finish, try first computing the possible periods, then2167try various different ``lifting_prime``.21682169ALGORITHM:2170Modulo each prime of good reduction `p` determine the set of periodic points modulo `p`.2171For each cycle modulo `p` compute the set of possible periods (`mrp^e`). Take the intersection2172of the list of possible periods modulo several primes of good reduction to get a possible list2173of minimal periods of rational periodic points. Take each point modulo `p` associated to each2174of these possible periods and try to lift it to a rational point with a combination of2175`p`-adic approximation and the LLL basis reducion algorithm.21762177See B. Hutz, Determination of all rational preperiodic points for morphisms of Pn, submitted, 2012.21782179INPUT:21802181kwds:21822183- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the2184limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)2185default: [1,20]21862187- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the2188lifting. default: 2321892190- ``periods`` - a list of positive integers which is the list of possible periods. (optional)21912192- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)21932194OUTPUT:21952196- a list of rational points in projective space.21972198Examples::21992200sage: P.<x,y> = ProjectiveSpace(QQ,1)2201sage: H = End(P)2202sage: f = H([x^2-3/4*y^2,y^2])2203sage: f.rational_periodic_points(prime_bound=20,lifting_prime=7) # long time2204[(-1/2 : 1), (1 : 0), (3/2 : 1)]22052206::22072208sage: P.<x,y,z> = ProjectiveSpace(QQ,2)2209sage: H = End(P)2210sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3])2211sage: f.rational_periodic_points(prime_bound=[1,20]) # long time2212[(-3 : 1 : 1), (3 : 1 : 1), (5 : 1 : 1), (-1 : 0 : 1), (3 : 3 : 1), (-32213: 3 : 1), (-1 : 3 : 1), (1 : 3 : 1), (-3 : -1 : 1), (5 : 3 : 1), (-1 :2214-1 : 1), (1 : 1 : 1), (3 : 0 : 1), (-3 : 0 : 1), (5 : 0 : 1), (3 : -1 :22151), (1 : 0 : 0), (5 : -1 : 1), (-1 : 1 : 1), (1 : -1 : 1), (0 : 1 : 0),2216(1 : 0 : 1)]22172218::22192220sage: P.<x,y> = ProjectiveSpace(QQ,1)2221sage: H = End(P)2222sage: f = H([-5*x^2 + 4*y^2,4*x*y])2223sage: f.rational_periodic_points() # long time2224[(2/3 : 1), (-2 : 1), (1 : 0), (2 : 1), (-2/3 : 1)]22252226.. TODO::22272228- move some of this to Cython so that it is faster especially the possible periods mod `p`.22292230- have the last prime of good redution used also return the list of points instead of getting the2231information again for all_points.2232"""2233if not self.is_endomorphism():2234raise NotImplementedError("Must be an endomorphism of projective space")2235if self.domain().base_ring()!=QQ:2236raise NotImplementedError("Must be QQ") #for p-adic lifting22372238primebound = kwds.pop("prime_bound",[1,20])2239p = kwds.pop("lifting_prime",23)2240periods = kwds.pop("periods",None)2241badprimes = kwds.pop("bad_primes",None)22422243if (isinstance(primebound,(list,tuple))==False):2244try:2245primebound=[1,ZZ(primebound)]2246except TypeError:2247raise TypeError("Bound on primes must be an integer")2248else:2249try:2250primebound[0]=ZZ(primebound[0])2251primebound[1]=ZZ(primebound[1])2252except TypeError:2253raise TypeError("Prime bounds must be integers")22542255if badprimes==None:2256badprimes=self.primes_of_bad_reduction()2257if periods==None:2258periods=self.possible_periods(prime_bound=primebound,bad_primes=badprimes)2259PS=self.domain()2260R=PS.base_ring()2261periodic=set()2262while p in badprimes:2263p = next_prime(p+1)2264B=e**self.height_difference_bound()22652266f=self.change_ring(GF(p))2267all_points=f.possible_periods(True) #return the list of points and their periods.2268pos_points=[]2269for i in range(len(all_points)):2270if all_points[i][1] in periods and (all_points[i] in pos_points)==False: #check period, remove duplicates2271pos_points.append(all_points[i])2272periodic_points=self.lift_to_rational_periodic(pos_points,B)2273for P,n in periodic_points:2274for k in range(n):2275P.normalize_coordinates()2276periodic.add(P)2277P=self(P)2278return(list(periodic))22792280def rational_preimages(self,Q):2281r"""2282Given a rational point `Q` in the domain of ``self``, return all the rational points `P`2283in the domain of ``self`` with `self(P)==Q`. In other words, the set of first pre-images of `Q`.2284``self`` must be defined over `\QQ` and be an endomorphism of projective space.22852286ALGORITHM:2287Use elimination via groebner bases to find the rational pre-images22882289INPUT:22902291- ``Q`` - a rational point in the domain of ``self``.22922293OUTPUT:22942295- a list of rational points in the domain of ``self``.22962297Examples::22982299sage: P.<x,y> = ProjectiveSpace(QQ,1)2300sage: H = End(P)2301sage: f = H([16*x^2 - 29*y^2,16*y^2])2302sage: f.rational_preimages(P(-1,4))2303[(5/4 : 1), (-5/4 : 1)]23042305::23062307sage: P.<x,y,z> = ProjectiveSpace(QQ,2)2308sage: H = End(P)2309sage: f = H([76*x^2 - 180*x*y + 45*y^2 + 14*x*z + 45*y*z - 90*z^2, 67*x^2 - 180*x*y - 157*x*z + 90*y*z, -90*z^2])2310sage: f.rational_preimages(P(-9,-4,1))2311[(0 : 4 : 1)]23122313::23142315A non-periodic example.2316sage: P.<x,y> = ProjectiveSpace(QQ,1)2317sage: H = End(P)2318sage: f = H([x^2 + y^2,2*x*y])2319sage: f.rational_preimages(P(17,15))2320[(5/3 : 1), (3/5 : 1)]2321"""2322if not self.is_endomorphism():2323raise NotImplementedError("Must be an endomorphism of projective space")2324if self.domain().base_ring()!=QQ:2325raise NotImplementedError("Must be QQ")2326PS=self.domain()2327R=PS.coordinate_ring()2328N=PS.dimension_relative()2329#need a lexicographic ordering for elimination2330R=PolynomialRing(R.base_ring(),N+1,R.gens(),order='lex')2331I=[]2332preimages=set()2333for i in range(N+1):2334for j in range(i+1,N+1):2335I.append(Q[i]*self[j]-Q[j]*self[i])2336I = I*R2337#Determine the points through elimination2338#This is much faster than using the I.variety() function on each affine chart.2339for k in range(N+1):2340#create the elimination ideal for the kth affine patch2341G=I.substitute({R.gen(k):1}).groebner_basis()2342if G!=[1]:2343P={}2344#keep track that we know the kth coordinate is 12345P.update({R.gen(k):1})2346points=[P]2347#work backwards from solving each equation for the possible2348#values of the next coordiante2349for i in range(len(G)-1,-1,-1):2350new_points=[]2351good=02352for P in points:2353#subsitute in our dictionary entry that has the values2354#of coordinates known so far. This results in a single2355#variable polynomial (by elimination)2356L=G[i].substitute(P)2357if L!=0:2358L=L.factor()2359#the linear factors give the possible rational values of2360#this coordinate2361for pol,pow in L:2362if pol.degree()==1 and len(pol.variables()) ==1:2363good=12364r=pol.variable()2365varindex=R.gens().index(r)2366#add this coordinates information to th2367#each dictionary entry2368P.update({R.gen(varindex):-pol.coefficient({r:0})/pol.coefficient({r:1})})2369new_points.append(copy(P))2370if good==1:2371points=new_points2372#the dictionary entries now have values for all coordiantes2373#they are the rational solutions to the equations2374#make them into projective points2375for i in range(len(points)):2376if len(points[i])==N+1:2377S=PS([points[i][R.gen(j)] for j in range(N+1)])2378S.normalize_coordinates()2379preimages.add(S)2380return(list(preimages))238123822383def all_rational_preimages(self,points):2384r"""2385Given a set of rational points in the domain of ``self``, return all the rational2386pre-images of those points. In others words, all the rational points which have some2387iterate in the set points. This function repeatedly calls ``rational_preimages``.2388If the degree is at least two, by Northocott, this is always a finite set.2389``self`` must be defined over `\QQ` and be an endomorphism of projective space.23902391INPUT:23922393- ``points`` - a list of rational points in the domain of ``self``23942395OUTPUT:23962397- a list of rational points in the domain of ``self``.23982399Examples::24002401sage: P.<x,y> = ProjectiveSpace(QQ,1)2402sage: H = End(P)2403sage: f = H([16*x^2 - 29*y^2,16*y^2])2404sage: f.all_rational_preimages([P(-1,4)])2405[(-1/4 : 1), (5/4 : 1), (-3/4 : 1), (3/4 : 1), (-5/4 : 1), (1/4 : 1),2406(7/4 : 1), (-7/4 : 1)]24072408::24092410sage: P.<x,y,z> = ProjectiveSpace(QQ,2)2411sage: H = End(P)2412sage: f = H([76*x^2 - 180*x*y + 45*y^2 + 14*x*z + 45*y*z - 90*z^2, 67*x^2 - 180*x*y - 157*x*z + 90*y*z, -90*z^2])2413sage: f.all_rational_preimages([P(-9,-4,1)])2414[(-9 : -4 : 1), (1 : 3 : 1), (0 : 4 : 1), (0 : -1 : 1), (0 : 0 : 1), (12415: 1 : 1), (0 : 1 : 1), (1 : 2 : 1), (1 : 0 : 1)]24162417::24182419A non-periodic example.2420sage: P.<x,y> = ProjectiveSpace(QQ,1)2421sage: H = End(P)2422sage: f = H([x^2 + y^2,2*x*y])2423sage: f.all_rational_preimages([P(17,15)])2424[(5/3 : 1), (1/3 : 1), (3/5 : 1), (3 : 1)]2425"""2426if not self.is_endomorphism():2427raise NotImplementedError("Must be an endomorphism of projective space")2428if self.domain().base_ring()!=QQ:2429raise NotImplementedError("Must be QQ")24302431PS=self.domain()2432RPS=PS.base_ring()2433all_preimages=set()2434while points!=[]:2435P=points.pop()2436preimages=self.rational_preimages(P)2437for i in range(len(preimages)):2438if not preimages[i] in all_preimages:2439points.append(preimages[i])2440all_preimages.add(preimages[i])2441return(list(all_preimages))24422443def rational_preperiodic_points(self,**kwds):2444r"""2445Determined the set of rational preperiodic points for ``self``.2446``self`` must be defined over `\QQ` and be an endomorphism of projective space.24472448The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having2449trouble getting a partiuclar map to finish, try first computing the possible periods, then2450try various different ``lifting_prime``.24512452ALGORITHM:24532454- Determines the list of possible periods.24552456- Determines the rational periodic points from the possible periods.24572458- Determines the rational preperiodic points from the rational periodic points2459by determining rational preimages.24602461INPUT:24622463kwds:24642465- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the2466limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)2467default: [1,20]24682469- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the2470lifting. default: 2324712472- ``periods`` - a list of positive integers which is the list of possible periods. (optional)24732474- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)24752476OUTPUT:24772478- a list of rational points in projective space.24792480Examples::24812482sage: PS.<x,y> = ProjectiveSpace(1,QQ)2483sage: H = End(PS)2484sage: f = H([x^2 -y^2,3*x*y])2485sage: f.rational_preperiodic_points()2486[(-2 : 1), (0 : 1), (-1/2 : 1), (1 : 0), (1 : 1), (2 : 1), (-1 : 1),2487(1/2 : 1)]24882489::24902491sage: PS.<x,y> = ProjectiveSpace(1,QQ)2492sage: H = End(PS)2493sage: f = H([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3])2494sage: f.rational_preperiodic_points(prime_bound=10)2495[(1 : 1), (1 : 0), (-1 : 1), (3 : 1), (0 : 1)]24962497::24982499sage: PS.<x,y,z> = ProjectiveSpace(2,QQ)2500sage: H = End(PS)2501sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2])2502sage: f.rational_preperiodic_points(prime_bound=[1,8],lifting_prime=7,periods=[2]) # long time2503[(5/4 : 1 : 1), (1/4 : 1 : 1), (1/4 : -2 : 1), (1/4 : 2 : 1), (5/4 : -1 : 1), (5/4 : 2 : 1),2504(-5/4 : 2 : 1), (1/4 : -1 : 1), (-1/4 : 2 : 1), (-1/4 : -2 : 1), (-5/4 : 1 : 1), (5/4 : 0 : 1),2505(-5/4 : 0 : 1), (-1/4 : 1 : 1), (-1/4 : 0 : 1), (-5/4 : -1 : 1), (5/4 : -2 : 1), (-1/4 : -1 : 1),2506(-5/4 : -2 : 1), (1/4 : 0 : 1)]2507"""2508#input error checking done in possible_periods and rational_peridic_points2509badprimes = kwds.pop("bad_primes",None)2510periods = kwds.pop("periods",None)2511primebound = kwds.pop("prime_bound",[1,20])2512if badprimes==None:2513badprimes=self.primes_of_bad_reduction()2514if periods==None:2515periods=self.possible_periods(prime_bound=primebound,bad_primes=badprimes) #determine the set of possible periods2516if periods==[]:2517return([]) #no rational preperiodic points2518else:2519p = kwds.pop("lifting_prime",23)2520T=self.rational_periodic_points(prime_bound=primebound,lifting_prime=p,periods=periods,bad_primes=badprimes) #find the rationla preperiodic points2521preper=self.all_rational_preimages(T) #find the preperiodic points2522preper=list(preper)2523return(preper)25242525def rational_preperiodic_graph(self,**kwds):2526r"""2527Determines the set of rational preperiodic points for ``self``.2528self must be defined over `\QQ` and be an endomorphism of projective space.25292530ALGORITHM:2531- Determines the list of possible periods.25322533- Determines the rational periodic points from the possible periods.25342535- Determines the rational preperiodic points from the rational periodic points2536by determining rational preimages.253725382539INPUT:25402541kwds:25422543- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the2544limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)2545default: [1,20]25462547- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the2548lifting. default: 2325492550- ``periods`` - a list of positive integers which is the list of possible periods. (optional)25512552- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)25532554OUTPUT:25552556- a digraph representing the orbits of the rational preperiodic points in projective space.25572558Examples::25592560sage: PS.<x,y> = ProjectiveSpace(1,QQ)2561sage: H = End(PS)2562sage: f = H([7*x^2 - 28*y^2,24*x*y])2563sage: f.rational_preperiodic_graph()2564Looped digraph on 12 vertices25652566::25672568sage: PS.<x,y> = ProjectiveSpace(1,QQ)2569sage: H = End(PS)2570sage: f = H([-3/2*x^3 +19/6*x*y^2,y^3])2571sage: f.rational_preperiodic_graph(prime_bound=[1,8])2572Looped digraph on 12 vertices25732574::25752576sage: PS.<x,y,z> = ProjectiveSpace(2,QQ)2577sage: H = End(PS)2578sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3])2579sage: f.rational_preperiodic_graph(prime_bound=[1,11],lifting_prime=13) # long time2580Looped digraph on 30 vertices2581"""2582#input checking done in .rational_preperiodic_points()2583preper=self.rational_preperiodic_points(**kwds)2584g=self._preperiodic_points_to_cyclegraph(preper)2585return(g)258625872588class SchemeMorphism_polynomial_projective_space_finite_field(SchemeMorphism_polynomial_projective_space_field):25892590def orbit_structure(self, P):2591r"""2592Every point is preperiodic over a finite field. This funtion returns the pair `[m,n]` where `m` is the2593preperiod and `n` the period of the point ``P`` by ``self``.25942595INPUT:25962597- ``P`` -- a point in ``self.domain()``25982599OUTPUT:26002601- a list `[m,n]` of integers26022603EXAMPLES::26042605sage: P.<x,y,z> = ProjectiveSpace(GF(5),2)2606sage: H = Hom(P,P)2607sage: f = H([x^2+y^2,y^2,z^2 + y*z])2608sage: f.orbit_structure(P(2,1,2))2609[0, 6]26102611::26122613sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)2614sage: X = P.subscheme(x^2-y^2)2615sage: H = Hom(X,X)2616sage: f = H([x^2,y^2,z^2])2617sage: f.orbit_structure(X(1,1,2))2618[0, 2]26192620::26212622sage: P.<x,y> = ProjectiveSpace(GF(13),1)2623sage: H = Hom(P,P)2624sage: f = H([x^2-y^2,y^2])2625sage: f.orbit_structure(P(3,4))2626[2, 3]2627"""2628return(P.orbit_structure(self))26292630def cyclegraph(self):2631r"""2632returns Digraph of all orbits of ``self`` mod `p`.26332634For subschemes, only points on the subscheme whose image are2635also on the subscheme are in the digraph.26362637OUTPUT:26382639- a digraph26402641EXAMPLES::26422643sage: P.<x,y> = ProjectiveSpace(GF(13),1)2644sage: H = Hom(P,P)2645sage: f = H([x^2-y^2,y^2])2646sage: f.cyclegraph()2647Looped digraph on 14 vertices26482649::26502651sage: P.<x,y,z> = ProjectiveSpace(GF(5^2,'t'),2)2652sage: H = Hom(P,P)2653sage: f = H([x^2+y^2,y^2,z^2+y*z])2654sage: f.cyclegraph()2655Looped digraph on 651 vertices26562657::26582659sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)2660sage: X = P.subscheme(x^2-y^2)2661sage: H = Hom(X,X)2662sage: f = H([x^2,y^2,z^2])2663sage: f.cyclegraph()2664Looped digraph on 15 vertices2665"""2666if self.domain() != self.codomain():2667raise NotImplementedError("Domain and Codomain must be equal")2668V=[]2669E=[]2670from sage.schemes.projective.projective_space import is_ProjectiveSpace2671if is_ProjectiveSpace(self.domain()) is True:2672for P in self.domain():2673V.append(str(P))2674Q=self(P)2675Q.normalize_coordinates()2676E.append([str(Q)])2677else:2678X=self.domain()2679for P in X.ambient_space():2680try:2681XP=X.point(P)2682V.append(str(XP))2683Q=self(XP)2684Q.normalize_coordinates()2685E.append([str(Q)])2686except TypeError: # not a point on the scheme2687pass2688from sage.graphs.digraph import DiGraph2689g=DiGraph(dict(zip(V,E)), loops=True)2690return g26912692def possible_periods(self,return_points=False):2693r"""2694Returns the list of possible minimal periods of a periodic point2695over `\QQ` and (optionally) a point in each cycle.26962697ALGORITHM:26982699The list comes from: Hutz, Good reduction of periodic points, Illinois Journal of2700Mathematics 53 (Winter 2009), no. 4, 1109-1126.27012702INPUT:27032704- ``return_points`` - Boolean (optional) - a value of True returns the points as well as the possible periods.27052706OUTPUT:27072708- a list of positive integers, or a list of pairs of projective points and periods if ``flag`` is 1.27092710Examples::27112712sage: P.<x,y> = ProjectiveSpace(GF(23),1)2713sage: H = End(P)2714sage: f = H([x^2-2*y^2,y^2])2715sage: f.possible_periods()2716[1, 5, 11, 22, 110]27172718::27192720sage: P.<x,y> = ProjectiveSpace(GF(13),1)2721sage: H = End(P)2722sage: f = H([x^2-y^2,y^2])2723sage: f.possible_periods(True)2724[[(1 : 0), 1], [(0 : 1), 2], [(3 : 1), 3], [(3 : 1), 36]]27252726::27272728sage: PS.<x,y,z> = ProjectiveSpace(2,GF(7))2729sage: H = End(PS)2730sage: f = H([-360*x^3 + 760*x*z^2, y^3 - 604*y*z^2 + 240*z^3, 240*z^3])2731sage: f.possible_periods()2732[1, 2, 4, 6, 12, 14, 28, 42, 84]27332734.. TODO::27352736- do not reutrn duplicate points27372738- check == False to speed up?27392740- move to Cython27412742"""2743if not is_PrimeFiniteField(self.domain().base_ring()):2744raise TypeError("Must be prime field")2745if not self.is_endomorphism():2746raise NotImplementedError("Must be an endomorphism of projective space")27472748PS=self.domain()2749p=PS.base_ring().order()2750N=PS.dimension_relative()2751pointsdict=PS.rational_points_dictionary() #assume p is prime2752pointslist=list(pointsdict)2753hashlist=pointsdict.values()2754pointtable=[[0,0] for i in range(len(pointsdict))]2755index=12756periods=set()2757points_periods=[]2758for j in range(len(pointsdict)):2759hashP=hashlist[j]2760if pointtable[hashP][1]==0:2761startindex=index2762P=pointslist[j]2763while pointtable[hashP][1]==0:2764pointtable[hashP][1]=index2765Q=self(P)2766Q.normalize_coordinates()2767hashQ=pointsdict[Q]2768pointtable[hashP][0]=hashQ2769P=Q2770hashP=hashQ2771index+=12772if pointtable[hashP][1]>= startindex:2773period=index-pointtable[hashP][1]2774periods.add(period)2775points_periods.append([P,period])2776l=P.multiplier(self,period,False)2777lorders=set()2778for poly,_ in l.charpoly().factor():2779if poly.degree() == 1:2780eig = -poly.constant_coefficient()2781if not eig:2782continue # exclude 02783else:2784eig = GF(p ** poly.degree(), 't', modulus=poly).gen()2785if eig:2786lorders.add(eig.multiplicative_order())2787S = subsets(lorders)2788S.next() # get rid of the empty set2789rvalues=set()2790for s in S:2791rvalues.add(lcm(s))2792rvalues=list(rvalues)2793if N==1:2794for k in range(len(rvalues)):2795r=rvalues[k]2796periods.add(period*r)2797points_periods.append([P,period*r])2798if p==2 or p==3: #need e=1 for N=1, QQ2799periods.add(period*r*p)2800points_periods.append([P,period*r*p])2801else:2802for k in range(len(rvalues)):2803r=rvalues[k]2804periods.add(period*r)2805periods.add(period*r*p)2806points_periods.append([P,period*r])2807points_periods.append([P,period*r*p])2808if p==2: #need e=3 for N>1, QQ2809periods.add(period*r*4)2810points_periods.append([P,period*r*4])2811periods.add(period*r*8)2812points_periods.append([P,period*r*8])28132814if return_points==False:2815return(sorted(periods))2816else:2817return(points_periods)2818281928202821