Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/schemes/projective/projective_morphism.py
8820 views
1
r"""
2
Morphisms on projective varieties
3
4
A morphism of schemes determined by rational functions that define
5
what the morphism does on points in the ambient projective space.
6
7
8
AUTHORS:
9
10
- David Kohel, William Stein
11
12
- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as
13
a projective point.
14
15
- Volker Braun (2011-08-08): Renamed classes, more documentation, misc
16
cleanups.
17
18
- Ben Hutz (2013-03) iteration functionality and new directory structure
19
for affine/projective, height functionality
20
21
- Brian Stout, Ben Hutz (Nov 2013) - added minimal model functionality
22
23
"""
24
25
# Historical note: in trac #11599, V.B. renamed
26
# * _point_morphism_class -> _morphism
27
# * _homset_class -> _point_homset
28
29
#*****************************************************************************
30
# Copyright (C) 2011 Volker Braun <[email protected]>
31
# Copyright (C) 2006 David Kohel <[email protected]>
32
# Copyright (C) 2006 William Stein <[email protected]>
33
#
34
# Distributed under the terms of the GNU General Public License (GPL)
35
# as published by the Free Software Foundation; either version 2 of
36
# the License, or (at your option) any later version.
37
# http://www.gnu.org/licenses/
38
#*****************************************************************************
39
40
from sage.categories.homset import Hom
41
from sage.functions.all import sqrt
42
from sage.matrix.constructor import matrix, identity_matrix
43
from sage.misc.cachefunc import cached_method
44
from sage.misc.misc import subsets
45
from sage.misc.mrange import xmrange
46
from sage.modules.free_module_element import vector
47
from sage.rings.all import Integer, moebius
48
from sage.rings.arith import gcd, lcm, next_prime, binomial, primes
49
from sage.rings.complex_field import ComplexField
50
from sage.rings.finite_rings.constructor import GF, is_PrimeFiniteField
51
from sage.rings.finite_rings.integer_mod_ring import Zmod
52
from sage.rings.fraction_field import FractionField
53
from sage.rings.integer_ring import ZZ
54
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
55
from sage.rings.quotient_ring import QuotientRing_generic
56
from sage.rings.rational_field import QQ
57
from sage.rings.real_mpfr import RealField
58
from sage.schemes.generic.morphism import SchemeMorphism_polynomial
59
from sage.symbolic.constants import e
60
from copy import copy
61
62
63
class SchemeMorphism_polynomial_projective_space(SchemeMorphism_polynomial):
64
"""
65
A morphism of schemes determined by rational functions that define
66
what the morphism does on points in the ambient projective space.
67
68
EXAMPLES::
69
70
sage: R.<x,y> = QQ[]
71
sage: P1 = ProjectiveSpace(R)
72
sage: H = P1.Hom(P1)
73
sage: H([y,2*x])
74
Scheme endomorphism of Projective Space of dimension 1 over Rational Field
75
Defn: Defined on coordinates by sending (x : y) to
76
(y : 2*x)
77
78
An example of a morphism between projective plane curves (see :trac:`10297`)::
79
80
sage: P2.<x,y,z> = ProjectiveSpace(QQ,2)
81
sage: f = x^3+y^3+60*z^3
82
sage: g = y^2*z-( x^3 - 6400*z^3/3)
83
sage: C = Curve(f)
84
sage: E = Curve(g)
85
sage: xbar,ybar,zbar = C.coordinate_ring().gens()
86
sage: H = C.Hom(E)
87
sage: H([zbar,xbar-ybar,-(xbar+ybar)/80])
88
Scheme morphism:
89
From: Projective Curve over Rational Field defined by x^3 + y^3 + 60*z^3
90
To: Projective Curve over Rational Field defined by -x^3 + y^2*z + 6400/3*z^3
91
Defn: Defined on coordinates by sending (x : y : z) to
92
(z : x - y : -1/80*x - 1/80*y)
93
94
A more complicated example::
95
96
sage: P2.<x,y,z> = ProjectiveSpace(2,QQ)
97
sage: P1 = P2.subscheme(x-y)
98
sage: H12 = P1.Hom(P2)
99
sage: H12([x^2,x*z, z^2])
100
Scheme morphism:
101
From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
102
x - y
103
To: Projective Space of dimension 2 over Rational Field
104
Defn: Defined on coordinates by sending (x : y : z) to
105
(y^2 : y*z : z^2)
106
107
We illustrate some error checking::
108
109
sage: R.<x,y> = QQ[]
110
sage: P1 = ProjectiveSpace(R)
111
sage: H = P1.Hom(P1)
112
sage: f = H([x-y, x*y])
113
Traceback (most recent call last):
114
...
115
ValueError: polys (=[x - y, x*y]) must be of the same degree
116
117
sage: H([x-1, x*y+x])
118
Traceback (most recent call last):
119
...
120
ValueError: polys (=[x - 1, x*y + x]) must be homogeneous
121
122
sage: H([exp(x),exp(y)])
123
Traceback (most recent call last):
124
...
125
TypeError: polys (=[e^x, e^y]) must be elements of
126
Multivariate Polynomial Ring in x, y over Rational Field
127
"""
128
129
def __init__(self, parent, polys, check=True):
130
"""
131
The Python constructor.
132
133
See :class:`SchemeMorphism_polynomial` for details.
134
135
EXAMPLES::
136
137
sage: P1.<x,y> = ProjectiveSpace(QQ,1)
138
sage: H = P1.Hom(P1)
139
sage: H([y,2*x])
140
Scheme endomorphism of Projective Space of dimension 1 over Rational Field
141
Defn: Defined on coordinates by sending (x : y) to
142
(y : 2*x)
143
"""
144
SchemeMorphism_polynomial.__init__(self, parent, polys, check)
145
if check:
146
# morphisms from projective space are always given by
147
# homogeneous polynomials of the same degree
148
try:
149
d = polys[0].degree()
150
except AttributeError:
151
polys = [f.lift() for f in polys]
152
if not all([f.is_homogeneous() for f in polys]):
153
raise ValueError("polys (=%s) must be homogeneous"%polys)
154
degs = [f.degree() for f in polys]
155
if not all([d == degs[0] for d in degs[1:]]):
156
raise ValueError("polys (=%s) must be of the same degree"%polys)
157
158
def __eq__(self, right):
159
"""
160
Tests the equality of two projective spaces.
161
162
INPUT:
163
164
- ``right`` - a map on projective space
165
166
OUTPUT:
167
168
- Boolean - True if ``self`` and ``right`` define the same projective map. False otherwise.
169
170
EXAMPLES::
171
172
sage: P1.<x,y> = ProjectiveSpace(RR,1)
173
sage: P2.<x,y> = ProjectiveSpace(QQ,1)
174
sage: P1==P2
175
False
176
177
::
178
179
sage: R.<x,y> = QQ[]
180
sage: P1 = ProjectiveSpace(R)
181
sage: P2.<x,y> = ProjectiveSpace(QQ,1)
182
sage: P1==P2
183
True
184
"""
185
if not isinstance(right, SchemeMorphism_polynomial):
186
return False
187
else:
188
n = len(self._polys)
189
for i in range(0,n):
190
for j in range(i+1,n):
191
if self._polys[i]*right._polys[j] != self._polys[j]*right._polys[i]:
192
return False
193
return True
194
195
def __ne__(self, right):
196
"""
197
Tests the inequality of two projective spaces.
198
199
INPUT:
200
201
- ``right`` -- a map on projective space
202
203
OUTPUT:
204
205
- Boolean -- True if ``self`` and ``right`` define different projective maps. False otherwise.
206
207
EXAMPLES::
208
209
sage: P1.<x,y> = ProjectiveSpace(RR,1)
210
sage: P2.<x,y> = ProjectiveSpace(QQ,1)
211
sage: P1!=P2
212
True
213
214
::
215
216
sage: R.<x,y> = QQ[]
217
sage: P1 = ProjectiveSpace(R)
218
sage: P2.<x,y> = ProjectiveSpace(QQ,1)
219
sage: P1!=P2
220
False
221
"""
222
if not isinstance(right, SchemeMorphism_polynomial):
223
return True
224
else:
225
n=len(self._polys)
226
for i in range(0,n):
227
for j in range(i+1,n):
228
if self._polys[i]*right._polys[j] != self._polys[j]*right._polys[i]:
229
return True
230
return False
231
232
def scale_by(self, t):
233
"""
234
Scales each coordinates by a factor of `t`.
235
236
A ``TypeError`` occurs if the point is not in the coordinate_ring
237
of the parent after scaling.
238
239
INPUT:
240
241
- ``t`` -- a ring element
242
243
OUTPUT:
244
245
- None.
246
247
EXAMPLES::
248
249
sage: A.<x,y> = ProjectiveSpace(QQ,1)
250
sage: H = Hom(A,A)
251
sage: f = H([x^3-2*x*y^2,x^2*y])
252
sage: f.scale_by(1/x)
253
sage: f
254
Scheme endomorphism of Projective Space of dimension 1 over Rational
255
Field
256
Defn: Defined on coordinates by sending (x : y) to
257
(x^2 - 2*y^2 : x*y)
258
259
::
260
261
sage: R.<t> = PolynomialRing(QQ)
262
sage: P.<x,y> = ProjectiveSpace(R,1)
263
sage: H = Hom(P,P)
264
sage: f = H([3/5*x^2,6*y^2])
265
sage: f.scale_by(5/3*t); f
266
Scheme endomorphism of Projective Space of dimension 1 over Univariate
267
Polynomial Ring in t over Rational Field
268
Defn: Defined on coordinates by sending (x : y) to
269
(t*x^2 : 10*t*y^2)
270
271
::
272
273
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)
274
sage: X = P.subscheme(x^2-y^2)
275
sage: H = Hom(X,X)
276
sage: f = H([x^2,y^2,z^2])
277
sage: f.scale_by(x-y);f
278
Scheme endomorphism of Closed subscheme of Projective Space of dimension
279
2 over Finite Field of size 7 defined by:
280
x^2 - y^2
281
Defn: Defined on coordinates by sending (x : y : z) to
282
(x*y^2 - y^3 : x*y^2 - y^3 : x*z^2 - y*z^2)
283
"""
284
if t == 0:
285
raise ValueError("Cannot scale by 0")
286
R=self.domain().coordinate_ring()
287
if isinstance(R, QuotientRing_generic):
288
phi=R.coerce_map_from(self.domain().ambient_space().coordinate_ring())
289
for i in range(self.codomain().ambient_space().dimension_relative()+1):
290
self._polys[i]=phi(self._polys[i]*t).lift()
291
else:
292
for i in range(self.codomain().ambient_space().dimension_relative()+1):
293
self._polys[i]=R(self._polys[i]*t)
294
295
def normalize_coordinates(self):
296
"""
297
Scales by 1/gcd of the coordinate functions. Also, scales to clear any denominators from the coefficients.
298
This is done in place.
299
300
OUTPUT:
301
302
- None.
303
304
EXAMPLES::
305
306
sage: P.<x,y> = ProjectiveSpace(QQ,1)
307
sage: H = Hom(P,P)
308
sage: f = H([5/4*x^3,5*x*y^2])
309
sage: f.normalize_coordinates(); f
310
Scheme endomorphism of Projective Space of dimension 1 over Rational
311
Field
312
Defn: Defined on coordinates by sending (x : y) to
313
(x^2 : 4*y^2)
314
315
::
316
317
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)
318
sage: X = P.subscheme(x^2-y^2)
319
sage: H = Hom(X,X)
320
sage: f = H([x^3+x*y^2,x*y^2,x*z^2])
321
sage: f.normalize_coordinates(); f
322
Scheme endomorphism of Closed subscheme of Projective Space of dimension
323
2 over Finite Field of size 7 defined by:
324
x^2 - y^2
325
Defn: Defined on coordinates by sending (x : y : z) to
326
(2*y^2 : y^2 : z^2)
327
328
.. NOTE:: gcd raises an error if the base_ring does not support gcds.
329
"""
330
GCD = gcd(self[0],self[1])
331
index=2
332
if self[0].lc()>0 or self[1].lc() >0:
333
neg=0
334
else:
335
neg=1
336
N=self.codomain().ambient_space().dimension_relative()+1
337
while GCD!=1 and index < N:
338
if self[index].lc()>0:
339
neg=0
340
GCD=gcd(GCD,self[index])
341
index+=+1
342
343
if GCD != 1:
344
R=self.domain().base_ring()
345
if neg == 1:
346
self.scale_by(R(-1)/GCD)
347
else:
348
self.scale_by(R(1)/GCD)
349
else:
350
if neg == 1:
351
self.scale_by(-1)
352
353
#clears any denominators from the coefficients
354
LCM = lcm([self[i].denominator() for i in range(N)])
355
self.scale_by(LCM)
356
357
#scales by 1/gcd of the coefficients.
358
GCD = gcd([self[i].content() for i in range(N)])
359
if GCD!=1:
360
self.scale_by(1/GCD)
361
362
def dynatomic_polynomial(self, period):
363
r"""
364
For a map `f:\mathbb{P}^1 \to \mathbb{P}^1` this function computes the dynatomic polynomial.
365
366
The dynatomic polynomial is the analog of the cyclotomic
367
polynomial and its roots are the points of formal period `n`.
368
369
ALGORITHM:
370
371
For a positive integer `n`, let `[F_n,G_n]` be the coordinates of the `nth` iterate of `f`.
372
Then construct
373
374
.. MATH::
375
376
\Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)}
377
378
where `\mu` is the Moebius function.
379
380
For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute
381
382
.. MATH::
383
384
\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})
385
386
REFERENCES:
387
388
.. [Hutz] B. Hutz. Efficient determination of rational preperiodic
389
points for endomorphisms of projective space.
390
:arxiv:`1210.6246`, 2012.
391
392
.. [MoPa] P. Morton and P. Patel. The Galois theory of periodic points
393
of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263.
394
395
INPUT:
396
397
- ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period
398
399
OUTPUT:
400
401
- If possible, a two variable polynomial in the coordinate ring of ``self``.
402
Otherwise a fraction field element of the coordinate ring of ``self``
403
404
.. TODO::
405
406
Do the division when the base ring is p-adic or a function field
407
so that the output is a polynomial.
408
409
EXAMPLES::
410
411
sage: P.<x,y> = ProjectiveSpace(QQ,1)
412
sage: H = Hom(P,P)
413
sage: f = H([x^2+y^2,y^2])
414
sage: f.dynatomic_polynomial(2)
415
x^2 + x*y + 2*y^2
416
417
::
418
419
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
420
sage: H = Hom(P,P)
421
sage: f = H([x^2+y^2,x*y])
422
sage: f.dynatomic_polynomial(4)
423
2*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^12
424
425
::
426
427
sage: P.<x,y> = ProjectiveSpace(CC,1)
428
sage: H = Hom(P,P)
429
sage: f = H([x^2+y^2,3*x*y])
430
sage: f.dynatomic_polynomial(3)
431
13.0000000000000*x^6 + 117.000000000000*x^4*y^2 +
432
78.0000000000000*x^2*y^4 + y^6
433
434
::
435
436
sage: P.<x,y> = ProjectiveSpace(QQ,1)
437
sage: H = Hom(P,P)
438
sage: f = H([x^2-10/9*y^2,y^2])
439
sage: f.dynatomic_polynomial([2,1])
440
x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6
441
442
::
443
444
sage: P.<x,y> = ProjectiveSpace(QQ,1)
445
sage: H = Hom(P,P)
446
sage: f = H([x^2-29/16*y^2,y^2])
447
sage: f.dynatomic_polynomial([2,3])
448
x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 +
449
8198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 +
450
172692729/16777216*y^12
451
452
::
453
454
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
455
sage: H = Hom(P,P)
456
sage: f = H([x^2-y^2,y^2])
457
sage: f.dynatomic_polynomial([1,2])
458
x^2 - x*y
459
460
::
461
462
sage: P.<x,y> = ProjectiveSpace(QQ,1)
463
sage: H = Hom(P,P)
464
sage: f = H([x^3-y^3,3*x*y^2])
465
sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4)
466
True
467
468
::
469
470
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
471
sage: H = Hom(P,P)
472
sage: f = H([x^2+y^2,x*y,z^2])
473
sage: f.dynatomic_polynomial(2)
474
Traceback (most recent call last):
475
...
476
TypeError: Does not make sense in dimension >1
477
478
::
479
480
sage: P.<x,y> = ProjectiveSpace(Qp(5),1)
481
sage: H = Hom(P,P)
482
sage: f = H([x^2+y^2,y^2])
483
sage: f.dynatomic_polynomial(2)
484
(x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y -
485
x*y^2 + y^3)
486
487
.. TODO:: It would be nice to get this to actually be a polynomial.
488
489
::
490
491
sage: L.<t> = PolynomialRing(QQ)
492
sage: P.<x,y> = ProjectiveSpace(L,1)
493
sage: H = Hom(P,P)
494
sage: f = H([x^2+t*y^2,y^2])
495
sage: f.dynatomic_polynomial(2)
496
x^2 + x*y + (t + 1)*y^2
497
498
::
499
500
sage: K.<c> = PolynomialRing(ZZ)
501
sage: P.<x,y> = ProjectiveSpace(K,1)
502
sage: H = Hom(P,P)
503
sage: f = H([x^2+ c*y^2,y^2])
504
sage: f.dynatomic_polynomial([1,2])
505
x^2 - x*y + (c + 1)*y^2
506
"""
507
if self.domain() != self.codomain():
508
raise TypeError("Must have same domain and codomain to iterate")
509
from sage.schemes.projective.projective_space import is_ProjectiveSpace
510
if is_ProjectiveSpace(self.domain()) is False:
511
raise NotImplementedError("Not implemented for subschemes")
512
if self.domain().dimension_relative()>1:
513
raise TypeError("Does not make sense in dimension >1")
514
515
if (isinstance(period,(list,tuple)) is False):
516
period=[0,period]
517
try:
518
period[0]=ZZ(period[0])
519
period[1]=ZZ(period[1])
520
except TypeError:
521
raise TypeError("Period and preperiod must be integers")
522
if period[1]<=0:
523
raise AttributeError("Period must be at least 1")
524
525
if period[0]!=0:
526
m=period[0]
527
fm=self.nth_iterate_map(m)
528
fm1=self.nth_iterate_map(m-1)
529
n=period[1]
530
PHI=1;
531
x=self.domain().gen(0)
532
y=self.domain().gen(1)
533
F=self._polys
534
f=F
535
for d in range(1,n+1):
536
if n%d == 0:
537
PHI=PHI*((y*F[0]-x*F[1])**moebius(n/d))
538
if d !=n: #avoid extra iteration
539
F=[f[0](F[0],F[1]),f[1](F[0],F[1])]
540
if m!=0:
541
PHI=PHI(fm._polys)/PHI(fm1._polys)
542
else:
543
PHI=1;
544
x=self.domain().gen(0)
545
y=self.domain().gen(1)
546
F=self._polys
547
f=F
548
for d in range(1,period[1]+1):
549
if period[1]%d == 0:
550
PHI=PHI*((y*F[0]-x*F[1])**moebius(period[1]/d))
551
if d !=period[1]: #avoid extra iteration
552
F=[f[0](F[0],F[1]),f[1](F[0],F[1])]
553
from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
554
from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic
555
if (self.domain().base_ring() == RealField()
556
or self.domain().base_ring() == ComplexField()):
557
PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
558
elif isinstance(self.domain().base_ring(),(PolynomialRing_general,MPolynomialRing_generic)):
559
from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing
560
from sage.rings.function_field.function_field import is_FunctionField
561
BR=self.domain().base_ring().base_ring()
562
if is_pAdicField(BR) or is_pAdicRing(BR) or is_FunctionField(BR):
563
raise NotImplementedError("Not implemented")
564
PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
565
#do it again to divide out by denominators of coefficients
566
PHI=PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage()
567
if PHI.denominator() == 1:
568
PHI=self.coordinate_ring()(PHI)
569
return(PHI)
570
571
def nth_iterate_map(self, n):
572
r"""
573
For a map ``self`` this function returns the nth iterate of ``self`` as a
574
function on ``self.domain()``
575
576
ALGORITHM:
577
578
Uses a form of successive squaring to reducing computations.
579
580
581
.. TODO:: This could be improved.
582
583
INPUT:
584
585
- ``n`` -- a positive integer.
586
587
OUTPUT:
588
589
- A map between projective spaces
590
591
EXAMPLES::
592
593
sage: P.<x,y> = ProjectiveSpace(QQ,1)
594
sage: H = Hom(P,P)
595
sage: f = H([x^2+y^2,y^2])
596
sage: f.nth_iterate_map(2)
597
Scheme endomorphism of Projective Space of dimension 1 over Rational
598
Field
599
Defn: Defined on coordinates by sending (x : y) to
600
(x^4 + 2*x^2*y^2 + 2*y^4 : y^4)
601
602
::
603
604
sage: P.<x,y> = ProjectiveSpace(CC,1)
605
sage: H = Hom(P,P)
606
sage: f = H([x^2-y^2,x*y])
607
sage: f.nth_iterate_map(3)
608
Scheme endomorphism of Projective Space of dimension 1 over Complex
609
Field with 53 bits of precision
610
Defn: Defined on coordinates by sending (x : y) to
611
(x^8 + (-7.00000000000000)*x^6*y^2 + 13.0000000000000*x^4*y^4 +
612
(-7.00000000000000)*x^2*y^6 + y^8 : x^7*y + (-4.00000000000000)*x^5*y^3
613
+ 4.00000000000000*x^3*y^5 - x*y^7)
614
615
::
616
617
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
618
sage: H = Hom(P,P)
619
sage: f = H([x^2-y^2,x*y,z^2+x^2])
620
sage: f.nth_iterate_map(2)
621
Scheme endomorphism of Projective Space of dimension 2 over Integer Ring
622
Defn: Defined on coordinates by sending (x : y : z) to
623
(x^4 - 3*x^2*y^2 + y^4 : x^3*y - x*y^3 : 2*x^4 - 2*x^2*y^2 + y^4
624
+ 2*x^2*z^2 + z^4)
625
626
::
627
628
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
629
sage: X = P.subscheme(x*z-y^2)
630
sage: H = Hom(X,X)
631
sage: f = H([x^2,x*z,z^2])
632
sage: f.nth_iterate_map(2)
633
Scheme endomorphism of Closed subscheme of Projective Space of dimension
634
2 over Rational Field defined by:
635
-y^2 + x*z
636
Defn: Defined on coordinates by sending (x : y : z) to
637
(x^4 : x^2*z^2 : z^4)
638
"""
639
if self.domain() != self.codomain():
640
raise TypeError("Domain and Codomain of function not equal")
641
if n <0:
642
raise TypeError("Iterate number must be a positive integer")
643
N=self.codomain().ambient_space().dimension_relative()+1
644
F=list(self._polys)
645
D=Integer(n).digits(2) #need base 2
646
Coord_ring=self.codomain().coordinate_ring()
647
if isinstance(Coord_ring, QuotientRing_generic):
648
PHI=[Coord_ring.gen(i).lift() for i in range(N)]
649
else:
650
PHI=[Coord_ring.gen(i) for i in range(N)]
651
for i in range(len(D)):
652
T=tuple([F[j] for j in range(N)])
653
for k in range(D[i]):
654
PHI=[PHI[j](T) for j in range(N)]
655
if i!=len(D)-1: #avoid extra iterate
656
F=[F[j](T) for j in range(N)] #'square'
657
H=Hom(self.domain(),self.codomain())
658
return(H(PHI))
659
660
def nth_iterate(self, P, n, normalize=False):
661
r"""
662
For a map ``self`` and a point `P` in ``self.domain()``
663
this function returns the nth iterate of `P` by ``self``.
664
665
If ``normalize`` is ``True``, then the coordinates are
666
automatically normalized.
667
668
.. TODO:: Is there a more efficient way to do this?
669
670
INPUT:
671
672
- ``P`` -- a point in ``self.domain()``
673
674
- ``n`` -- a positive integer.
675
676
- ``normalize`` - Boolean (optional Default: ``False``)
677
678
OUTPUT:
679
680
- A point in ``self.codomain()``
681
682
EXAMPLES::
683
684
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
685
sage: H = Hom(P,P)
686
sage: f = H([x^2+y^2,2*y^2])
687
sage: Q = P(1,1)
688
sage: f.nth_iterate(Q,4)
689
(32768 : 32768)
690
691
::
692
693
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
694
sage: H = Hom(P,P)
695
sage: f = H([x^2+y^2,2*y^2])
696
sage: Q = P(1,1)
697
sage: f.nth_iterate(Q,4,1)
698
(1 : 1)
699
700
Is this the right behavior? ::
701
702
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
703
sage: H = Hom(P,P)
704
sage: f = H([x^2,2*y^2,z^2-x^2])
705
sage: Q = P(2,7,1)
706
sage: f.nth_iterate(Q,2)
707
(-16/7 : -2744 : 1)
708
709
::
710
711
sage: R.<t> = PolynomialRing(QQ)
712
sage: P.<x,y,z> = ProjectiveSpace(R,2)
713
sage: H = Hom(P,P)
714
sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2])
715
sage: Q = P(2+t,7,t)
716
sage: f.nth_iterate(Q,2)
717
(t^4 + 2507*t^3 - 6787*t^2 + 10028*t + 16 : -2401*t^3 + 14406*t^2 -
718
28812*t + 19208 : t^4)
719
720
::
721
722
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
723
sage: X = P.subscheme(x^2-y^2)
724
sage: H = Hom(X,X)
725
sage: f = H([x^2,y^2,z^2])
726
sage: f.nth_iterate(X(2,2,3),3)
727
(256 : 256 : 6561)
728
729
::
730
731
sage: K.<c> = FunctionField(QQ)
732
sage: P.<x,y> = ProjectiveSpace(K,1)
733
sage: H = Hom(P,P)
734
sage: f = H([x^3-2*x*y^2 - c*y^3,x*y^2])
735
sage: f.nth_iterate(P(c,1),2)
736
((c^6 - 9*c^4 + 25*c^2 - c - 21)/(c^2 - 3) : 1)
737
"""
738
return(P.nth_iterate(self,n,normalize))
739
740
def degree(self):
741
r"""
742
This function returns the degree of ``self``.
743
744
The degree is defined as the degree of the homogeneous
745
polynomials that are the coordinates of ``self``.
746
747
OUTPUT:
748
749
- A positive integer
750
751
EXAMPLES::
752
753
sage: P.<x,y> = ProjectiveSpace(QQ,1)
754
sage: H = Hom(P,P)
755
sage: f = H([x^2+y^2,y^2])
756
sage: f.degree()
757
2
758
759
::
760
761
sage: P.<x,y,z> = ProjectiveSpace(CC,2)
762
sage: H = Hom(P,P)
763
sage: f = H([x^3+y^3,y^2*z,z*x*y])
764
sage: f.degree()
765
3
766
767
::
768
769
sage: R.<t> = PolynomialRing(QQ)
770
sage: P.<x,y,z> = ProjectiveSpace(R,2)
771
sage: H = Hom(P,P)
772
sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2])
773
sage: f.degree()
774
2
775
776
::
777
778
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
779
sage: X = P.subscheme(x^2-y^2)
780
sage: H = Hom(X,X)
781
sage: f = H([x^2,y^2,z^2])
782
sage: f.degree()
783
2
784
"""
785
return(self._polys[0].degree())
786
787
def dehomogenize(self, n):
788
r"""
789
Returns the standard dehomogenization at the nth coordinate `(\frac{self[0]}{self[n]},\frac{self[1]}{self[n]},...)`.
790
791
Note that the new function is defined over the fraction field
792
of the base ring of ``self``.
793
794
INPUT:
795
796
- ``n`` -- a nonnegative integer
797
798
OUTPUT:
799
800
- :class:`SchemeMorphism_polynomial_affine_space` (on nth affine patch)
801
802
EXAMPLES::
803
804
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
805
sage: H = Hom(P,P)
806
sage: f = H([x^2+y^2,y^2])
807
sage: f.dehomogenize(0)
808
Scheme endomorphism of Affine Space of dimension 1 over Integer Ring
809
Defn: Defined on coordinates by sending (x) to
810
(x^2/(x^2 + 1))
811
812
::
813
814
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
815
sage: H = Hom(P,P)
816
sage: f = H([x^2+y^2,y^2-z^2,2*z^2])
817
sage: f.dehomogenize(2)
818
Scheme endomorphism of Affine Space of dimension 2 over Rational Field
819
Defn: Defined on coordinates by sending (x0, x1) to
820
(1/2*x0^2 + 1/2*x1^2, 1/2*x1^2 - 1/2)
821
822
::
823
824
sage: R.<t> = PolynomialRing(QQ)
825
sage: P.<x,y,z> = ProjectiveSpace(FractionField(R),2)
826
sage: H = Hom(P,P)
827
sage: f = H([x^2+t*y^2,t*y^2-z^2,t*z^2])
828
sage: f.dehomogenize(2)
829
Scheme endomorphism of Affine Space of dimension 2 over Fraction Field
830
of Univariate Polynomial Ring in t over Rational Field
831
Defn: Defined on coordinates by sending (x0, x1) to
832
(1/t*x0^2 + x1^2, x1^2 - 1/t)
833
834
::
835
836
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
837
sage: X = P.subscheme(x^2-y^2)
838
sage: H = Hom(X,X)
839
sage: f = H([x^2,y^2,x*z])
840
sage: f.dehomogenize(2)
841
Scheme endomorphism of Closed subscheme of Affine Space of dimension 2
842
over Integer Ring defined by:
843
x0^2 - x1^2
844
Defn: Defined on coordinates by sending (x0, x1) to
845
(x1^2/x0, x1^2/x0)
846
"""
847
PS=self.domain()
848
A=PS.ambient_space()
849
if self._polys[n].substitute({A.gen(n):1}) == 0:
850
raise ValueError("Can't dehomogenize at 0 coordinate.")
851
else:
852
Aff=PS.affine_patch(n)
853
S=Aff.ambient_space().coordinate_ring()
854
R=A.coordinate_ring()
855
phi=R.hom([S.gen(j) for j in range(0,n)] + [1] + [S.gen(j) for j in range(n,A.dimension_relative())],S)
856
F=[]
857
for i in range(0,A.dimension_relative()+1):
858
if i !=n:
859
F.append(phi(self._polys[i])/phi(self._polys[n]))
860
H=Hom(Aff,Aff)
861
return(H(F))
862
863
def orbit(self, P, N, **kwds):
864
r"""
865
Returns the orbit of `P` by ``self``. If `n` is an integer it returns `[P,self(P),\ldots,self^n(P)]`.
866
If `n` is a list or tuple `n=[m,k]` it returns `[self^m(P),\ldots,self^k(P)]`.
867
Automatically normalize the points if ``normalize=True``. Perform the checks on point initialize if
868
``check=True``
869
870
INPUT:
871
872
- ``P`` -- a point in ``self.domain()``
873
874
- ``n`` -- a non-negative integer or list or tuple of two non-negative integers
875
876
kwds:
877
878
- ``check`` -- boolean (optional - default: ``True``)
879
880
- ``normalize`` -- boolean (optional - default: ``False``)
881
882
883
OUTPUT:
884
885
- a list of points in ``self.codomain()``
886
887
EXAMPLES::
888
889
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
890
sage: H = Hom(P,P)
891
sage: f = H([x^2+y^2,y^2-z^2,2*z^2])
892
sage: f.orbit(P(1,2,1),3)
893
[(1 : 2 : 1), (5 : 3 : 2), (34 : 5 : 8), (1181 : -39 : 128)]
894
895
::
896
897
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
898
sage: H = Hom(P,P)
899
sage: f = H([x^2+y^2,y^2-z^2,2*z^2])
900
sage: f.orbit(P(1,2,1),[2,4])
901
[(34 : 5 : 8), (1181 : -39 : 128), (1396282 : -14863 : 32768)]
902
903
::
904
905
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
906
sage: X = P.subscheme(x^2-y^2)
907
sage: H = Hom(X,X)
908
sage: f = H([x^2,y^2,x*z])
909
sage: f.orbit(X(2,2,3),3,normalize=True)
910
[(2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3)]
911
912
::
913
914
sage: P.<x,y> = ProjectiveSpace(QQ,1)
915
sage: H = Hom(P,P)
916
sage: f = H([x^2+y^2,y^2])
917
sage: f.orbit(P.point([1,2],False),4,check=False)
918
[(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)]
919
920
::
921
922
sage: K.<c> = FunctionField(QQ)
923
sage: P.<x,y> = ProjectiveSpace(K,1)
924
sage: H = Hom(P,P)
925
sage: f = H([x^2+c*y^2,y^2])
926
sage: f.orbit(P(0,1),3)
927
[(0 : 1), (c : 1), (c^2 + c : 1), (c^4 + 2*c^3 + c^2 + c : 1)]
928
"""
929
return(P.orbit(self,N,**kwds))
930
931
@cached_method
932
def is_morphism(self):
933
r"""
934
returns ``True`` if self is a morphism (no common zero of defining polynomials).
935
936
The map is a morphism if and only if the ideal generated by
937
the defining polynomials is the unit ideal.
938
939
OUTPUT:
940
941
- Boolean
942
943
EXAMPLES::
944
945
sage: P.<x,y> = ProjectiveSpace(QQ,1)
946
sage: H = Hom(P,P)
947
sage: f = H([x^2+y^2,y^2])
948
sage: f.is_morphism()
949
True
950
951
::
952
953
sage: P.<x,y,z> = ProjectiveSpace(RR,2)
954
sage: H = Hom(P,P)
955
sage: f = H([x*z-y*z,x^2-y^2,z^2])
956
sage: f.is_morphism()
957
False
958
959
::
960
961
sage: R.<t> = PolynomialRing(GF(5))
962
sage: P.<x,y,z> = ProjectiveSpace(R,2)
963
sage: H = Hom(P,P)
964
sage: f = H([x*z-t*y^2,x^2-y^2,t*z^2])
965
sage: f.is_morphism()
966
True
967
"""
968
from sage.schemes.projective.projective_space import is_ProjectiveSpace
969
if is_ProjectiveSpace(self.domain()) is False or is_ProjectiveSpace(self.codomain()) is False:
970
raise NotImplementedError
971
R=self.coordinate_ring()
972
F=self._polys
973
if R.base_ring().is_field():
974
J=R.ideal(F)
975
else:
976
S=PolynomialRing(R.base_ring().fraction_field(),R.gens(),R.ngens())
977
J=S.ideal([S.coerce(F[i]) for i in range(R.ngens())])
978
if J.dimension()>0:
979
return False
980
else:
981
return True
982
983
def resultant(self, normalize=False):
984
r"""
985
Computes the resultant of the defining polynomials of ``self`` if ``self`` is a map on the projective line.
986
987
If ``normalize`` is ``True``, then first normalize the coordinate
988
functions with :meth:`normalize_coordinates`.
989
990
INPUT:
991
992
- ``normalize`` -- Boolean (optional - default: ``False``)
993
994
OUTPUT:
995
996
- an element of ``self.codomain().base_ring()``
997
998
EXAMPLES::
999
1000
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1001
sage: H = Hom(P,P)
1002
sage: f = H([x^2+y^2,6*y^2])
1003
sage: f.resultant()
1004
36
1005
1006
::
1007
1008
sage: R.<t> = PolynomialRing(GF(17))
1009
sage: P.<x,y> = ProjectiveSpace(R,1)
1010
sage: H = Hom(P,P)
1011
sage: f = H([t*x^2+t*y^2,6*y^2])
1012
sage: f.resultant()
1013
2*t^2
1014
"""
1015
if self.domain().dimension_relative() > 1:
1016
raise TypeError("Only for dimension 1, use self.primes_of_bad_reduction() to get bad primes")
1017
if normalize is True:
1018
F=copy(self)
1019
F.normalize_coordinates()
1020
else:
1021
F=self
1022
x=self.domain().gen(0)
1023
y=self.domain().gen(1)
1024
d=self.degree()
1025
f=F[0].substitute({y:1})
1026
g=F[1].substitute({y:1})
1027
res=(f.lc()**(d-g.degree())*g.lc()**(d-f.degree())*f._pari_().polresultant(g, x))
1028
return(self.codomain().base_ring()(res))
1029
1030
@cached_method
1031
def primes_of_bad_reduction(self, check=True):
1032
r"""
1033
Determines the primes of bad reduction for a map `self: \mathbb{P}^N \to \mathbb{P}^N`
1034
defined over `\ZZ` or `\QQ`.
1035
1036
If ``check`` is ``True``, each prime is verified to be of bad reduction.
1037
1038
ALGORITHM:
1039
1040
`p` is a prime of bad reduction if and only if the defining
1041
polynomials of self have a common zero. Or stated another way,
1042
`p` is a prime of bad reducion if and only if the radical of
1043
the ideal defined by the defining polynomials of self is not
1044
`(x_0,x_1,\ldots,x_N)`. This happens if and only if some
1045
power of each `x_i` is not in the ideal defined by the
1046
defining polynomials of self. This last condition is what is
1047
checked. The lcm of the coefficients of the monomials `x_i` in
1048
a groebner basis is computed. This may return extra primes.
1049
1050
INPUT:
1051
1052
- ``check`` -- Boolean (optional - default: ``True``)
1053
1054
OUTPUT:
1055
1056
- a list of integer primes.
1057
1058
EXAMPLES::
1059
1060
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1061
sage: H = Hom(P,P)
1062
sage: f = H([1/3*x^2+1/2*y^2,y^2])
1063
sage: print f.primes_of_bad_reduction()
1064
[2, 3]
1065
1066
::
1067
1068
sage: P.<x,y,z,w> = ProjectiveSpace(QQ,3)
1069
sage: H = Hom(P,P)
1070
sage: f = H([12*x*z-7*y^2,31*x^2-y^2,26*z^2,3*w^2-z*w])
1071
sage: f.primes_of_bad_reduction()
1072
[2, 3, 7, 13, 31]
1073
1074
This is an example where check=False returns extra primes::
1075
1076
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
1077
sage: H = Hom(P,P)
1078
sage: 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])
1079
sage: f.primes_of_bad_reduction(False)
1080
[2, 5, 37, 2239, 304432717]
1081
sage: f.primes_of_bad_reduction()
1082
[5, 37, 2239, 304432717]
1083
"""
1084
if self.base_ring() != ZZ and self.base_ring() != QQ:
1085
raise TypeError("Must be ZZ or QQ")
1086
from sage.schemes.projective.projective_space import is_ProjectiveSpace
1087
if is_ProjectiveSpace(self.domain()) is False or is_ProjectiveSpace(self.codomain()) is False:
1088
raise NotImplementedError
1089
R=self.coordinate_ring()
1090
F=self._polys
1091
if R.base_ring().is_field():
1092
J=R.ideal(F)
1093
else:
1094
S=PolynomialRing(R.base_ring().fraction_field(),R.gens(),R.ngens())
1095
J=S.ideal([S.coerce(F[i]) for i in range(R.ngens())])
1096
if J.dimension()>0:
1097
raise TypeError("Not a morphism.")
1098
#normalize to coefficients in the ring not the fraction field.
1099
F=[F[i]*lcm([F[j].denominator() for j in range(len(F))]) for i in range(len(F))]
1100
1101
#move the ideal to the ring of integers
1102
if R.base_ring().is_field():
1103
S=PolynomialRing(R.base_ring().ring_of_integers(),R.gens(),R.ngens())
1104
F=[F[i].change_ring(R.base_ring().ring_of_integers()) for i in range(len(F))]
1105
J=S.ideal(F)
1106
else:
1107
J=R.ideal(F)
1108
GB=J.groebner_basis()
1109
badprimes=[]
1110
1111
#get the primes dividing the coefficients of the monomials x_i^k_i
1112
for i in range(len(GB)):
1113
LT=GB[i].lt().degrees()
1114
power=0
1115
for j in range(R.ngens()):
1116
if LT[j]!=0:
1117
power+=1
1118
if power == 1:
1119
badprimes=badprimes+GB[i].lt().coefficients()[0].support()
1120
badprimes=list(set(badprimes))
1121
badprimes.sort()
1122
1123
#check to return only the truly bad primes
1124
if check == True:
1125
index=0
1126
while index < len(badprimes): #figure out which primes are really bad primes...
1127
S=PolynomialRing(GF(badprimes[index]),R.gens(),R.ngens())
1128
J=S.ideal([S.coerce(F[j]) for j in range(R.ngens())])
1129
if J.dimension() == 0:
1130
badprimes.pop(index)
1131
else:
1132
index+=1
1133
return(badprimes)
1134
1135
def conjugate(self, M):
1136
r"""
1137
Conjugates ``self`` by ``M``, i.e. `M^{-1} \circ f \circ M`.
1138
1139
If possible the map will be defined over the same space as
1140
``self``. Otherwise, will try to coerce to the base_ring of
1141
``M``.
1142
1143
INPUT:
1144
1145
- ``M`` -- a square invertible matrix
1146
1147
OUTPUT:
1148
1149
- a map from ``self.domain()`` to ``self.codomain()``.
1150
1151
EXAMPLES::
1152
1153
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
1154
sage: H = Hom(P,P)
1155
sage: f = H([x^2+y^2,y^2])
1156
sage: f.conjugate(matrix([[1,2],[0,1]]))
1157
Scheme endomorphism of Projective Space of dimension 1 over Integer Ring
1158
Defn: Defined on coordinates by sending (x : y) to
1159
(x^2 + 4*x*y + 3*y^2 : y^2)
1160
1161
::
1162
1163
sage: R.<x> = PolynomialRing(QQ)
1164
sage: K.<i> = NumberField(x^2+1)
1165
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
1166
sage: H = Hom(P,P)
1167
sage: f = H([x^3+y^3,y^3])
1168
sage: f.conjugate(matrix([[i,0],[0,-i]]))
1169
Scheme endomorphism of Projective Space of dimension 1 over Integer Ring
1170
Defn: Defined on coordinates by sending (x : y) to
1171
(-x^3 + y^3 : -y^3)
1172
1173
::
1174
1175
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
1176
sage: H = Hom(P,P)
1177
sage: f = H([x^2+y^2,y^2,y*z])
1178
sage: f.conjugate(matrix([[1,2,3],[0,1,2],[0,0,1]]))
1179
Scheme endomorphism of Projective Space of dimension 2 over Integer Ring
1180
Defn: Defined on coordinates by sending (x : y : z) to
1181
(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)
1182
1183
::
1184
1185
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
1186
sage: H = Hom(P,P)
1187
sage: f = H([x^2+y^2,y^2])
1188
sage: f.conjugate(matrix([[2,0],[0,1/2]]))
1189
Scheme endomorphism of Projective Space of dimension 1 over Multivariate
1190
Polynomial Ring in x, y over Rational Field
1191
Defn: Defined on coordinates by sending (x : y) to
1192
(2*x^2 + 1/8*y^2 : 1/2*y^2)
1193
1194
::
1195
1196
sage: R.<x> = PolynomialRing(QQ)
1197
sage: K.<i> = NumberField(x^2+1)
1198
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1199
sage: H = Hom(P,P)
1200
sage: f = H([1/3*x^2+1/2*y^2,y^2])
1201
sage: f.conjugate(matrix([[i,0],[0,-i]]))
1202
Scheme endomorphism of Projective Space of dimension 1 over Multivariate
1203
Polynomial Ring in x, y over Number Field in i with defining polynomial
1204
x^2 + 1
1205
Defn: Defined on coordinates by sending (x : y) to
1206
((1/3*i)*x^2 + (1/2*i)*y^2 : (-i)*y^2)
1207
"""
1208
1209
if M.is_square() == 1 and M.determinant() != 0 and M.ncols() == self.domain().ambient_space().dimension_relative()+1:
1210
X=M*vector(self[0].parent().gens())
1211
F=vector(self._polys)
1212
F=F(list(X))
1213
N=M.inverse()
1214
F=N*F
1215
R=self.codomain().ambient_space().coordinate_ring()
1216
try:
1217
F = [R(f) for f in F]
1218
PS=self.codomain()
1219
except TypeError: #no longer defined over same ring
1220
R=R.change_ring(M.base_ring())
1221
F = [R(f) for f in F]
1222
PS=self.codomain().change_ring(R)
1223
H=Hom(PS,PS)
1224
return(H(F))
1225
else:
1226
raise TypeError("matrix must be invertible and size dimension +1 ")
1227
1228
def green_function(self, P, v, **kwds):
1229
r"""
1230
Evaluates the local Green's function at the place ``v`` for ``P`` with ``N`` terms of the
1231
series or, in dimension 1, to within a given error bound.
1232
1233
Use ``v=0`` for the archimedean place. Must be over `\ZZ` or `\QQ`.
1234
1235
ALGORITHM:
1236
1237
See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007.
1238
1239
INPUT:
1240
1241
- ``P`` - a projective point
1242
1243
- ``v`` - non-negative integer. a place, use v=0 for the archimedean place
1244
1245
kwds:
1246
1247
- ``N`` - positive integer. number of terms of the series to use
1248
1249
- ``prec`` - positive integer, float point or p-adic precision, default: 100
1250
1251
- ``error_bound`` - a positive real number
1252
1253
OUTPUT:
1254
1255
- a real number
1256
1257
EXAMPLES::
1258
1259
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1260
sage: H = Hom(P,P)
1261
sage: f = H([x^2+y^2,x*y])
1262
sage: f.green_function(P.point([5,2],False),0,N=30)
1263
1.7315451844777407992085512000
1264
sage: f.green_function(P.point([2,1],False),0,N=30)
1265
0.86577259223181088325226209926
1266
sage: f.green_function(P.point([1,1],False),0,N=30)
1267
0.43288629610862338612700146098
1268
"""
1269
if self.base_ring() != ZZ and self.base_ring() != QQ:
1270
raise TypeError("Must be ZZ or QQ")
1271
return(P.green_function(self,v,**kwds))
1272
1273
def canonical_height(self, P, **kwds):
1274
r"""
1275
Evaluates the canonical height of ``P`` with respect to ``self``. Must be over `\ZZ` or `\QQ`.
1276
1277
Specify either the number of terms of the series to evaluate
1278
or, in dimension 1, the error bound required.
1279
1280
ALGORITHM:
1281
1282
The sum of the Green's function at the archimedean place and the places of bad reduction.
1283
1284
INPUT:
1285
1286
- ``P`` -- a projective point
1287
1288
kwds:
1289
1290
- ``badprimes`` - a list of primes of bad reduction
1291
1292
- ``N`` - positive integer. number of terms of the series to use in the local green functions
1293
1294
- ``prec`` - positive integer, float point or p-adic precision, default: 100
1295
1296
- ``error_bound`` - a positive real number
1297
1298
OUTPUT:
1299
1300
- a real number
1301
1302
EXAMPLES::
1303
1304
sage: P.<x,y> = ProjectiveSpace(ZZ,1)
1305
sage: H = Hom(P,P)
1306
sage: f = H([x^2+y^2,2*x*y]);
1307
sage: f.canonical_height(P.point([5,4]),error_bound=0.001)
1308
2.1970553519503404898926835324
1309
sage: f.canonical_height(P.point([2,1]),error_bound=0.001)
1310
1.0984430632822307984974382955
1311
1312
Notice that preperiodic points may not be exactly 0::
1313
1314
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1315
sage: H = Hom(P,P)
1316
sage: f = H([x^2-29/16*y^2,y^2]);
1317
sage: f.canonical_height(P.point([1,4]),N=60)
1318
1.2024186864216154694752186858e-18
1319
1320
::
1321
1322
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
1323
sage: X = P.subscheme(x^2-y^2);
1324
sage: H = Hom(X,X)
1325
sage: f = H([x^2,y^2,4*z^2]);
1326
sage: Q = X([4,4,1])
1327
sage: f.canonical_height(Q,badprimes=[2])
1328
0.0013538030870311431824555314882
1329
"""
1330
if self.base_ring() != ZZ and self.base_ring() != QQ:
1331
raise TypeError("Must be ZZ or QQ")
1332
return(P.canonical_height(self,**kwds))
1333
1334
def global_height(self, prec=None):
1335
r"""
1336
Returns the maximum of the heights of the coefficients in any of the coordinate functions of ``self``.
1337
1338
INPUT:
1339
1340
- ``prec`` -- desired floating point precision (default:
1341
default RealField precision).
1342
1343
OUTPUT:
1344
1345
- a real number
1346
1347
EXAMPLES::
1348
1349
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1350
sage: H = Hom(P,P)
1351
sage: f = H([1/1331*x^2+1/4000*y^2,210*x*y]);
1352
sage: f.global_height()
1353
8.29404964010203
1354
1355
This function does not automatically normalize::
1356
1357
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
1358
sage: H = Hom(P,P)
1359
sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]);
1360
sage: f.global_height()
1361
9.21034037197618
1362
sage: f.normalize_coordinates()
1363
sage: f.global_height()
1364
8.51719319141624
1365
1366
::
1367
1368
sage: R.<z> = PolynomialRing(QQ)
1369
sage: K.<w> = NumberField(z^2-2)
1370
sage: O = K.maximal_order()
1371
sage: P.<x,y> = ProjectiveSpace(O,1)
1372
sage: H = Hom(P,P)
1373
sage: f = H([2*x^2 + 3*O(w)*y^2,O(w)*y^2])
1374
sage: f.global_height()
1375
1.44518587894808
1376
1377
.. TODO:: add heights to integer.pyx and remove special case
1378
"""
1379
if self.domain().base_ring() == ZZ:
1380
if prec is None:
1381
R = RealField()
1382
else:
1383
R = RealField(prec)
1384
H=R(0)
1385
for i in range(self.domain().ambient_space().dimension_relative()+1):
1386
C=self[i].coefficients()
1387
h=max([c.abs() for c in C])
1388
H=max(H,R(h).log())
1389
return(H)
1390
H=0
1391
for i in range(self.domain().ambient_space().dimension_relative()+1):
1392
C=self[i].coefficients()
1393
h=max([c.global_height(prec) for c in C])
1394
H=max(H,h)
1395
return(H)
1396
1397
def height_difference_bound(self, prec=None):
1398
r"""
1399
Returns an upper bound on the different bewtween the canonical height of a point with
1400
respect to ``self`` and the height of the point. ``self`` must be a morphism.
1401
1402
ALGORITHM:
1403
1404
Uses a Nullstellensatz argument to compute the constant.
1405
For details: B. Hutz, Efficient determination of rational preperiodic points for endomorphisms of projective
1406
space, arxiv:1210.6246 (2012).
1407
1408
INPUT:
1409
1410
- ``prec`` - positive integer, float point, default: RealField default
1411
1412
OUTPUT:
1413
1414
- a real number
1415
1416
EXAMPLES::
1417
1418
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1419
sage: H = End(P)
1420
sage: f = H([x^2+y^2,x*y]);
1421
sage: f.height_difference_bound()
1422
1.38629436111989
1423
1424
This function does not automatically normalize. ::
1425
1426
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
1427
sage: H = End(P)
1428
sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]);
1429
sage: f.height_difference_bound()
1430
11.0020998412042
1431
sage: f.normalize_coordinates()
1432
sage: f.height_difference_bound()
1433
10.7632079329219
1434
"""
1435
if self.domain().base_ring() != ZZ and self.domain().base_ring() != QQ:
1436
raise NotImplementedError("Must be over ZZ or QQ")
1437
if not self.is_endomorphism():
1438
raise NotImplementedError("Must be an endomorphism of projective space")
1439
if prec is None:
1440
R=RealField()
1441
else:
1442
R=RealField(prec)
1443
N=self.domain().dimension_relative()
1444
d=self.degree()
1445
D=(N+1)*(d-1) +1
1446
#compute upper bound
1447
U = self.global_height(prec) + R(binomial(N+d,d)).log()
1448
#compute lower bound - from explicit polynomials of Nullstellensatz
1449
CR=self.domain().coordinate_ring()
1450
CR=CR.change_ring(QQ) #.lift() only works over fields
1451
I=CR.ideal(self.defining_polynomials())
1452
MCP=[]
1453
for k in range(N+1):
1454
CoeffPolys=(CR.gen(k)**D).lift(I)
1455
Res=1
1456
h=1
1457
for j in range(len(CoeffPolys)):
1458
if CoeffPolys[j]!=0:
1459
for i in range(len(CoeffPolys[j].coefficients())):
1460
Res=lcm(Res,abs(CoeffPolys[j].coefficients()[i].denominator()))
1461
h=max(h,abs(CoeffPolys[j].coefficients()[i].numerator()))
1462
MCP.append([Res,Res*h]) #since we need to clear denominators
1463
maxh=1
1464
gcdRes=0
1465
for k in range(len(MCP)):
1466
gcdRes=gcd(gcdRes,MCP[k][0])
1467
maxh=max(maxh,MCP[k][1])
1468
L=abs(R(gcdRes/((N+1)*binomial(N + D-d,D-d)*maxh)).log())
1469
1470
C=max(U,L) #height difference dh(P) - L <= h(f(P)) <= dh(P) +U
1471
return(C/(d-1))
1472
1473
def multiplier(self,P,n, check=True):
1474
r"""
1475
Returns the multiplier of ``self`` at the `QQ`-rational point ``P`` of period ``n``.
1476
``self`` must be an endomorphism of projective space
1477
1478
INPUT:
1479
1480
- ``P`` - a point on domain of ``self``
1481
1482
- ``n`` - a positive integer, the period of ``P``
1483
1484
- ``check`` -- verify that ``P`` has period ``n``, Default:True
1485
1486
OUTPUT:
1487
1488
- a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of ``self``
1489
1490
EXAMPLES::
1491
1492
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
1493
sage: H = End(P)
1494
sage: f = H([x^2,y^2,4*z^2]);
1495
sage: Q = P.point([4,4,1],False);
1496
sage: f.multiplier(Q,1)
1497
[2 0]
1498
[0 2]
1499
1500
::
1501
1502
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1503
sage: H = End(P)
1504
sage: f = H([7*x^2 - 28*y^2,24*x*y])
1505
sage: f.multiplier(P(2,5),4)
1506
[231361/20736]
1507
1508
::
1509
1510
sage: P.<x,y> = ProjectiveSpace(CC,1)
1511
sage: H = End(P)
1512
sage: f = H([x^3 - 25*x*y^2 + 12*y^3,12*y^3])
1513
sage: f.multiplier(P(1,1),5)
1514
[0.389017489711935]
1515
1516
::
1517
1518
sage: P.<x,y> = ProjectiveSpace(RR,1)
1519
sage: H = End(P)
1520
sage: f = H([x^2-2*y^2,y^2])
1521
sage: f.multiplier(P(2,1),1)
1522
[4.00000000000000]
1523
1524
::
1525
1526
sage: P.<x,y> = ProjectiveSpace(Qp(13),1)
1527
sage: H = End(P)
1528
sage: f = H([x^2-29/16*y^2,y^2])
1529
sage: f.multiplier(P(5,4),3)
1530
[6 + 8*13 + 13^2 + 8*13^3 + 13^4 + 8*13^5 + 13^6 + 8*13^7 + 13^8 +
1531
8*13^9 + 13^10 + 8*13^11 + 13^12 + 8*13^13 + 13^14 + 8*13^15 + 13^16 +
1532
8*13^17 + 13^18 + 8*13^19 + O(13^20)]
1533
1534
::
1535
1536
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1537
sage: H = End(P)
1538
sage: f = H([x^2-y^2,y^2])
1539
sage: f.multiplier(P(0,1),1)
1540
Traceback (most recent call last):
1541
...
1542
ValueError: (0 : 1) is not periodic of period 1
1543
1544
.. TODO:: would be better to keep the dehomogenizations for reuse
1545
"""
1546
if not self.is_endomorphism():
1547
raise NotImplementedError("Must be an endomorphism of projective space")
1548
if check:
1549
if self.nth_iterate(P,n)!=P:
1550
raise ValueError("%s is not periodic of period %s"% (P,n))
1551
N=self.domain().dimension_relative()
1552
l=identity_matrix(FractionField(self.codomain().base_ring()),N,N)
1553
Q=P
1554
Q.normalize_coordinates()
1555
index=N
1556
indexlist=[]
1557
while Q[index]==0:
1558
index-=1
1559
indexlist.append(index)
1560
for i in range(0,n):
1561
F=[]
1562
R=self(Q)
1563
R.normalize_coordinates()
1564
index=N
1565
while R[index]==0:
1566
index-=1
1567
indexlist.append(index)
1568
S=PolynomialRing(FractionField(self.codomain().base_ring()),N,'x')
1569
CR=self.coordinate_ring()
1570
map_vars=list(S.gens())
1571
map_vars.insert(indexlist[i],1)
1572
phi=CR.hom(map_vars,S)
1573
#make map between correct affine patches
1574
for j in range(N+1):
1575
if j != indexlist[i+1]:
1576
F.append(phi(self._polys[j])/phi(self._polys[indexlist[i+1]]))
1577
J = matrix(FractionField(S),N,N)
1578
for j1 in range(0,N):
1579
for j2 in range(0,N):
1580
J[j1,j2]=F[j1].derivative(S.gen(j2))
1581
l=J(tuple(Q.dehomogenize(indexlist[i])))*l #get the correct order for chain rule matrix multiplication
1582
Q=R
1583
return l
1584
1585
def _multipliermod(self,P,n,p,k):
1586
r"""
1587
Returns the multiplier of ``self`` at the point ``P`` of period ``n`` modulo `p^k`.
1588
self must be an endomorphism of projective space defined over `\QQ` or '\ZZ'.
1589
This function should not be used at the top level as it does not perform input checks.
1590
It is used primarily for the rational preperiodic and periodic point algorithms.
1591
1592
INPUT:
1593
1594
- ``P`` - a point on domain of ``self``
1595
1596
- ``n`` - a positive integer, the period of ``P``
1597
1598
- ``p`` - a positive integer
1599
1600
- ``k`` - a positive integer
1601
1602
OUTPUT:
1603
1604
- a square matrix of size ``self.codomain().dimension_relative()`` in `Zmod(p^k)`.
1605
1606
EXAMPLES::
1607
1608
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1609
sage: H = End(P)
1610
sage: f = H([x^2-29/16*y^2,y^2])
1611
sage: f._multipliermod(P(5,4),3,11,1)
1612
[3]
1613
1614
::
1615
1616
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1617
sage: H = End(P)
1618
sage: f = H([x^2-29/16*y^2,y^2])
1619
sage: f._multipliermod(P(5,4),3,11,2)
1620
[80]
1621
1622
.. TODO:: would be better to keep the dehomogenizations for reuse
1623
"""
1624
N=self.domain().dimension_relative()
1625
BR=FractionField(self.codomain().base_ring())
1626
l=identity_matrix(BR,N,N)
1627
Q=copy(P)
1628
g=gcd(Q._coords) #we can't use normalize_coordinates since it can cause denominators
1629
Q.scale_by(1/g)
1630
index=N
1631
indexlist=[]
1632
while Q[index]%p==0:
1633
index-=1
1634
indexlist.append(index)
1635
for i in range(0,n):
1636
F=[]
1637
R=self(Q,False)
1638
g=gcd(R._coords)
1639
R.scale_by(1/g)
1640
for index in range(N+1):
1641
R._coords[index]=R._coords[index]%(p**k)
1642
index=N
1643
while R[index]%p==0:
1644
index-=1
1645
indexlist.append(index)
1646
S=PolynomialRing(BR,N,'x')
1647
CR=self.coordinate_ring()
1648
map_vars=list(S.gens())
1649
map_vars.insert(indexlist[i],1)
1650
phi=CR.hom(map_vars,S)
1651
for j in range(N+1):
1652
if j != indexlist[i+1]:
1653
F.append(phi(self._polys[j])/phi(self._polys[indexlist[i+1]]))
1654
J = matrix(FractionField(S),N,N)
1655
for j1 in range(0,N):
1656
for j2 in range(0,N):
1657
J[j1,j2]=F[j1].derivative(S.gen(j2))
1658
l=(J(tuple(Q.dehomogenize(indexlist[i])))*l)%(p**k)
1659
Q=R
1660
return(l)
1661
1662
def possible_periods(self,**kwds):
1663
r"""
1664
Returns the set of possible periods for rational periodic points of self.
1665
Must be defined over `\ZZ` or `\QQ`.
1666
1667
ALGORITHM:
1668
Calls ``self.possible_periods()`` modulo all primes of good reduction in range ``prime_bound``.
1669
Returns the intersection of those lists.
1670
1671
INPUT:
1672
1673
kwds:
1674
1675
- ``prime_bound`` - a list or tuple of two positive integers. Or an integer for the upper bound. (optional)
1676
default: [1,20].
1677
1678
- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)
1679
1680
OUTPUT:
1681
1682
- a list of positive integers.
1683
1684
EXAMPLES::
1685
1686
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1687
sage: H = End(P)
1688
sage: f = H([x^2-29/16*y^2,y^2])
1689
sage: f.possible_periods()
1690
[1, 3]
1691
1692
::
1693
1694
sage: PS.<x,y> = ProjectiveSpace(1,QQ)
1695
sage: H = End(PS)
1696
sage: f = H([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3])
1697
sage: f.possible_periods(prime_bound=[1,5])
1698
Traceback (most recent call last):
1699
...
1700
ValueError: No primes of good reduction in that range
1701
sage: f.possible_periods(prime_bound=[1,10])
1702
[1, 4, 12]
1703
sage: f.possible_periods(prime_bound=[1,20])
1704
[1, 4]
1705
1706
::
1707
1708
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2)
1709
sage: H = End(P)
1710
sage: 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])
1711
sage: f.possible_periods(prime_bound=10)
1712
[1, 2, 6, 20, 42, 60, 140, 420]
1713
sage: f.possible_periods(prime_bound=20) # long time
1714
[1, 20]
1715
"""
1716
if not self.is_endomorphism():
1717
raise NotImplementedError("Must be an endomorphism of projective space")
1718
if self.domain().base_ring()!=ZZ and self.domain().base_ring()!=QQ:
1719
raise NotImplementedError("Must be ZZ or QQ")
1720
1721
1722
primebound = kwds.pop("prime_bound",[1,20])
1723
badprimes = kwds.pop("bad_primes",None)
1724
1725
if (isinstance(primebound,(list,tuple))==False):
1726
try:
1727
primebound=[1,ZZ(primebound)]
1728
except TypeError:
1729
raise TypeError("Bound on primes must be an integer")
1730
else:
1731
try:
1732
primebound[0]=ZZ(primebound[0])
1733
primebound[1]=ZZ(primebound[1])
1734
except TypeError:
1735
raise TypeError("Prime bounds must be integers")
1736
1737
if badprimes==None:
1738
badprimes=self.primes_of_bad_reduction()
1739
firstgood=0
1740
for q in primes(primebound[0],primebound[1]+1):
1741
if q in badprimes: #bad reduction
1742
t=0
1743
#print "Bad Reduction at ", q
1744
elif firstgood==0:
1745
F=self.change_ring(GF(q))
1746
periods=set(F.possible_periods())
1747
firstgood=1
1748
else:
1749
F=self.change_ring(GF(q))
1750
periodsq=set(F.possible_periods())
1751
periods=periods.intersection(periodsq)
1752
if firstgood==0:
1753
raise ValueError("No primes of good reduction in that range")
1754
else:
1755
return(sorted(periods))
1756
1757
def _preperiodic_points_to_cyclegraph(self,preper):
1758
r"""
1759
Given the complete set of periodic or preperiodic points returns the
1760
digraph representing the orbit. If it is not the complete set, this function
1761
will not fill in the gaps.
1762
1763
1764
INPUT:
1765
1766
- ``preper`` - a list or tuple of projective points. The complete set of rational periodic or preperiodic points.
1767
1768
OUTPUT:
1769
1770
- a digraph representing the orbit the rational preperiodic points ``preper`` in projective space.
1771
1772
Examples::
1773
1774
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1775
sage: H = End(P)
1776
sage: f = H([x^2-2*y^2,y^2])
1777
sage: preper = [P(-2, 1), P(1, 0), P(0, 1), P(1, 1), P(2, 1), P(-1, 1)]
1778
sage: f._preperiodic_points_to_cyclegraph(preper)
1779
Looped digraph on 6 vertices
1780
"""
1781
V=[]
1782
E=[]
1783
for i in range(0,len(preper)):
1784
V.append(str(preper[i]))
1785
Q=self(preper[i])
1786
Q.normalize_coordinates()
1787
E.append([str(Q)])
1788
from sage.graphs.digraph import DiGraph
1789
g=DiGraph(dict(zip(V,E)), loops=True)
1790
return(g)
1791
1792
def is_PGL_minimal(self, prime_list=None):
1793
r"""
1794
Checks if ``self`` is a minimal model in its conjugacy class. See [Bruin-Molnar]
1795
and [Molnar] for a description of the algorithm.
1796
1797
INPUT:
1798
1799
- ``prime_list`` -- list of primes to check minimality, if None, check all places
1800
1801
OUTPUT:
1802
1803
- Boolean - True if ``self`` is minimal, False otherwise.
1804
1805
EXAMPLES::
1806
1807
sage: PS.<X,Y> = ProjectiveSpace(QQ,1)
1808
sage: H = End(PS)
1809
sage: f = H([X^2+3*Y^2,X*Y])
1810
sage: f.is_PGL_minimal()
1811
True
1812
1813
::
1814
1815
sage: PS.<x,y> = ProjectiveSpace(QQ,1)
1816
sage: H = End(PS)
1817
sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y])
1818
sage: f.is_PGL_minimal()
1819
False
1820
1821
::
1822
1823
sage: PS.<x,y> = ProjectiveSpace(QQ,1)
1824
sage: H = End(PS)
1825
sage: f = H([6*x^2+12*x*y+7*y^2,y^2])
1826
sage: f.is_PGL_minimal()
1827
Traceback (most recent call last):
1828
...
1829
TypeError: Affine minimality is only considered for maps not of the form
1830
f or 1/f for a polynomial f.
1831
"""
1832
if self.base_ring()!=QQ and self.base_ring()!=ZZ:
1833
raise NotImplementedError("Minimal models only implemented over ZZ or QQ")
1834
if not self.is_morphism():
1835
raise TypeError("The function is not a morphism")
1836
if self.degree()==1:
1837
raise NotImplementedError("Minimality is only for degree 2 or higher")
1838
1839
from endPN_minimal_model import affine_minimal
1840
return(affine_minimal(self, False ,prime_list ,True))
1841
1842
def minimal_model(self, return_transformation = False,prime_list=None):
1843
r"""
1844
Given ``self`` a scheme morphism on the projective line over the rationals,
1845
determine if ``self`` is minimal. In particular, determine
1846
if ``self`` is affine minimal, which is enough to decide if it is minimal
1847
or not. See Proposition 2.10 in [Bruin-Molnar].
1848
1849
REFERENCES:
1850
1851
.. [Bruin-Molnar] N. Bruin and A. Molnar, Minimal models for rational
1852
functions in a dynamical setting
1853
LMS Journal of Computation and Mathematics, Volume 15 (2012), pp 400-417.
1854
1855
.. [Molnar] A. Molnar, Fractional Linear Minimal Models of Rational Functions,
1856
M.Sc. Thesis.
1857
1858
INPUT:
1859
1860
- ``self`` -- scheme morphism on the projective line defined over `QQ`.
1861
1862
- ``return_transformation`` -- a boolean value, default value True. This
1863
signals a return of the ``PGL_2`` transformation
1864
to conjugate ``self`` to the calculated minimal
1865
model. default: False
1866
1867
- ``prime_list`` -- a list of primes, in case one only wants to determine minimality
1868
at those specific primes.
1869
1870
OUTPUT:
1871
1872
- a scheme morphism on the projective line which is a minimal model of ``self``.
1873
1874
- a `PGL(2,QQ)` element which conjugates ``self`` to a minimal model
1875
1876
EXAMPLES::
1877
1878
sage: PS.<X,Y> = ProjectiveSpace(QQ,1)
1879
sage: H = End(PS)
1880
sage: f = H([X^2+3*Y^2,X*Y])
1881
sage: f.minimal_model(return_transformation=True)
1882
(
1883
Scheme endomorphism of Projective Space of dimension 1 over Rational
1884
Field
1885
Defn: Defined on coordinates by sending (X : Y) to
1886
(X^2 + 3*Y^2 : X*Y)
1887
,
1888
[1 0]
1889
[0 1]
1890
)
1891
1892
::
1893
1894
sage: PS.<X,Y> = ProjectiveSpace(QQ,1)
1895
sage: H = End(PS)
1896
sage: 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])
1897
sage: f.minimal_model(return_transformation=True)
1898
(
1899
Scheme endomorphism of Projective Space of dimension 1 over Rational
1900
Field
1901
Defn: Defined on coordinates by sending (X : Y) to
1902
(22176*X^4 + 151956*X^3*Y + 390474*X^2*Y^2 + 445956*X*Y^3 +
1903
190999*Y^4 : -12329*X^4 - 84480*X^3*Y - 217080*X^2*Y^2 - 247920*X*Y^3 -
1904
106180*Y^4),
1905
[2 3]
1906
[0 1]
1907
)
1908
1909
::
1910
1911
sage: PS.<x,y> = ProjectiveSpace(QQ,1)
1912
sage: H = End(PS)
1913
sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y])
1914
sage: f.minimal_model()
1915
Scheme endomorphism of Projective Space of dimension 1 over Rational
1916
Field
1917
Defn: Defined on coordinates by sending (x : y) to
1918
(x^2 + 12*x*y + 42*y^2 : 2*x*y)
1919
1920
::
1921
1922
sage: PS.<x,y> = ProjectiveSpace(ZZ,1)
1923
sage: H = End(PS)
1924
sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y + 42*y^2])
1925
sage: g,M=f.minimal_model(return_transformation=True)
1926
sage: f.conjugate(M)==g
1927
True
1928
1929
::
1930
1931
sage: PS.<X,Y> = ProjectiveSpace(QQ,1)
1932
sage: H = End(PS)
1933
sage: f = H([X+Y,X-3*Y])
1934
sage: f.minimal_model()
1935
Traceback (most recent call last):
1936
...
1937
NotImplementedError: Minimality is only for degree 2 or higher
1938
1939
::
1940
1941
sage: PS.<X,Y> = ProjectiveSpace(QQ,1)
1942
sage: H = End(PS)
1943
sage: f = H([X^2-Y^2,X^2+X*Y])
1944
sage: f.minimal_model()
1945
Traceback (most recent call last):
1946
...
1947
TypeError: The function is not a morphism
1948
1949
"""
1950
if self.base_ring()!=QQ and self.base_ring()!=ZZ:
1951
raise NotImplementedError("Minimal models only implemented over ZZ or QQ")
1952
if not self.is_morphism():
1953
raise TypeError("The function is not a morphism")
1954
if self.degree()==1:
1955
raise NotImplementedError("Minimality is only for degree 2 or higher")
1956
1957
from endPN_minimal_model import affine_minimal
1958
return(affine_minimal(self, return_transformation,prime_list,False))
1959
1960
class SchemeMorphism_polynomial_projective_space_field(SchemeMorphism_polynomial_projective_space):
1961
1962
def lift_to_rational_periodic(self,points_modp,B=None):
1963
r"""
1964
Given a list of points in projective space over `GF(p)`, determine if they lift to
1965
`\QQ`-rational periodic points. ``self`` must be an endomorphism of projective
1966
space defined over `\QQ`
1967
1968
ALGORITHM:
1969
Use Hensel lifting to find a `p`-adic approximation for that rational point. The accuracy needed
1970
is determined by the height bound `B`. Then apply the the LLL algorithm to determine if the lift
1971
corresponds to a rational point.
1972
1973
If the point is a point of high multiplicity (multiplier 1) then procedure can be very slow.
1974
1975
1976
INPUT:
1977
1978
- ``points_modp`` - a list or tuple of pairs containing a point in projective space
1979
over `GF(p)` and the possible period.
1980
1981
- ``B`` - a positive integer - the height bound for a rational preperiodic point. (optional)
1982
1983
OUTPUT:
1984
1985
- a list of projective points.
1986
1987
EXAMPLES::
1988
1989
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1990
sage: H = End(P)
1991
sage: f = H([x^2 - y^2,y^2])
1992
sage: f.lift_to_rational_periodic([[P(0,1).change_ring(GF(7)),4]])
1993
[[(0 : 1), 2]]
1994
1995
::
1996
1997
There may be multiple points in the lift.
1998
sage: P.<x,y> = ProjectiveSpace(QQ,1)
1999
sage: H = End(P)
2000
sage: f = H([-5*x^2 + 4*y^2,4*x*y])
2001
sage: f.lift_to_rational_periodic([[P(1,0).change_ring(GF(3)),1]]) # long time
2002
[[(1 : 0), 1], [(2/3 : 1), 1], [(-2/3 : 1), 1]]
2003
2004
::
2005
2006
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2007
sage: H = End(P)
2008
sage: f = H([16*x^2 - 29*y^2,16*y^2])
2009
sage: f.lift_to_rational_periodic([[P(3,1).change_ring(GF(13)), 3]])
2010
[[(-1/4 : 1), 3]]
2011
2012
::
2013
2014
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
2015
sage: H = End(P)
2016
sage: 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])
2017
sage: f.lift_to_rational_periodic([[P(14,19,1).change_ring(GF(23)), 9]]) # long time
2018
[[(-9 : -4 : 1), 9]]
2019
"""
2020
if points_modp==[]:
2021
return([])
2022
else:
2023
if B==None:
2024
B=e**self.height_difference_bound()
2025
2026
p=points_modp[0][0].codomain().base_ring().characteristic()
2027
if p==0:
2028
raise TypeError("Must be positive characteristic")
2029
PS=self.domain()
2030
N=PS.dimension_relative()
2031
R=RealField()
2032
#compute the maximum p-adic precision needed to conclusively determine
2033
#if the rational point exists
2034
L=R((R(2**(N/2+1)*sqrt(N+1)*B**2).log())/R(p).log()+1).trunc()
2035
2036
points=[]
2037
for i in range(len(points_modp)):
2038
#[point mod p, period, current p-adic precision]
2039
points.append([points_modp[i][0].change_ring(QQ,False),points_modp[i][1],1])
2040
good_points=[]
2041
#shifts is used in non-Hensel lifting
2042
shifts=None
2043
#While there are still points to consider try to lift to next precision
2044
while points!=[]:
2045
q=points.pop()
2046
qindex=N
2047
#Find the last non-zero coordinate to use for normalizations
2048
while q[0][qindex]%p==0:
2049
qindex-=1
2050
T=q[0]
2051
n=q[1]
2052
k=q[2]
2053
T.scale_by(1/T[qindex]) #normalize
2054
bad=0
2055
#stop where we reach the needed precision or the point is bad
2056
while k< L and bad==0:
2057
l=self._multipliermod(T,n,p,2*k)
2058
l-=l.parent().one() #f^n(x) - x
2059
lp=l.change_ring(Zmod(p**k))
2060
ldet=lp.determinant()
2061
# if the matrix is invertible then we can Hensel lift
2062
if ldet%p!=0:
2063
RQ=ZZ.quo(p**(2*k))
2064
T.clear_denominators()
2065
newT = T.change_ring(RQ,False)
2066
fp=self.change_ring(RQ,False)
2067
S=newT.nth_iterate(fp,n,False).change_ring(QQ,False)
2068
T.scale_by(1/T[qindex])
2069
S.scale_by(1/S[qindex])
2070
for i in range(N+1):
2071
S._coords[i]=S._coords[i]-T._coords[i]
2072
if S[i]%(p**k) !=0 and i!=N:
2073
bad=1
2074
break
2075
if bad==1:
2076
break
2077
S.scale_by(-1/p**k)
2078
vecs=[Zmod(p**k)(S._coords[iS]) for iS in range(N+1)]
2079
vecs.pop(qindex)
2080
newvecs=list((lp.inverse())*vector(vecs)) #l.inverse should be mod p^k!!
2081
newS=[]
2082
[newS.append(QQ(newvecs[i])) for i in range(qindex)]
2083
newS.append(0)
2084
[newS.append(QQ(newvecs[i])) for i in range(qindex,N)]
2085
S=PS.point(newS, False) #don't check for [0,...,0]
2086
for i in range(N+1):
2087
S._coords[i]=S._coords[i]%(p**k)
2088
for i in range(N+1):
2089
T._coords[i]+=S._coords[i]*(p**k)
2090
T.normalize_coordinates()
2091
#Hensel gives us 2k for the newprecision
2092
k=min(2*k,L)
2093
else:
2094
#we are unable to Hensel Lift so must try all possible lifts
2095
#to the next precision (k+1)
2096
first=0
2097
newq=[]
2098
RQ=Zmod(p**(k+1))
2099
fp=self.change_ring(RQ,False)
2100
if shifts is None:
2101
shifts=xmrange([p for i in range(N)])
2102
for shift in shifts:
2103
newT=T.change_ring(RQ,False)
2104
shiftindex=0
2105
for i in range(N+1):
2106
if i != qindex:
2107
newT._coords[i]=newT[i]+shift[shiftindex]*p**k
2108
shiftindex+=1
2109
TT=fp.nth_iterate(newT,n,False)
2110
if TT== newT:
2111
if first==0:
2112
newq.append(newT.change_ring(QQ,False))
2113
newq.append(n)
2114
newq.append(k+1)
2115
first=1
2116
else:
2117
points.append([newT.change_ring(QQ,False),n,k+1])
2118
if newq==[]:
2119
bad=1
2120
break
2121
else:
2122
T=newq[0]
2123
k+=1
2124
#given a p-adic lift of appropriate precision
2125
#perform LLL to find the "smallest" rational approximation
2126
#If this height is small enough, then it is a valid rational point
2127
if bad==0:
2128
M=matrix(N+2,N+1)
2129
T.clear_denominators()
2130
for i in range(N+1):
2131
M[0,i]=T[i]
2132
M[i+1,i]=p**L
2133
M[N+1,N]=p**L
2134
M=M.LLL()
2135
Q=[]
2136
[Q.append(M[1,i]) for i in range(N+1)]
2137
g=gcd(Q)
2138
#remove gcds since this is a projective point
2139
newB=B*g
2140
for i in range(N+1):
2141
if abs(Q[i]) >newB:
2142
#height too big, so not a valid point
2143
bad=1
2144
break
2145
if bad==0:
2146
P=PS.point(Q,False)
2147
#check that it is actually periodic
2148
newP=copy(P)
2149
k=1
2150
done=False
2151
while done==False and k <= n:
2152
newP=self(newP)
2153
if newP==P:
2154
if ([P,k] in good_points)==False:
2155
good_points.append([newP,k])
2156
done=True
2157
k+=1
2158
2159
return(good_points)
2160
2161
def rational_periodic_points(self,**kwds):
2162
r"""
2163
Determine the set of rational periodic points for self an endomorphism of projective space.
2164
Must be defined over `\QQ`.
2165
2166
The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having
2167
trouble getting a partiuclar map to finish, try first computing the possible periods, then
2168
try various different ``lifting_prime``.
2169
2170
ALGORITHM:
2171
Modulo each prime of good reduction `p` determine the set of periodic points modulo `p`.
2172
For each cycle modulo `p` compute the set of possible periods (`mrp^e`). Take the intersection
2173
of the list of possible periods modulo several primes of good reduction to get a possible list
2174
of minimal periods of rational periodic points. Take each point modulo `p` associated to each
2175
of these possible periods and try to lift it to a rational point with a combination of
2176
`p`-adic approximation and the LLL basis reducion algorithm.
2177
2178
See B. Hutz, Determination of all rational preperiodic points for morphisms of Pn, submitted, 2012.
2179
2180
INPUT:
2181
2182
kwds:
2183
2184
- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the
2185
limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)
2186
default: [1,20]
2187
2188
- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the
2189
lifting. default: 23
2190
2191
- ``periods`` - a list of positive integers which is the list of possible periods. (optional)
2192
2193
- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)
2194
2195
OUTPUT:
2196
2197
- a list of rational points in projective space.
2198
2199
Examples::
2200
2201
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2202
sage: H = End(P)
2203
sage: f = H([x^2-3/4*y^2,y^2])
2204
sage: f.rational_periodic_points(prime_bound=20,lifting_prime=7) # long time
2205
[(-1/2 : 1), (1 : 0), (3/2 : 1)]
2206
2207
::
2208
2209
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
2210
sage: H = End(P)
2211
sage: 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])
2212
sage: f.rational_periodic_points(prime_bound=[1,20]) # long time
2213
[(-3 : 1 : 1), (3 : 1 : 1), (5 : 1 : 1), (-1 : 0 : 1), (3 : 3 : 1), (-3
2214
: 3 : 1), (-1 : 3 : 1), (1 : 3 : 1), (-3 : -1 : 1), (5 : 3 : 1), (-1 :
2215
-1 : 1), (1 : 1 : 1), (3 : 0 : 1), (-3 : 0 : 1), (5 : 0 : 1), (3 : -1 :
2216
1), (1 : 0 : 0), (5 : -1 : 1), (-1 : 1 : 1), (1 : -1 : 1), (0 : 1 : 0),
2217
(1 : 0 : 1)]
2218
2219
::
2220
2221
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2222
sage: H = End(P)
2223
sage: f = H([-5*x^2 + 4*y^2,4*x*y])
2224
sage: f.rational_periodic_points() # long time
2225
[(2/3 : 1), (-2 : 1), (1 : 0), (2 : 1), (-2/3 : 1)]
2226
2227
.. TODO::
2228
2229
- move some of this to Cython so that it is faster especially the possible periods mod `p`.
2230
2231
- have the last prime of good redution used also return the list of points instead of getting the
2232
information again for all_points.
2233
"""
2234
if not self.is_endomorphism():
2235
raise NotImplementedError("Must be an endomorphism of projective space")
2236
if self.domain().base_ring()!=QQ:
2237
raise NotImplementedError("Must be QQ") #for p-adic lifting
2238
2239
primebound = kwds.pop("prime_bound",[1,20])
2240
p = kwds.pop("lifting_prime",23)
2241
periods = kwds.pop("periods",None)
2242
badprimes = kwds.pop("bad_primes",None)
2243
2244
if (isinstance(primebound,(list,tuple))==False):
2245
try:
2246
primebound=[1,ZZ(primebound)]
2247
except TypeError:
2248
raise TypeError("Bound on primes must be an integer")
2249
else:
2250
try:
2251
primebound[0]=ZZ(primebound[0])
2252
primebound[1]=ZZ(primebound[1])
2253
except TypeError:
2254
raise TypeError("Prime bounds must be integers")
2255
2256
if badprimes==None:
2257
badprimes=self.primes_of_bad_reduction()
2258
if periods==None:
2259
periods=self.possible_periods(prime_bound=primebound,bad_primes=badprimes)
2260
PS=self.domain()
2261
R=PS.base_ring()
2262
periodic=set()
2263
while p in badprimes:
2264
p = next_prime(p+1)
2265
B=e**self.height_difference_bound()
2266
2267
f=self.change_ring(GF(p))
2268
all_points=f.possible_periods(True) #return the list of points and their periods.
2269
pos_points=[]
2270
for i in range(len(all_points)):
2271
if all_points[i][1] in periods and (all_points[i] in pos_points)==False: #check period, remove duplicates
2272
pos_points.append(all_points[i])
2273
periodic_points=self.lift_to_rational_periodic(pos_points,B)
2274
for P,n in periodic_points:
2275
for k in range(n):
2276
P.normalize_coordinates()
2277
periodic.add(P)
2278
P=self(P)
2279
return(list(periodic))
2280
2281
def rational_preimages(self,Q):
2282
r"""
2283
Given a rational point `Q` in the domain of ``self``, return all the rational points `P`
2284
in the domain of ``self`` with `self(P)==Q`. In other words, the set of first pre-images of `Q`.
2285
``self`` must be defined over `\QQ` and be an endomorphism of projective space.
2286
2287
ALGORITHM:
2288
Use elimination via groebner bases to find the rational pre-images
2289
2290
INPUT:
2291
2292
- ``Q`` - a rational point in the domain of ``self``.
2293
2294
OUTPUT:
2295
2296
- a list of rational points in the domain of ``self``.
2297
2298
Examples::
2299
2300
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2301
sage: H = End(P)
2302
sage: f = H([16*x^2 - 29*y^2,16*y^2])
2303
sage: f.rational_preimages(P(-1,4))
2304
[(5/4 : 1), (-5/4 : 1)]
2305
2306
::
2307
2308
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
2309
sage: H = End(P)
2310
sage: 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])
2311
sage: f.rational_preimages(P(-9,-4,1))
2312
[(0 : 4 : 1)]
2313
2314
::
2315
2316
A non-periodic example.
2317
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2318
sage: H = End(P)
2319
sage: f = H([x^2 + y^2,2*x*y])
2320
sage: f.rational_preimages(P(17,15))
2321
[(5/3 : 1), (3/5 : 1)]
2322
"""
2323
if not self.is_endomorphism():
2324
raise NotImplementedError("Must be an endomorphism of projective space")
2325
if self.domain().base_ring()!=QQ:
2326
raise NotImplementedError("Must be QQ")
2327
PS=self.domain()
2328
R=PS.coordinate_ring()
2329
N=PS.dimension_relative()
2330
#need a lexicographic ordering for elimination
2331
R=PolynomialRing(R.base_ring(),N+1,R.gens(),order='lex')
2332
I=[]
2333
preimages=set()
2334
for i in range(N+1):
2335
for j in range(i+1,N+1):
2336
I.append(Q[i]*self[j]-Q[j]*self[i])
2337
I = I*R
2338
#Determine the points through elimination
2339
#This is much faster than using the I.variety() function on each affine chart.
2340
for k in range(N+1):
2341
#create the elimination ideal for the kth affine patch
2342
G=I.substitute({R.gen(k):1}).groebner_basis()
2343
if G!=[1]:
2344
P={}
2345
#keep track that we know the kth coordinate is 1
2346
P.update({R.gen(k):1})
2347
points=[P]
2348
#work backwards from solving each equation for the possible
2349
#values of the next coordiante
2350
for i in range(len(G)-1,-1,-1):
2351
new_points=[]
2352
good=0
2353
for P in points:
2354
#subsitute in our dictionary entry that has the values
2355
#of coordinates known so far. This results in a single
2356
#variable polynomial (by elimination)
2357
L=G[i].substitute(P)
2358
if L!=0:
2359
L=L.factor()
2360
#the linear factors give the possible rational values of
2361
#this coordinate
2362
for pol,pow in L:
2363
if pol.degree()==1 and len(pol.variables()) ==1:
2364
good=1
2365
r=pol.variable()
2366
varindex=R.gens().index(r)
2367
#add this coordinates information to th
2368
#each dictionary entry
2369
P.update({R.gen(varindex):-pol.coefficient({r:0})/pol.coefficient({r:1})})
2370
new_points.append(copy(P))
2371
if good==1:
2372
points=new_points
2373
#the dictionary entries now have values for all coordiantes
2374
#they are the rational solutions to the equations
2375
#make them into projective points
2376
for i in range(len(points)):
2377
if len(points[i])==N+1:
2378
S=PS([points[i][R.gen(j)] for j in range(N+1)])
2379
S.normalize_coordinates()
2380
preimages.add(S)
2381
return(list(preimages))
2382
2383
2384
def all_rational_preimages(self,points):
2385
r"""
2386
Given a set of rational points in the domain of ``self``, return all the rational
2387
pre-images of those points. In others words, all the rational points which have some
2388
iterate in the set points. This function repeatedly calls ``rational_preimages``.
2389
If the degree is at least two, by Northocott, this is always a finite set.
2390
``self`` must be defined over `\QQ` and be an endomorphism of projective space.
2391
2392
INPUT:
2393
2394
- ``points`` - a list of rational points in the domain of ``self``
2395
2396
OUTPUT:
2397
2398
- a list of rational points in the domain of ``self``.
2399
2400
Examples::
2401
2402
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2403
sage: H = End(P)
2404
sage: f = H([16*x^2 - 29*y^2,16*y^2])
2405
sage: f.all_rational_preimages([P(-1,4)])
2406
[(-1/4 : 1), (5/4 : 1), (-3/4 : 1), (3/4 : 1), (-5/4 : 1), (1/4 : 1),
2407
(7/4 : 1), (-7/4 : 1)]
2408
2409
::
2410
2411
sage: P.<x,y,z> = ProjectiveSpace(QQ,2)
2412
sage: H = End(P)
2413
sage: 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])
2414
sage: f.all_rational_preimages([P(-9,-4,1)])
2415
[(-9 : -4 : 1), (1 : 3 : 1), (0 : 4 : 1), (0 : -1 : 1), (0 : 0 : 1), (1
2416
: 1 : 1), (0 : 1 : 1), (1 : 2 : 1), (1 : 0 : 1)]
2417
2418
::
2419
2420
A non-periodic example.
2421
sage: P.<x,y> = ProjectiveSpace(QQ,1)
2422
sage: H = End(P)
2423
sage: f = H([x^2 + y^2,2*x*y])
2424
sage: f.all_rational_preimages([P(17,15)])
2425
[(5/3 : 1), (1/3 : 1), (3/5 : 1), (3 : 1)]
2426
"""
2427
if not self.is_endomorphism():
2428
raise NotImplementedError("Must be an endomorphism of projective space")
2429
if self.domain().base_ring()!=QQ:
2430
raise NotImplementedError("Must be QQ")
2431
2432
PS=self.domain()
2433
RPS=PS.base_ring()
2434
all_preimages=set()
2435
while points!=[]:
2436
P=points.pop()
2437
preimages=self.rational_preimages(P)
2438
for i in range(len(preimages)):
2439
if not preimages[i] in all_preimages:
2440
points.append(preimages[i])
2441
all_preimages.add(preimages[i])
2442
return(list(all_preimages))
2443
2444
def rational_preperiodic_points(self,**kwds):
2445
r"""
2446
Determined the set of rational preperiodic points for ``self``.
2447
``self`` must be defined over `\QQ` and be an endomorphism of projective space.
2448
2449
The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having
2450
trouble getting a partiuclar map to finish, try first computing the possible periods, then
2451
try various different ``lifting_prime``.
2452
2453
ALGORITHM:
2454
2455
- Determines the list of possible periods.
2456
2457
- Determines the rational periodic points from the possible periods.
2458
2459
- Determines the rational preperiodic points from the rational periodic points
2460
by determining rational preimages.
2461
2462
INPUT:
2463
2464
kwds:
2465
2466
- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the
2467
limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)
2468
default: [1,20]
2469
2470
- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the
2471
lifting. default: 23
2472
2473
- ``periods`` - a list of positive integers which is the list of possible periods. (optional)
2474
2475
- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)
2476
2477
OUTPUT:
2478
2479
- a list of rational points in projective space.
2480
2481
Examples::
2482
2483
sage: PS.<x,y> = ProjectiveSpace(1,QQ)
2484
sage: H = End(PS)
2485
sage: f = H([x^2 -y^2,3*x*y])
2486
sage: f.rational_preperiodic_points()
2487
[(-2 : 1), (0 : 1), (-1/2 : 1), (1 : 0), (1 : 1), (2 : 1), (-1 : 1),
2488
(1/2 : 1)]
2489
2490
::
2491
2492
sage: PS.<x,y> = ProjectiveSpace(1,QQ)
2493
sage: H = End(PS)
2494
sage: f = H([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3])
2495
sage: f.rational_preperiodic_points(prime_bound=10)
2496
[(1 : 1), (1 : 0), (-1 : 1), (3 : 1), (0 : 1)]
2497
2498
::
2499
2500
sage: PS.<x,y,z> = ProjectiveSpace(2,QQ)
2501
sage: H = End(PS)
2502
sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2])
2503
sage: f.rational_preperiodic_points(prime_bound=[1,8],lifting_prime=7,periods=[2]) # long time
2504
[(5/4 : 1 : 1), (1/4 : 1 : 1), (1/4 : -2 : 1), (1/4 : 2 : 1), (5/4 : -1 : 1), (5/4 : 2 : 1),
2505
(-5/4 : 2 : 1), (1/4 : -1 : 1), (-1/4 : 2 : 1), (-1/4 : -2 : 1), (-5/4 : 1 : 1), (5/4 : 0 : 1),
2506
(-5/4 : 0 : 1), (-1/4 : 1 : 1), (-1/4 : 0 : 1), (-5/4 : -1 : 1), (5/4 : -2 : 1), (-1/4 : -1 : 1),
2507
(-5/4 : -2 : 1), (1/4 : 0 : 1)]
2508
"""
2509
#input error checking done in possible_periods and rational_peridic_points
2510
badprimes = kwds.pop("bad_primes",None)
2511
periods = kwds.pop("periods",None)
2512
primebound = kwds.pop("prime_bound",[1,20])
2513
if badprimes==None:
2514
badprimes=self.primes_of_bad_reduction()
2515
if periods==None:
2516
periods=self.possible_periods(prime_bound=primebound,bad_primes=badprimes) #determine the set of possible periods
2517
if periods==[]:
2518
return([]) #no rational preperiodic points
2519
else:
2520
p = kwds.pop("lifting_prime",23)
2521
T=self.rational_periodic_points(prime_bound=primebound,lifting_prime=p,periods=periods,bad_primes=badprimes) #find the rationla preperiodic points
2522
preper=self.all_rational_preimages(T) #find the preperiodic points
2523
preper=list(preper)
2524
return(preper)
2525
2526
def rational_preperiodic_graph(self,**kwds):
2527
r"""
2528
Determines the set of rational preperiodic points for ``self``.
2529
self must be defined over `\QQ` and be an endomorphism of projective space.
2530
2531
ALGORITHM:
2532
- Determines the list of possible periods.
2533
2534
- Determines the rational periodic points from the possible periods.
2535
2536
- Determines the rational preperiodic points from the rational periodic points
2537
by determining rational preimages.
2538
2539
2540
INPUT:
2541
2542
kwds:
2543
2544
- ``prime_bound`` - a pair (list or tuple) of positive integers that represent the
2545
limits of primes to use in the reduction step. Or an integer that represents the upper bound. (optional)
2546
default: [1,20]
2547
2548
- ``lifting_prime`` - a prime integer. (optional) argument that specifies modulo which prime to try and perform the
2549
lifting. default: 23
2550
2551
- ``periods`` - a list of positive integers which is the list of possible periods. (optional)
2552
2553
- ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional)
2554
2555
OUTPUT:
2556
2557
- a digraph representing the orbits of the rational preperiodic points in projective space.
2558
2559
Examples::
2560
2561
sage: PS.<x,y> = ProjectiveSpace(1,QQ)
2562
sage: H = End(PS)
2563
sage: f = H([7*x^2 - 28*y^2,24*x*y])
2564
sage: f.rational_preperiodic_graph()
2565
Looped digraph on 12 vertices
2566
2567
::
2568
2569
sage: PS.<x,y> = ProjectiveSpace(1,QQ)
2570
sage: H = End(PS)
2571
sage: f = H([-3/2*x^3 +19/6*x*y^2,y^3])
2572
sage: f.rational_preperiodic_graph(prime_bound=[1,8])
2573
Looped digraph on 12 vertices
2574
2575
::
2576
2577
sage: PS.<x,y,z> = ProjectiveSpace(2,QQ)
2578
sage: H = End(PS)
2579
sage: 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])
2580
sage: f.rational_preperiodic_graph(prime_bound=[1,11],lifting_prime=13) # long time
2581
Looped digraph on 30 vertices
2582
"""
2583
#input checking done in .rational_preperiodic_points()
2584
preper=self.rational_preperiodic_points(**kwds)
2585
g=self._preperiodic_points_to_cyclegraph(preper)
2586
return(g)
2587
2588
2589
class SchemeMorphism_polynomial_projective_space_finite_field(SchemeMorphism_polynomial_projective_space_field):
2590
2591
def orbit_structure(self, P):
2592
r"""
2593
Every point is preperiodic over a finite field. This funtion returns the pair `[m,n]` where `m` is the
2594
preperiod and `n` the period of the point ``P`` by ``self``.
2595
2596
INPUT:
2597
2598
- ``P`` -- a point in ``self.domain()``
2599
2600
OUTPUT:
2601
2602
- a list `[m,n]` of integers
2603
2604
EXAMPLES::
2605
2606
sage: P.<x,y,z> = ProjectiveSpace(GF(5),2)
2607
sage: H = Hom(P,P)
2608
sage: f = H([x^2+y^2,y^2,z^2 + y*z])
2609
sage: f.orbit_structure(P(2,1,2))
2610
[0, 6]
2611
2612
::
2613
2614
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)
2615
sage: X = P.subscheme(x^2-y^2)
2616
sage: H = Hom(X,X)
2617
sage: f = H([x^2,y^2,z^2])
2618
sage: f.orbit_structure(X(1,1,2))
2619
[0, 2]
2620
2621
::
2622
2623
sage: P.<x,y> = ProjectiveSpace(GF(13),1)
2624
sage: H = Hom(P,P)
2625
sage: f = H([x^2-y^2,y^2])
2626
sage: f.orbit_structure(P(3,4))
2627
[2, 3]
2628
"""
2629
return(P.orbit_structure(self))
2630
2631
def cyclegraph(self):
2632
r"""
2633
returns Digraph of all orbits of ``self`` mod `p`.
2634
2635
For subschemes, only points on the subscheme whose image are
2636
also on the subscheme are in the digraph.
2637
2638
OUTPUT:
2639
2640
- a digraph
2641
2642
EXAMPLES::
2643
2644
sage: P.<x,y> = ProjectiveSpace(GF(13),1)
2645
sage: H = Hom(P,P)
2646
sage: f = H([x^2-y^2,y^2])
2647
sage: f.cyclegraph()
2648
Looped digraph on 14 vertices
2649
2650
::
2651
2652
sage: P.<x,y,z> = ProjectiveSpace(GF(5^2,'t'),2)
2653
sage: H = Hom(P,P)
2654
sage: f = H([x^2+y^2,y^2,z^2+y*z])
2655
sage: f.cyclegraph()
2656
Looped digraph on 651 vertices
2657
2658
::
2659
2660
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2)
2661
sage: X = P.subscheme(x^2-y^2)
2662
sage: H = Hom(X,X)
2663
sage: f = H([x^2,y^2,z^2])
2664
sage: f.cyclegraph()
2665
Looped digraph on 15 vertices
2666
"""
2667
if self.domain() != self.codomain():
2668
raise NotImplementedError("Domain and Codomain must be equal")
2669
V=[]
2670
E=[]
2671
from sage.schemes.projective.projective_space import is_ProjectiveSpace
2672
if is_ProjectiveSpace(self.domain()) is True:
2673
for P in self.domain():
2674
V.append(str(P))
2675
Q=self(P)
2676
Q.normalize_coordinates()
2677
E.append([str(Q)])
2678
else:
2679
X=self.domain()
2680
for P in X.ambient_space():
2681
try:
2682
XP=X.point(P)
2683
V.append(str(XP))
2684
Q=self(XP)
2685
Q.normalize_coordinates()
2686
E.append([str(Q)])
2687
except TypeError: # not a point on the scheme
2688
pass
2689
from sage.graphs.digraph import DiGraph
2690
g=DiGraph(dict(zip(V,E)), loops=True)
2691
return g
2692
2693
def possible_periods(self,return_points=False):
2694
r"""
2695
Returns the list of possible minimal periods of a periodic point
2696
over `\QQ` and (optionally) a point in each cycle.
2697
2698
ALGORITHM:
2699
2700
The list comes from: Hutz, Good reduction of periodic points, Illinois Journal of
2701
Mathematics 53 (Winter 2009), no. 4, 1109-1126.
2702
2703
INPUT:
2704
2705
- ``return_points`` - Boolean (optional) - a value of True returns the points as well as the possible periods.
2706
2707
OUTPUT:
2708
2709
- a list of positive integers, or a list of pairs of projective points and periods if ``flag`` is 1.
2710
2711
Examples::
2712
2713
sage: P.<x,y> = ProjectiveSpace(GF(23),1)
2714
sage: H = End(P)
2715
sage: f = H([x^2-2*y^2,y^2])
2716
sage: f.possible_periods()
2717
[1, 5, 11, 22, 110]
2718
2719
::
2720
2721
sage: P.<x,y> = ProjectiveSpace(GF(13),1)
2722
sage: H = End(P)
2723
sage: f = H([x^2-y^2,y^2])
2724
sage: f.possible_periods(True)
2725
[[(1 : 0), 1], [(0 : 1), 2], [(3 : 1), 3], [(3 : 1), 36]]
2726
2727
::
2728
2729
sage: PS.<x,y,z> = ProjectiveSpace(2,GF(7))
2730
sage: H = End(PS)
2731
sage: f = H([-360*x^3 + 760*x*z^2, y^3 - 604*y*z^2 + 240*z^3, 240*z^3])
2732
sage: f.possible_periods()
2733
[1, 2, 4, 6, 12, 14, 28, 42, 84]
2734
2735
.. TODO::
2736
2737
- do not reutrn duplicate points
2738
2739
- check == False to speed up?
2740
2741
- move to Cython
2742
2743
"""
2744
if not is_PrimeFiniteField(self.domain().base_ring()):
2745
raise TypeError("Must be prime field")
2746
if not self.is_endomorphism():
2747
raise NotImplementedError("Must be an endomorphism of projective space")
2748
2749
PS=self.domain()
2750
p=PS.base_ring().order()
2751
N=PS.dimension_relative()
2752
pointsdict=PS.rational_points_dictionary() #assume p is prime
2753
pointslist=list(pointsdict)
2754
hashlist=pointsdict.values()
2755
pointtable=[[0,0] for i in range(len(pointsdict))]
2756
index=1
2757
periods=set()
2758
points_periods=[]
2759
for j in range(len(pointsdict)):
2760
hashP=hashlist[j]
2761
if pointtable[hashP][1]==0:
2762
startindex=index
2763
P=pointslist[j]
2764
while pointtable[hashP][1]==0:
2765
pointtable[hashP][1]=index
2766
Q=self(P)
2767
Q.normalize_coordinates()
2768
hashQ=pointsdict[Q]
2769
pointtable[hashP][0]=hashQ
2770
P=Q
2771
hashP=hashQ
2772
index+=1
2773
if pointtable[hashP][1]>= startindex:
2774
period=index-pointtable[hashP][1]
2775
periods.add(period)
2776
points_periods.append([P,period])
2777
l=P.multiplier(self,period,False)
2778
lorders=set()
2779
for poly,_ in l.charpoly().factor():
2780
if poly.degree() == 1:
2781
eig = -poly.constant_coefficient()
2782
if not eig:
2783
continue # exclude 0
2784
else:
2785
eig = GF(p ** poly.degree(), 't', modulus=poly).gen()
2786
if eig:
2787
lorders.add(eig.multiplicative_order())
2788
S = subsets(lorders)
2789
S.next() # get rid of the empty set
2790
rvalues=set()
2791
for s in S:
2792
rvalues.add(lcm(s))
2793
rvalues=list(rvalues)
2794
if N==1:
2795
for k in range(len(rvalues)):
2796
r=rvalues[k]
2797
periods.add(period*r)
2798
points_periods.append([P,period*r])
2799
if p==2 or p==3: #need e=1 for N=1, QQ
2800
periods.add(period*r*p)
2801
points_periods.append([P,period*r*p])
2802
else:
2803
for k in range(len(rvalues)):
2804
r=rvalues[k]
2805
periods.add(period*r)
2806
periods.add(period*r*p)
2807
points_periods.append([P,period*r])
2808
points_periods.append([P,period*r*p])
2809
if p==2: #need e=3 for N>1, QQ
2810
periods.add(period*r*4)
2811
points_periods.append([P,period*r*4])
2812
periods.add(period*r*8)
2813
points_periods.append([P,period*r*8])
2814
2815
if return_points==False:
2816
return(sorted(periods))
2817
else:
2818
return(points_periods)
2819
2820
2821