Skip to content

Commit

Permalink
gh-36000: New functions is_chebyshev and is_Lattes for one dimens…
Browse files Browse the repository at this point in the history
…ional projective dynamical systems

    
This PR carries the work and hopes to fix #28292.

All the codes in this PR were/are implemented by the participants in
said issue.

<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes #1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->

<!-- Why is this change required? What problem does it solve? -->
<!-- If this PR resolves an open issue, please link to it here. For
example "Fixes #12345". -->
<!-- If your change requires a documentation PR, please link it
appropriately. -->

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on
- #12345: short description why this is a dependency
- #34567: ...
-->

<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
    
URL: #36000
Reported by: Jing Guo
Reviewer(s):
  • Loading branch information
Release Manager committed Aug 26, 2023
2 parents a102fbd + ae3913d commit 72b41aa
Show file tree
Hide file tree
Showing 2 changed files with 306 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4588,6 +4588,9 @@ REFERENCES:
.. [Mil1974] \J. W. Milnor and J. D. Stasheff, *Characteristic Classes*,
University Press, Princeton and Tokyo, 1974.
.. [Mil2006] \J. W. Milnor, *On Lattes maps*,
Dynamics on the Riemann sphere, Eur. Math. Soc., 9–43
.. [Mil1978] \S. Milne, *A q-analog of restricted growth functions,
Dobinsky’s equality and Charlier
polynomials*. Trans. Amer. Math. Soc., 245 (1978),
Expand Down
303 changes: 303 additions & 0 deletions src/sage/dynamics/arithmetic_dynamics/projective_ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -6482,6 +6482,309 @@ def postcritical_set(self, check=True):
next_point = f(next_point)
return post_critical_list

def is_chebyshev(self):
r"""
Check if ``self`` is a Chebyshev polynomial.
OUTPUT: True if ``self`` is Chebyshev, False otherwise.
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([x^4, y^4])
sage: F.is_chebyshev()
False
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([x^2 + y^2, y^2])
sage: F.is_chebyshev()
False
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([2*x^2 - y^2, y^2])
sage: F.is_chebyshev()
True
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([x^3, 4*y^3 - 3*x^2*y])
sage: F.is_chebyshev()
True
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([2*x^2 - y^2, y^2])
sage: L.<i> = CyclotomicField(4)
sage: M = Matrix([[0,i],[-i,0]])
sage: F.conjugate(M)
Dynamical System of Projective Space of dimension 1 over Cyclotomic Field of order 4 and degree 2
Defn: Defined on coordinates by sending (x : y) to
((-i)*x^2 : (-i)*x^2 + (2*i)*y^2)
sage: F.is_chebyshev()
True
REFERENCES:
- [Mil2006]_
"""
# We need `F` to be defined over a number field for
# the function `is_postcrtically_finite` to work
if self.base_ring() not in NumberFields():
raise NotImplementedError("Base ring must be a number field")

if self.domain().dimension() != 1:
return False

# All Chebyshev polynomials are postcritically finite
if not self.is_postcritically_finite():
return False

# Get field of definition for critical points and change base field
critical_field, phi = self.field_of_definition_critical(return_embedding=True)
F_crit = self.change_ring(phi)

# Get the critical points and post-critical set
crit_set = set(F_crit.critical_points())
post_crit_set = set()
images_needed = copy(crit_set)
while len(images_needed) != 0:
Q = images_needed.pop()
Q2 = F_crit(Q)
if Q2 not in post_crit_set:
post_crit_set.add(Q2)
images_needed.add(Q2)

crit_fixed_pts = set()
for crit in crit_set:
if F_crit.nth_iterate(crit, 1) == crit:
crit_fixed_pts.add(crit)

# All Chebyshev maps have 3 post-critical values
if (len(post_crit_set) != 3) or (len(crit_fixed_pts) != 1):
return False

f = F_crit.dehomogenize(1)[0]
x = f.parent().gen()
ram_points = {}
for crit in crit_set:
g = f
new_crit = crit

# Check if critical point is infinity
if crit[1] == 0:
g = g.subs(x=1/x)
new_crit = F_crit.domain()([0, 1])

# Check if output is infinity
if F_crit.nth_iterate(crit, 1)[1] == 0:
g = 1/g

new_crit = new_crit.dehomogenize(1)[0]
e = 1
g = g.derivative(x)

while g(new_crit) == 0:
e += 1
g = g.derivative(x)

ram_points[crit] = e

r = {}

# Set r value to 0 to represent infinity
r[crit_fixed_pts.pop()] = 0

# Get non-fixed tail points in the post-critical portrait
crit_tails = crit_set.difference(post_crit_set)
for crit in crit_tails:
# Each critical tail point has r value 1
r[crit] = 1
point = crit

# Assign r values to every point in the orbit of crit.
# If we find a point in the orbit which already has an r value assigned,
# check that we get a consistent r value.
while F_crit.nth_iterate(point, 1) not in r.keys():
if point not in ram_points.keys():
r[F_crit.nth_iterate(point,1)] = r[point]
else:
r[F_crit.nth_iterate(point,1)] = r[point] * ram_points[point]

point = F_crit.nth_iterate(point,1)

# Once we get here, the image of point has an assigned r value
# We check that this value is consistent
if point not in ram_points.keys():
if r[F_crit.nth_iterate(point,1)] != r[point]:
return False
elif r[F_crit.nth_iterate(point,1)] != r[point] * ram_points[point]:
return False

# The non-one r values must be one of the following in order for F to be Chebyshev
r_vals = sorted([val for val in r.values() if val != 1])
return r_vals == [0, 2, 2]

def is_Lattes(self):
r"""
Check if ``self`` is a Lattes map
OUTPUT: True if ``self`` is Lattes, False otherwise
EXAMPLES::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([x^3, y^3])
sage: F.is_Lattes()
False
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([x^2 - 2*y^2, y^2])
sage: F.is_Lattes()
False
::
sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
sage: F = DynamicalSystem_projective([x^2 + y^2 + z^2, y^2, z^2])
sage: F.is_Lattes()
False
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([(x + y)*(x - y)^3, y*(2*x + y)^3])
sage: F.is_Lattes()
True
::
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
sage: F = DynamicalSystem_projective([(x + y)^4, 16*x*y*(x - y)^2])
sage: F.is_Lattes()
True
::
sage: f = P.Lattes_map(EllipticCurve([0, 0, 0, 0, 2]),2)
sage: f.is_Lattes()
True
::
sage: f = P.Lattes_map(EllipticCurve([0, 0, 0, 0, 2]), 2)
sage: L.<i> = CyclotomicField(4)
sage: M = Matrix([[i, 0], [0, -i]])
sage: f.conjugate(M)
Dynamical System of Projective Space of dimension 1 over Cyclotomic Field of order 4 and degree 2
Defn: Defined on coordinates by sending (x : y) to
((-1/4*i)*x^4 + (-4*i)*x*y^3 : (-i)*x^3*y + (2*i)*y^4)
sage: f.is_Lattes()
True
REFERENCES:
- [Mil2006]_
"""
# We need `f` to be defined over a number field for
# the function `is_postcrtically_finite` to work
if self.base_ring() not in NumberFields():
raise NotImplementedError("Base ring must be a number field")

if self.domain().dimension() != 1:
return False

# All Lattes maps are postcritically finite
if not self.is_postcritically_finite():
return False

# Get field of definition for critical points and change basefield
critical_field, phi = self.field_of_definition_critical(return_embedding=True)
F_crit = self.change_ring(phi)

# Get the critical points and post-critical set
crit = F_crit.critical_points()
post_crit = set()
images_needed = copy(crit)
while len(images_needed) != 0:
Q = images_needed.pop()
Q2 = F_crit(Q)
if Q2 not in post_crit:
post_crit.add(Q2)
images_needed.append(Q2)

(crit_set, post_crit_set) = crit, list(post_crit)

# All Lattes maps have 3 or 4 post critical values
if not len(post_crit_set) in [3, 4]:
return False

f = F_crit.dehomogenize(1)[0]
x = f.parent().gen()
ram_points = {}
for crit in crit_set:
g = f
new_crit = crit

# Check if critical point is infinity
if crit[1] == 0:
g = g.subs(x=1/x)
new_crit = F_crit.domain()([0, 1])

# Check if output is infinity
if F_crit.nth_iterate(crit, 1)[1] == 0:
g = 1/g

new_crit = new_crit.dehomogenize(1)[0]

e = 1
g = g.derivative(x)
while g(new_crit) == 0:
e += 1
g = g.derivative(x)
ram_points[crit] = e

r = {}

# Get tail points in the post-critical portrait
crit_tails = set(crit_set).difference(set(post_crit_set))
for crit in crit_tails:
# Each critical tail point has r value 1
r[crit] = 1
point = crit

# Assign r values to every point in the orbit of crit
# If we find a point in the orbit which already has an r value assigned,
# check that we get a consistent r value
while F_crit.nth_iterate(point, 1) not in r.keys():
if point not in ram_points.keys():
r[F_crit.nth_iterate(point, 1)] = r[point]
else:
r[F_crit.nth_iterate(point, 1)] = r[point] * ram_points[point]

point = F_crit.nth_iterate(point,1)

# Once we get here the image of point has an assigned r value
# We check that this value is consistent.
if point not in ram_points.keys():
if r[F_crit.nth_iterate(point, 1)] != r[point]:
return False
else:
if r[F_crit.nth_iterate(point, 1)] != r[point] * ram_points[point]:
return False

# The non-one r values must be one of the following in order for F to be Lattes
r_lattes_cases = [[2, 2, 2, 2], [3, 3, 3], [2, 4, 4], [2, 3, 6]]
r_vals = sorted([val for val in r.values() if val != 1])
return r_vals in r_lattes_cases


class DynamicalSystem_projective_field(DynamicalSystem_projective,
SchemeMorphism_polynomial_projective_space_field):
Expand Down

0 comments on commit 72b41aa

Please sign in to comment.