passagemath-schemes 10.8.1a4__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_schemes/.dylibs/libflint.22.0.dylib +0 -0
- passagemath_schemes/.dylibs/libgmp.10.dylib +0 -0
- passagemath_schemes/.dylibs/libgmpxx.4.dylib +0 -0
- passagemath_schemes/.dylibs/libmpfr.6.dylib +0 -0
- passagemath_schemes/__init__.py +3 -0
- passagemath_schemes-10.8.1a4.dist-info/METADATA +203 -0
- passagemath_schemes-10.8.1a4.dist-info/METADATA.bak +204 -0
- passagemath_schemes-10.8.1a4.dist-info/RECORD +312 -0
- passagemath_schemes-10.8.1a4.dist-info/WHEEL +6 -0
- passagemath_schemes-10.8.1a4.dist-info/top_level.txt +3 -0
- sage/all__sagemath_schemes.py +23 -0
- sage/databases/all__sagemath_schemes.py +7 -0
- sage/databases/cremona.py +1723 -0
- sage/dynamics/all__sagemath_schemes.py +2 -0
- sage/dynamics/arithmetic_dynamics/affine_ds.py +1083 -0
- sage/dynamics/arithmetic_dynamics/all.py +14 -0
- sage/dynamics/arithmetic_dynamics/berkovich_ds.py +1101 -0
- sage/dynamics/arithmetic_dynamics/dynamical_semigroup.py +1543 -0
- sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +2426 -0
- sage/dynamics/arithmetic_dynamics/endPN_minimal_model.py +1169 -0
- sage/dynamics/arithmetic_dynamics/generic_ds.py +663 -0
- sage/dynamics/arithmetic_dynamics/product_projective_ds.py +339 -0
- sage/dynamics/arithmetic_dynamics/projective_ds.py +9556 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314t-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2578 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +727 -0
- sage/lfunctions/pari.py +971 -0
- sage/lfunctions/zero_sums.cpython-314t-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5132 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +414 -0
- sage/modular/abvar/abvar_newform.py +246 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +187 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +721 -0
- sage/modular/abvar/homspace.py +989 -0
- sage/modular/abvar/lseries.py +415 -0
- sage/modular/abvar/morphism.py +935 -0
- sage/modular/abvar/torsion_point.py +274 -0
- sage/modular/abvar/torsion_subgroup.py +741 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1406 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +361 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +659 -0
- sage/modular/arithgroup/congroup_gammaH.py +1491 -0
- sage/modular/arithgroup/congroup_generic.py +630 -0
- sage/modular/arithgroup/congroup_sl2z.py +266 -0
- sage/modular/arithgroup/farey_symbol.cpython-314t-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1067 -0
- sage/modular/arithgroup/tests.py +425 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3736 -0
- sage/modular/btquotients/pautomorphicform.py +2564 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1107 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +571 -0
- sage/modular/dirichlet.py +3310 -0
- sage/modular/drinfeld_modform/all.py +2 -0
- sage/modular/drinfeld_modform/element.py +446 -0
- sage/modular/drinfeld_modform/ring.py +773 -0
- sage/modular/drinfeld_modform/tutorial.py +236 -0
- sage/modular/etaproducts.py +1076 -0
- sage/modular/hecke/algebra.py +725 -0
- sage/modular/hecke/all.py +19 -0
- sage/modular/hecke/ambient_module.py +994 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +302 -0
- sage/modular/hecke/hecke_operator.py +736 -0
- sage/modular/hecke/homspace.py +185 -0
- sage/modular/hecke/module.py +1744 -0
- sage/modular/hecke/morphism.py +139 -0
- sage/modular/hecke/submodule.py +970 -0
- sage/modular/hypergeometric_misc.cpython-314t-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2020 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1070 -0
- sage/modular/local_comp/smoothchar.py +1825 -0
- sage/modular/local_comp/type_space.py +748 -0
- sage/modular/modform/all.py +30 -0
- sage/modular/modform/ambient.py +817 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +120 -0
- sage/modular/modform/ambient_g1.py +199 -0
- sage/modular/modform/constructor.py +545 -0
- sage/modular/modform/cuspidal_submodule.py +708 -0
- sage/modular/modform/defaults.py +14 -0
- sage/modular/modform/eis_series.py +487 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4105 -0
- sage/modular/modform/half_integral.py +154 -0
- sage/modular/modform/hecke_operator_on_qexp.py +247 -0
- sage/modular/modform/j_invariant.py +47 -0
- sage/modular/modform/l_series_gross_zagier.py +127 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314t-darwin.so +0 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.pyx +177 -0
- sage/modular/modform/notes.py +45 -0
- sage/modular/modform/numerical.py +514 -0
- sage/modular/modform/periods.py +14 -0
- sage/modular/modform/ring.py +1257 -0
- sage/modular/modform/space.py +1859 -0
- sage/modular/modform/submodule.py +118 -0
- sage/modular/modform/tests.py +64 -0
- sage/modular/modform/theta.py +110 -0
- sage/modular/modform/vm_basis.py +380 -0
- sage/modular/modform/weight1.py +221 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2527 -0
- sage/modular/modform_hecketriangle/all.py +30 -0
- sage/modular/modform_hecketriangle/analytic_type.py +590 -0
- sage/modular/modform_hecketriangle/constructor.py +416 -0
- sage/modular/modform_hecketriangle/element.py +351 -0
- sage/modular/modform_hecketriangle/functors.py +752 -0
- sage/modular/modform_hecketriangle/graded_ring.py +541 -0
- sage/modular/modform_hecketriangle/graded_ring_element.py +2225 -0
- sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +3349 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1426 -0
- sage/modular/modform_hecketriangle/readme.py +1214 -0
- sage/modular/modform_hecketriangle/series_constructor.py +580 -0
- sage/modular/modform_hecketriangle/space.py +1037 -0
- sage/modular/modform_hecketriangle/subspace.py +423 -0
- sage/modular/modsym/all.py +17 -0
- sage/modular/modsym/ambient.py +3844 -0
- sage/modular/modsym/boundary.py +1420 -0
- sage/modular/modsym/element.py +336 -0
- sage/modular/modsym/g1list.py +178 -0
- sage/modular/modsym/ghlist.py +182 -0
- sage/modular/modsym/hecke_operator.py +73 -0
- sage/modular/modsym/manin_symbol.cpython-314t-darwin.so +0 -0
- sage/modular/modsym/manin_symbol.pxd +5 -0
- sage/modular/modsym/manin_symbol.pyx +497 -0
- sage/modular/modsym/manin_symbol_list.py +1291 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-314t-darwin.so +0 -0
- sage/modular/modsym/relation_matrix_pyx.pyx +108 -0
- sage/modular/modsym/space.py +2468 -0
- sage/modular/modsym/subspace.py +455 -0
- sage/modular/modsym/tests.py +376 -0
- sage/modular/multiple_zeta.py +2635 -0
- sage/modular/multiple_zeta_F_algebra.py +789 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1879 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +776 -0
- sage/modular/pollack_stevens/all.py +4 -0
- sage/modular/pollack_stevens/distributions.py +874 -0
- sage/modular/pollack_stevens/fund_domain.py +1572 -0
- sage/modular/pollack_stevens/manin_map.py +856 -0
- sage/modular/pollack_stevens/modsym.py +1590 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1078 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +846 -0
- sage/modular/quasimodform/ring.py +826 -0
- sage/modular/quatalg/all.py +3 -0
- sage/modular/quatalg/brandt.py +1642 -0
- sage/modular/ssmod/all.py +8 -0
- sage/modular/ssmod/ssmod.py +827 -0
- sage/rings/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/all__sagemath_schemes.py +1 -0
- sage/rings/polynomial/binary_form_reduce.py +585 -0
- sage/schemes/all.py +41 -0
- sage/schemes/berkovich/all.py +6 -0
- sage/schemes/berkovich/berkovich_cp_element.py +2582 -0
- sage/schemes/berkovich/berkovich_space.py +700 -0
- sage/schemes/curves/affine_curve.py +2924 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +397 -0
- sage/schemes/curves/curve.py +542 -0
- sage/schemes/curves/plane_curve_arrangement.py +1283 -0
- sage/schemes/curves/point.py +463 -0
- sage/schemes/curves/projective_curve.py +3203 -0
- sage/schemes/curves/weighted_projective_curve.py +106 -0
- sage/schemes/curves/zariski_vankampen.py +1931 -0
- sage/schemes/cyclic_covers/all.py +2 -0
- sage/schemes/cyclic_covers/charpoly_frobenius.py +320 -0
- sage/schemes/cyclic_covers/constructor.py +137 -0
- sage/schemes/cyclic_covers/cycliccover_finite_field.py +1309 -0
- sage/schemes/cyclic_covers/cycliccover_generic.py +310 -0
- sage/schemes/elliptic_curves/BSD.py +991 -0
- sage/schemes/elliptic_curves/Qcurves.py +592 -0
- sage/schemes/elliptic_curves/addition_formulas_ring.py +94 -0
- sage/schemes/elliptic_curves/all.py +49 -0
- sage/schemes/elliptic_curves/cardinality.py +609 -0
- sage/schemes/elliptic_curves/cm.py +1103 -0
- sage/schemes/elliptic_curves/constructor.py +1530 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3971 -0
- sage/schemes/elliptic_curves/ell_egros.py +457 -0
- sage/schemes/elliptic_curves/ell_field.py +2837 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3249 -0
- sage/schemes/elliptic_curves/ell_generic.py +3760 -0
- sage/schemes/elliptic_curves/ell_local_data.py +1207 -0
- sage/schemes/elliptic_curves/ell_modular_symbols.py +775 -0
- sage/schemes/elliptic_curves/ell_number_field.py +4220 -0
- sage/schemes/elliptic_curves/ell_padic_field.py +107 -0
- sage/schemes/elliptic_curves/ell_point.py +4944 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7184 -0
- sage/schemes/elliptic_curves/ell_tate_curve.py +671 -0
- sage/schemes/elliptic_curves/ell_torsion.py +436 -0
- sage/schemes/elliptic_curves/ell_wp.py +352 -0
- sage/schemes/elliptic_curves/formal_group.py +760 -0
- sage/schemes/elliptic_curves/gal_reps.py +1459 -0
- sage/schemes/elliptic_curves/gal_reps_number_field.py +1663 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7328 -0
- sage/schemes/elliptic_curves/height.py +2108 -0
- sage/schemes/elliptic_curves/hom.py +1788 -0
- sage/schemes/elliptic_curves/hom_composite.py +1084 -0
- sage/schemes/elliptic_curves/hom_fractional.py +544 -0
- sage/schemes/elliptic_curves/hom_frobenius.py +522 -0
- sage/schemes/elliptic_curves/hom_scalar.py +531 -0
- sage/schemes/elliptic_curves/hom_sum.py +681 -0
- sage/schemes/elliptic_curves/hom_velusqrt.py +1290 -0
- sage/schemes/elliptic_curves/homset.py +271 -0
- sage/schemes/elliptic_curves/isogeny_class.py +1523 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +247 -0
- sage/schemes/elliptic_curves/kodaira_symbol.py +344 -0
- sage/schemes/elliptic_curves/kraus.py +1014 -0
- sage/schemes/elliptic_curves/lseries_ell.py +915 -0
- sage/schemes/elliptic_curves/mod5family.py +105 -0
- sage/schemes/elliptic_curves/mod_poly.py +197 -0
- sage/schemes/elliptic_curves/mod_sym_num.cpython-314t-darwin.so +0 -0
- sage/schemes/elliptic_curves/mod_sym_num.pyx +3796 -0
- sage/schemes/elliptic_curves/modular_parametrization.py +305 -0
- sage/schemes/elliptic_curves/padic_lseries.py +1793 -0
- sage/schemes/elliptic_curves/padics.py +1816 -0
- sage/schemes/elliptic_curves/period_lattice.py +2234 -0
- sage/schemes/elliptic_curves/period_lattice_region.cpython-314t-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +716 -0
- sage/schemes/elliptic_curves/sha_tate.py +1158 -0
- sage/schemes/elliptic_curves/weierstrass_morphism.py +1117 -0
- sage/schemes/elliptic_curves/weierstrass_transform.py +200 -0
- sage/schemes/hyperelliptic_curves/all.py +6 -0
- sage/schemes/hyperelliptic_curves/constructor.py +369 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1948 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +936 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +1332 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_rational_field.py +84 -0
- sage/schemes/hyperelliptic_curves/invariants.py +410 -0
- sage/schemes/hyperelliptic_curves/jacobian_endomorphism_utils.py +312 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +437 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +878 -0
- sage/schemes/hyperelliptic_curves/kummer_surface.py +99 -0
- sage/schemes/hyperelliptic_curves/mestre.py +302 -0
- sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +3863 -0
- sage/schemes/jacobians/abstract_jacobian.py +277 -0
- sage/schemes/jacobians/all.py +2 -0
- sage/schemes/overview.py +161 -0
- sage/schemes/plane_conics/all.py +22 -0
- sage/schemes/plane_conics/con_field.py +1296 -0
- sage/schemes/plane_conics/con_finite_field.py +158 -0
- sage/schemes/plane_conics/con_number_field.py +456 -0
- sage/schemes/plane_conics/con_rational_field.py +406 -0
- sage/schemes/plane_conics/con_rational_function_field.py +581 -0
- sage/schemes/plane_conics/constructor.py +249 -0
- sage/schemes/plane_quartics/all.py +2 -0
- sage/schemes/plane_quartics/quartic_constructor.py +71 -0
- sage/schemes/plane_quartics/quartic_generic.py +53 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4177 -0
- sage_wheels/share/cremona/cremona_mini.db +0 -0
- sage_wheels/share/ellcurves/rank0 +30427 -0
- sage_wheels/share/ellcurves/rank1 +31871 -0
- sage_wheels/share/ellcurves/rank10 +6 -0
- sage_wheels/share/ellcurves/rank11 +6 -0
- sage_wheels/share/ellcurves/rank12 +1 -0
- sage_wheels/share/ellcurves/rank14 +1 -0
- sage_wheels/share/ellcurves/rank15 +1 -0
- sage_wheels/share/ellcurves/rank17 +1 -0
- sage_wheels/share/ellcurves/rank19 +1 -0
- sage_wheels/share/ellcurves/rank2 +2388 -0
- sage_wheels/share/ellcurves/rank20 +1 -0
- sage_wheels/share/ellcurves/rank21 +1 -0
- sage_wheels/share/ellcurves/rank22 +1 -0
- sage_wheels/share/ellcurves/rank23 +1 -0
- sage_wheels/share/ellcurves/rank24 +1 -0
- sage_wheels/share/ellcurves/rank28 +1 -0
- sage_wheels/share/ellcurves/rank3 +836 -0
- sage_wheels/share/ellcurves/rank4 +10 -0
- sage_wheels/share/ellcurves/rank5 +5 -0
- sage_wheels/share/ellcurves/rank6 +5 -0
- sage_wheels/share/ellcurves/rank7 +5 -0
- sage_wheels/share/ellcurves/rank8 +6 -0
- sage_wheels/share/ellcurves/rank9 +7 -0
|
@@ -0,0 +1,1169 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
r"""
|
|
3
|
+
Sage functions to compute minimal models of rational functions
|
|
4
|
+
under the conjugation action of `PGL_2(QQ)`.
|
|
5
|
+
|
|
6
|
+
AUTHORS:
|
|
7
|
+
|
|
8
|
+
- Alex Molnar (May 22, 2012)
|
|
9
|
+
|
|
10
|
+
- Brian Stout, Ben Hutz (Nov 2013): Modified code to use projective
|
|
11
|
+
morphism functionality so that it can be included in Sage.
|
|
12
|
+
|
|
13
|
+
REFERENCES: [BM2012]_, [Mol2015]_
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# ****************************************************************************
|
|
17
|
+
# Copyright (C) 2012 Alexander Molnar
|
|
18
|
+
#
|
|
19
|
+
# This program is free software: you can redistribute it and/or modify
|
|
20
|
+
# it under the terms of the GNU General Public License as published by
|
|
21
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
22
|
+
# (at your option) any later version.
|
|
23
|
+
# https://www.gnu.org/licenses/
|
|
24
|
+
# ****************************************************************************
|
|
25
|
+
|
|
26
|
+
from copy import copy
|
|
27
|
+
|
|
28
|
+
from sage.arith.misc import gcd
|
|
29
|
+
from sage.functions.hyperbolic import cosh
|
|
30
|
+
from sage.matrix.constructor import matrix
|
|
31
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
32
|
+
from sage.rings.cc import CC
|
|
33
|
+
from sage.rings.complex_mpfr import ComplexField
|
|
34
|
+
from sage.rings.finite_rings.integer_mod_ring import Zmod
|
|
35
|
+
from sage.rings.integer_ring import ZZ
|
|
36
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
37
|
+
from sage.rings.polynomial.binary_form_reduce import covariant_z0, epsinv
|
|
38
|
+
from sage.rings.rational_field import QQ
|
|
39
|
+
from sage.schemes.affine.affine_space import AffineSpace
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def bCheck(c, v, p, b):
|
|
43
|
+
r"""
|
|
44
|
+
Compute a lower bound on the value of ``b``.
|
|
45
|
+
|
|
46
|
+
This value is needed for a transformation
|
|
47
|
+
`A(z) = z*p^k + b` to satisfy `ord_p(Res(\phi^A)) < ord_p(Res(\phi))` for a
|
|
48
|
+
rational map `\phi`. See Theorem 3.3.5 in [Molnar]_.
|
|
49
|
+
|
|
50
|
+
INPUT:
|
|
51
|
+
|
|
52
|
+
- ``c`` -- list of polynomials in `b`. See v for their use
|
|
53
|
+
|
|
54
|
+
- ``v`` -- list of rational numbers, where we are considering the inequalities
|
|
55
|
+
`ord_p(c[i]) > v[i]`
|
|
56
|
+
|
|
57
|
+
- ``p`` -- a prime
|
|
58
|
+
|
|
59
|
+
- ``b`` -- local variable
|
|
60
|
+
|
|
61
|
+
OUTPUT: ``bval`` -- integer; lower bound in Theorem 3.3.5
|
|
62
|
+
|
|
63
|
+
EXAMPLES::
|
|
64
|
+
|
|
65
|
+
sage: R.<b> = PolynomialRing(QQ)
|
|
66
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import bCheck
|
|
67
|
+
sage: bCheck(11664*b^2 + 70227*b + 76059, 15/2, 3, b)
|
|
68
|
+
-1
|
|
69
|
+
"""
|
|
70
|
+
val = (v + 1).floor()
|
|
71
|
+
deg = c.degree()
|
|
72
|
+
coeffs = c.coefficients(sparse=False)
|
|
73
|
+
lcoeff = coeffs[deg]
|
|
74
|
+
coeffs.remove(lcoeff)
|
|
75
|
+
check1 = [(coeffs[i].valuation(p) - lcoeff.valuation(p))/(deg - i)
|
|
76
|
+
for i in range(len(coeffs)) if coeffs[i] != 0]
|
|
77
|
+
check2 = (val - lcoeff.valuation(p))/deg
|
|
78
|
+
check1.append(check2)
|
|
79
|
+
bval = min(check1)
|
|
80
|
+
return (bval).ceil()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def scale(c, v, p):
|
|
84
|
+
r"""
|
|
85
|
+
Create scaled integer polynomial with respect to prime ``p``.
|
|
86
|
+
|
|
87
|
+
Given an integral polynomial ``c``, we can write `c = p^i*c'`, where ``p`` does not
|
|
88
|
+
divide ``c``. Return ``c'`` and `v - i` where `i` is the smallest valuation of the
|
|
89
|
+
coefficients of `c`.
|
|
90
|
+
|
|
91
|
+
INPUT:
|
|
92
|
+
|
|
93
|
+
- ``c`` -- integer polynomial
|
|
94
|
+
|
|
95
|
+
- ``v`` -- integer; the bound on the exponent from :func:`blift`
|
|
96
|
+
|
|
97
|
+
- ``p`` -- a prime
|
|
98
|
+
|
|
99
|
+
OUTPUT:
|
|
100
|
+
|
|
101
|
+
- boolean -- the new exponent bound is 0 or negative
|
|
102
|
+
|
|
103
|
+
- the scaled integer polynomial
|
|
104
|
+
|
|
105
|
+
- an integer the new exponent bound
|
|
106
|
+
|
|
107
|
+
EXAMPLES::
|
|
108
|
+
|
|
109
|
+
sage: R.<b> = PolynomialRing(QQ)
|
|
110
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import scale
|
|
111
|
+
sage: scale(24*b^3 + 108*b^2 + 162*b + 81, 1, 3)
|
|
112
|
+
[False, 8*b^3 + 36*b^2 + 54*b + 27, 0]
|
|
113
|
+
"""
|
|
114
|
+
scaleval = min([coeff.valuation(p) for coeff in c.coefficients()])
|
|
115
|
+
if scaleval > 0:
|
|
116
|
+
c = c/(p**scaleval)
|
|
117
|
+
v = v - scaleval
|
|
118
|
+
if v <= 0:
|
|
119
|
+
flag = False
|
|
120
|
+
else:
|
|
121
|
+
flag = True
|
|
122
|
+
return [flag, c, v]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def blift(LF, Li, p, k, S=None, all_orbits=False):
|
|
126
|
+
r"""
|
|
127
|
+
Search for a solution to the given list of inequalities.
|
|
128
|
+
|
|
129
|
+
If found, lift the solution to
|
|
130
|
+
an appropriate valuation. See Lemma 3.3.6 in [Molnar]_
|
|
131
|
+
|
|
132
|
+
INPUT:
|
|
133
|
+
|
|
134
|
+
- ``LF`` -- list of integer polynomials in one variable (the normalized coefficients)
|
|
135
|
+
|
|
136
|
+
- ``Li`` -- integer; the bound on coefficients
|
|
137
|
+
|
|
138
|
+
- ``p`` -- a prime
|
|
139
|
+
|
|
140
|
+
- ``k`` -- the scaling factor that makes the solution a ``p``-adic integer
|
|
141
|
+
|
|
142
|
+
- ``S`` -- polynomial ring to use
|
|
143
|
+
|
|
144
|
+
- ``all_orbits`` -- boolean; whether or not to use ``==`` in the
|
|
145
|
+
inequalities to find all orbits
|
|
146
|
+
|
|
147
|
+
OUTPUT:
|
|
148
|
+
|
|
149
|
+
- boolean -- whether or not the lift is successful
|
|
150
|
+
|
|
151
|
+
- integer -- the lift
|
|
152
|
+
|
|
153
|
+
EXAMPLES::
|
|
154
|
+
|
|
155
|
+
sage: R.<b> = PolynomialRing(QQ)
|
|
156
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import blift
|
|
157
|
+
sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341,
|
|
158
|
+
....: -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3, 2)
|
|
159
|
+
[[True, 4]]
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
P = LF[0].parent()
|
|
163
|
+
#Determine which inequalities are trivial, and scale the rest, so that we only lift
|
|
164
|
+
#as many times as needed.
|
|
165
|
+
keepScaledIneqs = [scale(P(coeff),Li,p) for coeff in LF if coeff != 0]
|
|
166
|
+
keptVals = [i[2] for i in keepScaledIneqs if i[0]]
|
|
167
|
+
if keptVals:
|
|
168
|
+
# Determine the valuation to lift until.
|
|
169
|
+
# liftval = max(keptVals)
|
|
170
|
+
pass
|
|
171
|
+
else:
|
|
172
|
+
#All inequalities are satisfied.
|
|
173
|
+
if all_orbits:
|
|
174
|
+
return [[True, t] for t in range(p)]
|
|
175
|
+
return [[True, 1]]
|
|
176
|
+
if S is None:
|
|
177
|
+
S = PolynomialRing(Zmod(p), 'b')
|
|
178
|
+
keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]]
|
|
179
|
+
#We need a solution for each polynomial on the left hand side of the inequalities,
|
|
180
|
+
#so we need only find a solution for their gcd.
|
|
181
|
+
g = gcd(keptScaledIneqs)
|
|
182
|
+
rts = g.roots(multiplicities=False)
|
|
183
|
+
good = []
|
|
184
|
+
for r in rts:
|
|
185
|
+
#Recursively try to lift each root
|
|
186
|
+
r_initial = QQ(r)
|
|
187
|
+
newInput = P([r_initial, p])
|
|
188
|
+
LG = [F(newInput) for F in LF]
|
|
189
|
+
new_good = blift(LG, Li, p, k, S=S)
|
|
190
|
+
for lift,lifted in new_good:
|
|
191
|
+
if lift:
|
|
192
|
+
#Lift successful.
|
|
193
|
+
if not all_orbits:
|
|
194
|
+
return [[True, r_initial + p*lifted]]
|
|
195
|
+
|
|
196
|
+
#only need up to SL(2,ZZ) equivalence
|
|
197
|
+
#this helps control the size of the resulting coefficients
|
|
198
|
+
if r_initial + p*lifted < p**k:
|
|
199
|
+
good.append([True, r_initial + p*lifted])
|
|
200
|
+
else:
|
|
201
|
+
new_r = r_initial + p*lifted - p**k
|
|
202
|
+
while new_r > p**k:
|
|
203
|
+
new_r -= p**k
|
|
204
|
+
if [True, new_r] not in good:
|
|
205
|
+
good.append([True, new_r])
|
|
206
|
+
if good:
|
|
207
|
+
return good
|
|
208
|
+
#Lift non successful.
|
|
209
|
+
return [[False,0]]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def affine_minimal(vp, return_transformation=False, D=None, quick=False):
|
|
213
|
+
r"""
|
|
214
|
+
Determine if given map is affine minimal.
|
|
215
|
+
|
|
216
|
+
Given vp a scheme morphism on the projective line over the rationals,
|
|
217
|
+
this procedure determines if `\phi` is minimal. In particular, it determines
|
|
218
|
+
if the map is affine minimal, which is enough to decide if it is minimal
|
|
219
|
+
or not. See Proposition 2.10 in [BM2012]_.
|
|
220
|
+
|
|
221
|
+
INPUT:
|
|
222
|
+
|
|
223
|
+
- ``vp`` -- dynamical system on the projective line
|
|
224
|
+
|
|
225
|
+
- ``D`` -- list of primes, in case one only wants to check minimality
|
|
226
|
+
at those specific primes
|
|
227
|
+
|
|
228
|
+
- ``return_transformation`` -- boolean (default: ``False``); this
|
|
229
|
+
signals a return of the `PGL_2` transformation to conjugate
|
|
230
|
+
this map to the calculated models
|
|
231
|
+
|
|
232
|
+
- ``quick`` -- boolean value. If true the algorithm terminates once
|
|
233
|
+
algorithm determines F/G is not minimal, otherwise algorithm only
|
|
234
|
+
terminates once a minimal model has been found
|
|
235
|
+
|
|
236
|
+
OUTPUT: ``newvp`` -- dynamical system on the projective line
|
|
237
|
+
|
|
238
|
+
- ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp``
|
|
239
|
+
|
|
240
|
+
EXAMPLES::
|
|
241
|
+
|
|
242
|
+
sage: PS.<X,Y> = ProjectiveSpace(QQ, 1)
|
|
243
|
+
sage: vp = DynamicalSystem_projective([X^2 + 9*Y^2, X*Y])
|
|
244
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import affine_minimal
|
|
245
|
+
sage: affine_minimal(vp, True)
|
|
246
|
+
(
|
|
247
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
248
|
+
Defn: Defined on coordinates by sending (X : Y) to (X^2 + Y^2 : X*Y)
|
|
249
|
+
,
|
|
250
|
+
[3 0]
|
|
251
|
+
[0 1]
|
|
252
|
+
)
|
|
253
|
+
"""
|
|
254
|
+
from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine
|
|
255
|
+
BR = vp.domain().base_ring()
|
|
256
|
+
conj = matrix(BR, 2, 2, 1)
|
|
257
|
+
flag = True
|
|
258
|
+
vp.normalize_coordinates()
|
|
259
|
+
d = vp.degree()
|
|
260
|
+
|
|
261
|
+
Affvp = vp.dehomogenize(1)
|
|
262
|
+
R = Affvp.coordinate_ring()
|
|
263
|
+
if R.is_field():
|
|
264
|
+
#want the polynomial ring not the fraction field
|
|
265
|
+
R = R.ring()
|
|
266
|
+
F = R(Affvp[0].numerator())
|
|
267
|
+
G = R(Affvp[0].denominator())
|
|
268
|
+
if R(G.degree()) == 0 or R(F.degree()) == 0:
|
|
269
|
+
raise TypeError("affine minimality is only considered for maps not of the form f or 1/f for a polynomial f")
|
|
270
|
+
|
|
271
|
+
z = F.parent().gen(0)
|
|
272
|
+
minG = G
|
|
273
|
+
#If the valuation of a prime in the resultant is small enough, we can say the
|
|
274
|
+
#map is affine minimal at that prime without using the local minimality loop. See
|
|
275
|
+
#Theorem 3.2.2 in [Molnar, M.Sc. thesis]
|
|
276
|
+
if d % 2 == 0:
|
|
277
|
+
g = d
|
|
278
|
+
else:
|
|
279
|
+
g = 2*d
|
|
280
|
+
Res = vp.resultant()
|
|
281
|
+
|
|
282
|
+
#Some quantities needed for the local minimization loop, but we compute now
|
|
283
|
+
#since the value is constant, so we do not wish to compute in every local loop.
|
|
284
|
+
#See Theorem 3.3.3 in [Molnar, M.Sc thesis]
|
|
285
|
+
H = F - z * minG
|
|
286
|
+
A = AffineSpace(BR, 1, H.parent().variable_name())
|
|
287
|
+
ubRes = DynamicalSystem_affine([H/minG], domain=A).homogenize(1).resultant()
|
|
288
|
+
#Set the primes to check minimality at, if not already prescribed
|
|
289
|
+
if D is None:
|
|
290
|
+
D = ZZ(Res).prime_divisors()
|
|
291
|
+
|
|
292
|
+
#Check minimality at all primes in D. If D is all primes dividing
|
|
293
|
+
#Res(minF/minG), this is enough to show whether minF/minG is minimal or not. See
|
|
294
|
+
#Propositions 3.2.1 and 3.3.7 in [Molnar, M.Sc. thesis].
|
|
295
|
+
for p in D:
|
|
296
|
+
while True:
|
|
297
|
+
if Res.valuation(p) < g:
|
|
298
|
+
#The model is minimal at p
|
|
299
|
+
min = True
|
|
300
|
+
else:
|
|
301
|
+
#The model may not be minimal at p.
|
|
302
|
+
newvp,conj = Min(vp, p, ubRes, conj, all_orbits=False)
|
|
303
|
+
if newvp == vp:
|
|
304
|
+
min = True
|
|
305
|
+
else:
|
|
306
|
+
vp = newvp
|
|
307
|
+
Affvp = vp.dehomogenize(1)
|
|
308
|
+
min = False
|
|
309
|
+
if min:
|
|
310
|
+
#The model is minimal at p
|
|
311
|
+
break
|
|
312
|
+
elif F == Affvp[0].numerator() and G == Affvp[0].denominator():
|
|
313
|
+
#The model is minimal at p
|
|
314
|
+
break
|
|
315
|
+
else:
|
|
316
|
+
#The model is not minimal at p
|
|
317
|
+
flag = False
|
|
318
|
+
if quick:
|
|
319
|
+
break
|
|
320
|
+
if quick and not flag:
|
|
321
|
+
break
|
|
322
|
+
|
|
323
|
+
if quick: #only return whether the model is minimal
|
|
324
|
+
return flag
|
|
325
|
+
|
|
326
|
+
if return_transformation:
|
|
327
|
+
return vp, conj
|
|
328
|
+
return vp
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def Min(Fun, p, ubRes, conj, all_orbits=False):
|
|
332
|
+
r"""
|
|
333
|
+
Local loop for :func:`affine_minimal`, where we check minimality at the prime `p`.
|
|
334
|
+
|
|
335
|
+
First we bound the possible `k` in our transformations `A = zp^k + b`.
|
|
336
|
+
See Theorems 3.3.2 and 3.3.3 in [Molnar]_.
|
|
337
|
+
|
|
338
|
+
INPUT:
|
|
339
|
+
|
|
340
|
+
- ``Fun`` -- a dynamical system on projective space
|
|
341
|
+
|
|
342
|
+
- ``p`` -- a prime
|
|
343
|
+
|
|
344
|
+
- ``ubRes`` -- integer; the upper bound needed for Th. 3.3.3 in [Molnar]_
|
|
345
|
+
|
|
346
|
+
- ``conj`` -- a 2x2 matrix keeping track of the conjugation
|
|
347
|
+
|
|
348
|
+
- ``all_orbits`` -- boolean; whether or not to use ``==`` in the
|
|
349
|
+
inequalities to find all orbits
|
|
350
|
+
|
|
351
|
+
OUTPUT:
|
|
352
|
+
|
|
353
|
+
- boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise
|
|
354
|
+
|
|
355
|
+
- a dynamical system on projective space minimal at ``p``
|
|
356
|
+
|
|
357
|
+
EXAMPLES::
|
|
358
|
+
|
|
359
|
+
sage: P.<x,y> = ProjectiveSpace(QQ, 1)
|
|
360
|
+
sage: f = DynamicalSystem_projective([149*x^2 + 39*x*y + y^2,
|
|
361
|
+
....: -8*x^2 + 137*x*y + 33*y^2])
|
|
362
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import Min
|
|
363
|
+
sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]]))
|
|
364
|
+
[
|
|
365
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
366
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
367
|
+
(157*x^2 + 72*x*y + 3*y^2 : -24*x^2 + 121*x*y + 54*y^2) ,
|
|
368
|
+
<BLANKLINE>
|
|
369
|
+
[3 1]
|
|
370
|
+
[0 1]
|
|
371
|
+
]
|
|
372
|
+
"""
|
|
373
|
+
d = Fun.degree()
|
|
374
|
+
AffFun = Fun.dehomogenize(1)
|
|
375
|
+
R = AffFun.coordinate_ring()
|
|
376
|
+
if R.is_field():
|
|
377
|
+
#want the polynomial ring not the fraction field
|
|
378
|
+
R = R.ring()
|
|
379
|
+
F = R(AffFun[0].numerator())
|
|
380
|
+
G = R(AffFun[0].denominator())
|
|
381
|
+
dG = G.degree()
|
|
382
|
+
# all_orbits scales bounds for >= and <= if searching for orbits instead of min model
|
|
383
|
+
if dG > (d+1)/2:
|
|
384
|
+
lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor() - int(all_orbits)
|
|
385
|
+
else:
|
|
386
|
+
lowerBound = (-2*(F[d]).valuation(p)/(d-1) + 1).floor() - int(all_orbits)
|
|
387
|
+
upperBound = 2*(ubRes.valuation(p)) + int(all_orbits)
|
|
388
|
+
|
|
389
|
+
if upperBound < lowerBound:
|
|
390
|
+
# There are no possible transformations to reduce the resultant.
|
|
391
|
+
if not all_orbits:
|
|
392
|
+
return [Fun, conj]
|
|
393
|
+
return []
|
|
394
|
+
# Looping over each possible k, we search for transformations to reduce
|
|
395
|
+
# the resultant of F/G
|
|
396
|
+
all_found = []
|
|
397
|
+
k = lowerBound
|
|
398
|
+
Qb = PolynomialRing(QQ,'b')
|
|
399
|
+
b = Qb.gen(0)
|
|
400
|
+
Q = PolynomialRing(Qb,'z')
|
|
401
|
+
z = Q.gen(0)
|
|
402
|
+
while k <= upperBound:
|
|
403
|
+
A = (p**k)*z + b
|
|
404
|
+
Ft = Q(F(A) - b*G(A))
|
|
405
|
+
Gt = Q((p**k)*G(A))
|
|
406
|
+
Fcoeffs = Ft.coefficients(sparse=False)
|
|
407
|
+
Gcoeffs = Gt.coefficients(sparse=False)
|
|
408
|
+
coeffs = Fcoeffs + Gcoeffs
|
|
409
|
+
RHS = (d + 1) * k / 2
|
|
410
|
+
# If there is some b such that Res(phi^A) < Res(phi), we must have
|
|
411
|
+
# ord_p(c) > RHS for each c in coeffs.
|
|
412
|
+
# Make sure constant coefficients in coeffs satisfy the inequality.
|
|
413
|
+
if all(QQ(c).valuation(p) > RHS - int(all_orbits)
|
|
414
|
+
for c in coeffs if c.degree() == 0):
|
|
415
|
+
# Constant coefficients in coeffs have large enough valuation, so
|
|
416
|
+
# check the rest. We start by checking if simply picking b=0 works.
|
|
417
|
+
if all(c(0).valuation(p) > RHS - int(all_orbits) for c in coeffs):
|
|
418
|
+
# A = z*p^k satisfies the inequalities, and F/G is not minimal
|
|
419
|
+
# "Conjugating by", p,"^", k, "*z +", 0
|
|
420
|
+
newconj = matrix(QQ, 2, 2, [p**k, 0, 0, 1])
|
|
421
|
+
minFun = Fun.conjugate(newconj)
|
|
422
|
+
minFun.normalize_coordinates()
|
|
423
|
+
if not all_orbits:
|
|
424
|
+
return [minFun, conj*newconj]
|
|
425
|
+
all_found.append([p, k, 0])
|
|
426
|
+
|
|
427
|
+
# Otherwise we search if any value of b will work. We start by
|
|
428
|
+
# finding a minimum bound on the valuation of b that is necessary.
|
|
429
|
+
# See Theorem 3.3.5 in [Molnar, M.Sc. thesis].
|
|
430
|
+
bval = max(bCheck(coeff, RHS, p, b) for coeff in coeffs if coeff.degree() > 0)
|
|
431
|
+
|
|
432
|
+
# We scale the coefficients in coeffs, so that we may assume
|
|
433
|
+
# ord_p(b) is at least 0
|
|
434
|
+
scaledCoeffs = [coeff(b*(p**bval)) for coeff in coeffs]
|
|
435
|
+
|
|
436
|
+
# We now scale the inequalities, ord_p(coeff) > RHS, so that
|
|
437
|
+
# coeff is in ZZ[b]
|
|
438
|
+
scale = QQ(max(coeff.denominator() for coeff in scaledCoeffs))
|
|
439
|
+
normalizedCoeffs = [coeff * scale for coeff in scaledCoeffs]
|
|
440
|
+
scaleRHS = RHS + scale.valuation(p)
|
|
441
|
+
|
|
442
|
+
# We now search for integers that satisfy the inequality
|
|
443
|
+
# ord_p(coeff) > RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis].
|
|
444
|
+
bound = (scaleRHS + 1 - int(all_orbits)).floor()
|
|
445
|
+
all_blift = blift(normalizedCoeffs, bound, p, k, all_orbits=all_orbits)
|
|
446
|
+
|
|
447
|
+
# If bool is true after lifting, we have a solution b, and F/G
|
|
448
|
+
# is not minimal.
|
|
449
|
+
for boolval, sol in all_blift:
|
|
450
|
+
if boolval:
|
|
451
|
+
#Rescale, conjugate and return new map
|
|
452
|
+
bsol = QQ(sol * (p**bval))
|
|
453
|
+
#only add 'minimal orbit element'
|
|
454
|
+
while bsol.abs() >= p**k:
|
|
455
|
+
if bsol < 0:
|
|
456
|
+
bsol += p**k
|
|
457
|
+
else:
|
|
458
|
+
bsol -= p**k
|
|
459
|
+
#"Conjugating by ", p,"^", k, "*z +", bsol
|
|
460
|
+
newconj = matrix(QQ, 2, 2, [p**k, bsol, 0, 1])
|
|
461
|
+
minFun = Fun.conjugate(newconj)
|
|
462
|
+
|
|
463
|
+
minFun.normalize_coordinates()
|
|
464
|
+
if not all_orbits:
|
|
465
|
+
return [minFun, conj*newconj]
|
|
466
|
+
if [p,k,bsol] not in all_found:
|
|
467
|
+
all_found.append([p, k, bsol])
|
|
468
|
+
k = k + 1
|
|
469
|
+
if not all_orbits:
|
|
470
|
+
return [Fun, conj]
|
|
471
|
+
return all_found
|
|
472
|
+
|
|
473
|
+
###################################################
|
|
474
|
+
# algorithms from Hutz-Stoll
|
|
475
|
+
###################################################
|
|
476
|
+
|
|
477
|
+
#modification of Bruin-Molnar for all representatives
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def BM_all_minimal(vp, return_transformation=False, D=None):
|
|
481
|
+
r"""
|
|
482
|
+
Determine a representative in each `SL(2,\ZZ)` orbit with minimal
|
|
483
|
+
resultant.
|
|
484
|
+
|
|
485
|
+
This function modifies the Bruin-Molnar algorithm ([BM2012]_) to solve
|
|
486
|
+
in the inequalities as ``<=`` instead of ``<``. Among the list of
|
|
487
|
+
solutions is all conjugations that preserve the resultant. From that
|
|
488
|
+
list the `SL(2,\ZZ)` orbits are identified and one representative from
|
|
489
|
+
each orbit is returned. This function assumes that the given model is
|
|
490
|
+
a minimal model.
|
|
491
|
+
|
|
492
|
+
INPUT:
|
|
493
|
+
|
|
494
|
+
- ``vp`` -- a minimal model of a dynamical system on the projective line
|
|
495
|
+
|
|
496
|
+
- ``return_transformation`` -- boolean (default: ``False``); this
|
|
497
|
+
signals a return of the ``PGL_2`` transformation to conjugate ``vp``
|
|
498
|
+
to the calculated minimal model
|
|
499
|
+
|
|
500
|
+
- ``D`` -- list of primes, in case one only wants to check minimality
|
|
501
|
+
at those specific primes
|
|
502
|
+
|
|
503
|
+
OUTPUT:
|
|
504
|
+
|
|
505
|
+
List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a
|
|
506
|
+
`2 \times 2` matrix.
|
|
507
|
+
|
|
508
|
+
EXAMPLES::
|
|
509
|
+
|
|
510
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
511
|
+
sage: f = DynamicalSystem([x^3 - 13^2*y^3, x*y^2])
|
|
512
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal
|
|
513
|
+
sage: BM_all_minimal(f)
|
|
514
|
+
[Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
515
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
516
|
+
(x^3 - 169*y^3 : x*y^2),
|
|
517
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
518
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
519
|
+
(13*x^3 - y^3 : x*y^2)]
|
|
520
|
+
|
|
521
|
+
::
|
|
522
|
+
|
|
523
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
524
|
+
sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2])
|
|
525
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal
|
|
526
|
+
sage: BM_all_minimal(f, D=[3])
|
|
527
|
+
[Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
528
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
529
|
+
(x^3 - 36*y^3 : x*y^2),
|
|
530
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
531
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
532
|
+
(3*x^3 - 4*y^3 : x*y^2)]
|
|
533
|
+
|
|
534
|
+
::
|
|
535
|
+
|
|
536
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
537
|
+
sage: f = DynamicalSystem([x^3 - 4^2*y^3, x*y^2])
|
|
538
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal
|
|
539
|
+
sage: cl = BM_all_minimal(f, return_transformation=True)
|
|
540
|
+
sage: all(f.conjugate(m) == g for g, m in cl)
|
|
541
|
+
True
|
|
542
|
+
"""
|
|
543
|
+
mp = copy(vp)
|
|
544
|
+
mp.normalize_coordinates()
|
|
545
|
+
BR = mp.domain().base_ring()
|
|
546
|
+
MS = MatrixSpace(QQ, 2)
|
|
547
|
+
M_Id = MS.one()
|
|
548
|
+
F, G = list(mp) #coordinate polys
|
|
549
|
+
aff_map = mp.dehomogenize(1)
|
|
550
|
+
f, g = aff_map[0].numerator(), aff_map[0].denominator()
|
|
551
|
+
z = aff_map.domain().gen(0)
|
|
552
|
+
Res = mp.resultant()
|
|
553
|
+
|
|
554
|
+
# because of how the bound is compute in lemma 3.3
|
|
555
|
+
from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine
|
|
556
|
+
h = f - z*g
|
|
557
|
+
A = AffineSpace(BR, 1, h.parent().variable_name())
|
|
558
|
+
res = DynamicalSystem_affine([h/g], domain=A).homogenize(1).resultant()
|
|
559
|
+
|
|
560
|
+
if D is None:
|
|
561
|
+
D = ZZ(Res).prime_divisors()
|
|
562
|
+
|
|
563
|
+
# get the conjugations for each prime independently
|
|
564
|
+
# these are returning (p,k,b) so that the matrix is [p^k,b,0,1]
|
|
565
|
+
all_pM = []
|
|
566
|
+
for p in D:
|
|
567
|
+
# all_orbits used to scale inequalities to equalities
|
|
568
|
+
all_pM.append(Min(mp, p, res, M_Id, all_orbits=True))
|
|
569
|
+
# need the identity for each prime
|
|
570
|
+
if [p, 0, 0] not in all_pM[-1]:
|
|
571
|
+
all_pM[-1].append([p, 0, 0])
|
|
572
|
+
|
|
573
|
+
#combine conjugations for all primes
|
|
574
|
+
all_M = [M_Id]
|
|
575
|
+
for prime_data in all_pM:
|
|
576
|
+
#these are (p,k,b) so that the matrix is [p^k,b,0,1]
|
|
577
|
+
new_M = []
|
|
578
|
+
if prime_data:
|
|
579
|
+
p = prime_data[0][0]
|
|
580
|
+
for m in prime_data:
|
|
581
|
+
mat = MS([m[0]**m[1], m[2], 0, 1])
|
|
582
|
+
new_map = mp.conjugate(mat)
|
|
583
|
+
new_map.normalize_coordinates()
|
|
584
|
+
# make sure the resultant didn't change and that it is a different SL(2,ZZ) orbit
|
|
585
|
+
if (mat == M_Id) or (new_map.resultant().valuation(p) == Res.valuation(p)
|
|
586
|
+
and mat.det() not in [1,-1]):
|
|
587
|
+
new_M.append(m)
|
|
588
|
+
if new_M:
|
|
589
|
+
all_M = [m1 * MS([m[0]**m[1], m[2], 0, 1])
|
|
590
|
+
for m1 in all_M for m in new_M]
|
|
591
|
+
|
|
592
|
+
#get all models with same resultant
|
|
593
|
+
all_maps = []
|
|
594
|
+
for M in all_M:
|
|
595
|
+
new_map = mp.conjugate(M)
|
|
596
|
+
new_map.normalize_coordinates()
|
|
597
|
+
if [new_map, M] not in all_maps:
|
|
598
|
+
all_maps.append([new_map, M])
|
|
599
|
+
|
|
600
|
+
#Split into conjugacy classes
|
|
601
|
+
#We just keep track of the two matrices that come from
|
|
602
|
+
#the original to get the conjugation that goes between these!!
|
|
603
|
+
classes = []
|
|
604
|
+
for funct, mat in all_maps:
|
|
605
|
+
if not classes:
|
|
606
|
+
classes.append([funct, mat])
|
|
607
|
+
else:
|
|
608
|
+
found = False
|
|
609
|
+
for Func, Mat in classes:
|
|
610
|
+
#get conjugation
|
|
611
|
+
M = mat.inverse() * Mat
|
|
612
|
+
assert funct.conjugate(M) == Func
|
|
613
|
+
if M.det() in [1,-1]:
|
|
614
|
+
#same SL(2,Z) orbit
|
|
615
|
+
found = True
|
|
616
|
+
break
|
|
617
|
+
if found is False:
|
|
618
|
+
classes.append([funct, mat])
|
|
619
|
+
|
|
620
|
+
if return_transformation:
|
|
621
|
+
return classes
|
|
622
|
+
else:
|
|
623
|
+
return [funct for funct, matr in classes]
|
|
624
|
+
|
|
625
|
+
###################################################
|
|
626
|
+
# enumerative algorithms from Hutz-Stoll
|
|
627
|
+
###################################################
|
|
628
|
+
|
|
629
|
+
#find minimal model
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def HS_minimal(f, return_transformation=False, D=None):
|
|
633
|
+
r"""
|
|
634
|
+
Compute a minimal model for the given projective dynamical system.
|
|
635
|
+
|
|
636
|
+
This function implements the algorithm in Hutz-Stoll [HS2018]_.
|
|
637
|
+
A representative with minimal resultant in the conjugacy class
|
|
638
|
+
of ``f`` returned.
|
|
639
|
+
|
|
640
|
+
INPUT:
|
|
641
|
+
|
|
642
|
+
- ``f`` -- dynamical system on the projective line with minimal resultant
|
|
643
|
+
|
|
644
|
+
- ``return_transformation`` -- boolean (default: ``False``); this
|
|
645
|
+
signals a return of the `PGL_2` transformation to conjugate
|
|
646
|
+
this map to the calculated models
|
|
647
|
+
|
|
648
|
+
- ``D`` -- list of primes, in case one only wants to check minimality
|
|
649
|
+
at those specific primes
|
|
650
|
+
|
|
651
|
+
OUTPUT:
|
|
652
|
+
|
|
653
|
+
- a dynamical system
|
|
654
|
+
- (optional) a `2 \times 2` matrix
|
|
655
|
+
|
|
656
|
+
EXAMPLES::
|
|
657
|
+
|
|
658
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
659
|
+
sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y])
|
|
660
|
+
sage: m = matrix(QQ,2,2,[5,1,0,1])
|
|
661
|
+
sage: g = f.conjugate(m)
|
|
662
|
+
sage: g.normalize_coordinates()
|
|
663
|
+
sage: g.resultant().factor()
|
|
664
|
+
2^4 * 3^4 * 5^12
|
|
665
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_minimal
|
|
666
|
+
sage: HS_minimal(g).resultant().factor()
|
|
667
|
+
2^4 * 3^4
|
|
668
|
+
sage: HS_minimal(g, D=[2]).resultant().factor()
|
|
669
|
+
2^4 * 3^4 * 5^12
|
|
670
|
+
sage: F,m = HS_minimal(g, return_transformation=True)
|
|
671
|
+
sage: g.conjugate(m) == F
|
|
672
|
+
True
|
|
673
|
+
"""
|
|
674
|
+
F = copy(f)
|
|
675
|
+
d = F.degree()
|
|
676
|
+
F.normalize_coordinates()
|
|
677
|
+
MS = MatrixSpace(ZZ, 2, 2)
|
|
678
|
+
m = MS.one()
|
|
679
|
+
res = ZZ(F.resultant())
|
|
680
|
+
if D is None:
|
|
681
|
+
D = res.prime_divisors()
|
|
682
|
+
|
|
683
|
+
# minimize for each prime
|
|
684
|
+
for p in D:
|
|
685
|
+
vp = res.valuation(p)
|
|
686
|
+
minimal = False
|
|
687
|
+
while not minimal:
|
|
688
|
+
if (d % 2 == 0 and vp < d) or (d % 2 == 1 and vp < 2 * d):
|
|
689
|
+
# must be minimal
|
|
690
|
+
minimal = True
|
|
691
|
+
break
|
|
692
|
+
minimal = True
|
|
693
|
+
t = MS([1, 0, 0, p])
|
|
694
|
+
F1 = F.conjugate(t)
|
|
695
|
+
F1.normalize_coordinates()
|
|
696
|
+
res1 = F1.resultant()
|
|
697
|
+
vp1 = res1.valuation(p)
|
|
698
|
+
if vp1 < vp: # check if smaller
|
|
699
|
+
F = F1
|
|
700
|
+
vp = vp1
|
|
701
|
+
m = m * t # keep track of conjugation
|
|
702
|
+
minimal = False
|
|
703
|
+
else:
|
|
704
|
+
# still search for smaller
|
|
705
|
+
for b in range(p):
|
|
706
|
+
t = matrix(ZZ,2,2,[p, b, 0, 1])
|
|
707
|
+
F1 = F.conjugate(t)
|
|
708
|
+
F1.normalize_coordinates()
|
|
709
|
+
res1 = ZZ(F1.resultant())
|
|
710
|
+
vp1 = res1.valuation(p)
|
|
711
|
+
if vp1 < vp: # check if smaller
|
|
712
|
+
F = F1
|
|
713
|
+
m = m * t # keep track of transformation
|
|
714
|
+
minimal = False
|
|
715
|
+
vp = vp1
|
|
716
|
+
break # exit for loop
|
|
717
|
+
if return_transformation:
|
|
718
|
+
return F, m
|
|
719
|
+
return F
|
|
720
|
+
|
|
721
|
+
#find all representatives of orbits for one prime
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def HS_all_minimal_p(p, f, m=None, return_transformation=False):
|
|
725
|
+
r"""
|
|
726
|
+
Find a representative in each distinct `SL(2,\ZZ)` orbit with
|
|
727
|
+
minimal `p`-resultant.
|
|
728
|
+
|
|
729
|
+
This function implements the algorithm in Hutz-Stoll [HS2018]_.
|
|
730
|
+
A representatives in each distinct `SL(2,\ZZ)` orbit with minimal
|
|
731
|
+
valuation with respect to the prime ``p`` is returned. The input
|
|
732
|
+
``f`` must have minimal resultant in its conjugacy class.
|
|
733
|
+
|
|
734
|
+
INPUT:
|
|
735
|
+
|
|
736
|
+
- ``p`` -- a prime
|
|
737
|
+
|
|
738
|
+
- ``f`` -- dynamical system on the projective line with minimal resultant
|
|
739
|
+
|
|
740
|
+
- ``m`` -- (optional) `2 \times 2` matrix associated with ``f``
|
|
741
|
+
|
|
742
|
+
- ``return_transformation`` -- boolean (default: ``False``); this
|
|
743
|
+
signals a return of the ``PGL_2`` transformation to conjugate ``vp``
|
|
744
|
+
to the calculated minimal model
|
|
745
|
+
|
|
746
|
+
OUTPUT:
|
|
747
|
+
|
|
748
|
+
List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a
|
|
749
|
+
`2 \times 2` matrix.
|
|
750
|
+
|
|
751
|
+
EXAMPLES::
|
|
752
|
+
|
|
753
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
754
|
+
sage: f = DynamicalSystem([x^5 - 6^4*y^5, x^2*y^3])
|
|
755
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal_p
|
|
756
|
+
sage: HS_all_minimal_p(2, f)
|
|
757
|
+
[Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
758
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
759
|
+
(x^5 - 1296*y^5 : x^2*y^3),
|
|
760
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
761
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
762
|
+
(4*x^5 - 162*y^5 : x^2*y^3)]
|
|
763
|
+
sage: cl = HS_all_minimal_p(2, f, return_transformation=True)
|
|
764
|
+
sage: all(f.conjugate(m) == g for g, m in cl)
|
|
765
|
+
True
|
|
766
|
+
"""
|
|
767
|
+
count = 0
|
|
768
|
+
prev = 0 # no exclusions
|
|
769
|
+
F = copy(f)
|
|
770
|
+
res = ZZ(F.resultant())
|
|
771
|
+
vp = res.valuation(p)
|
|
772
|
+
MS = MatrixSpace(ZZ, 2)
|
|
773
|
+
if m is None:
|
|
774
|
+
m = MS.one()
|
|
775
|
+
if f.degree() % 2 == 0 or vp == 0:
|
|
776
|
+
# there is only one orbit for even degree
|
|
777
|
+
# nothing to do if the prime doesn't divide the resultant
|
|
778
|
+
if return_transformation:
|
|
779
|
+
return [[f, m]]
|
|
780
|
+
else:
|
|
781
|
+
return [f]
|
|
782
|
+
to_do = [[F, m, prev]] # repns left to check
|
|
783
|
+
reps = [[F, m]] # orbit representatives for f
|
|
784
|
+
while to_do:
|
|
785
|
+
F, m, prev = to_do.pop()
|
|
786
|
+
# there are at most two directions preserving the resultant
|
|
787
|
+
if prev == 0:
|
|
788
|
+
count = 0
|
|
789
|
+
else:
|
|
790
|
+
count = 1
|
|
791
|
+
if prev != 2: # [p,a,0,1]
|
|
792
|
+
t = MS([1, 0, 0, p])
|
|
793
|
+
F1 = F.conjugate(t)
|
|
794
|
+
F1.normalize_coordinates()
|
|
795
|
+
res1 = ZZ(F1.resultant())
|
|
796
|
+
vp1 = res1.valuation(p)
|
|
797
|
+
if vp1 == vp:
|
|
798
|
+
count += 1
|
|
799
|
+
# we have a new representative
|
|
800
|
+
reps.append([F1, m*t])
|
|
801
|
+
# need to check if it has any neighbors
|
|
802
|
+
to_do.append([F1, m*t, 1])
|
|
803
|
+
for b in range(p):
|
|
804
|
+
if not (b == 0 and prev == 1):
|
|
805
|
+
t = MS([p, b, 0, 1])
|
|
806
|
+
F1 = F.conjugate(t)
|
|
807
|
+
F1.normalize_coordinates()
|
|
808
|
+
res1 = ZZ(F1.resultant())
|
|
809
|
+
vp1 = res1.valuation(p)
|
|
810
|
+
if vp1 == vp:
|
|
811
|
+
count += 1
|
|
812
|
+
# we have a new representative
|
|
813
|
+
reps.append([F1, m*t])
|
|
814
|
+
# need to check if it has any neighbors
|
|
815
|
+
to_do.append([F1, m*t, 2])
|
|
816
|
+
if count >= 2: # at most two neighbors
|
|
817
|
+
break
|
|
818
|
+
|
|
819
|
+
if return_transformation:
|
|
820
|
+
return reps
|
|
821
|
+
else:
|
|
822
|
+
return [funct for funct, matr in reps]
|
|
823
|
+
|
|
824
|
+
#find all representatives of orbits
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def HS_all_minimal(f, return_transformation=False, D=None):
|
|
828
|
+
r"""
|
|
829
|
+
Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant.
|
|
830
|
+
|
|
831
|
+
This function implements the algorithm in Hutz-Stoll [HS2018]_.
|
|
832
|
+
A representative in each distinct `SL(2,\ZZ)` orbit is returned.
|
|
833
|
+
The input ``f`` must have minimal resultant in its conjugacy class.
|
|
834
|
+
|
|
835
|
+
INPUT:
|
|
836
|
+
|
|
837
|
+
- ``f`` -- dynamical system on the projective line with minimal resultant
|
|
838
|
+
|
|
839
|
+
- ``return_transformation`` -- boolean (default: ``False``); this
|
|
840
|
+
signals a return of the ``PGL_2`` transformation to conjugate ``vp``
|
|
841
|
+
to the calculated minimal model
|
|
842
|
+
|
|
843
|
+
- ``D`` -- list of primes, in case one only wants to check minimality
|
|
844
|
+
at those specific primes
|
|
845
|
+
|
|
846
|
+
OUTPUT:
|
|
847
|
+
|
|
848
|
+
List of pairs ``[f, m]``, where ``f`` is a dynamical system and ``m``
|
|
849
|
+
is a `2 \times 2` matrix.
|
|
850
|
+
|
|
851
|
+
EXAMPLES::
|
|
852
|
+
|
|
853
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
854
|
+
sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y])
|
|
855
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal
|
|
856
|
+
sage: HS_all_minimal(f)
|
|
857
|
+
[Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
858
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
859
|
+
(x^3 - 36*y^3 : x^2*y),
|
|
860
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
861
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
862
|
+
(9*x^3 - 12*y^3 : 9*x^2*y),
|
|
863
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
864
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
865
|
+
(4*x^3 - 18*y^3 : 4*x^2*y),
|
|
866
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
867
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
868
|
+
(36*x^3 - 6*y^3 : 36*x^2*y)]
|
|
869
|
+
sage: HS_all_minimal(f, D=[3])
|
|
870
|
+
[Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
871
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
872
|
+
(x^3 - 36*y^3 : x^2*y),
|
|
873
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
874
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
875
|
+
(9*x^3 - 12*y^3 : 9*x^2*y)]
|
|
876
|
+
|
|
877
|
+
::
|
|
878
|
+
|
|
879
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
880
|
+
sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2])
|
|
881
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal
|
|
882
|
+
sage: cl = HS_all_minimal(f, return_transformation=True)
|
|
883
|
+
sage: all(f.conjugate(m) == g for g, m in cl)
|
|
884
|
+
True
|
|
885
|
+
"""
|
|
886
|
+
MS = MatrixSpace(ZZ, 2)
|
|
887
|
+
m = MS.one()
|
|
888
|
+
F = copy(f)
|
|
889
|
+
F.normalize_coordinates()
|
|
890
|
+
if F.degree() == 1:
|
|
891
|
+
raise ValueError("function must be degree at least 2")
|
|
892
|
+
if f.degree() % 2 == 0:
|
|
893
|
+
#there is only one orbit for even degree
|
|
894
|
+
if return_transformation:
|
|
895
|
+
return [[f, m]]
|
|
896
|
+
else:
|
|
897
|
+
return [f]
|
|
898
|
+
if D is None:
|
|
899
|
+
res = ZZ(F.resultant())
|
|
900
|
+
D = res.prime_divisors()
|
|
901
|
+
M = [[F, m]]
|
|
902
|
+
for p in D:
|
|
903
|
+
# get p-orbits
|
|
904
|
+
Mp = HS_all_minimal_p(p, F, m, return_transformation=True)
|
|
905
|
+
# combine with previous orbits representatives
|
|
906
|
+
M = [[g.conjugate(t), t*s] for g,s in M for G,t in Mp]
|
|
907
|
+
|
|
908
|
+
if return_transformation:
|
|
909
|
+
return M
|
|
910
|
+
else:
|
|
911
|
+
return [funct for funct, matr in M]
|
|
912
|
+
|
|
913
|
+
#######################
|
|
914
|
+
#functionality for smallest coefficients
|
|
915
|
+
#
|
|
916
|
+
# Ben Hutz July 2018
|
|
917
|
+
#####################################3
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
def get_bound_dynamical(F, f, m=1, dynatomic=True, prec=53, emb=None):
|
|
921
|
+
"""
|
|
922
|
+
The hyperbolic distance from `j` which must contain the smallest map.
|
|
923
|
+
|
|
924
|
+
This defines the maximum possible distance from `j` to the `z_0` covariant
|
|
925
|
+
of the associated binary form `F` in the hyperbolic 3-space
|
|
926
|
+
for which the map `f` could have smaller coefficients.
|
|
927
|
+
|
|
928
|
+
INPUT:
|
|
929
|
+
|
|
930
|
+
- ``F`` -- binary form of degree at least 3 with no multiple roots associated
|
|
931
|
+
to ``f``
|
|
932
|
+
|
|
933
|
+
- ``f`` -- a dynamical system on `P^1`
|
|
934
|
+
|
|
935
|
+
- ``m`` -- positive integer. the period used to create ``F``
|
|
936
|
+
|
|
937
|
+
- ``dynatomic`` -- boolean. whether ``F`` is the periodic points or the
|
|
938
|
+
formal periodic points of period ``m`` for ``f``
|
|
939
|
+
|
|
940
|
+
- ``prec`` -- positive integer. precision to use in CC
|
|
941
|
+
|
|
942
|
+
- ``emb`` -- embedding into CC
|
|
943
|
+
|
|
944
|
+
OUTPUT: a positive real number
|
|
945
|
+
|
|
946
|
+
EXAMPLES::
|
|
947
|
+
|
|
948
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import get_bound_dynamical
|
|
949
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
950
|
+
sage: f = DynamicalSystem([50*x^2 + 795*x*y + 2120*y^2, 265*x^2 + 106*y^2])
|
|
951
|
+
sage: get_bound_dynamical(f.dynatomic_polynomial(1), f) # needs sage.symbolic
|
|
952
|
+
35.5546923182219
|
|
953
|
+
"""
|
|
954
|
+
from sage.symbolic.constants import e
|
|
955
|
+
|
|
956
|
+
def coshdelta(z):
|
|
957
|
+
#The cosh of the hyperbolic distance from z = t+uj to j
|
|
958
|
+
return (z.norm() + 1)/(2*z.imag())
|
|
959
|
+
if F.base_ring() != ComplexField(prec=prec):
|
|
960
|
+
if emb is None:
|
|
961
|
+
compF = F.change_ring(ComplexField(prec=prec))
|
|
962
|
+
else:
|
|
963
|
+
compF = F.change_ring(emb)
|
|
964
|
+
else:
|
|
965
|
+
compF = F
|
|
966
|
+
n = F.degree()
|
|
967
|
+
|
|
968
|
+
z0F, thetaF = covariant_z0(compF, prec=prec, emb=emb)
|
|
969
|
+
d = f.degree()
|
|
970
|
+
hF = e**f.global_height(prec=prec)
|
|
971
|
+
#get precomputed constants C,k
|
|
972
|
+
if m == 1:
|
|
973
|
+
C = 4*d+2
|
|
974
|
+
k = 2
|
|
975
|
+
else:
|
|
976
|
+
Ck_values = {(False, 2, 2): (322, 6), (False, 2, 3): (385034, 14),
|
|
977
|
+
(False, 2, 4): (4088003923454, 30), (False, 3, 2): (18044, 8),
|
|
978
|
+
(False, 4, 2): (1761410, 10), (False, 5, 2): (269283820, 12),
|
|
979
|
+
(True, 2, 2): (43, 4), (True, 2, 3): (106459, 12),
|
|
980
|
+
(True, 2, 4): (39216735905, 24), (True, 3, 2): (1604, 6),
|
|
981
|
+
(True, 4, 2): (114675, 8), (True, 5, 2): (14158456, 10)}
|
|
982
|
+
try:
|
|
983
|
+
C, k = Ck_values[(dynatomic,d,m)]
|
|
984
|
+
except KeyError:
|
|
985
|
+
raise ValueError("constants not computed for this (m,d) pair")
|
|
986
|
+
if n == 2 and d == 2:
|
|
987
|
+
#bound with epsilonF = 1
|
|
988
|
+
bound = 2*((2*C*(hF**k))/(thetaF))
|
|
989
|
+
else:
|
|
990
|
+
bound = cosh(epsinv(F, (2**(n-1))*C*(hF**k)/thetaF, prec=prec))
|
|
991
|
+
return bound
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
def smallest_dynamical(f, dynatomic=True, start_n=1, prec=53, emb=None, algorithm='HS', check_minimal=True):
|
|
995
|
+
r"""
|
|
996
|
+
Determine the poly with smallest coefficients in `SL(2,\ZZ)` orbit of ``F``.
|
|
997
|
+
|
|
998
|
+
Smallest is in the sense of global height.
|
|
999
|
+
The method is the algorithm in Hutz-Stoll [HS2018]_.
|
|
1000
|
+
A binary form defining the periodic points is associated to ``f``.
|
|
1001
|
+
From this polynomial a bound on the search space can be determined.
|
|
1002
|
+
|
|
1003
|
+
``f`` should already be a minimal model or finding the orbit
|
|
1004
|
+
representatives may give wrong results.
|
|
1005
|
+
|
|
1006
|
+
INPUT:
|
|
1007
|
+
|
|
1008
|
+
- ``f`` -- a dynamical system on `P^1`
|
|
1009
|
+
|
|
1010
|
+
- ``dynatomic`` -- boolean. whether ``F`` is the periodic points or the
|
|
1011
|
+
formal periodic points of period ``m`` for ``f``
|
|
1012
|
+
|
|
1013
|
+
- ``start_n`` -- positive integer. the period used to start trying to
|
|
1014
|
+
create associate binary form ``F``
|
|
1015
|
+
|
|
1016
|
+
- ``prec`` -- positive integer. precision to use in CC
|
|
1017
|
+
|
|
1018
|
+
- ``emb`` -- embedding into CC
|
|
1019
|
+
|
|
1020
|
+
- ``algorithm`` -- (optional) string; either ``'BM'`` for the Bruin-Molnar
|
|
1021
|
+
algorithm or ``'HS'`` for the Hutz-Stoll algorithm. If not specified,
|
|
1022
|
+
properties of the map are utilized to choose how to compute minimal
|
|
1023
|
+
orbit representatives
|
|
1024
|
+
|
|
1025
|
+
- ``check_minimal`` -- (default: ``True``), boolean, whether to check
|
|
1026
|
+
if this map is a minimal model
|
|
1027
|
+
|
|
1028
|
+
OUTPUT: pair [dynamical system, matrix]
|
|
1029
|
+
|
|
1030
|
+
EXAMPLES::
|
|
1031
|
+
|
|
1032
|
+
sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import smallest_dynamical
|
|
1033
|
+
sage: P.<x,y> = ProjectiveSpace(QQ,1)
|
|
1034
|
+
sage: f = DynamicalSystem([50*x^2 + 795*x*y + 2120*y^2, 265*x^2 + 106*y^2])
|
|
1035
|
+
sage: smallest_dynamical(f) # long time
|
|
1036
|
+
[
|
|
1037
|
+
Dynamical System of Projective Space of dimension 1 over Rational Field
|
|
1038
|
+
Defn: Defined on coordinates by sending (x : y) to
|
|
1039
|
+
(-480*x^2 - 1125*x*y + 1578*y^2 : 265*x^2 + 1060*x*y + 1166*y^2),
|
|
1040
|
+
<BLANKLINE>
|
|
1041
|
+
[1 2]
|
|
1042
|
+
[0 1]
|
|
1043
|
+
]
|
|
1044
|
+
"""
|
|
1045
|
+
from sage.symbolic.constants import e
|
|
1046
|
+
|
|
1047
|
+
def insert_item(pts, item, index):
|
|
1048
|
+
# binary insertion to maintain list of points left to consider
|
|
1049
|
+
N = len(pts)
|
|
1050
|
+
if N == 0:
|
|
1051
|
+
return [item]
|
|
1052
|
+
elif N == 1:
|
|
1053
|
+
if item[index] > pts[0][index]:
|
|
1054
|
+
pts.insert(0, item)
|
|
1055
|
+
else:
|
|
1056
|
+
pts.append(item)
|
|
1057
|
+
return pts
|
|
1058
|
+
else: # binary insertion
|
|
1059
|
+
left = 1
|
|
1060
|
+
right = N
|
|
1061
|
+
mid = (left + right) // 2 # these are ints so this is .floor()
|
|
1062
|
+
if item[index] > pts[mid][index]: # item goes into first half
|
|
1063
|
+
return insert_item(pts[:mid], item, index) + pts[mid:N]
|
|
1064
|
+
else: # item goes into second half
|
|
1065
|
+
return pts[:mid] + insert_item(pts[mid:N], item, index)
|
|
1066
|
+
|
|
1067
|
+
def coshdelta(z):
|
|
1068
|
+
# The cosh of the hyperbolic distance from z = t+uj to j
|
|
1069
|
+
return (z.norm() + 1)/(2*z.imag())
|
|
1070
|
+
|
|
1071
|
+
# can't be smaller if height 0
|
|
1072
|
+
f.normalize_coordinates()
|
|
1073
|
+
if f.global_height(prec=prec) == 0:
|
|
1074
|
+
return [f, matrix(ZZ,2,2,[1,0,0,1])]
|
|
1075
|
+
all_min = f.all_minimal_models(return_transformation=True, algorithm=algorithm, check_minimal=check_minimal)
|
|
1076
|
+
|
|
1077
|
+
current_min = None
|
|
1078
|
+
current_size = None
|
|
1079
|
+
# search for minimum over all orbits
|
|
1080
|
+
for g,M in all_min:
|
|
1081
|
+
PS = g.domain()
|
|
1082
|
+
CR = PS.coordinate_ring()
|
|
1083
|
+
x,y = CR.gens()
|
|
1084
|
+
n = start_n # sometimes you get a problem later with 0,infty as roots
|
|
1085
|
+
if dynatomic:
|
|
1086
|
+
pts_poly = g.dynatomic_polynomial(n)
|
|
1087
|
+
else:
|
|
1088
|
+
gn = g.nth_iterate_map(n)
|
|
1089
|
+
pts_poly = y*gn[0] - x*gn[1]
|
|
1090
|
+
d = ZZ(pts_poly.degree())
|
|
1091
|
+
max_mult = max([ex for p,ex in pts_poly.factor()])
|
|
1092
|
+
while ((d < 3) or (max_mult >= d/2) and (n < 5)):
|
|
1093
|
+
n = n+1
|
|
1094
|
+
if dynatomic:
|
|
1095
|
+
pts_poly = g.dynatomic_polynomial(n)
|
|
1096
|
+
else:
|
|
1097
|
+
gn = g.nth_iterate_map(n)
|
|
1098
|
+
pts_poly = y*gn[0] - x*gn[1]
|
|
1099
|
+
d = ZZ(pts_poly.degree())
|
|
1100
|
+
max_mult = max([ex for _, ex in pts_poly.factor()])
|
|
1101
|
+
assert (n <= 4), "n > 4, failed to find usable poly"
|
|
1102
|
+
|
|
1103
|
+
R = get_bound_dynamical(pts_poly, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb)
|
|
1104
|
+
# search starts in fundamental domain
|
|
1105
|
+
G,MG = pts_poly.reduced_form(prec=prec, emb=emb, smallest_coeffs=False)
|
|
1106
|
+
red_g = f.conjugate(M*MG)
|
|
1107
|
+
if G != pts_poly:
|
|
1108
|
+
R2 = get_bound_dynamical(G, red_g, m=n, dynatomic=dynatomic, prec=prec, emb=emb)
|
|
1109
|
+
R = min(R2, R)
|
|
1110
|
+
red_g.normalize_coordinates()
|
|
1111
|
+
if red_g.global_height(prec=prec) == 0:
|
|
1112
|
+
return [red_g, M*MG]
|
|
1113
|
+
|
|
1114
|
+
# height
|
|
1115
|
+
if current_size is None:
|
|
1116
|
+
current_size = e**red_g.global_height(prec=prec)
|
|
1117
|
+
v0, th = covariant_z0(G, prec=prec, emb=emb)
|
|
1118
|
+
rep = 2*CC.gen(0)
|
|
1119
|
+
from math import isnan
|
|
1120
|
+
if isnan(v0.abs()):
|
|
1121
|
+
raise ValueError("invalid covariant: %s" % v0)
|
|
1122
|
+
|
|
1123
|
+
# get orbit
|
|
1124
|
+
S = matrix(ZZ,2,2,[0,-1,1,0])
|
|
1125
|
+
T = matrix(ZZ,2,2,[1,1,0,1])
|
|
1126
|
+
TI = matrix(ZZ,2,2,[1,-1,0,1])
|
|
1127
|
+
|
|
1128
|
+
count = 0
|
|
1129
|
+
pts = [[G, red_g, v0, rep, M*MG, coshdelta(v0), 0]] # label - 0:None, 1:S, 2:T, 3:T^(-1)
|
|
1130
|
+
if current_min is None:
|
|
1131
|
+
current_min = [G, red_g, v0, rep, M*MG, coshdelta(v0)]
|
|
1132
|
+
while pts != []:
|
|
1133
|
+
G, g, v, rep, M, D, label = pts.pop()
|
|
1134
|
+
# apply ST and keep z, Sz
|
|
1135
|
+
if D > R:
|
|
1136
|
+
break # all remaining pts are too far away
|
|
1137
|
+
# check if it is smaller. If so, we can improve the bound
|
|
1138
|
+
count += 1
|
|
1139
|
+
new_size = e**g.global_height(prec=prec)
|
|
1140
|
+
if new_size < current_size:
|
|
1141
|
+
current_min = [G, g, v, rep, M, coshdelta(v)]
|
|
1142
|
+
current_size = new_size
|
|
1143
|
+
if new_size == 1: # early exit
|
|
1144
|
+
return [current_min[1], current_min[4]]
|
|
1145
|
+
new_R = get_bound_dynamical(G, g, m=n, dynatomic=dynatomic, prec=prec, emb=emb)
|
|
1146
|
+
R = min(new_R, R)
|
|
1147
|
+
|
|
1148
|
+
# add new points to check
|
|
1149
|
+
if label != 1 and min((rep+1).norm(), (rep-1).norm()) >= 1: # don't undo S
|
|
1150
|
+
# the 2nd condition is equivalent to |\Re(-1/rep)| <= 1/2
|
|
1151
|
+
# this means that rep can have resulted from an inversion step in
|
|
1152
|
+
# the shift-and-invert procedure, so don't invert
|
|
1153
|
+
|
|
1154
|
+
# do inversion
|
|
1155
|
+
z = -1/v
|
|
1156
|
+
new_pt = [G.subs({x:-y, y:x}), g.conjugate(S), z, -1/rep, M*S, coshdelta(z), 1]
|
|
1157
|
+
pts = insert_item(pts, new_pt, 5)
|
|
1158
|
+
if label != 3: # don't undo T on g
|
|
1159
|
+
# do right shift
|
|
1160
|
+
z = v-1
|
|
1161
|
+
new_pt = [G.subs({x:x+y}), g.conjugate(TI), z, rep-1, M*TI, coshdelta(z), 2]
|
|
1162
|
+
pts = insert_item(pts, new_pt, 5)
|
|
1163
|
+
if label != 2: # don't undo TI on g
|
|
1164
|
+
# do left shift
|
|
1165
|
+
z = v+1
|
|
1166
|
+
new_pt = [G.subs({x:x-y}), g.conjugate(T), z, rep+1, M*T, coshdelta(z), 3]
|
|
1167
|
+
pts = insert_item(pts, new_pt, 5)
|
|
1168
|
+
|
|
1169
|
+
return [current_min[1], current_min[4]]
|