Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- r""" Dynamical systems on projective schemes
A dynamical system of projective schemes determined by homogeneous polynomials functions that define what the morphism does on points in the ambient projective space.
The main constructor functions are given by :class:`DynamicalSystem` and :class:`DynamicalSystem_projective`. The constructors function can take either polynomials or a morphism from which to construct a dynamical system. If the domain is not specified, it is constructed. However, if you plan on working with points or subvarieties in the domain, it recommended to specify the domain.
The initialization checks are always performed by the constructor functions. It is possible, but not recommended, to skip these checks by calling the class initialization directly.
AUTHORS:
- David Kohel, William Stein
- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as a projective point.
- Volker Braun (2011-08-08): Renamed classes, more documentation, misc cleanups.
- Ben Hutz (2013-03) iteration functionality and new directory structure for affine/projective, height functionality
- Brian Stout, Ben Hutz (Nov 2013) - added minimal model functionality
- Dillon Rose (2014-01): Speed enhancements
- Ben Hutz (2015-11): iteration of subschemes
- Ben Hutz (2017-7): relocate code and create class
"""
#***************************************************************************** # Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com> # Copyright (C) 2006 David Kohel <kohel@maths.usyd.edu.au> # Copyright (C) 2006 William Stein <wstein@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import
from sage.arith.misc import is_prime from sage.categories.fields import Fields from sage.categories.function_fields import FunctionFields from sage.categories.number_fields import NumberFields from sage.categories.homset import End from sage.dynamics.arithmetic_dynamics.generic_ds import DynamicalSystem from sage.functions.all import sqrt from sage.functions.other import ceil from sage.libs.pari.all import PariError from sage.matrix.constructor import matrix, identity_matrix from sage.misc.cachefunc import cached_method from sage.misc.classcall_metaclass import typecall from sage.misc.mrange import xmrange from sage.modules.free_module_element import vector from sage.rings.all import Integer, CIF from sage.arith.all import gcd, lcm, next_prime, binomial, primes, moebius from sage.categories.finite_fields import FiniteFields from sage.rings.finite_rings.finite_field_constructor import (is_FiniteField, GF, is_PrimeFiniteField) from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.rings.fraction_field import (FractionField, is_FractionField) from sage.rings.fraction_field_element import is_FractionFieldElement, FractionFieldElement from sage.rings.integer_ring import ZZ from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal from sage.rings.padics.all import Qp from sage.rings.polynomial.multi_polynomial_ring_generic import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.qqbar import QQbar from sage.rings.quotient_ring import QuotientRing_generic from sage.rings.rational_field import QQ from sage.rings.real_double import RDF from sage.rings.real_mpfr import (RealField, is_RealField) from sage.schemes.generic.morphism import SchemeMorphism_polynomial from sage.schemes.projective.projective_subscheme import AlgebraicScheme_subscheme_projective from sage.schemes.projective.projective_morphism import ( SchemeMorphism_polynomial_projective_space, SchemeMorphism_polynomial_projective_space_field, SchemeMorphism_polynomial_projective_space_finite_field) from sage.schemes.projective.projective_space import (ProjectiveSpace, is_ProjectiveSpace) from sage.schemes.product_projective.space import is_ProductProjectiveSpaces from sage.symbolic.constants import e from copy import copy from sage.parallel.ncpus import ncpus from sage.parallel.use_fork import p_iter_fork from sage.dynamics.arithmetic_dynamics.projective_ds_helper import _fast_possible_periods from sage.sets.set import Set from sage.combinat.permutation import Arrangements from sage.combinat.subset import Subsets from sage.symbolic.ring import SR
class DynamicalSystem_projective(SchemeMorphism_polynomial_projective_space, DynamicalSystem): r"""A dynamical system of projective schemes determined by homogeneous polynomials that define what the morphism does on points in the ambient projective space.
.. WARNING::
You should not create objects of this class directly because no type or consistency checking is performed. The preferred method to construct such dynamical systems is to use :func:`~sage.dynamics.arithmetic_dynamics.generic_ds.DynamicalSystem_projective` function
INPUT:
- ``morphism_or_polys`` -- a SchemeMorphism, a polynomial, a rational function, or a list or tuple of homogeneous polynomials.
- ``domain`` -- optional projective space or projective subscheme.
- ``names`` -- optional tuple of strings to be used as coordinate names for a projective space that is constructed; defaults to ``'X','Y'``.
The following combinations of ``morphism_or_polys`` and ``domain`` are meaningful:
* ``morphism_or_polys`` is a SchemeMorphism; ``domain`` is ignored in this case.
* ``morphism_or_polys`` is a list of homogeneous polynomials that define a rational endomorphism of ``domain``.
* ``morphism_or_polys`` is a list of homogeneous polynomials and ``domain`` is unspecified; ``domain`` is then taken to be the projective space of appropriate dimension over the base ring of the first element of ``morphism_or_polys``.
* ``morphism_or_polys`` is a single polynomial or rational function; ``domain`` is ignored and taken to be a 1-dimensional projective space over the base ring of ``morphism_or_polys`` with coordinate names given by ``names``.
OUTPUT: :class:`DynamicalSystem_projectve`.
EXAMPLES::
sage: P1.<x,y> = ProjectiveSpace(QQ,1) sage: DynamicalSystem_projective([y, 2*x]) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (y : 2*x)
We can define dynamical systems on `P^1` by giving a polynomial or rational function::
sage: R.<t> = QQ[] sage: DynamicalSystem_projective(t^2 - 3) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (X^2 - 3*Y^2 : Y^2) sage: DynamicalSystem_projective(1/t^2) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (Y^2 : X^2)
::
sage: R.<x> = PolynomialRing(QQ,1) sage: DynamicalSystem_projective(x^2, names=['a','b']) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (a : b) to (a^2 : b^2)
Symbolic Ring elements are not allows::
sage: x,y = var('x,y') sage: DynamicalSystem_projective([x^2,y^2]) Traceback (most recent call last): ... ValueError: [x^2, y^2] must be elements of a polynomial ring
::
sage: R.<x> = PolynomialRing(QQ,1) sage: DynamicalSystem_projective(x^2) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (X^2 : Y^2)
::
sage: R.<t> = PolynomialRing(QQ) sage: P.<x,y,z> = ProjectiveSpace(R, 2) sage: X = P.subscheme([x]) sage: DynamicalSystem_projective([x^2, t*y^2, x*z], domain=X) Dynamical System of Closed subscheme of Projective Space of dimension 2 over Univariate Polynomial Ring in t over Rational Field defined by: x Defn: Defined on coordinates by sending (x : y : z) to (x^2 : t*y^2 : x*z)
When elements of the quotient ring are used, they are reduced::
sage: P.<x,y,z> = ProjectiveSpace(CC, 2) sage: X = P.subscheme([x-y]) sage: u,v,w = X.coordinate_ring().gens() sage: DynamicalSystem_projective([u^2, v^2, w*u], domain=X) Dynamical System of Closed subscheme of Projective Space of dimension 2 over Complex Field with 53 bits of precision defined by: x - y Defn: Defined on coordinates by sending (x : y : z) to (y^2 : y^2 : y*z)
We can also compute the forward image of subschemes through elimination. In particular, let `X = V(h_1,\ldots, h_t)` and define the ideal `I = (h_1,\ldots,h_t,y_0-f_0(\bar{x}), \ldots, y_n-f_n(\bar{x}))`. Then the elimination ideal `I_{n+1} = I \cap K[y_0,\ldots,y_n]` is a homogeneous ideal and `f(X) = V(I_{n+1})`::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([(x-2*y)^2, (x-2*z)^2, x^2]) sage: X = P.subscheme(y-z) sage: f(f(f(X))) Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: y - z
::
sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3) sage: f = DynamicalSystem_projective([(x-2*y)^2, (x-2*z)^2, (x-2*w)^2, x^2]) sage: f(P.subscheme([x,y,z])) Closed subscheme of Projective Space of dimension 3 over Rational Field defined by: w, y, x
::
sage: T.<x,y,z,w,u> = ProductProjectiveSpaces([2, 1], QQ) sage: DynamicalSystem_projective([x^2*u, y^2*w, z^2*u, w^2, u^2], domain=T) Dynamical System of Product of projective spaces P^2 x P^1 over Rational Field Defn: Defined by sending (x : y : z , w : u) to (x^2*u : y^2*w : z^2*u , w^2 : u^2). """
@staticmethod def __classcall_private__(cls, morphism_or_polys, domain=None, names=None): r""" Return the appropriate dynamical system on a projective scheme.
TESTS::
sage: R.<x,y> = QQ[] sage: P1 = ProjectiveSpace(R) sage: f = DynamicalSystem_projective([x-y, x*y]) Traceback (most recent call last): ... ValueError: polys (=[x - y, x*y]) must be of the same degree sage: DynamicalSystem_projective([x-1, x*y+x]) Traceback (most recent call last): ... ValueError: polys (=[x - 1, x*y + x]) must be homogeneous
::
sage: DynamicalSystem_projective([exp(x),exp(y)]) Traceback (most recent call last): ... ValueError: [e^x, e^y] must be elements of a polynomial ring
::
sage: T.<x,y,z,w,u> = ProductProjectiveSpaces([2, 1], QQ) sage: DynamicalSystem_projective([x^2*u, y^2*w, z^2*u, w^2, u*z], domain=T) Traceback (most recent call last): ... TypeError: polys (=[x^2*u, y^2*w, z^2*u, w^2, z*u]) must be multi-homogeneous of the same degrees (by component)
::
sage: A.<x,y> = AffineSpace(ZZ, 2) sage: DynamicalSystem_projective([x^2,y^2], A) Traceback (most recent call last): ... ValueError: "domain" must be a projective scheme sage: H = End(A) sage: f = H([x,y]) sage: DynamicalSystem_projective(f) Traceback (most recent call last): ... ValueError: "domain" must be a projective scheme
::
sage: R.<x> = PolynomialRing(QQ,1) sage: DynamicalSystem_projective(x^2, names='t') Traceback (most recent call last): ... ValueError: specify 2 variable names
::
sage: P1.<x,y> = ProjectiveSpace(QQ,1) sage: DynamicalSystem_projective([y, x, y], domain=P1) Traceback (most recent call last): ... ValueError: polys (=[y, x, y]) do not define a rational endomorphism of the domain
::
sage: A.<x,y> = AffineSpace(QQ,2) sage: DynamicalSystem_projective([y,x], domain=A) Traceback (most recent call last): ... ValueError: "domain" must be a projective scheme
::
sage: R.<x> = QQ[] sage: DynamicalSystem([x^2]) Traceback (most recent call last): ... ValueError: list/tuple must have at least 2 polynomials """
raise ValueError('domain and codomain do not agree') return typecall(cls, polys, domain) return DynamicalSystem_projective_finite_field(polys, domain)
else: # homogenize! and not (is_MPolynomialRing(aff_CR) and aff_CR.ngens() == 1)): msg = '{} is not a single variable polynomial or rational function' raise ValueError(msg.format(f)) else: raise TypeError("Symbolic Ring cannot be the base ring")
# Now polys define an endomorphism of a scheme in P^n
def __init__(self, polys, domain): r""" The Python constructor.
See :class:`DynamicalSystem` for details.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: DynamicalSystem_projective([3/5*x^2, y^2], domain=P) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (3/5*x^2 : y^2) """ # Next attribute needed for _fast_eval and _fastpolys
def __copy__(self): r""" Return a copy of this dynamical system.
OUTPUT: :class:`DynamicalSystem_projective`
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([3/5*x^2,6*y^2]) sage: g = copy(f) sage: f == g True sage: f is g False """
def dehomogenize(self, n): r""" Return the standard dehomogenization at the ``n[0]`` coordinate for the domain and the ``n[1]`` coordinate for the codomain.
Note that the new function is defined over the fraction field of the base ring of this map.
INPUT:
- ``n`` -- a tuple of nonnegative integers; if ``n`` is an integer, then the two values of the tuple are assumed to be the same
OUTPUT:
:class:`DynamicalSystem_affine` given by dehomogenizing the source and target of `self` with respect to the given indices.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.dehomogenize(0) Dynamical System of Affine Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x) to (x^2/(x^2 + 1)) """
def dynatomic_polynomial(self, period): r""" For a dynamical system of `\mathbb{P}^1` compute the dynatomic polynomial.
The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `period`. If possible the division is done in the coordinate ring of this map and a polynomial is returned. In rings where that is not possible, a :class:`FractionField` element will be returned. In certain cases, when the conversion back to a polynomial fails, a :class:`SymbolRing` element will be returned.
ALGORITHM:
For a positive integer `n`, let `[F_n,G_n]` be the coordinates of the `nth` iterate of `f`. Then construct
.. MATH::
\Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)},
where `\mu` is the Möbius function.
For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute
.. MATH::
\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})
REFERENCES:
- [Hutz2015]_ - [MoPa1994]_
INPUT:
- ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period
OUTPUT:
If possible, a two variable polynomial in the coordinate ring of this map. Otherwise a fraction field element of the coordinate ring of this map. Or, a :class:`SymbolicRing` element.
.. TODO::
- Do the division when the base ring is `p`-adic so that the output is a polynomial.
- Convert back to a polynomial when the base ring is a function field (not over `\QQ` or `F_p`).
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + 2*y^2
::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: f.dynatomic_polynomial(4) 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
::
sage: P.<x,y> = ProjectiveSpace(CC,1) sage: f = DynamicalSystem_projective([x^2 + y^2, 3*x*y]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4*y^2 + 78.0000000000000*x^2*y^4 + y^6
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - 10/9*y^2, y^2]) sage: f.dynatomic_polynomial([2,1]) x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - 29/16*y^2, y^2]) sage: f.dynatomic_polynomial([2,3]) x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 + 8198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 + 172692729/16777216*y^12
::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2 - y^2, y^2]) sage: f.dynatomic_polynomial([1,2]) x^2 - x*y
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3 - y^3, 3*x*y^2]) sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4) True
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y, z^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... TypeError: does not make sense in dimension >1
::
sage: P.<x,y> = ProjectiveSpace(Qp(5),1) sage: f = DynamicalSystem_projective([x^2 + y^2, y^2]) sage: f.dynatomic_polynomial(2) (x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y - x*y^2 + y^3)
::
sage: L.<t> = PolynomialRing(QQ) sage: P.<x,y> = ProjectiveSpace(L,1) sage: f = DynamicalSystem_projective([x^2 + t*y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t + 1)*y^2
::
sage: K.<c> = PolynomialRing(ZZ) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2 + c*y^2, y^2]) sage: f.dynatomic_polynomial([1, 2]) x^2 - x*y + (c + 1)*y^2
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + 2*y^2 sage: R.<X> = PolynomialRing(QQ) sage: K.<c> = NumberField(X^2 + X + 2) sage: PP = P.change_ring(K) sage: ff = f.change_ring(K) sage: p = PP((c, 1)) sage: ff(ff(p)) == p True
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: f.dynatomic_polynomial([2, 2]) x^4 + 4*x^2*y^2 + y^4 sage: R.<X> = PolynomialRing(QQ) sage: K.<c> = NumberField(X^4 + 4*X^2 + 1) sage: PP = P.change_ring(K) sage: ff = f.change_ring(K) sage: p = PP((c, 1)) sage: ff.nth_iterate(p, 4) == ff.nth_iterate(p, 2) True
::
sage: P.<x,y> = ProjectiveSpace(CC, 1) sage: f = DynamicalSystem_projective([x^2 - CC.0/3*y^2, y^2]) sage: f.dynatomic_polynomial(2) (x^4*y + (-0.666666666666667*I)*x^2*y^3 - x*y^4 + (-0.111111111111111 - 0.333333333333333*I)*y^5)/(x^2*y - x*y^2 + (-0.333333333333333*I)*y^3)
::
sage: P.<x,y> = ProjectiveSpace(CC, 1) sage: f = DynamicalSystem_projective([x^2-CC.0/5*y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (1.00000000000000 - 0.200000000000000*I)*y^2
::
sage: L.<t> = PolynomialRing(QuadraticField(2).maximal_order()) sage: P.<x, y> = ProjectiveSpace(L.fraction_field() , 1) sage: f = DynamicalSystem_projective([x^2 + (t^2 + 1)*y^2 , y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t^2 + 2)*y^2
::
sage: P.<x,y> = ProjectiveSpace(ZZ, 1) sage: f = DynamicalSystem_projective([x^2 - 5*y^2, y^2]) sage: f.dynatomic_polynomial([3,0 ]) 0
TESTS:
We check that the dynatomic polynomial has the right parent (see :trac:`18409`)::
sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: f = DynamicalSystem_projective([x^2 - 1/3*y^2, y^2]) sage: f.dynatomic_polynomial(2).parent() Multivariate Polynomial Ring in x, y over Algebraic Field
::
sage: T.<v> = QuadraticField(33) sage: S.<t> = PolynomialRing(T) sage: P.<x,y> = ProjectiveSpace(FractionField(S),1) sage: f = DynamicalSystem_projective([t*x^2 - 1/t*y^2, y^2]) sage: f.dynatomic_polynomial([1, 2]).parent() Multivariate Polynomial Ring in x, y over Fraction Field of Univariate Polynomial Ring in t over Number Field in v with defining polynomial x^2 - 33
::
sage: P.<x, y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^3 - y^3*2, y^3]) sage: f.dynatomic_polynomial(1).parent() Multivariate Polynomial Ring in x, y over Rational Field
::
sage: R.<c> = QQ[] sage: P.<x,y> = ProjectiveSpace(R,1) sage: f = DynamicalSystem_projective([x^2 + c*y^2, y^2]) sage: f.dynatomic_polynomial([1,2]).parent() Multivariate Polynomial Ring in x, y over Univariate Polynomial Ring in c over Rational Field
::
sage: R.<c> = QQ[] sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, (1)*y^2 + (1)*x*y]) sage: f.dynatomic_polynomial([1,2]).parent() Multivariate Polynomial Ring in x, y over Integer Ring
::
sage: P.<x, y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 + y^2, y^2]) sage: f.dynatomic_polynomial(0) 0 sage: f.dynatomic_polynomial([0,0]) 0 sage: f.dynatomic_polynomial(-1) Traceback (most recent call last): ... TypeError: period must be a positive integer
::
sage: R.<c> = QQ[] sage: P.<x,y> = ProjectiveSpace(R,1) sage: f = DynamicalSystem_projective([x^2 + c*y^2,y^2]) sage: f.dynatomic_polynomial([1,2]).parent() Multivariate Polynomial Ring in x, y over Univariate Polynomial Ring in c over Rational Field
Some rings still return :class:`SymoblicRing` elements::
sage: S.<t> = FunctionField(CC) sage: P.<x,y> = ProjectiveSpace(S,1) sage: f = DynamicalSystem_projective([t*x^2-1*y^2, t*y^2]) sage: f.dynatomic_polynomial([1, 2]).parent() Symbolic Ring
::
sage: R.<x,y> = PolynomialRing(QQ) sage: S = R.quo(R.ideal(y^2-x+1)) sage: P.<u,v> = ProjectiveSpace(FractionField(S),1) sage: f = DynamicalSystem_projective([u^2 + S(x^2)*v^2, v^2]) sage: dyn = f.dynatomic_polynomial([1,1]); dyn v^3*xbar^2 + u^2*v + u*v^2 sage: dyn.parent() Symbolic Ring """ #even when the ring can be passed to singular in quo_rem, #it can't always do the division, so we call Maxima # do it again to divide out by denominators of coefficients
def nth_iterate_map(self, n, normalize=False): r""" Return the ``n``-th iterate of this dynamical system.
ALGORITHM:
Uses a form of successive squaring to reducing computations.
.. TODO:: This could be improved.
INPUT:
- ``n`` -- positive integer
- ``normalize`` -- boolean; remove gcd's during iteration
OUTPUT: a projective dynamical system
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.nth_iterate_map(2) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^4 + 2*x^2*y^2 + 2*y^4 : y^4)
::
sage: P.<x,y> = ProjectiveSpace(CC,1) sage: f = DynamicalSystem_projective([x^2-y^2, x*y]) sage: f.nth_iterate_map(3) Dynamical System of Projective Space of dimension 1 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y) to (x^8 + (-7.00000000000000)*x^6*y^2 + 13.0000000000000*x^4*y^4 + (-7.00000000000000)*x^2*y^6 + y^8 : x^7*y + (-4.00000000000000)*x^5*y^3 + 4.00000000000000*x^3*y^5 - x*y^7)
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([x^2-y^2, x*y, z^2+x^2]) sage: f.nth_iterate_map(2) Dynamical System of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (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 + 2*x^2*z^2 + z^4)
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x*z-y^2) sage: f = DynamicalSystem_projective([x^2, x*z, z^2], domain=X) sage: f.nth_iterate_map(2) Dynamical System of Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: -y^2 + x*z Defn: Defined on coordinates by sending (x : y : z) to (x^4 : x^2*z^2 : z^4)
::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([y^2 * z^3, y^3 * z^2, x^5]) sage: f.nth_iterate_map( 5, normalize=True) Dynamical System of Projective Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (y^202*z^443 : x^140*y^163*z^342 : x^645) """ raise TypeError("iterate number must be a positive integer") else:
def nth_iterate(self, P, n, **kwds): r""" Return the ``n``-th iterate of the point ``P`` by this dynamical system.
If ``normalize`` is ``True``, then the coordinates are automatically normalized.
.. TODO:: Is there a more efficient way to do this?
INPUT:
- ``P`` -- a point in this map's domain
- ``n`` -- a positive integer
kwds:
- ``normalize`` -- (default: ``False``) boolean
OUTPUT: a point in this map's codomain
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, 2*y^2]) sage: Q = P(1,1) sage: f.nth_iterate(Q,4) (32768 : 32768)
::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, 2*y^2]) sage: Q = P(1,1) sage: f.nth_iterate(Q, 4, normalize=True) (1 : 1)
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2, 2*y^2, z^2-x^2]) sage: Q = P(2,7,1) sage: f.nth_iterate(Q,2) (-16/7 : -2744 : 1)
::
sage: R.<t> = PolynomialRing(QQ) sage: P.<x,y,z> = ProjectiveSpace(R,2) sage: f = DynamicalSystem_projective([x^2+t*y^2, (2-t)*y^2, z^2]) sage: Q = P(2+t,7,t) sage: f.nth_iterate(Q,2) (t^4 + 2507*t^3 - 6787*t^2 + 10028*t + 16 : -2401*t^3 + 14406*t^2 - 28812*t + 19208 : t^4)
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: f = DynamicalSystem_projective([x^2, y^2, z^2], domain=X) sage: f.nth_iterate(X(2,2,3), 3) (256 : 256 : 6561)
::
sage: K.<c> = FunctionField(QQ) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^3 - 2*x*y^2 - c*y^3, x*y^2]) sage: f.nth_iterate(P(c,1), 2) ((c^6 - 9*c^4 + 25*c^2 - c - 21)/(c^2 - 3) : 1)
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) sage: f = H([x^2+3*y^2, 2*y^2,z^2]) sage: P(2, 7, 1).nth_iterate(f, -2) Traceback (most recent call last): ... TypeError: must be a forward orbit
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^3, x*y^2], domain=P) sage: f.nth_iterate(P(0, 1), 3, check=False) (0 : 0) sage: f.nth_iterate(P(0, 1), 3) Traceback (most recent call last): ... ValueError: [0, 0] does not define a valid point since all entries are 0
::
sage: P.<x,y> = ProjectiveSpace(ZZ, 1) sage: f = DynamicalSystem_projective([x^3, x*y^2], domain=P) sage: f.nth_iterate(P(2,1), 3, normalize=False) (134217728 : 524288) sage: f.nth_iterate(P(2,1), 3, normalize=True) (256 : 1) """ raise TypeError("must be a forward orbit")
def degree_sequence(self, iterates=2): r""" Return sequence of degrees of normalized iterates starting with the degree of this dynamical system.
INPUT: ``iterates`` -- (default: 2) positive integer
OUTPUT: list of integers
EXAMPLES::
sage: P2.<X,Y,Z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([Z^2, X*Y, Y^2]) sage: f.degree_sequence(15) [2, 3, 5, 8, 11, 17, 24, 31, 45, 56, 68, 91, 93, 184, 275]
::
sage: F.<t> = PolynomialRing(QQ) sage: P2.<X,Y,Z> = ProjectiveSpace(F, 2) sage: f = DynamicalSystem_projective([Y*Z, X*Y, Y^2 + t*X*Z]) sage: f.degree_sequence(5) [2, 3, 5, 8, 13]
::
sage: P2.<X,Y,Z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([X^2, Y^2, Z^2]) sage: f.degree_sequence(10) [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
::
sage: P2.<X,Y,Z> = ProjectiveSpace(ZZ, 2) sage: f = DynamicalSystem_projective([X*Y, Y*Z+Z^2, Z^2]) sage: f.degree_sequence(10) [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] """ raise TypeError("number of iterates must be a positive integer")
else:
def dynamical_degree(self, N=3, prec=53): r""" Return an approximation to the dynamical degree of this dynamical system. The dynamical degree is defined as `\lim_{n \to \infty} \sqrt[n]{\deg(f^n)}`.
INPUT:
- ``N`` -- (default: 3) positive integer, iterate to use for approximation
- ``prec`` -- (default: 53) positive integer, real precision to use when computing root
OUTPUT: real number
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 + (x*y), y^2]) sage: f.dynamical_degree() 2.00000000000000
::
sage: P2.<X,Y,Z> = ProjectiveSpace(ZZ, 2) sage: f = DynamicalSystem_projective([X*Y, Y*Z+Z^2, Z^2]) sage: f.dynamical_degree(N=5, prec=100) 1.4309690811052555010452244131 """ raise TypeError("number of iterates must be a positive integer")
else:
def orbit(self, P, N, **kwds): r""" Return the orbit of the point ``P`` by this dynamical system.
Let `F` be this dynamical system. If ``N`` is an integer return `[P,F(P),\ldots,F^N(P)]`. If ``N`` is a list or tuple `N=[m,k]` return `[F^m(P),\ldots,F^k(P)]`. Automatically normalize the points if ``normalize=True``. Perform the checks on point initialization if ``check=True``.
INPUT:
- ``P`` -- a point in this dynamical system's domain
- ``n`` -- a non-negative integer or list or tuple of two non-negative integers
kwds:
- ``check`` -- (default: ``True``) boolean
- ``normalize`` -- (default: ``False``) boolean
OUTPUT: a list of points in this dynamical system's codomain
EXAMPLES::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([x^2+y^2, y^2-z^2, 2*z^2]) sage: f.orbit(P(1,2,1), 3) [(1 : 2 : 1), (5 : 3 : 2), (34 : 5 : 8), (1181 : -39 : 128)]
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([x^2+y^2, y^2-z^2, 2*z^2]) sage: f.orbit(P(1,2,1), [2,4]) [(34 : 5 : 8), (1181 : -39 : 128), (1396282 : -14863 : 32768)]
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: f = DynamicalSystem_projective([x^2, y^2, x*z], domain=X) sage: f.orbit(X(2,2,3), 3, normalize=True) [(2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.orbit(P.point([1,2],False), 4, check=False) [(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)]
::
sage: K.<c> = FunctionField(QQ) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2+c*y^2, y^2]) sage: f.orbit(P(0,1), 3) [(0 : 1), (c : 1), (c^2 + c : 1), (c^4 + 2*c^3 + c^2 + c : 1)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2,y^2], domain=P) sage: f.orbit(P.point([1, 2], False), 4, check=False) [(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2, 2*y^2], domain=P) sage: P(2, 1).orbit(f,[-1, 4]) Traceback (most recent call last): ... TypeError: orbit bounds must be non-negative sage: P(2, 1).orbit(f, 0.1) Traceback (most recent call last): ... TypeError: Attempt to coerce non-integral RealNumber to Integer
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3, x*y^2], domain=P) sage: P(0, 1).orbit(f, 3) Traceback (most recent call last): ... ValueError: [0, 0] does not define a valid point since all entries are 0 sage: P(0, 1).orbit(f, 3, check=False) [(0 : 1), (0 : 0), (0 : 0), (0 : 0)]
::
sage: P.<x,y> = ProjectiveSpace(ZZ, 1) sage: f = DynamicalSystem_projective([x^3, x*y^2], domain=P) sage: P(2,1).orbit(f, 3, normalize=False) [(2 : 1), (8 : 2), (512 : 32), (134217728 : 524288)] sage: P(2, 1).orbit(f, 3, normalize=True) [(2 : 1), (4 : 1), (16 : 1), (256 : 1)] """ raise TypeError("orbit bounds must be non-negative") return([])
def resultant(self, normalize=False): r""" Computes the resultant of the defining polynomials of this dynamical system.
If ``normalize`` is ``True``, then first normalize the coordinate functions with :meth:`normalize_coordinates`.
INPUT:
- ``normalize`` -- (default: ``False``) boolean
OUTPUT: an element of the base ring of this map
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, 6*y^2]) sage: f.resultant() 36
::
sage: R.<t> = PolynomialRing(GF(17)) sage: P.<x,y> = ProjectiveSpace(R,1) sage: f = DynamicalSystem_projective([t*x^2+t*y^2, 6*y^2]) sage: f.resultant() 2*t^2
::
sage: R.<t> = PolynomialRing(GF(17)) sage: P.<x,y,z> = ProjectiveSpace(R,2) sage: f = DynamicalSystem_projective([t*x^2+t*y^2, 6*y^2, 2*t*z^2]) sage: f.resultant() 13*t^8
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: F = DynamicalSystem_projective([x^2+y^2,6*y^2,10*x*z+z^2+y^2]) sage: F.resultant() 1296
::
sage: R.<t>=PolynomialRing(QQ) sage: s = (t^3+t+1).roots(QQbar)[0][0] sage: P.<x,y>=ProjectiveSpace(QQbar,1) sage: f = DynamicalSystem_projective([s*x^3-13*y^3, y^3-15*y^3]) sage: f.resultant() 871.6925062959149? """ else:
#Try to use pari first, as it is faster for one dimensional case #however the coercion from a Pari object to a sage object breaks #in the case of QQbar, so we just pass it into the macaulay resultant * f.__pari__().polresultant(g, x)) #Otherwise, use Macaulay
@cached_method def primes_of_bad_reduction(self, check=True): r""" Determine the primes of bad reduction for this dynamical system.
Must be defined over a number field.
If ``check`` is ``True``, each prime is verified to be of bad reduction.
ALGORITHM:
`p` is a prime of bad reduction if and only if the defining polynomials of self have a common zero. Or stated another way, `p` is a prime of bad reduction if and only if the radical of the ideal defined by the defining polynomials of self is not `(x_0,x_1,\ldots,x_N)`. This happens if and only if some power of each `x_i` is not in the ideal defined by the defining polynomials of self. This last condition is what is checked. The lcm of the coefficients of the monomials `x_i` in a Groebner basis is computed. This may return extra primes.
INPUT:
- ``check`` -- (default: ``True``) boolean
OUTPUT: a list of primes
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([1/3*x^2+1/2*y^2, y^2]) sage: f.primes_of_bad_reduction() [2, 3]
::
sage: P.<x,y,z,w> = ProjectiveSpace(QQ,3) sage: f = DynamicalSystem_projective([12*x*z-7*y^2, 31*x^2-y^2, 26*z^2, 3*w^2-z*w]) sage: f.primes_of_bad_reduction() [2, 3, 7, 13, 31]
A number field example::
sage: R.<z> = QQ[] sage: K.<a> = NumberField(z^2 - 2) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([1/3*x^2+1/a*y^2, y^2]) sage: f.primes_of_bad_reduction() [Fractional ideal (a), Fractional ideal (3)]
This is an example where check = False returns extra primes::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([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]) sage: f.primes_of_bad_reduction(False) [2, 5, 37, 2239, 304432717] sage: f.primes_of_bad_reduction() [5, 37, 2239, 304432717] """ raise NotImplementedError("not implemented for subschemes") #The primes of bad reduction are the support of the resultant for number fields
else: #For the rationals, we can use groebner basis, as it is quicker in practice
else: raise TypeError("not a morphism") #normalize to coefficients in the ring not the fraction field.
#move the ideal to the ring of integers else:
#get the primes dividing the coefficients of the monomials x_i^k_i
#check to return only the truly bad primes else: else: raise TypeError("base ring must be number field or number field ring")
def conjugate(self, M): r""" Conjugate this dynamical system by ``M``, i.e. `M^{-1} \circ f \circ M`.
If possible the new map will be defined over the same space. Otherwise, will try to coerce to the base ring of ``M``.
INPUT:
- ``M`` -- a square invertible matrix
OUTPUT: a dynamical system
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.conjugate(matrix([[1,2], [0,1]])) Dynamical System of Projective Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x : y) to (x^2 + 4*x*y + 3*y^2 : y^2)
::
sage: R.<x> = PolynomialRing(QQ) sage: K.<i> = NumberField(x^2+1) sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^3+y^3, y^3]) sage: f.conjugate(matrix([[i,0], [0,-i]])) Dynamical System of Projective Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x : y) to (-x^3 + y^3 : -y^3)
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([x^2+y^2 ,y^2, y*z]) sage: f.conjugate(matrix([[1,2,3], [0,1,2], [0,0,1]])) Dynamical System of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (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)
::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.conjugate(matrix([[2,0], [0,1/2]])) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (2*x^2 + 1/8*y^2 : 1/2*y^2)
::
sage: R.<x> = PolynomialRing(QQ) sage: K.<i> = NumberField(x^2+1) sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([1/3*x^2+1/2*y^2, y^2]) sage: f.conjugate(matrix([[i,0], [0,-i]])) Dynamical System of Projective Space of dimension 1 over Number Field in i with defining polynomial x^2 + 1 Defn: Defined on coordinates by sending (x : y) to ((1/3*i)*x^2 + (1/2*i)*y^2 : (-i)*y^2) """ and M.ncols() == self.domain().ambient_space().dimension_relative() + 1): raise TypeError("matrix must be invertible and size dimension + 1")
def green_function(self, P, v, **kwds): r""" Evaluate the local Green's function at the place ``v`` for ``P`` with ``N`` terms of the series or to within a given error bound.
Must be over a number field or order of a number field. Note that this is the absolute local Green's function so is scaled by the degree of the base field.
Use ``v=0`` for the archimedean place over `\QQ` or field embedding. Non-archimedean places are prime ideals for number fields or primes over `\QQ`.
ALGORITHM:
See Exercise 5.29 and Figure 5.6 of [Sil2007]_.
INPUT:
- ``P`` -- a projective point
- ``v`` -- non-negative integer. a place, use ``0`` for the archimedean place
kwds:
- ``N`` -- (optional - default: 10) positive integer. number of terms of the series to use
- ``prec`` -- (default: 100) positive integer, float point or `p`-adic precision
- ``error_bound`` -- (optional) a positive real number
OUTPUT: a real number
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, x*y]); sage: Q = P(5, 1) sage: f.green_function(Q, 0, N=30) 1.6460930159932946233759277576
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, x*y]); sage: Q = P(5, 1) sage: f.green_function(Q, 0, N=200, prec=200) 1.6460930160038721802875250367738355497198064992657997569827
::
sage: K.<w> = QuadraticField(3) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([17*x^2+1/7*y^2, 17*w*x*y]) sage: f.green_function(P.point([w, 2], False), K.places()[1]) 1.7236334013785676107373093775 sage: f.green_function(P([2, 1]), K.ideal(7), N=7) 0.48647753726382832627633818586 sage: f.green_function(P([w, 1]), K.ideal(17), error_bound=0.001) -0.70813041039490996737374178059
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, x*y]) sage: f.green_function(P.point([5,2], False), 0, N=30) 1.7315451844777407992085512000 sage: f.green_function(P.point([2,1], False), 0, N=30) 0.86577259223181088325226209926 sage: f.green_function(P.point([1,1], False), 0, N=30) 0.43288629610862338612700146098 """
raise NotImplementedError("must be over a number field or a number field order") raise TypeError("must be an absolute field")
#For QQ the 'flip-trick' works better over RR or Qp else: raise ValueError("invalid valuation (=%s) entered"%v)
#Coerce all polynomials in F into polynomials with coefficients in K
raise ValueError("error bound (=%s) must be positive"%err)
#if doing error estimates, compute needed number of iterates #compute upper bound else: #non-archimedean
#compute lower bound - from explicit polynomials of Nullstellensatz else: else: #non-archimedean else: #non-archimedean else: #we just need log||P||_v N=1
#START GREEN FUNCTION CALCULATION #compute the maximum absolute value of entries of a, and where it occurs # add to sum for the Green's function #get the next iterate
#else - prime or prime ideal for non-archimedean else: #compute the maximum absolute value of entries of a, and where it occurs # add to sum for the Green's function #get the next iterate
def canonical_height(self, P, **kwds): r""" Evaluate the (absolute) canonical height of ``P`` with respect to this dynamical system.
Must be over number field or order of a number field. Specify either the number of terms of the series to evaluate or the error bound required.
ALGORITHM:
The sum of the Green's function at the archimedean places and the places of bad reduction.
If function is defined over `\QQ` uses Wells' Algorithm, which allows us to not have to factor the resultant.
INPUT:
- ``P`` -- a projective point
kwds:
- ``badprimes`` -- (optional) a list of primes of bad reduction
- ``N`` -- (default: 10) positive integer. number of terms of the series to use in the local green functions
- ``prec`` -- (default: 100) positive integer, float point or `p`-adic precision
- ``error_bound`` -- (optional) a positive real number
OUTPUT: a real number
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([x^2+y^2, 2*x*y]); sage: f.canonical_height(P.point([5,4]), error_bound=0.001) 2.1970553519503404898926835324 sage: f.canonical_height(P.point([2,1]), error_bound=0.001) 1.0984430632822307984974382955
Notice that preperiodic points may not return exactly 0::
sage: R.<X> = PolynomialRing(QQ) sage: K.<a> = NumberField(X^2 + X - 1) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2-2*y^2, y^2]) sage: Q = P.point([a,1]) sage: f.canonical_height(Q, error_bound=0.000001) # Answer only within error_bound of 0 5.7364919788790160119266380480e-8 sage: f.nth_iterate(Q,2) == Q # but it is indeed preperiodic True
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x^2-y^2); sage: f = DynamicalSystem_projective([x^2,y^2, 4*z^2], domain=X); sage: Q = X([4,4,1]) sage: f.canonical_height(Q, badprimes=[2]) 0.0013538030870311431824555314882
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x^2-y^2); sage: f = DynamicalSystem_projective([x^2,y^2, 30*z^2], domain=X) sage: Q = X([4, 4, 1]) sage: f.canonical_height(Q, badprimes=[2,3,5], prec=200) 2.7054056208276961889784303469356774912979228770208655455481
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([1000*x^2-29*y^2, 1000*y^2]) sage: Q = P(-1/4, 1) sage: f.canonical_height(Q, error_bound=0.01) 3.7996079979254623065837411853
::
sage: RSA768 = 123018668453011775513049495838496272077285356959533479219732245215\ ....: 1726400507263657518745202199786469389956474942774063845925192557326303453731548\ ....: 2685079170261221429134616704292143116022212404792747377940806653514195974598569\ ....: 02143413 sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([RSA768*x^2 + y^2, x*y]) sage: Q = P(RSA768,1) sage: f.canonical_height(Q, error_bound=0.00000000000000001) 931.18256422718241278672729195 """
raise NotImplementedError("must be over a number field or a number field order or QQbar") else: #since this an absolute height, we can compute the height of a QQbar point #by choosing any number field it is defined over. else: K, phi, psi, b = K.composite_fields(f.base_ring(), both_maps=True)[0] Q = Q.change_ring(K, embedding=phi) f = f.change_ring(K, embedding=psi) else: raise TypeError("must be an absolute field")
# After moving from QQbar to K being something like QQ, we need # to renormalize f, especially to match the normalized resultant.
# If our map and point are defined on P^1(QQ), use Wells' Algorithm # instead of the usual algorithm using local Green's functions: # write our point with coordinates whose gcd is 1 #assures integer coeffcients #computes the error bound as defined in Algorithm 3.1 of [WELLS] N = 1 # this looks different than Wells' Algorithm because of the difference # between what Wells' calls H_infty, # and what Green's Function returns for the infinite place # The value returned by Well's algorithm may be negative. As the canonical height # is always nonnegative, so if this value is within -err of 0, return 0. "This should be impossible, please report bug on trac.sagemath.org." # This should be impossible. The error bound for Wells' is rigorous # and the actual height is always >= 0. If we see something less than -err, # something has g one very wrong.
else:
##update the keyword dictionary for use in green_function
# Archimedean local heights # :: WARNING: If places is fed the default Sage precision of 53 bits, # it uses Real or Complex Double Field in place of RealField(prec) or ComplexField(prec) # the function is_RealField does not identify RDF as real, so we test for that ourselves. else:
# Non-Archimedean local heights else:
def height_difference_bound(self, prec=None): r""" Return an upper bound on the different between the canonical height of a point with respect to this dynamical system and the absolute height of the point.
This map must be a morphism.
ALGORITHM:
Uses a Nullstellensatz argument to compute the constant. For details: see [Hutz2015]_.
INPUT:
- ``prec`` -- (default: :class:`RealField` default) positive integer, float point precision
OUTPUT: a real number
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2+y^2, x*y]) sage: f.height_difference_bound() 1.38629436111989
This function does not automatically normalize. ::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([4*x^2+100*y^2, 210*x*y, 10000*z^2]) sage: f.height_difference_bound() 11.0020998412042 sage: f.normalize_coordinates() sage: f.height_difference_bound() 10.3089526606443
A number field example::
sage: R.<x> = QQ[] sage: K.<c> = NumberField(x^3 - 2) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: f = DynamicalSystem_projective([1/(c+1)*x^2+c*y^2, 210*x*y, 10000*z^2]) sage: f.height_difference_bound() 11.0020998412042
::
sage: P.<x,y,z> = ProjectiveSpace(QQbar,2) sage: f = DynamicalSystem_projective([x^2, QQbar(sqrt(-1))*y^2, QQbar(sqrt(3))*z^2]) sage: f.height_difference_bound() 3.43967790223022 """ #since this is absolute height, we can choose any number field over which the #function is defined. else: raise NotImplementedError("fraction field of the base ring must be a number field or QQbar") else: else: R = RealField(prec) #compute upper bound #compute lower bound - from explicit polynomials of Nullstellensatz for coeff in val.coefficients()])
def multiplier(self, P, n, check=True): r""" Return the multiplier of the point ``P`` of period ``n`` with respect to this dynamical system.
INPUT:
- ``P`` -- a point on domain of this map
- ``n`` -- a positive integer, the period of ``P``
- ``check`` -- (default: ``True``) boolean; verify that ``P`` has period ``n``
OUTPUT:
A square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of this dynamical system.
EXAMPLES::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2,y^2, 4*z^2]); sage: Q = P.point([4,4,1], False); sage: f.multiplier(Q,1) [2 0] [0 2]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([7*x^2 - 28*y^2, 24*x*y]) sage: f.multiplier(P(2,5), 4) [231361/20736]
::
sage: P.<x,y> = ProjectiveSpace(CC,1) sage: f = DynamicalSystem_projective([x^3 - 25*x*y^2 + 12*y^3, 12*y^3]) sage: f.multiplier(P(1,1), 5) [0.389017489711935]
::
sage: P.<x,y> = ProjectiveSpace(RR,1) sage: f = DynamicalSystem_projective([x^2-2*y^2, y^2]) sage: f.multiplier(P(2,1), 1) [4.00000000000000]
::
sage: P.<x,y> = ProjectiveSpace(Qp(13),1) sage: f = DynamicalSystem_projective([x^2-29/16*y^2, y^2]) sage: f.multiplier(P(5,4), 3) [6 + 8*13 + 13^2 + 8*13^3 + 13^4 + 8*13^5 + 13^6 + 8*13^7 + 13^8 + 8*13^9 + 13^10 + 8*13^11 + 13^12 + 8*13^13 + 13^14 + 8*13^15 + 13^16 + 8*13^17 + 13^18 + 8*13^19 + O(13^20)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-y^2, y^2]) sage: f.multiplier(P(0,1), 1) Traceback (most recent call last): ... ValueError: (0 : 1) is not periodic of period 1 """ raise ValueError("period must be a positive integer") #dehomogenize and compute multiplier #get the correct order for chain rule matrix multiplication
def _multipliermod(self, P, n, p, k): r""" Return the multiplier of the point ``P`` of period ``n`` with respect to this dynamical system modulo `p^k`.
This map must be an endomorphism of projective space defined over `\QQ` or `\ZZ`. This function should not be used at the top level as it does not perform input checks. It is used primarily for the rational preperiodic and periodic point algorithms.
INPUT:
- ``P`` -- a point on domain of this map
- ``n`` -- a positive integer, the period of ``P``
- ``p`` -- a positive integer
- ``k`` -- a positive integer
OUTPUT:
A square matrix of size ``self.codomain().dimension_relative()`` in `\ZZ/(p^k)\ZZ`.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-29/16*y^2, y^2]) sage: f._multipliermod(P(5,4), 3, 11, 1) [3]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-29/16*y^2, y^2]) sage: f._multipliermod(P(5,4), 3, 11, 2) [80] """ #dehomogenize and compute multiplier
def possible_periods(self, **kwds): r""" Return the set of possible periods for rational periodic points of this dynamical system.
Must be defined over `\ZZ` or `\QQ`.
ALGORITHM:
Calls ``self.possible_periods()`` modulo all primes of good reduction in range ``prime_bound``. Return the intersection of those lists.
INPUT:
kwds:
- ``prime_bound`` -- (default: ``[1, 20]``) a list or tuple of two positive integers or an integer for the upper bound
- ``bad_primes`` -- (optional) a list or tuple of integer primes, the primes of bad reduction
- ``ncpus`` -- (default: all cpus) number of cpus to use in parallel
OUTPUT: a list of positive integers
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-29/16*y^2, y^2]) sage: f.possible_periods(ncpus=1) [1, 3]
::
sage: PS.<x,y> = ProjectiveSpace(1,QQ) sage: f = DynamicalSystem_projective([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3]) sage: f.possible_periods(prime_bound=[1,5]) Traceback (most recent call last): ... ValueError: no primes of good reduction in that range sage: f.possible_periods(prime_bound=[1,10]) [1, 4, 12] sage: f.possible_periods(prime_bound=[1,20]) [1, 4]
::
sage: P.<x,y,z> = ProjectiveSpace(ZZ,2) sage: f = DynamicalSystem_projective([2*x^3 - 50*x*z^2 + 24*z^3, ....: 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) sage: f.possible_periods(prime_bound=10) [1, 2, 6, 20, 42, 60, 140, 420] sage: f.possible_periods(prime_bound=20) # long time [1, 20] """ raise NotImplementedError("must be ZZ or QQ")
except TypeError: raise TypeError("prime bound must be an integer") else: except TypeError: raise TypeError("prime bounds must be integers")
return morphism.possible_periods()
# Calling possible_periods for each prime in parallel
else:
else:
def _preperiodic_points_to_cyclegraph(self, preper): r""" Given the complete set of periodic or preperiodic points return the digraph representing the orbit.
If ``preper`` is not the complete set, this function will not fill in the gaps.
INPUT:
- ``preper`` -- a list or tuple of projective points; the complete set of rational periodic or preperiodic points
OUTPUT:
A digraph representing the orbit the rational preperiodic points ``preper`` in projective space.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-2*y^2, y^2]) sage: preper = [P(-2, 1), P(1, 0), P(0, 1), P(1, 1), P(2, 1), P(-1, 1)] sage: f._preperiodic_points_to_cyclegraph(preper) Looped digraph on 6 vertices """ #We store the points we encounter is a list, D. Each new point is checked to #see if it is in that list (which uses ==) so that equal points with different #representations only appear once in the graph.
def is_PGL_minimal(self, prime_list=None): r""" Check if this dynamical system is a minimal model in its conjugacy class.
See [BM2012]_ and [Mol2015]_ for a description of the algorithm.
INPUT:
- ``prime_list`` -- (optional) list of primes to check minimality
OUTPUT: boolean
EXAMPLES::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([X^2+3*Y^2, X*Y]) sage: f.is_PGL_minimal() True
::
sage: PS.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([6*x^2+12*x*y+7*y^2, 12*x*y]) sage: f.is_PGL_minimal() False
::
sage: PS.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([6*x^2+12*x*y+7*y^2, y^2]) sage: f.is_PGL_minimal() Traceback (most recent call last): ... TypeError: affine minimality is only considered for maps not of the form f or 1/f for a polynomial f """ raise NotImplementedError("minimal models only implemented over ZZ or QQ") raise TypeError("the function is not a morphism") raise NotImplementedError("minimality is only for degree 2 or higher")
def minimal_model(self, return_transformation=False, prime_list=None): r""" Determine if this dynamical system is minimal.
This dynamical system must be defined over the projective line over the rationals. In particular, determine if this map is affine minimal, which is enough to decide if it is minimal or not. See Proposition 2.10 in [BM2012]_.
REFERENCES:
- [BM2012]_ - [Mol2015]_
INPUT:
- ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the `PGL_2` transformation to conjugate this map to the calculated minimal model
- ``prime_list`` -- (optional) a list of primes, in case one only wants to determine minimality at those specific primes
OUTPUT:
- a scheme morphism on the projective line which is a minimal model of this map
- a `PGL(2,\QQ)` element which conjugates this map to a minimal model
EXAMPLES::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([X^2+3*Y^2, X*Y]) sage: f.minimal_model(return_transformation=True) ( Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (X^2 + 3*Y^2 : X*Y) , [1 0] [0 1] )
::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([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]) sage: f.minimal_model(return_transformation=True) ( Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (22176*X^4 + 151956*X^3*Y + 390474*X^2*Y^2 + 445956*X*Y^3 + 190999*Y^4 : -12329*X^4 - 84480*X^3*Y - 217080*X^2*Y^2 - 247920*X*Y^3 - 106180*Y^4), [2 3] [0 1] )
::
sage: PS.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([6*x^2+12*x*y+7*y^2, 12*x*y]) sage: f.minimal_model() Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^2 + 12*x*y + 42*y^2 : 2*x*y)
::
sage: PS.<x,y> = ProjectiveSpace(ZZ,1) sage: f = DynamicalSystem_projective([6*x^2+12*x*y+7*y^2, 12*x*y + 42*y^2]) sage: g,M=f.minimal_model(return_transformation=True) sage: f.conjugate(M) == g True
::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([X+Y, X-3*Y]) sage: f.minimal_model() Traceback (most recent call last): ... NotImplementedError: minimality is only for degree 2 or higher
::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([X^2-Y^2, X^2+X*Y]) sage: f.minimal_model() Traceback (most recent call last): ... TypeError: the function is not a morphism
""" raise NotImplementedError("minimal models only implemented over ZZ or QQ")
def automorphism_group(self, **kwds): r""" Calculates the subgroup of `PGL2` that is the automorphism group of this dynamical system.
The automorphism group is the set of `PGL(2)` elements that fixes this map under conjugation.
INPUT:
keywords:
- ``starting_prime`` -- (default: 5) the first prime to use for CRT
- ``algorithm``-- (optional) can be one of the following:
* ``'CRT'`` - Chinese Remainder Theorem * ``'fixed_points'`` - fixed points algorithm
- ``return_functions``-- (default: ``False``) boolean; ``True`` returns elements as linear fractional transformations and ``False`` returns elements as `PGL2` matrices
- ``iso_type`` -- (default: ``False``) boolean; ``True`` returns the isomorphism type of the automorphism group
OUTPUT: a list of elements in the automorphism group
AUTHORS:
- Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray
- Modified by Joao Alberto de Faria, Ben Hutz, Bianca Thompson
REFERENCES:
- [FMV2014]_
EXAMPLES::
sage: R.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-y^2, x*y]) sage: f.automorphism_group(return_functions=True) [x, -x]
::
sage: R.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + 5*x*y + 5*y^2, 5*x^2 + 5*x*y + y^2]) sage: f.automorphism_group() [ [1 0] [0 2] [0 1], [2 0] ]
::
sage: R.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-2*x*y-2*y^2, -2*x^2-2*x*y+y^2]) sage: f.automorphism_group(return_functions=True) [x, 2/(2*x), -x - 1, -2*x/(2*x + 2), (-x - 1)/x, -1/(x + 1)]
::
sage: R.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([3*x^2*y - y^3, x^3 - 3*x*y^2]) sage: f.automorphism_group(algorithm='CRT', return_functions=True, iso_type=True) ([x, (x + 1)/(x - 1), (-x + 1)/(x + 1), -x, 1/x, -1/x, (x - 1)/(x + 1), (-x - 1)/(x - 1)], 'Dihedral of order 8')
::
sage: A.<z> = AffineSpace(QQ,1) sage: f = DynamicalSystem_affine([1/z^3]) sage: F = f.homogenize(1) sage: F.automorphism_group() [ [1 0] [0 2] [-1 0] [ 0 -2] [0 1], [2 0], [ 0 1], [ 2 0] ]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x**2 + x*z, y**2, z**2]) sage: f.automorphism_group() # long time [ [1 0 0] [0 1 0] [0 0 1] ]
::
sage: K.<w> = CyclotomicField(3) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: D6 = DynamicalSystem_projective([y^2,x^2]) sage: D6.automorphism_group() [ [1 0] [0 w] [0 1] [w 0] [-w - 1 0] [ 0 -w - 1] [0 1], [1 0], [1 0], [0 1], [ 0 1], [ 1 0] ] """ return self.conjugating_set(self) else: F = f[0].univariate_polynomial(R) return(automorphism_group_QQ_CRT(F, p, return_functions, iso_type)) return(automorphism_group_QQ_fixedpoints(F, return_functions, iso_type))
def critical_subscheme(self): r""" Return the critical subscheme of this dynamical system.
OUTPUT: projective subscheme
EXAMPLES::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3-2*x*y^2 + 2*y^3, y^3]) sage: f.critical_subscheme() Closed subscheme of Projective Space of dimension 1 over Rational Field defined by: 9*x^2*y^2 - 6*y^4
::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([2*x^2-y^2, x*y]) sage: f.critical_subscheme() Closed subscheme of Projective Space of dimension 1 over Rational Field defined by: 4*x^2 + 2*y^2
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([2*x^2-y^2, x*y, z^2]) sage: f.critical_subscheme() Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: 8*x^2*z + 4*y^2*z
::
sage: P.<x,y,z,w> = ProjectiveSpace(GF(81),3) sage: g = DynamicalSystem_projective([x^3+y^3, y^3+z^3, z^3+x^3, w^3]) sage: g.critical_subscheme() Closed subscheme of Projective Space of dimension 3 over Finite Field in z4 of size 3^4 defined by: 0
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2,x*y]) sage: f.critical_subscheme() Traceback (most recent call last): ... TypeError: the function is not a morphism """ raise NotImplementedError("not implemented for subschemes")
def critical_points(self, R=None): r""" Return the critical points of this dynamical system defined over the ring ``R`` or the base ring of this map.
Must be dimension 1.
INPUT:
- ``R`` -- (optional) a ring
OUTPUT: a list of projective space points defined over ``R``
EXAMPLES::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3-2*x*y^2 + 2*y^3, y^3]) sage: f.critical_points() [(1 : 0)] sage: K.<w> = QuadraticField(6) sage: f.critical_points(K) [(-1/3*w : 1), (1/3*w : 1), (1 : 0)]
::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([2*x^2-y^2, x*y]) sage: f.critical_points(QQbar) [(-0.7071067811865475?*I : 1), (0.7071067811865475?*I : 1)] """ raise NotImplementedError("use .wronskian_ideal() for dimension > 1") else:
def is_postcritically_finite(self, err=0.01, embedding=None): r""" Determine if this dynamical system is post-critically finite.
Only for endomorphisms of `\mathbb{P}^1`. It checks if each critical point is preperiodic. The optional parameter ``err`` is passed into ``is_preperiodic()`` as part of the preperiodic check.
INPUT:
- ``err`` -- (default: 0.01) positive real number
- ``embedding`` -- embedding of base ring into `\QQbar`
OUTPUT: boolean
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - y^2, y^2]) sage: f.is_postcritically_finite() True
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3- y^3, y^3]) sage: f.is_postcritically_finite() False
::
sage: R.<z> = QQ[] sage: K.<v> = NumberField(z^8 + 3*z^6 + 3*z^4 + z^2 + 1) sage: PS.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^3+v*y^3, y^3]) sage: f.is_postcritically_finite(embedding=K.embeddings(QQbar)[0]) # long time True
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([6*x^2+16*x*y+16*y^2, -3*x^2-4*x*y-4*y^2]) sage: f.is_postcritically_finite() True """ #iteration of subschemes not yet implemented raise NotImplementedError("only implemented in dimension 1")
#Since is_preperiodic uses heights we need to be over a numberfield raise NotImplementedError("must be over a number field or a number field order or QQbar")
else: F = self.change_ring(embedding)
def critical_point_portrait(self, check=True, embedding=None): r""" If this dynamical system is post-critically finite, return its critical point portrait.
This is the directed graph of iterates starting with the critical points. Must be dimension 1. If ``check`` is ``True``, then the map is first checked to see if it is postcritically finite.
INPUT:
- ``check`` -- boolean
- ``embedding`` -- embedding of base ring into `\QQbar`
OUTPUT: a digraph
EXAMPLES::
sage: R.<z> = QQ[] sage: K.<v> = NumberField(z^6 + 2*z^5 + 2*z^4 + 2*z^3 + z^2 + 1) sage: PS.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2+v*y^2, y^2]) sage: f.critical_point_portrait(check=False, embedding=K.embeddings(QQbar)[0]) # long time Looped digraph on 6 vertices
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^5 + 5/4*x*y^4, y^5]) sage: f.critical_point_portrait(check=False) Looped digraph on 5 vertices
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + 2*y^2, y^2]) sage: f.critical_point_portrait() Traceback (most recent call last): ... TypeError: map must be post-critically finite """ #input checking done in is_postcritically_finite else: F = self.change_ring(embedding) else: crit_points.append(Q)
def critical_height(self, **kwds): r""" Compute the critical height of this dynamical system.
The critical height is defined by J. Silverman as the sum of the canonical heights of the critical points. This must be dimension 1 and defined over a number field or number field order.
INPUT:
kwds:
- ``badprimes`` -- (optional) a list of primes of bad reduction
- ``N`` -- (default: 10) positive integer; number of terms of the series to use in the local green functions
- ``prec`` -- (default: 100) positive integer, float point or `p`-adic precision
- ``error_bound`` -- (optional) a positive real number
- ``embedding`` -- (optional) the embedding of the base field to `\QQbar`
OUTPUT: real number
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3+7*y^3, 11*y^3]) sage: f.critical_height() 1.1989273321156851418802151128
::
sage: K.<w> = QuadraticField(2) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2+w*y^2, y^2]) sage: f.critical_height() 0.16090842452312941163719755472
Postcritically finite maps have critical height 0::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3-3/4*x*y^2 + 3/4*y^3, y^3]) sage: f.critical_height(error_bound=0.0001) 0.00000000000000000000000000000 """ raise NotImplementedError("only implemented in dimension 1")
raise NotImplementedError("must be over a number field or a number field order or QQbar") #doesn't really matter which we choose as Galois conjugates have the same height
def periodic_points(self, n, minimal=True, R=None, algorithm='variety', return_scheme=False): r""" Computes the periodic points of period ``n`` of this dynamical system defined over the ring ``R`` or the base ring of the map.
This can be done either by finding the rational points on the variety defining the points of period ``n``, or, for finite fields, finding the cycle of appropriate length in the cyclegraph. For small cardinality fields, the cyclegraph algorithm is effective for any map and length cycle, but is slow when the cyclegraph is large. The variety algorithm is good for small period, degree, and dimension, but is slow as the defining equations of the variety get more complicated.
For rational map, where there are potentially infinitely many peiodic points of a given period, you must use the ``return_scheme`` option. Note that this scheme will include the indeterminacy locus.
INPUT:
- ``n`` - a positive integer
- ``minimal`` -- (default: ``True``) boolean; ``True`` specifies to find only the periodic points of minimal period ``n`` and ``False`` specifies to find all periodic points of period ``n``
- ``R`` a commutative ring
- ``algorithm`` -- (default: ``'variety'``) must be one of the following:
* ``'variety'`` - find the rational points on the appropriate variety * ``'cyclegraph'`` - find the cycles from the cycle graph
- ``return_scheme`` -- return a subscheme of the ambient space that defines the ``n`` th periodic points
OUTPUT:
A list of periodic points of this map or the subscheme defining the periodic points.
EXAMPLES::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: f = DynamicalSystem_projective([x^2-x*y+y^2, x^2-y^2+x*y]) sage: f.periodic_points(1) [(-0.500000000000000? - 0.866025403784439?*I : 1), (-0.500000000000000? + 0.866025403784439?*I : 1), (1 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QuadraticField(5,'t'),2) sage: f = DynamicalSystem_projective([x^2 - 21/16*z^2, y^2-z^2, z^2]) sage: f.periodic_points(2) [(-5/4 : -1 : 1), (-5/4 : -1/2*t + 1/2 : 1), (-5/4 : 0 : 1), (-5/4 : 1/2*t + 1/2 : 1), (-3/4 : -1 : 1), (-3/4 : 0 : 1), (1/4 : -1 : 1), (1/4 : -1/2*t + 1/2 : 1), (1/4 : 0 : 1), (1/4 : 1/2*t + 1/2 : 1), (7/4 : -1 : 1), (7/4 : 0 : 1)]
::
sage: w = QQ['w'].0 sage: K = NumberField(w^6 - 3*w^5 + 5*w^4 - 5*w^3 + 5*w^2 - 3*w + 1,'s') sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: f = DynamicalSystem_projective([x^2+z^2, y^2+x^2, z^2+y^2]) sage: f.periodic_points(1) [(-s^5 + 3*s^4 - 5*s^3 + 4*s^2 - 3*s + 1 : s^5 - 2*s^4 + 3*s^3 - 3*s^2 + 4*s - 1 : 1), (-2*s^5 + 4*s^4 - 5*s^3 + 3*s^2 - 4*s : -2*s^5 + 5*s^4 - 7*s^3 + 6*s^2 - 7*s + 3 : 1), (-s^5 + 3*s^4 - 4*s^3 + 4*s^2 - 4*s + 2 : -s^5 + 2*s^4 - 2*s^3 + s^2 - s : 1), (s^5 - 2*s^4 + 3*s^3 - 3*s^2 + 3*s - 1 : -s^5 + 3*s^4 - 5*s^3 + 4*s^2 - 4*s + 2 : 1), (2*s^5 - 6*s^4 + 9*s^3 - 8*s^2 + 7*s - 4 : 2*s^5 - 5*s^4 + 7*s^3 - 5*s^2 + 6*s - 2 : 1), (1 : 1 : 1), (s^5 - 2*s^4 + 2*s^3 + s : s^5 - 3*s^4 + 4*s^3 - 3*s^2 + 2*s - 1 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: f.periodic_points(2, False) [(-5/4 : -1 : 1), (-5/4 : 2 : 1), (-3/4 : -1 : 1), (-3/4 : 2 : 1), (0 : 1 : 0), (1/4 : -1 : 1), (1/4 : 2 : 1), (1 : 0 : 0), (1 : 1 : 0), (7/4 : -1 : 1), (7/4 : 2 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: f.periodic_points(2) [(-5/4 : -1 : 1), (-5/4 : 2 : 1), (1/4 : -1 : 1), (1/4 : 2 : 1)]
::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(ZZ, 1) sage: f = DynamicalSystem_projective([x^2+y^2,y^2]) sage: f.periodic_points(2, R=QQbar, minimal=False) [(-0.500000000000000? - 1.322875655532296?*I : 1), (-0.500000000000000? + 1.322875655532296?*I : 1), (0.500000000000000? - 0.866025403784439?*I : 1), (0.500000000000000? + 0.866025403784439?*I : 1), (1 : 0)]
::
sage: P.<x,y> = ProjectiveSpace(GF(307), 1) sage: f = DynamicalSystem_projective([x^10+y^10, y^10]) sage: f.periodic_points(16, minimal=True, algorithm='cyclegraph') [(69 : 1), (185 : 1), (120 : 1), (136 : 1), (97 : 1), (183 : 1), (170 : 1), (105 : 1), (274 : 1), (275 : 1), (154 : 1), (156 : 1), (87 : 1), (95 : 1), (161 : 1), (128 : 1)]
::
sage: P.<x,y> = ProjectiveSpace(GF(13^2,'t'),1) sage: f = DynamicalSystem_projective([x^3 + 3*y^3, x^2*y]) sage: f.periodic_points(30, minimal=True, algorithm='cyclegraph') [(t + 3 : 1), (6*t + 6 : 1), (7*t + 1 : 1), (2*t + 8 : 1), (3*t + 4 : 1), (10*t + 12 : 1), (8*t + 10 : 1), (5*t + 11 : 1), (7*t + 4 : 1), (4*t + 8 : 1), (9*t + 1 : 1), (2*t + 2 : 1), (11*t + 9 : 1), (5*t + 7 : 1), (t + 10 : 1), (12*t + 4 : 1), (7*t + 12 : 1), (6*t + 8 : 1), (11*t + 10 : 1), (10*t + 7 : 1), (3*t + 9 : 1), (5*t + 5 : 1), (8*t + 3 : 1), (6*t + 11 : 1), (9*t + 12 : 1), (4*t + 10 : 1), (11*t + 4 : 1), (2*t + 7 : 1), (8*t + 12 : 1), (12*t + 11 : 1)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([3*x^2+5*y^2,y^2]) sage: f.periodic_points(2, R=GF(3), minimal=False) [(2 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([x^2, x*y, z^2]) sage: f.periodic_points(1) Traceback (most recent call last): ... TypeError: use return_scheme=True
::
sage: R.<x> = QQ[] sage: K.<u> = NumberField(x^2 - x + 3) sage: P.<x,y,z> = ProjectiveSpace(K,2) sage: X = P.subscheme(2*x-y) sage: f = DynamicalSystem_projective([x^2-y^2, 2*(x^2-y^2), y^2-z^2], domain=X) sage: f.periodic_points(2) [(-1/5*u - 1/5 : -2/5*u - 2/5 : 1), (1/5*u - 2/5 : 2/5*u - 4/5 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2-y^2, x^2-z^2, y^2-z^2]) sage: f.periodic_points(1) [(-1 : 0 : 1)] sage: f.periodic_points(1, return_scheme=True) Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: -x^3 + x^2*y - y^3 + x*z^2, -x*y^2 + x^2*z - y^2*z + x*z^2, -y^3 + x^2*z + y*z^2 - z^3 sage: f.periodic_points(2, minimal=True, return_scheme=True) Traceback (most recent call last): ... NotImplementedError: return_subscheme only implemented for minimal=False """ raise ValueError("a positive integer period must be specified") else: else: if n % m == 0: points = points + cycle[:-1] else: raise TypeError("ring must be finite to generate cyclegraph") for j in range(i+1, N)]
else: #we want only the points with minimal period n #so we go through the list and remove any that #have smaller period by checking the iterates # iterate points to check if minimal else: raise NotImplementedError("ring must a number field or finite field") else: #a higher dimensional scheme else: raise ValueError("algorithm must be either 'variety' or 'cyclegraph'")
def multiplier_spectra(self, n, formal=False, embedding=None, type='point'): r""" Computes the ``n`` multiplier spectra of this dynamical system.
This is the set of multipliers of the periodic points of formal period ``n`` included with the appropriate multiplicity. User can also specify to compute the ``n`` multiplier spectra instead which includes the multipliers of all periodic points of period ``n``. The map must be defined over projective space of dimension 1 over a number field.
INPUT:
- ``n`` -- a positive integer, the period
- ``formal`` -- (default: ``False``) boolean; ``True`` specifies to find the formal ``n`` multiplier spectra of this map and ``False`` specifies to find the ``n`` multiplier spectra
- ``embedding`` -- embedding of the base field into `\QQbar`
- ``type`` -- (default: ``'point'``) string; either ``'point'`` or ``'cycle'`` depending on whether you compute one multiplier per point or one per cycle
OUTPUT: a list of `\QQbar` elements
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([4608*x^10 - 2910096*x^9*y + 325988068*x^8*y^2 + 31825198932*x^7*y^3 - 4139806626613*x^6*y^4\ - 44439736715486*x^5*y^5 + 2317935971590902*x^4*y^6 - 15344764859590852*x^3*y^7 + 2561851642765275*x^2*y^8\ + 113578270285012470*x*y^9 - 150049940203963800*y^10, 4608*y^10]) sage: f.multiplier_spectra(1) [0, -7198147681176255644585/256, 848446157556848459363/19683, -3323781962860268721722583135/35184372088832, 529278480109921/256, -4290991994944936653/2097152, 1061953534167447403/19683, -3086380435599991/9, 82911372672808161930567/8192, -119820502365680843999, 3553497751559301575157261317/8192]
::
sage: set_verbose(None) sage: z = QQ['z'].0 sage: K.<w> = NumberField(z^4 - 4*z^2 + 1,'z') sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2 - w/4*y^2, y^2]) sage: f.multiplier_spectra(2, formal=False, embedding=K.embeddings(QQbar)[0], type='cycle') [0, 5.931851652578137? + 0.?e-47*I, 0.0681483474218635? - 1.930649271699173?*I, 0.0681483474218635? + 1.930649271699173?*I]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - 3/4*y^2, y^2]) sage: f.multiplier_spectra(2, formal=False, type='cycle') [0, 1, 1, 9] sage: f.multiplier_spectra(2, formal=False, type='point') [0, 1, 1, 1, 9]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - 7/4*y^2, y^2]) sage: f.multiplier_spectra(3, formal=True, type='cycle') [1, 1] sage: f.multiplier_spectra(3, formal=True, type='point') [1, 1, 1, 1, 1, 1]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: f.multiplier_spectra(1) [1, 1, 1] """
raise ValueError("period must be a positive integer") raise NotImplementedError("not implemented for subschemes") raise NotImplementedError("only implemented for dimension 1") raise NotImplementedError("self must be a map over a number field")
else:
else: # periodic points of formal period n are the roots of the nth dynatomic polynomial abspoly = K.domain().base_ring().absolute_polynomial() phi = K.domain().base_ring().hom(QQbar.polynomial_root(abspoly,abspoly.any_root(CIF)),QQbar) Kx = K.coordinate_ring() QQbarx = QQbar[Kx.variable_names()] phix = Kx.hom(phi,QQbarx) F = phix(F)
# should include one representative point per cycle, included with the right multiplicity
def sigma_invariants(self, n, formal=False, embedding=None, type='point'): r""" Computes the values of the elementary symmetric polynomials of the ``n`` multiplier spectra of this dynamical system.
Can specify to instead compute the values corresponding to the elementary symmetric polynomials of the formal ``n`` multiplier spectra. The map must be defined over projective space of dimension `1`. The base ring should be a number field, number field order, or a finite field or a polynomial ring or function field over a number field, number field order, or finite field.
The parameter ``type`` determines if the sigma are computed from the multipliers calculated at one per cycle (with multiplicity) or one per point (with multiplicity). Note that in the ``cycle`` case, a map with a cycle which collapses into multiple smaller cycles, this is still considered one cycle. In other words, if a 4-cycle collapses into a 2-cycle with multiplicity 2, there is only one multiplier used for the doubled 2-cycle when computing ``n=4``.
ALGORITHM:
We use the Poisson product of the resultant of two polynomials:
.. MATH::
res(f,g) = \prod_{f(a)=0} g(a).
Letting `f` be the polynomial defining the periodic or formal periodic points and `g` the polynomial `w - f'` for an auxilarly variable `w`. Note that if `f` is a rational function, we clear denominators for `g`.
INPUT:
- ``n`` -- a positive integer, the period
- ``formal`` -- (default: ``False``) boolean; ``True`` specifies to find the values of the elementary symmetric polynomials corresponding to the formal ``n`` multiplier spectra and ``False`` specifies to instead find the values corresponding to the ``n`` multiplier spectra, which includes the multipliers of all periodic points of period ``n``
- ``embedding`` -- deprecated in :trac:`23333`
- ``type`` -- (default: ``'point'``) string; either ``'point'`` or ``'cycle'`` depending on whether you compute with one multiplier per point or one per cycle
OUTPUT: a list of elements in the base ring
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([512*x^5 - 378128*x^4*y + 76594292*x^3*y^2 - 4570550136*x^2*y^3 - 2630045017*x*y^4\ + 28193217129*y^5, 512*y^5]) sage: f.sigma_invariants(1) [19575526074450617/1048576, -9078122048145044298567432325/2147483648, -2622661114909099878224381377917540931367/1099511627776, -2622661107937102104196133701280271632423/549755813888, 338523204830161116503153209450763500631714178825448006778305/72057594037927936, 0]
::
sage: set_verbose(None) sage: z = QQ['z'].0 sage: K = NumberField(z^4 - 4*z^2 + 1, 'z') sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x^2 - 5/4*y^2, y^2]) sage: f.sigma_invariants(2, formal=False, type='cycle') [13, 11, -25, 0] sage: f.sigma_invariants(2, formal=False, type='point') [12, -2, -36, 25, 0]
check that infinity as part of a longer cycle is handled correctly::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([y^2, x^2]) sage: f.sigma_invariants(2, type='cycle') [12, 48, 64, 0] sage: f.sigma_invariants(2, type='point') [12, 48, 64, 0, 0] sage: f.sigma_invariants(2, type='cycle', formal=True) [0] sage: f.sigma_invariants(2, type='point', formal=True) [0, 0]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([x^2, y^2, z^2]) sage: f.sigma_invariants(1) Traceback (most recent call last): ... NotImplementedError: only implemented for dimension 1
::
sage: K.<w> = QuadraticField(3) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x^2 - w*y^2, (1-w)*x*y]) sage: f.sigma_invariants(2, formal=False, type='cycle') [6*w + 21, 78*w + 159, 210*w + 367, 90*w + 156] sage: f.sigma_invariants(2, formal=False, type='point') [6*w + 24, 96*w + 222, 444*w + 844, 720*w + 1257, 270*w + 468]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + x*y + y^2, y^2 + x*y]) sage: f.sigma_invariants(1) [3, 3, 1]
::
sage: R.<c> = QQ[] sage: Pc.<x,y> = ProjectiveSpace(R, 1) sage: f = DynamicalSystem_projective([x^2 + c*y^2, y^2]) sage: f.sigma_invariants(1) [2, 4*c, 0] sage: f.sigma_invariants(2, formal=True, type='point') [8*c + 8, 16*c^2 + 32*c + 16] sage: f.sigma_invariants(2, formal=True, type='cycle') [4*c + 4]
doubled fixed point::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 - 3/4*y^2, y^2]) sage: f.sigma_invariants(2, formal=True) [2, 1]
doubled 2 cycle::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 - 5/4*y^2, y^2]) sage: f.sigma_invariants(4, formal=False, type='cycle') [170, 5195, 172700, 968615, 1439066, 638125, 0] """ raise ValueError("period must be a positive integer") raise NotImplementedError("not implemented for subschemes") from sage.misc.superseded import deprecation deprecation(23333, "embedding keyword no longer used") raise TypeError("must have degree at least 2") raise ValueError("type must be either point or cycle")
base_ring = base_ring.ring() or base_ring in FunctionFields()): or (base_ring in FiniteFields())): raise NotImplementedError("incompatible base field, see documentation")
#now we find the two polynomials for the resultant
#polynomial to be evaluated at the periodic points
#polynomial defining the periodic points else:
#check infinity #get multiplicity
#now we need to deal with having the correct number of factors #1 multiplier for each cycle. But we need to be careful about #the length of the cycle and the multiplicities #then we are working with the n-th dynatomic and just need #to take one multiplier per cycle
#evaluate the resultant #take infinity into consideration #adjust multiplicities else: #For each d-th dynatomic for d dividing n, take #one multiplier per cycle; e.g., this treats a double 2 #cycle as a single 4 cycle for n=4 #check infinity else: #type is 'point' #evaluate the resultant #take infinity into consideration
#the sigmas are the coeficients #need to fix the signs and the order
def reduced_form(self, prec=300, return_conjugation=True, error_limit=0.000001): r""" Return reduced form of this dynamical system.
The reduced form is the `SL(2, \ZZ)` equivalent morphism obtained by applying the binary form reduction algorithm from Stoll and Cremona [SC]_ to the homogeneous polynomial defining the periodic points (the dynatomic polynomial). The smallest period `n` with enough periodic points is used.
This should also minimize the sum of the squares of the coefficients, but this is not always the case.
See :meth:`sage.rings.polynomial.multi_polynomial.reduced_form` for the information on binary form reduction.
Implemented by Rebecca Lauren Miller as part of GSOC 2016.
INPUT:
- ``prec`` -- (default: 300) integer, desired precision
- ``return_conjuagtion`` -- (default: ``True``) boolean; return an element of `SL(2, \ZZ)`
- ``error_limit`` -- (default: 0.000001) a real number, sets the error tolerance
OUTPUT:
- a projective morphism
- a matrix
EXAMPLES::
sage: PS.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^3 + x*y^2, y^3]) sage: m = matrix(QQ, 2, 2, [-221, -1, 1, 0]) sage: f = f.conjugate(m) sage: f.reduced_form(prec=100) #needs 2 periodic Traceback (most recent call last): ... ValueError: not enough precision sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 + x*y^2 : y^3) , [ 0 -1] [ 1 221] )
::
sage: PS.<x,y> = ProjectiveSpace(ZZ, 1) sage: f = DynamicalSystem_projective([x^2+ x*y, y^2]) #needs 3 periodic sage: m = matrix(QQ, 2, 2, [-221, -1, 1, 0]) sage: f = f.conjugate(m) sage: f.reduced_form(prec=200) ( Dynamical System of Projective Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x : y) to (-x^2 + x*y - y^2 : -y^2) , [ 0 -1] [ 1 220] )
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^3, y^3]) sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 : y^3) , <BLANKLINE> [-1 0] [ 0 -1] )
::
sage: PS.<X,Y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([7365*X^4 + 12564*X^3*Y + 8046*X^2*Y^2 + 2292*X*Y^3 + 245*Y^4,\ -12329*X^4 - 21012*X^3*Y - 13446*X^2*Y^2 - 3828*X*Y^3 - 409*Y^4]) sage: f.reduced_form(prec=30) Traceback (most recent call last): ... ValueError: accuracy of Newton's root not within tolerance(1.2519607 > 1e-06), increase precision sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (X : Y) to (-7*X^4 - 12*X^3*Y - 42*X^2*Y^2 - 12*X*Y^3 - 7*Y^4 : -X^4 - 4*X^3*Y - 6*X^2*Y^2 - 4*X*Y^3 - Y^4), <BLANKLINE> [-1 2] [ 2 -5] )
::
sage: P.<x,y> = ProjectiveSpace(RR, 1) sage: f = DynamicalSystem_projective([x^4, RR(sqrt(2))*y^4]) sage: m = matrix(RR, 2, 2, [1,12,0,1]) sage: f = f.conjugate(m) sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Real Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y) to (-x^4 + 2.86348722511320e-12*y^4 : -1.41421356237310*y^4) , <BLANKLINE> [-1 12] [ 0 -1] )
::
sage: P.<x,y> = ProjectiveSpace(CC, 1) sage: f = DynamicalSystem_projective([x^4, CC(sqrt(-2))*y^4]) sage: m = matrix(CC, 2, 2, [1,12,0,1]) sage: f = f.conjugate(m) sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Complex Field with 53 bits of precision Defn: Defined on coordinates by sending (x : y) to (-x^4 + (-1.03914726748259e-15)*y^4 : (-8.65956056235493e-17 - 1.41421356237309*I)*y^4) , <BLANKLINE> [-1 12] [ 0 -1] )
::
sage: K.<w> = QuadraticField(2) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x^3, w*y^3]) sage: m = matrix(K, 2, 2, [1,12,0,1]) sage: f = f.conjugate(m) sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Number Field in w with defining polynomial x^2 - 2 Defn: Defined on coordinates by sending (x : y) to (x^3 : (w)*y^3) , <BLANKLINE> [-1 12] [ 0 -1] )
::
sage: R.<x> = QQ[] sage: K.<w> = NumberField(x^5+x-3, embedding=(x^5+x-3).roots(ring=CC)[0][0]) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([12*x^3, 2334*w*y^3]) sage: m = matrix(K, 2, 2, [-12,1,1,0]) sage: f = f.conjugate(m) sage: f.reduced_form() ( Dynamical System of Projective Space of dimension 1 over Number Field in w with defining polynomial x^5 + x - 3 Defn: Defined on coordinates by sending (x : y) to (12*x^3 : (2334*w)*y^3) , [ 0 -1] [ 1 -12] ) """ # Checks to make sure there are enough distinct, roots we need 3 # if there are not it finds the nth periodic points until there are enough return self.conjugate(m)
def _is_preperiodic(self, P, err=0.1, return_period=False): r""" Determine if the point is preperiodic with respect to this dynamical system.
.. NOTE::
This is only implemented for projective space (not subschemes).
ALGORITHM:
We know that a point is preperiodic if and only if it has canonical height zero. However, we can only compute the canonical height up to numerical precision. This function first computes the canonical height of the point to the given error bound. If it is larger than that error bound, then it must not be preperiodic. If it is less than the error bound, then we expect preperiodic. In this case we begin computing the orbit stopping if either we determine the orbit is finite, or the height of the point is large enough that it must be wandering. We can determine the height cutoff by computing the height difference constant, i.e., the bound between the height and the canonical height of a point (which depends only on the map and not the point itself). If the height of the point is larger than the difference bound, then the canonical height cannot be zero so the point cannot be preperiodic.
INPUT:
- ``P`` -- a point of this dynamical system's codomain
kwds:
- ``error_bound`` -- (default: 0.1) a positive real number; sets the error_bound used in the canonical height computation and ``return_period`` a boolean which
- ``return_period`` -- (default: ``False``) boolean; controls if the period is returned if the point is preperiodic
OUTPUT:
- boolean -- ``True`` if preperiodic
- if ``return_period`` is ``True``, then ``(0,0)`` if wandering, and ``(m,n)`` if preperiod ``m`` and period ``n``
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^3-3*x*y^2, y^3], domain=P) sage: Q = P(-1, 1) sage: f._is_preperiodic(Q) True
Check that :trac:`23814` is fixed (works even if domain is not specified)::
sage: R.<X> = PolynomialRing(QQ) sage: K.<a> = NumberField(X^2 + X - 1) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2-2*y^2, y^2]) sage: Q = P.point([a,1]) sage: Q.is_preperiodic(f) True """ raise NotImplementedError("must be over projective space") raise TypeError("must be a morphism") raise TypeError("point must be in domain of map")
raise NotImplementedError("must be over a number field or" " a number field order or QQbar")
# we know canonical height 0 if and only if preperiodic # however precision issues can occur so we can only tell *not* preperiodic # if the value is larger than the error # if the canonical height is less than than the # error, then we suspect preperiodic so check # either we can find the cycle or the height is # larger than the difference between the canonical height # and the height, so the canonical height cannot be 0 else: else:
class DynamicalSystem_projective_field(DynamicalSystem_projective, SchemeMorphism_polynomial_projective_space_field):
def lift_to_rational_periodic(self, points_modp, B=None): r""" Given a list of points in projective space over `\GF{p}`, determine if they lift to `\QQ`-rational periodic points.
The map must be an endomorphism of projective space defined over `\QQ`.
ALGORITHM:
Use Hensel lifting to find a `p`-adic approximation for that rational point. The accuracy needed is determined by the height bound ``B``. Then apply the LLL algorithm to determine if the lift corresponds to a rational point.
If the point is a point of high multiplicity (multiplier 1), the procedure can be very slow.
INPUT:
- ``points_modp`` -- a list or tuple of pairs containing a point in projective space over `\GF{p}` and the possible period
- ``B`` -- (optional) a positive integer; the height bound for a rational preperiodic point
OUTPUT: a list of projective points
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - y^2, y^2]) sage: f.lift_to_rational_periodic([[P(0,1).change_ring(GF(7)), 4]]) [[(0 : 1), 2]]
::
There may be multiple points in the lift. sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([-5*x^2 + 4*y^2, 4*x*y]) sage: f.lift_to_rational_periodic([[P(1,0).change_ring(GF(3)), 1]]) # long time [[(1 : 0), 1], [(2/3 : 1), 1], [(-2/3 : 1), 1]]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([16*x^2 - 29*y^2, 16*y^2]) sage: f.lift_to_rational_periodic([[P(3,1).change_ring(GF(13)), 3]]) [[(-1/4 : 1), 3]]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2) sage: f = DynamicalSystem_projective([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]) sage: f.lift_to_rational_periodic([[P(14,19,1).change_ring(GF(23)), 9]]) # long time [[(-9 : -4 : 1), 9]] """ return []
raise TypeError("must be positive characteristic") #compute the maximum p-adic precision needed to conclusively determine #if the rational point exists
#[point mod p, period, current p-adic precision] #shifts is used in non-Hensel lifting #While there are still points to consider try to lift to next precision #Find the last non-zero coordinate to use for normalizations #stop where we reach the needed precision or the point is bad # if the matrix is invertible then we can Hensel lift bad = 1 break break #Hensel gives us 2k for the newprecision else: #we are unable to Hensel Lift so must try all possible lifts #to the next precision (k+1) first = 0 newq = [] RQ = Zmod(p**(k+1)) fp = self.change_ring(RQ, check=False) if shifts is None: shifts = xmrange([p for i in range(N)]) for shift in shifts: newT = [RQ(t) for t in T] #T.change_ring(RQ, check = False) shiftindex = 0 for i in range(N + 1): if i != qindex: newT[i] = newT[i] + shift[shiftindex] * p**k shiftindex += 1 newT = fp.domain().point(newT, check=False) TT = fp.nth_iterate(newT, n, normalize=False) if TT == newT: if first == 0: newq.append(newT.change_ring(QQ, check=False)) newq.append(n) newq.append(k + 1) first = 1 else: points.append([newT.change_ring(QQ, check=False), n, k+1]) if not newq: bad = 1 break else: T = newq[0] k += 1 #given a p-adic lift of appropriate precision #perform LLL to find the "smallest" rational approximation #If this height is small enough, then it is a valid rational point #remove gcds since this is a projective point #height too big, so not a valid point #check that it is actually periodic
def rational_periodic_points(self, **kwds): r""" Determine the set of rational periodic points for this dynamical system.
The map must be defined over `\QQ` and be an endomorphism of projective space. If the map is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then the base ring can be an absolute number field. This is done by passing to the Weil restriction.
The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having trouble getting a particular map to finish, try first computing the possible periods, then try various different ``lifting_prime`` values.
ALGORITHM:
Modulo each prime of good reduction `p` determine the set of periodic points modulo `p`. For each cycle modulo `p` compute the set of possible periods (`mrp^e`). Take the intersection of the list of possible periods modulo several primes of good reduction to get a possible list of minimal periods of rational periodic points. Take each point modulo `p` associated to each of these possible periods and try to lift it to a rational point with a combination of `p`-adic approximation and the LLL basis reduction algorithm.
See [Hutz2015]_.
INPUT:
kwds:
- ``prime_bound`` -- (default: ``[1,20]``) a pair (list or tuple) of positive integers that represent the limits of primes to use in the reduction step or an integer that represents the upper bound
- ``lifting_prime`` -- (default: 23) a prime integer; argument that specifies modulo which prime to try and perform the lifting
- ``periods`` -- (optional) a list of positive integers that is the list of possible periods
- ``bad_primes`` -- (optional) a list or tuple of integer primes; the primes of bad reduction
- ``ncpus`` -- (default: all cpus) number of cpus to use in parallel
OUTPUT: a list of rational points in projective space
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-3/4*y^2, y^2]) sage: sorted(f.rational_periodic_points(prime_bound=20, lifting_prime=7)) # long time [(-1/2 : 1), (1 : 0), (3/2 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([2*x^3 - 50*x*z^2 + 24*z^3, ....: 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) sage: sorted(f.rational_periodic_points(prime_bound=[1,20])) # long time [(-3 : -1 : 1), (-3 : 0 : 1), (-3 : 1 : 1), (-3 : 3 : 1), (-1 : -1 : 1), (-1 : 0 : 1), (-1 : 1 : 1), (-1 : 3 : 1), (0 : 1 : 0), (1 : -1 : 1), (1 : 0 : 0), (1 : 0 : 1), (1 : 1 : 1), (1 : 3 : 1), (3 : -1 : 1), (3 : 0 : 1), (3 : 1 : 1), (3 : 3 : 1), (5 : -1 : 1), (5 : 0 : 1), (5 : 1 : 1), (5 : 3 : 1)]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([-5*x^2 + 4*y^2, 4*x*y]) sage: sorted(f.rational_periodic_points()) # long time [(-2 : 1), (-2/3 : 1), (2/3 : 1), (1 : 0), (2 : 1)]
::
sage: R.<x> = QQ[] sage: K.<w> = NumberField(x^2-x+1) sage: P.<u,v> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([u^2 + v^2,v^2]) sage: f.rational_periodic_points() [(w : 1), (1 : 0), (-w + 1 : 1)]
::
sage: R.<x> = QQ[] sage: K.<w> = NumberField(x^2-x+1) sage: P.<u,v> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([u^2+v^2,u*v]) sage: f.rational_periodic_points() Traceback (most recent call last): ... NotImplementedError: rational periodic points for number fields only implemented for polynomials """ raise TypeError("base field must be an absolute field") #check that we are not over QQ raise NotImplementedError("rational periodic points for number fields only implemented in dimension 1") #we need to dehomogenize for the Weil restriction and will check that point at infty #separately. We also check here that we are working with a polynomial. If the map #is not a polynomial, the Weil restriction will not be a morphism and we cannot #apply this algorithm. #determine rational periodic points #infinity is a totally ramified fixed point for a polynomial #compute the weil restriction #find the QQ rational periodic points for the weil restriction #take the 'good' points in the weil restriction and find the #associated number field points. #for each periodic point get the entire cycle #check periodic not preperiodic and add all points in cycle orb.add(Q2) Q2 = self(Q2) else:
except TypeError: raise TypeError("bound on primes must be an integer") else: except TypeError: raise TypeError("prime bounds must be integers")
p = next_prime(p + 1)
else: raise TypeError("base field must be an absolute number field")
def all_rational_preimages(self, points): r""" Given a set of rational points in the domain of this dynamical system, return all the rational preimages of those points.
In others words, all the rational points which have some iterate in the set points. This function repeatedly calls ``rational_preimages``. If the degree is at least two, by Northocott, this is always a finite set. The map must be defined over number fields and be an endomorphism.
INPUT:
- ``points`` -- a list of rational points in the domain of this map
OUTPUT: a list of rational points in the domain of this map
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([16*x^2 - 29*y^2, 16*y^2]) sage: sorted(f.all_rational_preimages([P(-1,4)])) [(-7/4 : 1), (-5/4 : 1), (-3/4 : 1), (-1/4 : 1), (1/4 : 1), (3/4 : 1), (5/4 : 1), (7/4 : 1)]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([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]) sage: sorted(f.all_rational_preimages([P(-9,-4,1)])) [(-9 : -4 : 1), (0 : -1 : 1), (0 : 0 : 1), (0 : 1 : 1), (0 : 4 : 1), (1 : 0 : 1), (1 : 1 : 1), (1 : 2 : 1), (1 : 3 : 1)]
A non-periodic example ::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + y^2, 2*x*y]) sage: sorted(f.all_rational_preimages([P(17,15)])) [(1/3 : 1), (3/5 : 1), (5/3 : 1), (3 : 1)]
A number field example::
sage: z = QQ['z'].0 sage: K.<w> = NumberField(z^3 + (z^2)/4 - (41/16)*z + 23/64); sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([16*x^2 - 29*y^2, 16*y^2]) sage: f.all_rational_preimages([P(16*w^2 - 29,16)]) [(-w^2 + 21/16 : 1), (w : 1), (w + 1/2 : 1), (w^2 + w - 33/16 : 1), (-w^2 - w + 25/16 : 1), (w^2 - 21/16 : 1), (-w^2 - w + 33/16 : 1), (-w : 1), (-w - 1/2 : 1), (-w^2 + 29/16 : 1), (w^2 - 29/16 : 1), (w^2 + w - 25/16 : 1)]
::
sage: K.<w> = QuadraticField(3) sage: P.<u,v> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([u^2+v^2, v^2]) sage: f.all_rational_preimages(P(4)) [(-w : 1), (w : 1)] """ raise TypeError("field won't return finite list of elements")
def rational_preperiodic_points(self, **kwds): r""" Determine the set of rational preperiodic points for this dynamical system.
The map must be defined over `\QQ` and be an endomorphism of projective space. If the map is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then the base ring can be an absolute number field. This is done by passing to the Weil restriction.
The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having trouble getting a particular map to finish, try first computing the possible periods, then try various different values for ``lifting_prime``.
ALGORITHM:
- Determines the list of possible periods.
- Determines the rational periodic points from the possible periods.
- Determines the rational preperiodic points from the rational periodic points by determining rational preimages.
INPUT:
kwds:
- ``prime_bound`` -- (default: ``[1, 20]``) a pair (list or tuple) of positive integers that represent the limits of primes to use in the reduction step or an integer that represents the upper bound
- ``lifting_prime`` -- (default: 23) a prime integer; specifies modulo which prime to try and perform the lifting
- ``periods`` -- (optional) a list of positive integers that is the list of possible periods
- ``bad_primes`` -- (optional) a list or tuple of integer primes; the primes of bad reduction
- ``ncpus`` -- (default: all cpus) number of cpus to use in parallel
OUTPUT: a list of rational points in projective space
EXAMPLES::
sage: PS.<x,y> = ProjectiveSpace(1,QQ) sage: f = DynamicalSystem_projective([x^2 -y^2, 3*x*y]) sage: sorted(f.rational_preperiodic_points()) [(-2 : 1), (-1 : 1), (-1/2 : 1), (0 : 1), (1/2 : 1), (1 : 0), (1 : 1), (2 : 1)]
::
sage: PS.<x,y> = ProjectiveSpace(1,QQ) sage: f = DynamicalSystem_projective([5*x^3 - 53*x*y^2 + 24*y^3, 24*y^3]) sage: sorted(f.rational_preperiodic_points(prime_bound=10)) [(-1 : 1), (0 : 1), (1 : 0), (1 : 1), (3 : 1)]
::
sage: PS.<x,y,z> = ProjectiveSpace(2,QQ) sage: f = DynamicalSystem_projective([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: sorted(f.rational_preperiodic_points(prime_bound=[1,8], lifting_prime=7, periods=[2])) # long time [(-5/4 : -2 : 1), (-5/4 : -1 : 1), (-5/4 : 0 : 1), (-5/4 : 1 : 1), (-5/4 : 2 : 1), (-1/4 : -2 : 1), (-1/4 : -1 : 1), (-1/4 : 0 : 1), (-1/4 : 1 : 1), (-1/4 : 2 : 1), (1/4 : -2 : 1), (1/4 : -1 : 1), (1/4 : 0 : 1), (1/4 : 1 : 1), (1/4 : 2 : 1), (5/4 : -2 : 1), (5/4 : -1 : 1), (5/4 : 0 : 1), (5/4 : 1 : 1), (5/4 : 2 : 1)]
::
sage: K.<w> = QuadraticField(33) sage: PS.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2-71/48*y^2, y^2]) sage: sorted(f.rational_preperiodic_points()) # long time [(-1/12*w - 1 : 1), (-1/6*w - 1/4 : 1), (-1/12*w - 1/2 : 1), (-1/6*w + 1/4 : 1), (1/12*w - 1 : 1), (1/12*w - 1/2 : 1), (-1/12*w + 1/2 : 1), (-1/12*w + 1 : 1), (1/6*w - 1/4 : 1), (1/12*w + 1/2 : 1), (1 : 0), (1/6*w + 1/4 : 1), (1/12*w + 1 : 1)] """ raise TypeError("base field must be an absolute field") #check that we are not over QQ if PS.dimension_relative() != 1: raise NotImplementedError("rational preperiodic points for number fields only implemented in dimension 1") w = K.absolute_generator() #we need to dehomogenize for the Weil restriction and will check that point at infty #separately. We also check here that we are working with a polynomial. If the map #is not a polynomial, the Weil restriction will not be a morphism and we cannot #apply this algorithm. g = self.dehomogenize(1) inf = PS([1,0]) k = 1 if isinstance(g[0], FractionFieldElement): g = self.dehomogenize(0) inf = PS([0,1]) k = 0 if isinstance(g[0], FractionFieldElement): raise NotImplementedError("rational preperiodic points for number fields only implemented for polynomials") #determine rational preperiodic points #infinity is a totally ramified fixed point for a polynomial preper = set([inf]) #compute the weil restriction G = g.weil_restriction() F = G.homogenize(d) #find the QQ rational preperiodic points for the weil restriction Fpre = F.rational_preperiodic_points(**kwds) for P in Fpre: #take the 'good' points in the weil restriction and find the #associated number field points. if P[d] == 1: pt = [sum([P[i]*w**i for i in range(d)])] pt.insert(k,1) Q = PS(pt) #for each preperiodic point get the entire connected component if not Q in preper: for t in self.connected_rational_component(Q): preper.add(t) preper = list(preper) else: #input error checking done in possible_periods and rational_periodic_points #determine the set of possible periods bad_primes=badprimes, ncpus=num_cpus) return([]) #no rational preperiodic points else: #find the rational preperiodic points periods=periods, bad_primes=badprimes, ncpus=num_cpus)
def rational_preperiodic_graph(self, **kwds): r""" Determine the directed graph of the rational preperiodic points for this dynamical system.
The map must be defined over `\QQ` and be an endomorphism of projective space. If this map is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then the base ring can be an absolute number field. This is done by passing to the Weil restriction.
ALGORITHM:
- Determines the list of possible periods.
- Determines the rational periodic points from the possible periods.
- Determines the rational preperiodic points from the rational periodic points by determining rational preimages.
INPUT:
kwds:
- ``prime_bound`` -- (default: ``[1, 20]``) a pair (list or tuple) of positive integers that represent the limits of primes to use in the reduction step or an integer that represents the upper bound
- ``lifting_prime`` -- (default: 23) a prime integer; specifies modulo which prime to try and perform the lifting
- ``periods`` -- (optional) a list of positive integers that is the list of possible periods
- ``bad_primes`` -- (optional) a list or tuple of integer primes; the primes of bad reduction
- ``ncpus`` -- (default: all cpus) number of cpus to use in parallel
OUTPUT:
A digraph representing the orbits of the rational preperiodic points in projective space.
EXAMPLES::
sage: PS.<x,y> = ProjectiveSpace(1,QQ) sage: f = DynamicalSystem_projective([7*x^2 - 28*y^2, 24*x*y]) sage: f.rational_preperiodic_graph() Looped digraph on 12 vertices
::
sage: PS.<x,y> = ProjectiveSpace(1,QQ) sage: f = DynamicalSystem_projective([-3/2*x^3 +19/6*x*y^2, y^3]) sage: f.rational_preperiodic_graph(prime_bound=[1,8]) Looped digraph on 12 vertices
::
sage: PS.<x,y,z> = ProjectiveSpace(2,QQ) sage: f = DynamicalSystem_projective([2*x^3 - 50*x*z^2 + 24*z^3, ....: 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) sage: f.rational_preperiodic_graph(prime_bound=[1,11], lifting_prime=13) # long time Looped digraph on 30 vertices
::
sage: K.<w> = QuadraticField(-3) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2+y^2, y^2]) sage: f.rational_preperiodic_graph() # long time Looped digraph on 5 vertices """ #input checking done in .rational_preperiodic_points()
def connected_rational_component(self, P, n=0): r""" Computes the connected component of a rational preperiodic point ``P`` by this dynamical system.
Will work for non-preperiodic points if ``n`` is positive. Otherwise this will not terminate.
INPUT:
- ``P`` -- a rational preperiodic point of this map
- ``n`` -- (default: 0) integer; maximum distance from ``P`` to branch out; a value of 0 indicates no bound
OUTPUT:
A list of points connected to ``P`` up to the specified distance.
EXAMPLES::
sage: R.<x> = PolynomialRing(QQ) sage: K.<w> = NumberField(x^3+1/4*x^2-41/16*x+23/64) sage: PS.<x,y> = ProjectiveSpace(1,K) sage: f = DynamicalSystem_projective([x^2 - 29/16*y^2, y^2]) sage: P = PS([w,1]) sage: f.connected_rational_component(P) [(w : 1), (w^2 - 29/16 : 1), (-w^2 - w + 25/16 : 1), (w^2 + w - 25/16 : 1), (-w : 1), (-w^2 + 29/16 : 1), (w + 1/2 : 1), (-w - 1/2 : 1), (-w^2 + 21/16 : 1), (w^2 - 21/16 : 1), (w^2 + w - 33/16 : 1), (-w^2 - w + 33/16 : 1)]
::
sage: PS.<x,y,z> = ProjectiveSpace(2,QQ) sage: f = DynamicalSystem_projective([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: P = PS([17/16,7/4,1]) sage: f.connected_rational_component(P,3) [(17/16 : 7/4 : 1), (-47/256 : 17/16 : 1), (-83807/65536 : -223/256 : 1), (-17/16 : -7/4 : 1), (-17/16 : 7/4 : 1), (17/16 : -7/4 : 1), (1386468673/4294967296 : -81343/65536 : 1), (-47/256 : -17/16 : 1), (47/256 : -17/16 : 1), (47/256 : 17/16 : 1), (-1/2 : -1/2 : 1), (-1/2 : 1/2 : 1), (1/2 : -1/2 : 1), (1/2 : 1/2 : 1)]
"""
# forward image # preimages # add any points that are not already in the connected component # done if max level was achieved or if there were no more points to add
def conjugating_set(self, other): r""" Return the set of elements in PGL that conjugates one dynamical system to the other.
Given two nonconstant rational functions of equal degree determine to see if there is an element of PGL that conjugates one rational function to another. It does this by taking the fixed points of one map and mapping them to all unique permutations of the fixed points of the other map. If there are not enough fixed points the function compares the mapping between rational preimages of fixed points and the rational preimages of the preimages of fixed points until there are enough points; such that there are `n+2` points with all `n+1` subsets linearly independent.
ALGORITHM:
Implementing invariant set algorithim from the paper [FMV2014]_. Given that the set of `n` th preimages of fixed points is invariant under conjugation find all elements of PGL that take one set to another.
INPUT:
- ``other`` -- a nonconstant rational function of same degree as ``self``
OUTPUT:
Set of conjugating `n+1` by `n+1` matrices.
AUTHORS:
- Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray [FMV2014]_.
- Implimented by Rebecca Lauren Miller, as part of GSOC 2016.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) sage: m = matrix(QQbar, 2, 2, [-1, 3, 2, 1]) sage: g = f.conjugate(m) sage: f.conjugating_set(g) [ [-1 3] [ 2 1] ]
::
sage: K.<w> = QuadraticField(-1) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2 + y^2, x*y]) sage: m = matrix(K, 2, 2, [1, 1, 2, 1]) sage: g = f.conjugate(m) sage: f.conjugating_set(g) # long time [ [1 1] [-1 -1] [2 1], [ 2 1] ]
::
sage: K.<i> = QuadraticField(-1) sage: P.<x,y> = ProjectiveSpace(K,1) sage: D8 = DynamicalSystem_projective([y^3, x^3]) sage: D8.conjugating_set(D8) # long time [ [1 0] [0 1] [ 0 -i] [i 0] [ 0 -1] [-1 0] [-i 0] [0 i] [0 1], [1 0], [ 1 0], [0 1], [ 1 0], [ 0 1], [ 0 1], [1 0] ]
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: D8 = DynamicalSystem_projective([y^2, x^2]) sage: D8.conjugating_set(D8) Traceback (most recent call last): ... ValueError: not enough rational preimages
::
sage: P.<x,y> = ProjectiveSpace(GF(7),1) sage: D6 = DynamicalSystem_projective([y^2, x^2]) sage: D6.conjugating_set(D6) [ [1 0] [0 1] [0 2] [4 0] [2 0] [0 4] [0 1], [1 0], [1 0], [0 1], [0 1], [1 0] ]
::
sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([x^2 + x*z, y^2, z^2]) sage: f.conjugating_set(f) # long time [ [1 0 0] [0 1 0] [0 0 1] ] """ except (ValueError): pass return [] return [] # make sure all n+1 subsets are linearly independent # finds preimages of fixed points return [] for i in Subsets(L, n+2): Ml = matrix(r, [list(s) for s in i]) if not any([j == 0 for j in Ml.minors(n+1)]): more = False Tf = list(i) break # try all possible conjugations between invariant sets except (ValueError): pass
def is_conjugate(self, other): r""" Return whether or not two dynamical systems are conjugate.
ALGORITHM:
Implementing invariant set algorithim from the paper [FMV2014]_. Given that the set of `n` th preimages is invariant under conjugation this function finds whether two maps are conjugate.
INPUT:
- ``other`` -- a nonconstant rational function of same degree as ``self``
OUTPUT: boolean
AUTHORS:
- Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray [FMV2014]_.
- Implimented by Rebecca Lauren Miller as part of GSOC 2016.
EXAMPLES::
sage: K.<w> = CyclotomicField(3) sage: P.<x,y> = ProjectiveSpace(K,1) sage: D8 = DynamicalSystem_projective([y^2, x^2]) sage: D8.is_conjugate(D8) True
::
sage: set_verbose(None) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: f = DynamicalSystem_projective([x^2 + x*y,y^2]) sage: m = matrix(QQbar, 2, 2, [1, 1, 2, 1]) sage: g = f.conjugate(m) sage: f.is_conjugate(g) # long time True
::
sage: P.<x,y> = ProjectiveSpace(GF(5),1) sage: f = DynamicalSystem_projective([x^3 + x*y^2,y^3]) sage: m = matrix(GF(5), 2, 2, [1, 3, 2, 9]) sage: g = f.conjugate(m) sage: f.is_conjugate(g) True
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + x*y,y^2]) sage: g = DynamicalSystem_projective([x^3 + x^2*y, y^3]) sage: f.is_conjugate(g) False
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2 + x*y, y^2]) sage: g = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) sage: f.is_conjugate(g) False """ except (ValueError): pass # finds preimages of fixed points return False raise ValueError("not enough rational preimages") # make sure all n+1 subsets are linearly independent # try all possible conjugations between invariant sets except (ValueError): pass return False
def is_polynomial(self): r""" Check to see if the dynamical system has a totally ramified fixed point.
The function must be defined over an absolute number field or a finite field.
OUTPUT: boolean
EXAMPLES::
sage: R.<x> = QQ[] sage: K.<w> = QuadraticField(7) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x**2 + 2*x*y - 5*y**2, 2*x*y]) sage: f.is_polynomial() False
::
sage: R.<x> = QQ[] sage: K.<w> = QuadraticField(7) sage: P.<x,y> = ProjectiveSpace(K, 1) sage: f = DynamicalSystem_projective([x**2 - 7*x*y, 2*y**2]) sage: m = matrix(K, 2, 2, [w, 1, 0, 1]) sage: f = f.conjugate(m) sage: f.is_polynomial() True
::
sage: K.<w> = QuadraticField(4/27) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x**3 + w*y^3,x*y**2]) sage: f.is_polynomial() False
::
sage: K = GF(3**2, prefix='w') sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x**2 + K.gen()*y**2, x*y]) sage: f.is_polynomial() False
::
sage: PS.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([6*x^2+12*x*y+7*y^2, 12*x*y + 42*y^2]) sage: f.is_polynomial() False """ raise NotImplementedError("space must have dimension equal to 1") raise NotImplementedError("must be over an absolute number field or finite field") #get polynomial defining fixed points # see if infty = (1,0) is fixed #check if infty is totally ramified #otherwise we need to create the tower of extensions #which contain the fixed points. We do #this successively so we can exit early if #we find one and not go all the way to the splitting field return True else: #create the next extension deg = deg*G.degree() K = GF(q**(deg), prefix=var) else: G = G.change_ring(K) g = g.change_ring(K) else:
def normal_form(self, return_conjugation=False): r""" Return a normal form in the moduli space of dynamical systems.
Currently implemented only for polynomials. The totally ramified fixed point is moved to infinity and the map is conjugated to the form `x^n + a_{n-2} x^{n-2} + \cdots + a_{0}`. Note that for finite fields we can only remove the `(n-1)`-st term when the characteristic does not divide `n`.
INPUT:
- ``return_conjugation`` -- (default: ``False``) boolean; if ``True``, then return the conjugation element of PGL along with the embedding into the new field
OUTPUT:
- :class:`SchemeMorphism_polynomial`
- (optional) an element of PGL as a matrix
- (optional) the field embedding
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem_projective([x^2 + 2*x*y - 5*x^2, 2*x*y]) sage: f.normal_form() Traceback (most recent call last): ... NotImplementedError: map is not a polynomial
::
sage: R.<x> = QQ[] sage: K.<w> = NumberField(x^2 - 5) sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^2 + w*x*y, y^2]) sage: g,m,psi = f.normal_form(return_conjugation = True);m [ 1 -1/2*w] [ 0 1] sage: f.change_ring(psi).conjugate(m) == g True
::
sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([13*x^2 + 4*x*y + 3*y^2, 5*y^2]) sage: f.normal_form() Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (5*x^2 + 9*y^2 : 5*y^2)
::
sage: K = GF(3^3, prefix = 'w') sage: P.<x,y> = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([x^3 + 2*x^2*y + 2*x*y^2 + K.gen()*y^3, y^3]) sage: f.normal_form() Dynamical System of Projective Space of dimension 1 over Finite Field in w3 of size 3^3 Defn: Defined on coordinates by sending (x : y) to (x^3 + x^2*y + x*y^2 + (-w3)*y^3 : y^3) """ #defines the field of fixed points raise NotImplementedError("space must have dimension equal to 1") raise NotImplementedError("must be over an absolute number field or finite field") else: #check infty = (1,0) is fixed #check infty totally ramified #otherwise we need to create the tower of extensions #which contain the fixed points. We do #this successively so we can early exit if #we find one and not go all the way to the splitting field else: #no other fixed points raise NotImplementedError("map is not a polynomial") #check other fixed points T = self.domain()(-p[0], p[1]) bad = False done = True break # bc only 1 totally ramified fixed pt else: done = False u = p #extend if K == QQ: from sage.rings.number_field.number_field import NumberField L = NumberField(u, 't'+str(i)) i += 1 phi = K.embeddings(L)[0] psi = phi * psi K = L elif K in FiniteFields(): deg = deg * G.degree() K = GF(q**(deg), prefix=var) else: L = K.extension(u, 't'+str(i)) i += 1 phi1 = K.embeddings(L)[0] K = L L = K.absolute_field('t'+str(i)) i += 1 phi = K.embeddings(L)[0]*phi1 psi = phi * psi K = L #switch to the new field if K in FiniteFields(): G = G.change_ring(K) g = g.change_ring(K) else: G = G.change_ring(phi) g = g.change_ring(phi) #conjugate to normal form #moved totally ramified fixed point to infty #make monic #need a (d-1)-st root to make monic #we need to extend again if N in FiniteFields(): deg = deg*(d-1) M = GF(q**(deg), prefix=var) else: L = N.extension(u,'t'+str(i)) i += 1 phi1 = N.embeddings(L)[0] M = L.absolute_field('t'+str(i)) phi = L.embeddings(M)[0]*phi1 psi = phi*psi if M in FiniteFields(): gc = gc.change_ring(M) else: gc = gc.change_ring(phi) m = matrix(M, 2, 2, [phi(s) for t in list(m) for s in t]) rv = phi(v).nth_root(d-1) else: #root is already in the field #remove 2nd order term / (d*gcc[1].coefficient([0, d]))).constant_coefficient()), 0, 1]) else: return gccc, m * mc * mc2 else:
class DynamicalSystem_projective_finite_field(DynamicalSystem_projective_field, SchemeMorphism_polynomial_projective_space_finite_field): def orbit_structure(self, P): r""" Return the pair ``[m,n]``, where ``m`` is the preperiod and ``n`` is the period of the point ``P`` by this dynamical system.
Every point is preperiodic over a finite field so every point will be preperiodic.
INPUT:
- ``P`` -- a point in the domain of this map
OUTPUT: a list ``[m,n]`` of integers
EXAMPLES::
sage: P.<x,y,z> = ProjectiveSpace(GF(5),2) sage: f = DynamicalSystem_projective([x^2 + y^2,y^2, z^2 + y*z], domain=P) sage: f.orbit_structure(P(2,1,2)) [0, 6]
::
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2) sage: X = P.subscheme(x^2-y^2) sage: f = DynamicalSystem_projective([x^2, y^2, z^2], domain=X) sage: f.orbit_structure(X(1,1,2)) [0, 2]
::
sage: P.<x,y> = ProjectiveSpace(GF(13),1) sage: f = DynamicalSystem_projective([x^2 - y^2, y^2], domain=P) sage: f.orbit_structure(P(3,4)) [2, 3]
::
sage: R.<t> = GF(13^3) sage: P.<x,y> = ProjectiveSpace(R,1) sage: f = DynamicalSystem_projective([x^2 - y^2, y^2], domain=P) sage: f.orbit_structure(P(t, 4)) [11, 6] """
def cyclegraph(self): r""" Return the digraph of all orbits of this dyanmical system.
Over a finite field this is a finite graph. For subscheme domains, only points on the subscheme whose image are also on the subscheme are in the digraph.
OUTPUT: a digraph
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(GF(13),1) sage: f = DynamicalSystem_projective([x^2-y^2, y^2]) sage: f.cyclegraph() Looped digraph on 14 vertices
::
sage: P.<x,y,z> = ProjectiveSpace(GF(3^2,'t'),2) sage: f = DynamicalSystem_projective([x^2+y^2, y^2, z^2+y*z]) sage: f.cyclegraph() Looped digraph on 91 vertices
::
sage: P.<x,y,z> = ProjectiveSpace(GF(7),2) sage: X = P.subscheme(x^2-y^2) sage: f = DynamicalSystem_projective([x^2, y^2, z^2], domain=X) sage: f.cyclegraph() Looped digraph on 15 vertices
::
sage: P.<x,y,z> = ProjectiveSpace(GF(3),2) sage: f = DynamicalSystem_projective([x*z-y^2, x^2-y^2, y^2-z^2]) sage: f.cyclegraph() Looped digraph on 13 vertices
::
sage: P.<x,y,z> = ProjectiveSpace(GF(3),2) sage: X = P.subscheme([x-y]) sage: f = DynamicalSystem_projective([x^2-y^2, x^2-y^2, y^2-z^2], domain=X) sage: f.cyclegraph() Looped digraph on 4 vertices """ else:
def possible_periods(self, return_points=False): r""" Return the list of possible minimal periods of a periodic point over `\QQ` and (optionally) a point in each cycle.
ALGORITHM:
See [Hutz2009]_.
INPUT:
- ``return_points`` -- (default: ``False``) boolean; if ``True``, then return the points as well as the possible periods
OUTPUT:
A list of positive integers, or a list of pairs of projective points and periods if ``return_points`` is ``True``.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(GF(23),1) sage: f = DynamicalSystem_projective([x^2-2*y^2, y^2]) sage: f.possible_periods() [1, 5, 11, 22, 110]
::
sage: P.<x,y> = ProjectiveSpace(GF(13),1) sage: f = DynamicalSystem_projective([x^2-y^2, y^2]) sage: sorted(f.possible_periods(True)) [[(0 : 1), 2], [(1 : 0), 1], [(3 : 1), 3], [(3 : 1), 36]]
::
sage: PS.<x,y,z> = ProjectiveSpace(2,GF(7)) sage: f = DynamicalSystem_projective([-360*x^3 + 760*x*z^2, ....: y^3 - 604*y*z^2 + 240*z^3, 240*z^3]) sage: f.possible_periods() [1, 2, 4, 6, 12, 14, 28, 42, 84]
.. TODO::
- do not return duplicate points
- improve hash to reduce memory of point-table """
def automorphism_group(self, absolute=False, iso_type=False, return_functions=False): r""" Return the subgroup of `PGL2` that is the automorphism group of this dynamical system.
Only for dimension 1. The automorphism group is the set of `PGL2` elements that fixed the map under conjugation. See [FMV2014]_ for the algorithm.
INPUT:
- ``absolute``-- (default: ``False``) boolean; if ``True``, then return the absolute automorphism group and a field of definition
- ``iso_type`` -- (default: ``False``) boolean; if ``True``, then return the isomorphism type of the automorphism group
- ``return_functions`` -- (default: ``False``) boolean; ``True`` returns elements as linear fractional transformations and ``False`` returns elements as `PGL2` matrices
OUTPUT: a list of elements of the automorphism group
AUTHORS:
- Original algorithm written by Xander Faber, Michelle Manes, Bianca Viray
- Modified by Joao Alberto de Faria, Ben Hutz, Bianca Thompson
EXAMPLES::
sage: R.<x,y> = ProjectiveSpace(GF(7^3,'t'),1) sage: f = DynamicalSystem_projective([x^2-y^2, x*y]) sage: f.automorphism_group() [ [1 0] [6 0] [0 1], [0 1] ]
::
sage: R.<x,y> = ProjectiveSpace(GF(3^2,'t'),1) sage: f = DynamicalSystem_projective([x^3,y^3]) sage: f.automorphism_group(return_functions=True, iso_type=True) # long time ([x, x/(x + 1), x/(2*x + 1), 2/(x + 2), (2*x + 1)/(2*x), (2*x + 2)/x, 1/(2*x + 2), x + 1, x + 2, x/(x + 2), 2*x/(x + 1), 2*x, 1/x, 2*x + 1, 2*x + 2, ((t + 2)*x + t + 2)/((2*t + 1)*x + t + 2), (t*x + 2*t)/(t*x + t), 2/x, (x + 1)/(x + 2), (2*t*x + t)/(t*x), (2*t + 1)/((2*t + 1)*x + 2*t + 1), ((2*t + 1)*x + 2*t + 1)/((2*t + 1)*x), t/(t*x + 2*t), (2*x + 1)/(x + 1)], 'PGL(2,3)')
::
sage: R.<x,y> = ProjectiveSpace(GF(2^5,'t'),1) sage: f = DynamicalSystem_projective([x^5,y^5]) sage: f.automorphism_group(return_functions=True, iso_type=True) ([x, 1/x], 'Cyclic of order 2')
::
sage: R.<x,y> = ProjectiveSpace(GF(3^4,'t'),1) sage: f = DynamicalSystem_projective([x^2+25*x*y+y^2, x*y+3*y^2]) sage: f.automorphism_group(absolute=True) [Univariate Polynomial Ring in w over Finite Field in b of size 3^4, [ [1 0] [0 1] ]] """ raise NotImplementedError("must be dimension 1") else: else:
|