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,3863 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
r"""
|
|
3
|
+
Computation of Frobenius matrix on Monsky-Washnitzer cohomology
|
|
4
|
+
|
|
5
|
+
The most interesting functions to be exported here are
|
|
6
|
+
:func:`matrix_of_frobenius` and :func:`adjusted_prec`.
|
|
7
|
+
|
|
8
|
+
Currently this code is limited to the case `p \geq 5` (no
|
|
9
|
+
`GF(p^n)` for `n > 1`), and only handles the
|
|
10
|
+
elliptic curve case (not more general hyperelliptic curves).
|
|
11
|
+
|
|
12
|
+
REFERENCES:
|
|
13
|
+
|
|
14
|
+
- [Ked2001]_
|
|
15
|
+
|
|
16
|
+
- [Edix]_
|
|
17
|
+
|
|
18
|
+
AUTHORS:
|
|
19
|
+
|
|
20
|
+
- David Harvey and Robert Bradshaw: initial code developed at the 2006
|
|
21
|
+
MSRI graduate workshop, working with Jennifer Balakrishnan and Liang
|
|
22
|
+
Xiao
|
|
23
|
+
|
|
24
|
+
- David Harvey (2006-08): cleaned up, rewrote some chunks, lots more
|
|
25
|
+
documentation, added Newton iteration method, added more complete
|
|
26
|
+
'trace trick', integrated better into Sage.
|
|
27
|
+
|
|
28
|
+
- David Harvey (2007-02): added algorithm with sqrt(p) complexity
|
|
29
|
+
(removed in May 2007 due to better C++ implementation)
|
|
30
|
+
|
|
31
|
+
- Robert Bradshaw (2007-03): keep track of exact form in reduction
|
|
32
|
+
algorithms
|
|
33
|
+
|
|
34
|
+
- Robert Bradshaw (2007-04): generalization to hyperelliptic curves
|
|
35
|
+
|
|
36
|
+
- Julian Rueth (2014-05-09): improved caching
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# ****************************************************************************
|
|
40
|
+
# Copyright (C) 2006 William Stein <wstein@gmail.com>
|
|
41
|
+
# 2006 Robert Bradshaw <robertwb@math.washington.edu>
|
|
42
|
+
# 2006 David Harvey <dmharvey@math.harvard.edu>
|
|
43
|
+
# 2014 Julian Rueth <julian.rueth@fsfe.org>
|
|
44
|
+
#
|
|
45
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
46
|
+
# https://www.gnu.org/licenses/
|
|
47
|
+
# ****************************************************************************
|
|
48
|
+
|
|
49
|
+
from sage.arith.misc import binomial
|
|
50
|
+
from sage.arith.misc import integer_ceil as ceil
|
|
51
|
+
from sage.categories.algebras import Algebras
|
|
52
|
+
from sage.categories.integral_domains import IntegralDomains
|
|
53
|
+
from sage.matrix.constructor import matrix
|
|
54
|
+
from sage.misc.cachefunc import cached_method
|
|
55
|
+
from sage.misc.lazy_import import lazy_import
|
|
56
|
+
from sage.misc.repr import repr_lincomb
|
|
57
|
+
from sage.modules.free_module import FreeModule
|
|
58
|
+
from sage.modules.free_module_element import FreeModuleElement, vector
|
|
59
|
+
from sage.modules.module import Module
|
|
60
|
+
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as Integers
|
|
61
|
+
from sage.rings.infinity import Infinity
|
|
62
|
+
from sage.rings.integer import Integer
|
|
63
|
+
from sage.rings.integer_ring import ZZ
|
|
64
|
+
from sage.rings.laurent_series_ring import LaurentSeriesRing
|
|
65
|
+
from sage.rings.polynomial.polynomial_element import Polynomial
|
|
66
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
67
|
+
from sage.rings.power_series_ring import PowerSeriesRing
|
|
68
|
+
from sage.rings.rational import Rational
|
|
69
|
+
from sage.rings.rational_field import QQ, RationalField as Rationals
|
|
70
|
+
from sage.schemes.elliptic_curves.constructor import EllipticCurve
|
|
71
|
+
from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic
|
|
72
|
+
from sage.schemes.hyperelliptic_curves.constructor import HyperellipticCurve
|
|
73
|
+
from sage.schemes.hyperelliptic_curves.hyperelliptic_generic import HyperellipticCurve_generic
|
|
74
|
+
from sage.structure.element import ModuleElement
|
|
75
|
+
from sage.structure.parent import Parent
|
|
76
|
+
from sage.structure.richcmp import richcmp
|
|
77
|
+
from sage.structure.unique_representation import UniqueRepresentation
|
|
78
|
+
|
|
79
|
+
lazy_import('sage.functions.log', 'log')
|
|
80
|
+
lazy_import('sage.rings.lazy_series_ring', 'LazyLaurentSeriesRing')
|
|
81
|
+
lazy_import('sage.rings.padics.factory', 'Qp', as_='pAdicField')
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class SpecialCubicQuotientRingElement(ModuleElement):
|
|
85
|
+
"""
|
|
86
|
+
An element of a :class:`SpecialCubicQuotientRing`.
|
|
87
|
+
"""
|
|
88
|
+
def __init__(self, parent, p0, p1, p2, check=True):
|
|
89
|
+
"""
|
|
90
|
+
Construct the element `p_0 + p_1*x + p_2*x^2`, where
|
|
91
|
+
the `p_i` are polynomials in `T`.
|
|
92
|
+
|
|
93
|
+
INPUT:
|
|
94
|
+
|
|
95
|
+
- ``parent`` -- a :class:`SpecialCubicQuotientRing`
|
|
96
|
+
|
|
97
|
+
- ``p0``, ``p1``, ``p2`` -- coefficients; must be coercible
|
|
98
|
+
into parent.poly_ring()
|
|
99
|
+
|
|
100
|
+
- ``check`` -- boolean (default: ``True``); whether to carry
|
|
101
|
+
out coercion
|
|
102
|
+
|
|
103
|
+
EXAMPLES::
|
|
104
|
+
|
|
105
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
106
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
107
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import SpecialCubicQuotientRingElement
|
|
108
|
+
sage: SpecialCubicQuotientRingElement(R, 2, 3, 4)
|
|
109
|
+
(2) + (3)*x + (4)*x^2
|
|
110
|
+
|
|
111
|
+
TESTS::
|
|
112
|
+
|
|
113
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
114
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
115
|
+
sage: TestSuite(R).run()
|
|
116
|
+
sage: p = R.create_element(t, t^2 - 2, 3)
|
|
117
|
+
sage: -p
|
|
118
|
+
(124*T) + (124*T^2 + 2)*x + (122)*x^2
|
|
119
|
+
"""
|
|
120
|
+
if not isinstance(parent, SpecialCubicQuotientRing):
|
|
121
|
+
raise TypeError(f"parent (={parent}) must be a SpecialCubicQuotientRing")
|
|
122
|
+
|
|
123
|
+
ModuleElement.__init__(self, parent)
|
|
124
|
+
|
|
125
|
+
if check:
|
|
126
|
+
poly_ring = parent._poly_ring
|
|
127
|
+
p0 = poly_ring(p0)
|
|
128
|
+
p1 = poly_ring(p1)
|
|
129
|
+
p2 = poly_ring(p2)
|
|
130
|
+
|
|
131
|
+
self._triple = (p0, p1, p2)
|
|
132
|
+
|
|
133
|
+
def coeffs(self):
|
|
134
|
+
"""
|
|
135
|
+
Return list of three lists of coefficients, corresponding to the
|
|
136
|
+
`x^0`, `x^1`, `x^2` coefficients.
|
|
137
|
+
|
|
138
|
+
The lists are zero padded to the same length. The list entries
|
|
139
|
+
belong to the base ring.
|
|
140
|
+
|
|
141
|
+
EXAMPLES::
|
|
142
|
+
|
|
143
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
144
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
145
|
+
sage: p = R.create_element(t, t^2 - 2, 3)
|
|
146
|
+
sage: p.coeffs()
|
|
147
|
+
[[0, 1, 0], [123, 0, 1], [3, 0, 0]]
|
|
148
|
+
"""
|
|
149
|
+
coeffs = [column.coefficients(sparse=False) for column in self._triple]
|
|
150
|
+
degree = max([len(x) for x in coeffs])
|
|
151
|
+
base_ring = self.parent().base_ring()
|
|
152
|
+
for column in coeffs:
|
|
153
|
+
column.extend([base_ring(0)] * (degree - len(column)))
|
|
154
|
+
return coeffs
|
|
155
|
+
|
|
156
|
+
def __bool__(self) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
EXAMPLES::
|
|
159
|
+
|
|
160
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
161
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
162
|
+
sage: x, T = R.gens()
|
|
163
|
+
sage: not x
|
|
164
|
+
False
|
|
165
|
+
sage: not T
|
|
166
|
+
False
|
|
167
|
+
sage: not R.create_element(0, 0, 0)
|
|
168
|
+
True
|
|
169
|
+
"""
|
|
170
|
+
return bool(self._triple[0]) or bool(self._triple[1]) or bool(self._triple[2])
|
|
171
|
+
|
|
172
|
+
def _richcmp_(self, other, op) -> bool:
|
|
173
|
+
"""
|
|
174
|
+
EXAMPLES::
|
|
175
|
+
|
|
176
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
177
|
+
sage: x, t = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4)).gens()
|
|
178
|
+
sage: x == t
|
|
179
|
+
False
|
|
180
|
+
sage: x == x
|
|
181
|
+
True
|
|
182
|
+
sage: x == x + x - x
|
|
183
|
+
True
|
|
184
|
+
"""
|
|
185
|
+
return richcmp(self._triple, other._triple, op)
|
|
186
|
+
|
|
187
|
+
def _repr_(self) -> str:
|
|
188
|
+
"""
|
|
189
|
+
EXAMPLES::
|
|
190
|
+
|
|
191
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
192
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
193
|
+
sage: x, T = R.gens()
|
|
194
|
+
sage: x + T*x - 2*T^2
|
|
195
|
+
(123*T^2) + (T + 1)*x + (0)*x^2
|
|
196
|
+
"""
|
|
197
|
+
return "(%s) + (%s)*x + (%s)*x^2" % self._triple
|
|
198
|
+
|
|
199
|
+
def _latex_(self) -> str:
|
|
200
|
+
"""
|
|
201
|
+
EXAMPLES::
|
|
202
|
+
|
|
203
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
204
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
205
|
+
sage: x, T = R.gens()
|
|
206
|
+
sage: f = x + T*x - 2*T^2
|
|
207
|
+
sage: latex(f)
|
|
208
|
+
(123 T^{2}) + (T + 1)x + (0)x^2
|
|
209
|
+
"""
|
|
210
|
+
return ("(%s) + (%s)x + (%s)x^2"
|
|
211
|
+
% tuple(column._latex_() for column in self._triple))
|
|
212
|
+
|
|
213
|
+
def _add_(self, other):
|
|
214
|
+
"""
|
|
215
|
+
EXAMPLES::
|
|
216
|
+
|
|
217
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
218
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
219
|
+
sage: f = R.create_element(2, t, t^2 - 3)
|
|
220
|
+
sage: g = R.create_element(3 + t, -t, t)
|
|
221
|
+
sage: f + g
|
|
222
|
+
(T + 5) + (0)*x + (T^2 + T + 122)*x^2
|
|
223
|
+
"""
|
|
224
|
+
P = self.parent()
|
|
225
|
+
return P.element_class(P,
|
|
226
|
+
self._triple[0] + other._triple[0],
|
|
227
|
+
self._triple[1] + other._triple[1],
|
|
228
|
+
self._triple[2] + other._triple[2],
|
|
229
|
+
check=False)
|
|
230
|
+
|
|
231
|
+
def _sub_(self, other):
|
|
232
|
+
"""
|
|
233
|
+
EXAMPLES::
|
|
234
|
+
|
|
235
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
236
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
237
|
+
sage: f = R.create_element(2, t, t^2 - 3)
|
|
238
|
+
sage: g = R.create_element(3 + t, -t, t)
|
|
239
|
+
sage: f - g
|
|
240
|
+
(124*T + 124) + (2*T)*x + (T^2 + 124*T + 122)*x^2
|
|
241
|
+
"""
|
|
242
|
+
P = self.parent()
|
|
243
|
+
return P.element_class(P,
|
|
244
|
+
self._triple[0] - other._triple[0],
|
|
245
|
+
self._triple[1] - other._triple[1],
|
|
246
|
+
self._triple[2] - other._triple[2],
|
|
247
|
+
check=False)
|
|
248
|
+
|
|
249
|
+
def shift(self, n):
|
|
250
|
+
"""
|
|
251
|
+
Return this element multiplied by `T^n`.
|
|
252
|
+
|
|
253
|
+
EXAMPLES::
|
|
254
|
+
|
|
255
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
256
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
257
|
+
sage: f = R.create_element(2, t, t^2 - 3)
|
|
258
|
+
sage: f
|
|
259
|
+
(2) + (T)*x + (T^2 + 122)*x^2
|
|
260
|
+
sage: f.shift(1)
|
|
261
|
+
(2*T) + (T^2)*x + (T^3 + 122*T)*x^2
|
|
262
|
+
sage: f.shift(2)
|
|
263
|
+
(2*T^2) + (T^3)*x + (T^4 + 122*T^2)*x^2
|
|
264
|
+
"""
|
|
265
|
+
P = self.parent()
|
|
266
|
+
return P.element_class(P,
|
|
267
|
+
self._triple[0].shift(n),
|
|
268
|
+
self._triple[1].shift(n),
|
|
269
|
+
self._triple[2].shift(n),
|
|
270
|
+
check=False)
|
|
271
|
+
|
|
272
|
+
def scalar_multiply(self, scalar):
|
|
273
|
+
"""
|
|
274
|
+
Multiply this element by a scalar, i.e. just multiply each
|
|
275
|
+
coefficient of `x^j` by the scalar.
|
|
276
|
+
|
|
277
|
+
INPUT:
|
|
278
|
+
|
|
279
|
+
- ``scalar`` -- either an element of ``base_ring``, or an
|
|
280
|
+
element of ``poly_ring``
|
|
281
|
+
|
|
282
|
+
EXAMPLES::
|
|
283
|
+
|
|
284
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
285
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
286
|
+
sage: x, T = R.gens()
|
|
287
|
+
sage: f = R.create_element(2, t, t^2 - 3)
|
|
288
|
+
sage: f
|
|
289
|
+
(2) + (T)*x + (T^2 + 122)*x^2
|
|
290
|
+
sage: f.scalar_multiply(2)
|
|
291
|
+
(4) + (2*T)*x + (2*T^2 + 119)*x^2
|
|
292
|
+
sage: f.scalar_multiply(t)
|
|
293
|
+
(2*T) + (T^2)*x + (T^3 + 122*T)*x^2
|
|
294
|
+
"""
|
|
295
|
+
P = self.parent()
|
|
296
|
+
scalar = P._poly_ring(scalar)
|
|
297
|
+
return P.element_class(P,
|
|
298
|
+
scalar * self._triple[0],
|
|
299
|
+
scalar * self._triple[1],
|
|
300
|
+
scalar * self._triple[2],
|
|
301
|
+
check=False)
|
|
302
|
+
|
|
303
|
+
def square(self):
|
|
304
|
+
"""
|
|
305
|
+
Return the square of the element.
|
|
306
|
+
|
|
307
|
+
EXAMPLES::
|
|
308
|
+
|
|
309
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
310
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
311
|
+
sage: x, T = R.gens()
|
|
312
|
+
|
|
313
|
+
::
|
|
314
|
+
|
|
315
|
+
sage: f = R.create_element(1 + 2*t + 3*t^2, 4 + 7*t + 9*t^2, 3 + 5*t + 11*t^2)
|
|
316
|
+
sage: f.square()
|
|
317
|
+
(73*T^5 + 16*T^4 + 38*T^3 + 39*T^2 + 70*T + 120)
|
|
318
|
+
+ (121*T^5 + 113*T^4 + 73*T^3 + 8*T^2 + 51*T + 61)*x
|
|
319
|
+
+ (18*T^4 + 60*T^3 + 22*T^2 + 108*T + 31)*x^2
|
|
320
|
+
"""
|
|
321
|
+
return self * self
|
|
322
|
+
|
|
323
|
+
def _mul_(self, other):
|
|
324
|
+
"""
|
|
325
|
+
EXAMPLES::
|
|
326
|
+
|
|
327
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
328
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
329
|
+
sage: x, T = R.gens()
|
|
330
|
+
|
|
331
|
+
::
|
|
332
|
+
|
|
333
|
+
sage: f = R.create_element(1 + 2*t + 3*t^2, 4 + 7*t + 9*t^2, 3 + 5*t + 11*t^2)
|
|
334
|
+
sage: g = R.create_element(4 + 3*t + 7*t^2, 2 + 3*t + t^2, 8 + 4*t + 6*t^2)
|
|
335
|
+
sage: f * g
|
|
336
|
+
(65*T^5 + 27*T^4 + 33*T^3 + 75*T^2 + 120*T + 57)
|
|
337
|
+
+ (66*T^5 + T^4 + 123*T^3 + 95*T^2 + 24*T + 50)*x
|
|
338
|
+
+ (45*T^4 + 75*T^3 + 37*T^2 + 2*T + 52)*x^2
|
|
339
|
+
"""
|
|
340
|
+
# Here we do Toom-Cook three-way multiplication, which reduces
|
|
341
|
+
# the naive 9 polynomial multiplications to only 5 polynomial
|
|
342
|
+
# multiplications.
|
|
343
|
+
|
|
344
|
+
a0, a1, a2 = self._triple
|
|
345
|
+
b0, b1, b2 = other._triple
|
|
346
|
+
M = self.parent()._speedup_matrix
|
|
347
|
+
|
|
348
|
+
if self is other:
|
|
349
|
+
# faster method if we are squaring
|
|
350
|
+
p0 = a0 * a0
|
|
351
|
+
temp = a0 + 2*a1 + 4*a2
|
|
352
|
+
p1 = temp * temp
|
|
353
|
+
temp = a0 + a1 + a2
|
|
354
|
+
p2 = temp * temp
|
|
355
|
+
temp = 4*a0 + 2*a1 + a2
|
|
356
|
+
p3 = temp * temp
|
|
357
|
+
p4 = a2 * a2
|
|
358
|
+
|
|
359
|
+
else:
|
|
360
|
+
p0 = a0 * b0
|
|
361
|
+
p1 = (a0 + 2*a1 + 4*a2) * (b0 + 2*b1 + 4*b2)
|
|
362
|
+
p2 = (a0 + a1 + a2) * (b0 + b1 + b2)
|
|
363
|
+
p3 = (4*a0 + 2*a1 + a2) * (4*b0 + 2*b1 + b2)
|
|
364
|
+
p4 = a2 * b2
|
|
365
|
+
|
|
366
|
+
q1 = p1 - p0 - 16*p4
|
|
367
|
+
q2 = p2 - p0 - p4
|
|
368
|
+
q3 = p3 - 16*p0 - p4
|
|
369
|
+
|
|
370
|
+
c0 = p0
|
|
371
|
+
c1 = M[0]*q1 + M[1]*q2 + M[2]*q3
|
|
372
|
+
c2 = M[3]*q1 + M[4]*q2 + M[5]*q3
|
|
373
|
+
c3 = M[6]*q1 + M[7]*q2 + M[8]*q3
|
|
374
|
+
c4 = p4
|
|
375
|
+
|
|
376
|
+
# Now the product is c0 + c1 x + c2 x^2 + c3 x^3 + c4 x^4.
|
|
377
|
+
# We need to reduce mod y = x^3 + ax + b and return result.
|
|
378
|
+
|
|
379
|
+
parent = self.parent()
|
|
380
|
+
T = parent._poly_generator
|
|
381
|
+
b = parent._b
|
|
382
|
+
a = parent._a
|
|
383
|
+
|
|
384
|
+
# todo: These lines are necessary to get binop stuff working
|
|
385
|
+
# for certain base rings, e.g. when we compute b*c3 in the
|
|
386
|
+
# final line. They shouldn't be necessary. Need to fix this
|
|
387
|
+
# somewhere else in Sage.
|
|
388
|
+
a = parent._poly_ring(a)
|
|
389
|
+
b = parent._poly_ring(b)
|
|
390
|
+
|
|
391
|
+
return parent.element_class(parent,
|
|
392
|
+
-b*c3 + c0 + c3*T,
|
|
393
|
+
-b*c4 - a*c3 + c1 + c4*T,
|
|
394
|
+
-a*c4 + c2,
|
|
395
|
+
check=False)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class SpecialCubicQuotientRing(UniqueRepresentation, Parent):
|
|
399
|
+
r"""
|
|
400
|
+
Specialised class for representing the quotient ring
|
|
401
|
+
`R[x,T]/(T - x^3 - ax - b)`, where `R` is an
|
|
402
|
+
arbitrary commutative base ring (in which 2 and 3 are invertible),
|
|
403
|
+
`a` and `b` are elements of that ring.
|
|
404
|
+
|
|
405
|
+
Polynomials are represented internally in the form
|
|
406
|
+
`p_0 + p_1 x + p_2 x^2` where the `p_i` are
|
|
407
|
+
polynomials in `T`. Multiplication of polynomials always
|
|
408
|
+
reduces high powers of `x` (i.e. beyond `x^2`) to
|
|
409
|
+
powers of `T`.
|
|
410
|
+
|
|
411
|
+
Hopefully this ring is faster than a general quotient ring because
|
|
412
|
+
it uses the special structure of this ring to speed multiplication
|
|
413
|
+
(which is the dominant operation in the frobenius matrix
|
|
414
|
+
calculation). I haven't actually tested this theory though...
|
|
415
|
+
|
|
416
|
+
.. TODO::
|
|
417
|
+
|
|
418
|
+
Eventually we will want to run this in characteristic 3, so we
|
|
419
|
+
need to: (a) Allow `Q(x)` to contain an `x^2` term, and (b) Remove
|
|
420
|
+
the requirement that 3 be invertible. Currently this is used in
|
|
421
|
+
the Toom-Cook algorithm to speed multiplication.
|
|
422
|
+
|
|
423
|
+
EXAMPLES::
|
|
424
|
+
|
|
425
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
426
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
427
|
+
sage: R
|
|
428
|
+
SpecialCubicQuotientRing over Ring of integers modulo 125
|
|
429
|
+
with polynomial T = x^3 + 124*x + 94
|
|
430
|
+
sage: TestSuite(R).run()
|
|
431
|
+
|
|
432
|
+
Get generators::
|
|
433
|
+
|
|
434
|
+
sage: x, T = R.gens()
|
|
435
|
+
sage: x
|
|
436
|
+
(0) + (1)*x + (0)*x^2
|
|
437
|
+
sage: T
|
|
438
|
+
(T) + (0)*x + (0)*x^2
|
|
439
|
+
|
|
440
|
+
Coercions::
|
|
441
|
+
|
|
442
|
+
sage: R(7)
|
|
443
|
+
(7) + (0)*x + (0)*x^2
|
|
444
|
+
|
|
445
|
+
Create elements directly from polynomials::
|
|
446
|
+
|
|
447
|
+
sage: A = R.poly_ring()
|
|
448
|
+
sage: A
|
|
449
|
+
Univariate Polynomial Ring in T over Ring of integers modulo 125
|
|
450
|
+
sage: z = A.gen()
|
|
451
|
+
sage: R.create_element(z^2, z+1, 3)
|
|
452
|
+
(T^2) + (T + 1)*x + (3)*x^2
|
|
453
|
+
|
|
454
|
+
Some arithmetic::
|
|
455
|
+
|
|
456
|
+
sage: x^3
|
|
457
|
+
(T + 31) + (1)*x + (0)*x^2
|
|
458
|
+
sage: 3 * x**15 * T**2 + x - T
|
|
459
|
+
(3*T^7 + 90*T^6 + 110*T^5 + 20*T^4 + 58*T^3 + 26*T^2 + 124*T) +
|
|
460
|
+
(15*T^6 + 110*T^5 + 35*T^4 + 63*T^2 + 1)*x +
|
|
461
|
+
(30*T^5 + 40*T^4 + 8*T^3 + 38*T^2)*x^2
|
|
462
|
+
|
|
463
|
+
Retrieve coefficients (output is zero-padded)::
|
|
464
|
+
|
|
465
|
+
sage: x^10
|
|
466
|
+
(3*T^2 + 61*T + 8) + (T^3 + 93*T^2 + 12*T + 40)*x + (3*T^2 + 61*T + 9)*x^2
|
|
467
|
+
sage: (x^10).coeffs()
|
|
468
|
+
[[8, 61, 3, 0], [40, 12, 93, 1], [9, 61, 3, 0]]
|
|
469
|
+
|
|
470
|
+
.. TODO::
|
|
471
|
+
|
|
472
|
+
write an example checking multiplication of these polynomials
|
|
473
|
+
against Sage's ordinary quotient ring arithmetic. I cannot seem
|
|
474
|
+
to get the quotient ring stuff happening right now...
|
|
475
|
+
"""
|
|
476
|
+
def __init__(self, Q, laurent_series=False):
|
|
477
|
+
"""
|
|
478
|
+
Constructor.
|
|
479
|
+
|
|
480
|
+
INPUT:
|
|
481
|
+
|
|
482
|
+
- ``Q`` -- a polynomial of the form
|
|
483
|
+
`Q(x) = x^3 + ax + b`, where `a`, `b` belong to a ring in which
|
|
484
|
+
2, 3 are invertible.
|
|
485
|
+
|
|
486
|
+
- ``laurent_series`` -- boolean (default: ``False``); whether or not to allow
|
|
487
|
+
negative powers of `T`
|
|
488
|
+
|
|
489
|
+
EXAMPLES::
|
|
490
|
+
|
|
491
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
492
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
493
|
+
sage: R
|
|
494
|
+
SpecialCubicQuotientRing over Ring of integers modulo 125
|
|
495
|
+
with polynomial T = x^3 + 124*x + 94
|
|
496
|
+
|
|
497
|
+
::
|
|
498
|
+
|
|
499
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 + 2*t^2 - t + B(1/4))
|
|
500
|
+
Traceback (most recent call last):
|
|
501
|
+
...
|
|
502
|
+
ValueError: Q (=t^3 + 2*t^2 + 124*t + 94) must be of the form x^3 + ax + b
|
|
503
|
+
|
|
504
|
+
::
|
|
505
|
+
|
|
506
|
+
sage: B.<t> = PolynomialRing(Integers(10))
|
|
507
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + 1)
|
|
508
|
+
Traceback (most recent call last):
|
|
509
|
+
...
|
|
510
|
+
ArithmeticError: 2 and 3 must be invertible in the coefficient ring
|
|
511
|
+
(=Ring of integers modulo 10) of Q
|
|
512
|
+
"""
|
|
513
|
+
if not isinstance(Q, Polynomial):
|
|
514
|
+
raise TypeError("Q (=%s) must be a polynomial" % Q)
|
|
515
|
+
|
|
516
|
+
if Q.degree() != 3 or not Q[2].is_zero():
|
|
517
|
+
raise ValueError("Q (=%s) must be of the form x^3 + ax + b" % Q)
|
|
518
|
+
|
|
519
|
+
base_ring = Q.parent().base_ring()
|
|
520
|
+
|
|
521
|
+
if not base_ring(6).is_unit():
|
|
522
|
+
raise ArithmeticError("2 and 3 must be invertible in the "
|
|
523
|
+
"coefficient ring (=%s) of Q" % base_ring)
|
|
524
|
+
|
|
525
|
+
self._a = Q[1]
|
|
526
|
+
self._b = Q[0]
|
|
527
|
+
if laurent_series:
|
|
528
|
+
self._poly_ring = LaurentSeriesRing(base_ring, 'T') # R[T]
|
|
529
|
+
else:
|
|
530
|
+
self._poly_ring = PolynomialRing(base_ring, 'T') # R[T]
|
|
531
|
+
self._poly_generator = self._poly_ring.gen(0) # the generator T
|
|
532
|
+
Parent.__init__(self, base=base_ring,
|
|
533
|
+
category=Algebras(base_ring).Commutative())
|
|
534
|
+
|
|
535
|
+
# Precompute a matrix that is used in the Toom-Cook multiplication.
|
|
536
|
+
# This is where we need 2 and 3 invertible.
|
|
537
|
+
|
|
538
|
+
# a good description of Toom-Cook is online at:
|
|
539
|
+
# https://gmplib.org/manual/Multiplication-Algorithms
|
|
540
|
+
m = matrix(QQ, [[1, -12, 2], [-3, 30, -3], [2, -12, 1]]) / 6
|
|
541
|
+
self._speedup_matrix = m.change_ring(base_ring).list()
|
|
542
|
+
|
|
543
|
+
def _repr_(self) -> str:
|
|
544
|
+
"""
|
|
545
|
+
String representation.
|
|
546
|
+
|
|
547
|
+
EXAMPLES::
|
|
548
|
+
|
|
549
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
550
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
551
|
+
sage: R
|
|
552
|
+
SpecialCubicQuotientRing over Ring of integers modulo 125
|
|
553
|
+
with polynomial T = x^3 + 124*x + 94
|
|
554
|
+
"""
|
|
555
|
+
return "SpecialCubicQuotientRing over %s with polynomial T = %s" % \
|
|
556
|
+
(self.base_ring(), PolynomialRing(self.base_ring(), 'x')(
|
|
557
|
+
[self._b, self._a, 0, 1]))
|
|
558
|
+
|
|
559
|
+
def poly_ring(self):
|
|
560
|
+
"""
|
|
561
|
+
Return the underlying polynomial ring in `T`.
|
|
562
|
+
|
|
563
|
+
EXAMPLES::
|
|
564
|
+
|
|
565
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
566
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
567
|
+
sage: R.poly_ring()
|
|
568
|
+
Univariate Polynomial Ring in T over Ring of integers modulo 125
|
|
569
|
+
"""
|
|
570
|
+
return self._poly_ring
|
|
571
|
+
|
|
572
|
+
def gens(self) -> tuple:
|
|
573
|
+
"""
|
|
574
|
+
Return (x, T) where x and T are the generators of the ring
|
|
575
|
+
(as elements *of this ring*).
|
|
576
|
+
|
|
577
|
+
EXAMPLES::
|
|
578
|
+
|
|
579
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
580
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
581
|
+
sage: x, T = R.gens()
|
|
582
|
+
sage: x
|
|
583
|
+
(0) + (1)*x + (0)*x^2
|
|
584
|
+
sage: T
|
|
585
|
+
(T) + (0)*x + (0)*x^2
|
|
586
|
+
"""
|
|
587
|
+
zero = self._poly_ring.zero()
|
|
588
|
+
one = self._poly_ring.one()
|
|
589
|
+
return (self.element_class(self, zero, one, zero, check=False),
|
|
590
|
+
self.element_class(self, self._poly_generator, zero, zero,
|
|
591
|
+
check=False))
|
|
592
|
+
|
|
593
|
+
def _element_constructor_(self, *args, check=True):
|
|
594
|
+
"""
|
|
595
|
+
Create the element `p_0 + p_1*x + p_2*x^2`, where the `p_i`
|
|
596
|
+
are polynomials in `T`.
|
|
597
|
+
|
|
598
|
+
INPUT:
|
|
599
|
+
|
|
600
|
+
- ``p0``, ``p1``, ``p2`` -- coefficients; must be coercible
|
|
601
|
+
into poly_ring()
|
|
602
|
+
|
|
603
|
+
- ``check`` -- boolean (default: ``True``); whether to carry
|
|
604
|
+
out coercion
|
|
605
|
+
|
|
606
|
+
EXAMPLES::
|
|
607
|
+
|
|
608
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
609
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
610
|
+
sage: A, z = R.poly_ring().objgen()
|
|
611
|
+
sage: R.create_element(z^2, z+1, 3) # indirect doctest
|
|
612
|
+
(T^2) + (T + 1)*x + (3)*x^2
|
|
613
|
+
"""
|
|
614
|
+
if len(args) == 1 and args[0] in self.base_ring():
|
|
615
|
+
p0 = self._poly_ring.coerce(args[0])
|
|
616
|
+
p1 = p2 = self._poly_ring.zero()
|
|
617
|
+
else:
|
|
618
|
+
p0, p1, p2 = args
|
|
619
|
+
return self.element_class(self, p0, p1, p2, check=check)
|
|
620
|
+
|
|
621
|
+
create_element = _element_constructor_
|
|
622
|
+
|
|
623
|
+
@cached_method
|
|
624
|
+
def one(self):
|
|
625
|
+
"""
|
|
626
|
+
Return the unit of ``self``.
|
|
627
|
+
|
|
628
|
+
EXAMPLES::
|
|
629
|
+
|
|
630
|
+
sage: B.<t> = PolynomialRing(Integers(125))
|
|
631
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
632
|
+
sage: R.one()
|
|
633
|
+
(1) + (0)*x + (0)*x^2
|
|
634
|
+
"""
|
|
635
|
+
R = self._poly_ring
|
|
636
|
+
return self.element_class(self, R.one(), R.zero(), R.zero(), check=False)
|
|
637
|
+
|
|
638
|
+
def _coerce_map_from_(self, R):
|
|
639
|
+
"""
|
|
640
|
+
Coercion system.
|
|
641
|
+
|
|
642
|
+
EXAMPLES::
|
|
643
|
+
|
|
644
|
+
sage: Z125 = Integers(125)
|
|
645
|
+
sage: B.<t> = PolynomialRing(Z125)
|
|
646
|
+
sage: R = monsky_washnitzer.SpecialCubicQuotientRing(t^3 - t + B(1/4))
|
|
647
|
+
sage: R.has_coerce_map_from(Z125)
|
|
648
|
+
True
|
|
649
|
+
"""
|
|
650
|
+
return self._poly_ring.has_coerce_map_from(R)
|
|
651
|
+
|
|
652
|
+
Element = SpecialCubicQuotientRingElement
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def transpose_list(input) -> list[list]:
|
|
656
|
+
"""
|
|
657
|
+
INPUT:
|
|
658
|
+
|
|
659
|
+
- ``input`` -- list of lists, each list of the same length
|
|
660
|
+
|
|
661
|
+
OUTPUT: list of lists such that ``output[i][j] = input[j][i]``
|
|
662
|
+
|
|
663
|
+
EXAMPLES::
|
|
664
|
+
|
|
665
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import transpose_list
|
|
666
|
+
sage: L = [[1, 2], [3, 4], [5, 6]]
|
|
667
|
+
sage: transpose_list(L)
|
|
668
|
+
[[1, 3, 5], [2, 4, 6]]
|
|
669
|
+
"""
|
|
670
|
+
w = len(input[0])
|
|
671
|
+
return [[input_j[i] for input_j in input] for i in range(w)]
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def helper_matrix(Q):
|
|
675
|
+
r"""
|
|
676
|
+
Compute the (constant) matrix used to calculate the linear
|
|
677
|
+
combinations of the `d(x^i y^j)` needed to eliminate the
|
|
678
|
+
negative powers of `y` in the cohomology (i.e., in
|
|
679
|
+
:func:`reduce_negative`).
|
|
680
|
+
|
|
681
|
+
INPUT:
|
|
682
|
+
|
|
683
|
+
- ``Q`` -- cubic polynomial
|
|
684
|
+
|
|
685
|
+
EXAMPLES::
|
|
686
|
+
|
|
687
|
+
sage: t = polygen(QQ,'t')
|
|
688
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import helper_matrix
|
|
689
|
+
sage: helper_matrix(t**3-4*t-691)
|
|
690
|
+
[ 64/12891731 -16584/12891731 4297329/12891731]
|
|
691
|
+
[ 6219/12891731 -32/12891731 8292/12891731]
|
|
692
|
+
[ -24/12891731 6219/12891731 -32/12891731]
|
|
693
|
+
"""
|
|
694
|
+
a = Q[1]
|
|
695
|
+
b = Q[0]
|
|
696
|
+
|
|
697
|
+
# Discriminant (should be invertible for a curve of good reduction)
|
|
698
|
+
D = 4*a**3 + 27*b**2
|
|
699
|
+
Dinv = D**(-1) # NB do not use 1/D
|
|
700
|
+
|
|
701
|
+
# This is the inverse of the matrix
|
|
702
|
+
# [ a, -3b, 0 ]
|
|
703
|
+
# [ 0, -2a, -3b ]
|
|
704
|
+
# [ 3, 0, -2a ]
|
|
705
|
+
|
|
706
|
+
return Dinv * matrix([[4*a**2, -6*b*a, 9*b**2],
|
|
707
|
+
[-9*b, -2*a**2, 3*b*a],
|
|
708
|
+
[6*a, -9*b, -2*a**2]])
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def lift(x):
|
|
712
|
+
r"""
|
|
713
|
+
Try to call ``x.lift()``, presumably from the `p`-adics to `\ZZ`.
|
|
714
|
+
|
|
715
|
+
If this fails, it assumes the input is a power series, and tries to
|
|
716
|
+
lift it to a power series over `\QQ`.
|
|
717
|
+
|
|
718
|
+
This function is just a very kludgy solution to the problem of
|
|
719
|
+
trying to make the reduction code (below) work over both `\ZZ_p` and
|
|
720
|
+
`\ZZ_p[[t]]`.
|
|
721
|
+
|
|
722
|
+
EXAMPLES::
|
|
723
|
+
|
|
724
|
+
sage: # needs sage.rings.padics
|
|
725
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import lift
|
|
726
|
+
sage: l = lift(Qp(13)(131)); l
|
|
727
|
+
131
|
|
728
|
+
sage: l.parent()
|
|
729
|
+
Integer Ring
|
|
730
|
+
sage: x = PowerSeriesRing(Qp(17),'x').gen()
|
|
731
|
+
sage: l = lift(4 + 5*x + 17*x**6); l
|
|
732
|
+
4 + 5*t + 17*t^6
|
|
733
|
+
sage: l.parent()
|
|
734
|
+
Power Series Ring in t over Rational Field
|
|
735
|
+
"""
|
|
736
|
+
try:
|
|
737
|
+
return x.lift()
|
|
738
|
+
except AttributeError:
|
|
739
|
+
return PowerSeriesRing(Rationals(), "t")(x.list(), x.prec())
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def reduce_negative(Q, p, coeffs, offset, exact_form=None):
|
|
743
|
+
"""
|
|
744
|
+
Apply cohomology relations to incorporate negative powers of
|
|
745
|
+
`y` into the `y^0` term.
|
|
746
|
+
|
|
747
|
+
INPUT:
|
|
748
|
+
|
|
749
|
+
- ``p`` -- prime
|
|
750
|
+
|
|
751
|
+
- ``Q`` -- cubic polynomial
|
|
752
|
+
|
|
753
|
+
- ``coeffs`` -- list of length 3 lists. The
|
|
754
|
+
`i`-th list ``[a, b, c]`` represents
|
|
755
|
+
`y^{2(i - offset)} (a + bx + cx^2) dx/y`.
|
|
756
|
+
|
|
757
|
+
- ``offset`` -- nonnegative integer
|
|
758
|
+
|
|
759
|
+
OUTPUT:
|
|
760
|
+
|
|
761
|
+
The reduction is performed in-place. The output is placed
|
|
762
|
+
in coeffs[offset]. Note that coeffs[i] will be meaningless for i
|
|
763
|
+
offset after this function is finished.
|
|
764
|
+
|
|
765
|
+
EXAMPLES::
|
|
766
|
+
|
|
767
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
768
|
+
sage: Q = x^3 - x + R(1/4)
|
|
769
|
+
sage: coeffs = [[10, 15, 20], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
770
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
771
|
+
sage: monsky_washnitzer.reduce_negative(Q, 5, coeffs, 3)
|
|
772
|
+
sage: coeffs[3]
|
|
773
|
+
[28, 52, 9]
|
|
774
|
+
|
|
775
|
+
::
|
|
776
|
+
|
|
777
|
+
sage: R.<x> = Integers(7^3)['x']
|
|
778
|
+
sage: Q = x^3 - x + R(1/4)
|
|
779
|
+
sage: coeffs = [[7, 14, 21], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
780
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
781
|
+
sage: monsky_washnitzer.reduce_negative(Q, 7, coeffs, 3)
|
|
782
|
+
sage: coeffs[3]
|
|
783
|
+
[245, 332, 9]
|
|
784
|
+
"""
|
|
785
|
+
|
|
786
|
+
m = helper_matrix(Q).list()
|
|
787
|
+
base_ring = Q.base_ring()
|
|
788
|
+
next_a = coeffs[0]
|
|
789
|
+
|
|
790
|
+
if exact_form is not None:
|
|
791
|
+
x = exact_form.parent().gen(0)
|
|
792
|
+
y = exact_form.parent()(exact_form.parent().base_ring().gen(0))
|
|
793
|
+
|
|
794
|
+
try:
|
|
795
|
+
three_j_plus_5 = 5 - base_ring(6*offset)
|
|
796
|
+
three_j_plus_7 = 7 - base_ring(6*offset)
|
|
797
|
+
six = base_ring(6)
|
|
798
|
+
|
|
799
|
+
for i in range(offset):
|
|
800
|
+
|
|
801
|
+
j = 2*(i-offset)
|
|
802
|
+
a = next_a
|
|
803
|
+
next_a = coeffs[i+1]
|
|
804
|
+
|
|
805
|
+
# todo: the following divisions will sometimes involve
|
|
806
|
+
# a division by (a power of) p. In all cases, we know (from
|
|
807
|
+
# Kedlaya's estimates) that the answer should be p-integral.
|
|
808
|
+
# However, since we're working over $Z/p^k Z$, we're not allowed
|
|
809
|
+
# to "divide by p". So currently we lift to Q, divide, and coerce
|
|
810
|
+
# back. Eventually, when pAdicInteger is implemented, and plays
|
|
811
|
+
# nicely with pAdicField, we should reimplement this stuff
|
|
812
|
+
# using pAdicInteger.
|
|
813
|
+
|
|
814
|
+
if (p.divides(j+1)):
|
|
815
|
+
# need to lift here to perform the division
|
|
816
|
+
a[0] = base_ring(lift(a[0]) / (j+1))
|
|
817
|
+
a[1] = base_ring(lift(a[1]) / (j+1))
|
|
818
|
+
a[2] = base_ring(lift(a[2]) / (j+1))
|
|
819
|
+
else:
|
|
820
|
+
j_plus_1_inv = ~base_ring(j+1)
|
|
821
|
+
a[0] = a[0] * j_plus_1_inv
|
|
822
|
+
a[1] = a[1] * j_plus_1_inv
|
|
823
|
+
a[2] = a[2] * j_plus_1_inv
|
|
824
|
+
|
|
825
|
+
c1 = m[3]*a[0] + m[4]*a[1] + m[5]*a[2]
|
|
826
|
+
c2 = m[6]*a[0] + m[7]*a[1] + m[8]*a[2]
|
|
827
|
+
next_a[0] = next_a[0] - three_j_plus_5 * c1
|
|
828
|
+
next_a[1] = next_a[1] - three_j_plus_7 * c2
|
|
829
|
+
|
|
830
|
+
three_j_plus_7 = three_j_plus_7 + six
|
|
831
|
+
three_j_plus_5 = three_j_plus_5 + six
|
|
832
|
+
|
|
833
|
+
if exact_form is not None:
|
|
834
|
+
c0 = m[0]*a[0] + m[1]*a[1] + m[2]*a[2]
|
|
835
|
+
exact_form += (c0 + c1*x + c2 * x**2) * y**(j+1)
|
|
836
|
+
|
|
837
|
+
except NotImplementedError:
|
|
838
|
+
raise NotImplementedError("It looks like you've found a "
|
|
839
|
+
"non-integral matrix of Frobenius! "
|
|
840
|
+
"(Q=%s, p=%s)\nTime to write a paper." % (Q, p))
|
|
841
|
+
|
|
842
|
+
coeffs[int(offset)] = next_a
|
|
843
|
+
|
|
844
|
+
return exact_form
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def reduce_positive(Q, p, coeffs, offset, exact_form=None):
|
|
848
|
+
"""
|
|
849
|
+
Apply cohomology relations to incorporate positive powers of
|
|
850
|
+
`y` into the `y^0` term.
|
|
851
|
+
|
|
852
|
+
INPUT:
|
|
853
|
+
|
|
854
|
+
- ``Q`` -- cubic polynomial
|
|
855
|
+
|
|
856
|
+
- ``coeffs`` -- list of length 3 lists. The
|
|
857
|
+
`i`-th list [a, b, c] represents
|
|
858
|
+
`y^{2(i - offset)} (a + bx + cx^2) dx/y`.
|
|
859
|
+
|
|
860
|
+
- ``offset`` -- nonnegative integer
|
|
861
|
+
|
|
862
|
+
OUTPUT:
|
|
863
|
+
|
|
864
|
+
The reduction is performed in-place. The output is placed
|
|
865
|
+
in coeffs[offset]. Note that coeffs[i] will be meaningless for i
|
|
866
|
+
offset after this function is finished.
|
|
867
|
+
|
|
868
|
+
EXAMPLES::
|
|
869
|
+
|
|
870
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
871
|
+
sage: Q = x^3 - x + R(1/4)
|
|
872
|
+
|
|
873
|
+
::
|
|
874
|
+
|
|
875
|
+
sage: coeffs = [[1, 2, 3], [10, 15, 20]]
|
|
876
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
877
|
+
sage: monsky_washnitzer.reduce_positive(Q, 5, coeffs, 0)
|
|
878
|
+
sage: coeffs[0]
|
|
879
|
+
[16, 102, 88]
|
|
880
|
+
|
|
881
|
+
::
|
|
882
|
+
|
|
883
|
+
sage: coeffs = [[9, 8, 7], [10, 15, 20]]
|
|
884
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
885
|
+
sage: monsky_washnitzer.reduce_positive(Q, 5, coeffs, 0)
|
|
886
|
+
sage: coeffs[0]
|
|
887
|
+
[24, 108, 92]
|
|
888
|
+
"""
|
|
889
|
+
|
|
890
|
+
base_ring = Q.base_ring()
|
|
891
|
+
next_a = coeffs[len(coeffs) - 1]
|
|
892
|
+
|
|
893
|
+
Qa = Q[1]
|
|
894
|
+
Qb = Q[0]
|
|
895
|
+
|
|
896
|
+
A = 2*Qa
|
|
897
|
+
B = 3*Qb
|
|
898
|
+
|
|
899
|
+
offset = Integer(offset)
|
|
900
|
+
|
|
901
|
+
if exact_form is not None:
|
|
902
|
+
x = exact_form.parent().gen(0)
|
|
903
|
+
y = exact_form.parent().base_ring().gen(0)
|
|
904
|
+
# y = exact_form.parent()(exact_form.parent().base_ring().gen(0))
|
|
905
|
+
|
|
906
|
+
for i in range(len(coeffs)-1, offset, -1):
|
|
907
|
+
j = 2*(i-offset) - 2
|
|
908
|
+
a = next_a
|
|
909
|
+
next_a = coeffs[i-1]
|
|
910
|
+
|
|
911
|
+
a[0] = a[0] - Qa*a[2]/3 # subtract d(y^j + 3)
|
|
912
|
+
if exact_form is not None:
|
|
913
|
+
exact_form += Q.base_ring()(a[2].lift() / (3*j+9)) * y**(j+3)
|
|
914
|
+
|
|
915
|
+
# todo: see comments about pAdicInteger in reduceNegative()
|
|
916
|
+
|
|
917
|
+
# subtract off c1 of d(x y^j + 1), and
|
|
918
|
+
if p.divides(3*j + 5):
|
|
919
|
+
c1 = base_ring(lift(a[0]) / (3*j + 5))
|
|
920
|
+
else:
|
|
921
|
+
c1 = a[0] / (3*j + 5)
|
|
922
|
+
|
|
923
|
+
# subtract off c2 of d(x^2 y^j + 1)
|
|
924
|
+
if p.divides(3*j + 7):
|
|
925
|
+
c2 = base_ring(lift(a[1]) / (3*j + 7))
|
|
926
|
+
else:
|
|
927
|
+
c2 = a[1] / (3*j + 7)
|
|
928
|
+
|
|
929
|
+
next_a[0] = next_a[0] + B*c1*(j+1)
|
|
930
|
+
next_a[1] = next_a[1] + A*c1*(j+1) + B*c2*(j+1)
|
|
931
|
+
next_a[2] = next_a[2] + A*c2*(j+1)
|
|
932
|
+
|
|
933
|
+
if exact_form is not None:
|
|
934
|
+
exact_form += (c1*x + c2 * x**2) * y**(j+1)
|
|
935
|
+
|
|
936
|
+
coeffs[int(offset)] = next_a
|
|
937
|
+
|
|
938
|
+
return exact_form
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
def reduce_zero(Q, coeffs, offset, exact_form=None):
|
|
942
|
+
"""
|
|
943
|
+
Apply cohomology relation to incorporate `x^2 y^0` term
|
|
944
|
+
into `x^0 y^0` and `x^1 y^0` terms.
|
|
945
|
+
|
|
946
|
+
INPUT:
|
|
947
|
+
|
|
948
|
+
- ``Q`` -- cubic polynomial
|
|
949
|
+
|
|
950
|
+
- ``coeffs`` -- list of length 3 lists. The
|
|
951
|
+
`i`-th list [a, b, c] represents
|
|
952
|
+
`y^{2(i - offset)} (a + bx + cx^2) dx/y`.
|
|
953
|
+
|
|
954
|
+
- ``offset`` -- nonnegative integer
|
|
955
|
+
|
|
956
|
+
OUTPUT:
|
|
957
|
+
|
|
958
|
+
The reduction is performed in-place. The output is placed
|
|
959
|
+
in coeffs[offset]. This method completely ignores coeffs[i] for i
|
|
960
|
+
!= offset.
|
|
961
|
+
|
|
962
|
+
EXAMPLES::
|
|
963
|
+
|
|
964
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
965
|
+
sage: Q = x^3 - x + R(1/4)
|
|
966
|
+
sage: coeffs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
967
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
968
|
+
sage: monsky_washnitzer.reduce_zero(Q, coeffs, 1)
|
|
969
|
+
sage: coeffs[1]
|
|
970
|
+
[6, 5, 0]
|
|
971
|
+
"""
|
|
972
|
+
|
|
973
|
+
a = coeffs[int(offset)]
|
|
974
|
+
if a[2] == 0:
|
|
975
|
+
return exact_form
|
|
976
|
+
|
|
977
|
+
Qa = Q[1]
|
|
978
|
+
|
|
979
|
+
a[0] = a[0] - a[2]*Qa/3 # $3x^2 dx/y = -a dx/y$
|
|
980
|
+
|
|
981
|
+
coeffs[int(offset)] = a
|
|
982
|
+
|
|
983
|
+
if exact_form is not None:
|
|
984
|
+
y = exact_form.parent()(exact_form.parent().base_ring().gen(0))
|
|
985
|
+
exact_form += Q.base_ring()(a[2] / 3) * y
|
|
986
|
+
|
|
987
|
+
a[2] = 0
|
|
988
|
+
|
|
989
|
+
coeffs[int(offset)] = a
|
|
990
|
+
return exact_form
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def reduce_all(Q, p, coeffs, offset, compute_exact_form=False):
|
|
994
|
+
"""
|
|
995
|
+
Apply cohomology relations to reduce all terms to a linear
|
|
996
|
+
combination of `dx/y` and `x dx/y`.
|
|
997
|
+
|
|
998
|
+
INPUT:
|
|
999
|
+
|
|
1000
|
+
- ``Q`` -- cubic polynomial
|
|
1001
|
+
|
|
1002
|
+
- ``coeffs`` -- list of length 3 lists. The
|
|
1003
|
+
`i`-th list [a, b, c] represents
|
|
1004
|
+
`y^{2(i - offset)} (a + bx + cx^2) dx/y`.
|
|
1005
|
+
|
|
1006
|
+
- ``offset`` -- nonnegative integer
|
|
1007
|
+
|
|
1008
|
+
OUTPUT:
|
|
1009
|
+
|
|
1010
|
+
- ``A``, ``B`` -- pair such that the input differential is
|
|
1011
|
+
cohomologous to (A + Bx) dx/y
|
|
1012
|
+
|
|
1013
|
+
.. NOTE::
|
|
1014
|
+
|
|
1015
|
+
The algorithm operates in-place, so the data in coeffs is
|
|
1016
|
+
destroyed.
|
|
1017
|
+
|
|
1018
|
+
EXAMPLES::
|
|
1019
|
+
|
|
1020
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
1021
|
+
sage: Q = x^3 - x + R(1/4)
|
|
1022
|
+
sage: coeffs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
1023
|
+
sage: coeffs = [[R.base_ring()(a) for a in row] for row in coeffs]
|
|
1024
|
+
sage: monsky_washnitzer.reduce_all(Q, 5, coeffs, 1)
|
|
1025
|
+
(21, 106)
|
|
1026
|
+
"""
|
|
1027
|
+
|
|
1028
|
+
R = Q.base_ring()
|
|
1029
|
+
|
|
1030
|
+
if compute_exact_form:
|
|
1031
|
+
# exact_form = SpecialCubicQuotientRing(Q, laurent_series=True)(0)
|
|
1032
|
+
exact_form = PolynomialRing(LaurentSeriesRing(Q.base_ring(), 'y'), 'x').zero()
|
|
1033
|
+
# t = (Q.base_ring().order().factor())[0]
|
|
1034
|
+
# from sage.rings.padics.qp import pAdicField
|
|
1035
|
+
# exact_form = PolynomialRing(LaurentSeriesRing(pAdicField(p, t[1]), 'y'), 'x')(0)
|
|
1036
|
+
else:
|
|
1037
|
+
exact_form = None
|
|
1038
|
+
|
|
1039
|
+
while len(coeffs) <= offset:
|
|
1040
|
+
coeffs.append([R(0), R(0), R(0)])
|
|
1041
|
+
|
|
1042
|
+
exact_form = reduce_negative(Q, p, coeffs, offset, exact_form)
|
|
1043
|
+
exact_form = reduce_positive(Q, p, coeffs, offset, exact_form)
|
|
1044
|
+
exact_form = reduce_zero(Q, coeffs, offset, exact_form)
|
|
1045
|
+
|
|
1046
|
+
if exact_form is None:
|
|
1047
|
+
return coeffs[int(offset)][0], coeffs[int(offset)][1]
|
|
1048
|
+
else:
|
|
1049
|
+
return (coeffs[int(offset)][0], coeffs[int(offset)][1]), exact_form
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def frobenius_expansion_by_newton(Q, p, M):
|
|
1053
|
+
r"""
|
|
1054
|
+
Compute the action of Frobenius on `dx/y` and on
|
|
1055
|
+
`x dx/y`, using Newton's method (as suggested in Kedlaya's
|
|
1056
|
+
paper [Ked2001]_).
|
|
1057
|
+
|
|
1058
|
+
(This function does *not* yet use the cohomology relations - that
|
|
1059
|
+
happens afterwards in the "reduction" step.)
|
|
1060
|
+
|
|
1061
|
+
More specifically, it finds `F_0` and `F_1` in
|
|
1062
|
+
the quotient ring `R[x, T]/(T - Q(x))`, such that
|
|
1063
|
+
|
|
1064
|
+
.. MATH::
|
|
1065
|
+
|
|
1066
|
+
F( dx/y) = T^{-r} F0 dx/y, \text{\ and\ } F(x dx/y) = T^{-r} F1 dx/y
|
|
1067
|
+
|
|
1068
|
+
where
|
|
1069
|
+
|
|
1070
|
+
.. MATH::
|
|
1071
|
+
|
|
1072
|
+
r = ( (2M-3)p - 1 )/2.
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
(Here `T` is `y^2 = z^{-2}`, and `R` is the
|
|
1076
|
+
coefficient ring of `Q`.)
|
|
1077
|
+
|
|
1078
|
+
`F_0` and `F_1` are computed in the
|
|
1079
|
+
:class:`SpecialCubicQuotientRing` associated to `Q`, so all powers
|
|
1080
|
+
of `x^j` for `j \geq 3` are reduced to powers of
|
|
1081
|
+
`T`.
|
|
1082
|
+
|
|
1083
|
+
INPUT:
|
|
1084
|
+
|
|
1085
|
+
- ``Q`` -- cubic polynomial of the form
|
|
1086
|
+
`Q(x) = x^3 + ax + b`, whose coefficient ring is a
|
|
1087
|
+
`Z/(p^M)Z`-algebra
|
|
1088
|
+
|
|
1089
|
+
- ``p`` -- residue characteristic of the `p`-adic field
|
|
1090
|
+
|
|
1091
|
+
- ``M`` -- `p`-adic precision of the coefficient ring
|
|
1092
|
+
(this will be used to determine the number of Newton iterations)
|
|
1093
|
+
|
|
1094
|
+
OUTPUT:
|
|
1095
|
+
|
|
1096
|
+
- ``F0``, ``F1`` -- elements of
|
|
1097
|
+
``SpecialCubicQuotientRing(Q)``, as described above
|
|
1098
|
+
|
|
1099
|
+
- ``r`` -- nonnegative integer, as described above
|
|
1100
|
+
|
|
1101
|
+
EXAMPLES::
|
|
1102
|
+
|
|
1103
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import frobenius_expansion_by_newton
|
|
1104
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
1105
|
+
sage: Q = x^3 - x + R(1/4)
|
|
1106
|
+
sage: frobenius_expansion_by_newton(Q,5,3)
|
|
1107
|
+
((25*T^5 + 75*T^3 + 100*T^2 + 100*T + 100) + (5*T^6 + 80*T^5 + 100*T^3
|
|
1108
|
+
+ 25*T + 50)*x + (55*T^5 + 50*T^4 + 75*T^3 + 25*T^2 + 25*T + 25)*x^2,
|
|
1109
|
+
(5*T^8 + 15*T^7 + 95*T^6 + 10*T^5 + 25*T^4 + 25*T^3 + 100*T^2 + 50)
|
|
1110
|
+
+ (65*T^7 + 55*T^6 + 70*T^5 + 100*T^4 + 25*T^2 + 100*T)*x
|
|
1111
|
+
+ (15*T^6 + 115*T^5 + 75*T^4 + 100*T^3 + 50*T^2 + 75*T + 75)*x^2, 7)
|
|
1112
|
+
"""
|
|
1113
|
+
|
|
1114
|
+
S = SpecialCubicQuotientRing(Q)
|
|
1115
|
+
x, _ = S.gens() # T = y^2
|
|
1116
|
+
base_ring = S.base_ring()
|
|
1117
|
+
|
|
1118
|
+
# When we compute Frob(1/y) we actually only need precision M-1, since
|
|
1119
|
+
# we're going to multiply by p at the end anyway.
|
|
1120
|
+
M = float(M - 1)
|
|
1121
|
+
|
|
1122
|
+
# Kedlaya sets s = Q(x^p)/T^p = 1 + p T^{-p} E, where
|
|
1123
|
+
# E = (Q(x^p) - Q(x)^p) / p (has integral coefficients).
|
|
1124
|
+
# Then he computes s^{-1/2} in S, using Newton's method to find
|
|
1125
|
+
# successive approximations. We follow this plan, but we normalise our
|
|
1126
|
+
# approximations so that we only ever need positive powers of T.
|
|
1127
|
+
|
|
1128
|
+
# Start by setting r = Q(x^p)/2 = 1/2 T^p s.
|
|
1129
|
+
# (The 1/2 is for convenience later on.)
|
|
1130
|
+
x_to_p_less_one = x**(p-1)
|
|
1131
|
+
x_to_p = x_to_p_less_one * x
|
|
1132
|
+
x_to_p_cubed = x_to_p.square() * x_to_p
|
|
1133
|
+
r = (base_ring(1) / base_ring(2)) * (x_to_p_cubed + Q[1]*x_to_p + S(Q[0]))
|
|
1134
|
+
|
|
1135
|
+
# todo: this next loop would be clearer if it used the newton_method_sizes()
|
|
1136
|
+
# function
|
|
1137
|
+
|
|
1138
|
+
# We will start with a hard-coded initial approximation, which we provide
|
|
1139
|
+
# up to precision 3. First work out what precision is best to start with.
|
|
1140
|
+
if M <= 3:
|
|
1141
|
+
initial_precision = M
|
|
1142
|
+
elif ceil(log(M/2, 2)) == ceil(log(M/3, 2)):
|
|
1143
|
+
# In this case there is no advantage to starting with precision three,
|
|
1144
|
+
# because we'll overshoot at the end. E.g. suppose the final precision
|
|
1145
|
+
# is 8. If we start with precision 2, we need two iterations to get us
|
|
1146
|
+
# to 8. If we start at precision 3, we will still need two iterations,
|
|
1147
|
+
# but we do more work along the way. So may as well start with only 2.
|
|
1148
|
+
initial_precision = 2
|
|
1149
|
+
else:
|
|
1150
|
+
initial_precision = 3
|
|
1151
|
+
|
|
1152
|
+
# Now compute the first approximation. In the main loop below, X is the
|
|
1153
|
+
# normalised approximation, and k is the precision. More specifically,
|
|
1154
|
+
# X = T^{p(k-1)} x_i, where x_i is an approximation to s^{-1/2}, and the
|
|
1155
|
+
# approximation is correct mod p^k.
|
|
1156
|
+
if initial_precision == 1:
|
|
1157
|
+
k = 1
|
|
1158
|
+
X = S(1)
|
|
1159
|
+
elif initial_precision == 2:
|
|
1160
|
+
# approximation is 3/2 - 1/2 s
|
|
1161
|
+
k = 2
|
|
1162
|
+
X = S(base_ring(3) / base_ring(2)).shift(p) - r
|
|
1163
|
+
elif initial_precision == 3:
|
|
1164
|
+
# approximation is (15 - 10 s + 3 s^2) / 8
|
|
1165
|
+
k = 3
|
|
1166
|
+
X = (base_ring(1) / base_ring(8)) * (S(15).shift(2*p)
|
|
1167
|
+
- (base_ring(20) * r).shift(p) +
|
|
1168
|
+
(base_ring(12) * r.square()))
|
|
1169
|
+
# The key to the following calculation is that the T^{-m} coefficient
|
|
1170
|
+
# of every x_i is divisible by p^(ceil(m/p)) (for m >= 0). Therefore if
|
|
1171
|
+
# we are only expecting an answer correct mod p^k, we can truncate
|
|
1172
|
+
# beyond the T^{-(k-1)p} term without any problems.
|
|
1173
|
+
|
|
1174
|
+
# todo: what would be really nice is to be able to work in a lower
|
|
1175
|
+
# precision *coefficient ring* when we start the iteration, and move up to
|
|
1176
|
+
# higher precision rings as the iteration proceeds. This would be feasible
|
|
1177
|
+
# over Integers(p**n), but quite complicated (maybe impossible) over a more
|
|
1178
|
+
# general base ring. This might give a decent constant factor speedup;
|
|
1179
|
+
# or it might not, depending on how much the last iteration dominates the
|
|
1180
|
+
# whole runtime. My guess is that it isn't worth the effort.
|
|
1181
|
+
|
|
1182
|
+
three_halves = base_ring(3) / base_ring(2)
|
|
1183
|
+
|
|
1184
|
+
# Newton iteration loop
|
|
1185
|
+
while k < M:
|
|
1186
|
+
# target_k = k' = precision we want our answer to be after this iteration
|
|
1187
|
+
target_k = 2*k
|
|
1188
|
+
|
|
1189
|
+
# This prevents us overshooting. For example if the current precision
|
|
1190
|
+
# is 3 and we want to get to 10, we're better off going up to 5
|
|
1191
|
+
# instead of 6, because it is less work to get from 5 to 10 than it
|
|
1192
|
+
# is to get from 6 to 10.
|
|
1193
|
+
if ceil(log(M/target_k, 2)) == ceil(log(M/(target_k-1), 2)):
|
|
1194
|
+
target_k -= 1
|
|
1195
|
+
|
|
1196
|
+
# temp = T^{p(3k-2)} 1/2 s x_i^3
|
|
1197
|
+
temp = X.square() * (X * r)
|
|
1198
|
+
|
|
1199
|
+
# We know that the final result is only going to be correct mod
|
|
1200
|
+
# p^(target_k), so we might as well truncate the extraneous terms now.
|
|
1201
|
+
# temp = T^{p(k'-1)} 1/2 s x_i^3
|
|
1202
|
+
temp = temp.shift(-p*(3*k - target_k - 1))
|
|
1203
|
+
|
|
1204
|
+
# X = T^{p(k'-1)} (3/2 x_i - 1/2 s x_i^3)
|
|
1205
|
+
# = T^{p(k'-1)} x_{i+1}
|
|
1206
|
+
X = (three_halves * X).shift(p*(target_k - k)) - temp
|
|
1207
|
+
|
|
1208
|
+
k = target_k
|
|
1209
|
+
|
|
1210
|
+
# Now k should equal M, since we're up to the correct precision
|
|
1211
|
+
assert k == M, "Oops, something went wrong in the iteration"
|
|
1212
|
+
|
|
1213
|
+
# We should have s^{-1/2} correct to precision M.
|
|
1214
|
+
# The following line can be uncommented to verify this.
|
|
1215
|
+
# (It is a slow verification though, can double the whole computation time.)
|
|
1216
|
+
|
|
1217
|
+
# assert (p * X.square() * r * base_ring(2)).coeffs() == \
|
|
1218
|
+
# R(p).shift(p*(2*M - 1)).coeffs()
|
|
1219
|
+
|
|
1220
|
+
# Finally incorporate frobenius of dx and x dx, and choose offset that
|
|
1221
|
+
# compensates for our normalisations by powers of T.
|
|
1222
|
+
F0 = base_ring(p) * x_to_p_less_one * X
|
|
1223
|
+
F1 = F0 * x_to_p
|
|
1224
|
+
offset = ((2*k-1)*p - 1)/2
|
|
1225
|
+
|
|
1226
|
+
return F0, F1, offset
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
def frobenius_expansion_by_series(Q, p, M):
|
|
1230
|
+
r"""
|
|
1231
|
+
Compute the action of Frobenius on `dx/y` and on `x dx/y`, using a
|
|
1232
|
+
series expansion.
|
|
1233
|
+
|
|
1234
|
+
(This function computes the same thing as
|
|
1235
|
+
frobenius_expansion_by_newton(), using a different method.
|
|
1236
|
+
Theoretically the Newton method should be asymptotically faster,
|
|
1237
|
+
when the precision gets large. However, in practice, this functions
|
|
1238
|
+
seems to be marginally faster for moderate precision, so I'm
|
|
1239
|
+
keeping it here until I figure out exactly why it is faster.)
|
|
1240
|
+
|
|
1241
|
+
(This function does *not* yet use the cohomology relations - that
|
|
1242
|
+
happens afterwards in the "reduction" step.)
|
|
1243
|
+
|
|
1244
|
+
More specifically, it finds F0 and F1 in the quotient ring
|
|
1245
|
+
`R[x, T]/(T - Q(x))`, such that
|
|
1246
|
+
`F( dx/y) = T^{-r} F0 dx/y`, and
|
|
1247
|
+
`F(x dx/y) = T^{-r} F1 dx/y` where
|
|
1248
|
+
`r = ( (2M-3)p - 1 )/2`. (Here `T` is `y^2 = z^{-2}`,
|
|
1249
|
+
and `R` is the coefficient ring of `Q`.)
|
|
1250
|
+
|
|
1251
|
+
`F_0` and `F_1` are computed in the
|
|
1252
|
+
:class:`SpecialCubicQuotientRing` associated to `Q`, so all powers
|
|
1253
|
+
of `x^j` for `j \geq 3` are reduced to powers of
|
|
1254
|
+
`T`.
|
|
1255
|
+
|
|
1256
|
+
It uses the sum
|
|
1257
|
+
|
|
1258
|
+
.. MATH::
|
|
1259
|
+
|
|
1260
|
+
F0 = \sum_{k=0}^{M-2} \binom{-1/2}{k} p x^{p-1} E^k T^{(M-2-k)p}
|
|
1261
|
+
|
|
1262
|
+
and
|
|
1263
|
+
|
|
1264
|
+
.. MATH::
|
|
1265
|
+
|
|
1266
|
+
F1 = x^p F0,
|
|
1267
|
+
|
|
1268
|
+
where `E = Q(x^p) - Q(x)^p`.
|
|
1269
|
+
|
|
1270
|
+
INPUT:
|
|
1271
|
+
|
|
1272
|
+
- ``Q`` -- cubic polynomial of the form
|
|
1273
|
+
`Q(x) = x^3 + ax + b`, whose coefficient ring is a
|
|
1274
|
+
`\ZZ/(p^M)\ZZ` -algebra
|
|
1275
|
+
|
|
1276
|
+
- ``p`` -- residue characteristic of the `p`-adic field
|
|
1277
|
+
|
|
1278
|
+
- ``M`` -- `p`-adic precision of the coefficient ring
|
|
1279
|
+
(this will be used to determine the number of terms in the
|
|
1280
|
+
series)
|
|
1281
|
+
|
|
1282
|
+
OUTPUT:
|
|
1283
|
+
|
|
1284
|
+
- ``F0``, ``F1`` -- elements of
|
|
1285
|
+
``SpecialCubicQuotientRing(Q)``, as described above
|
|
1286
|
+
|
|
1287
|
+
- ``r`` -- nonnegative integer, as described above
|
|
1288
|
+
|
|
1289
|
+
EXAMPLES::
|
|
1290
|
+
|
|
1291
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import frobenius_expansion_by_series
|
|
1292
|
+
sage: R.<x> = Integers(5^3)['x']
|
|
1293
|
+
sage: Q = x^3 - x + R(1/4)
|
|
1294
|
+
sage: frobenius_expansion_by_series(Q,5,3) # needs sage.libs.pari
|
|
1295
|
+
((25*T^5 + 75*T^3 + 100*T^2 + 100*T + 100) + (5*T^6 + 80*T^5 + 100*T^3
|
|
1296
|
+
+ 25*T + 50)*x + (55*T^5 + 50*T^4 + 75*T^3 + 25*T^2 + 25*T + 25)*x^2,
|
|
1297
|
+
(5*T^8 + 15*T^7 + 95*T^6 + 10*T^5 + 25*T^4 + 25*T^3 + 100*T^2 + 50)
|
|
1298
|
+
+ (65*T^7 + 55*T^6 + 70*T^5 + 100*T^4 + 25*T^2 + 100*T)*x
|
|
1299
|
+
+ (15*T^6 + 115*T^5 + 75*T^4 + 100*T^3 + 50*T^2 + 75*T + 75)*x^2, 7)
|
|
1300
|
+
"""
|
|
1301
|
+
|
|
1302
|
+
S = SpecialCubicQuotientRing(Q)
|
|
1303
|
+
x, _ = S.gens()
|
|
1304
|
+
base_ring = S.base_ring()
|
|
1305
|
+
|
|
1306
|
+
x_to_p_less_1 = x**(p-1)
|
|
1307
|
+
x_to_p = x_to_p_less_1 * x
|
|
1308
|
+
|
|
1309
|
+
# compute frobQ = Q(x^p)
|
|
1310
|
+
x_to_p_squared = x_to_p * x_to_p
|
|
1311
|
+
x_to_p_cubed = x_to_p_squared * x_to_p
|
|
1312
|
+
frobQ = x_to_p_cubed + Q[1]*x_to_p + Q[0]*S.one()
|
|
1313
|
+
# anticipating the day when p = 3 is supported:
|
|
1314
|
+
# frobQ = x_to_p_cubed + Q[2]*x_to_p_squared + Q[1]*x_to_p + Q[0]*S(1)
|
|
1315
|
+
|
|
1316
|
+
E = frobQ - S.one().shift(p) # E = Q(x^p) - Q(x)^p
|
|
1317
|
+
|
|
1318
|
+
offset = int(((2*M-3)*p-1) / 2)
|
|
1319
|
+
term = p * x_to_p_less_1
|
|
1320
|
+
F0 = term.shift((M-2) * p)
|
|
1321
|
+
|
|
1322
|
+
# todo: Possible speedup idea, perhaps by a factor of 2, but
|
|
1323
|
+
# it requires a lot of work:
|
|
1324
|
+
# Note that p divides E, so p^k divides E^k. So when we are
|
|
1325
|
+
# working with high powers of E, we're doing a lot more work
|
|
1326
|
+
# in the multiplications than we need to. To take advantage of
|
|
1327
|
+
# this we would need some protocol for "lowering the precision"
|
|
1328
|
+
# of a SpecialCubicQuotientRing. This would be quite messy to
|
|
1329
|
+
# do properly over an arbitrary base ring. Perhaps it is
|
|
1330
|
+
# feasible to do for the most common case (i.e. Z/p^nZ).
|
|
1331
|
+
# (but it probably won't save much time unless p^n is very
|
|
1332
|
+
# large, because the machine word size is probably pretty
|
|
1333
|
+
# big anyway.)
|
|
1334
|
+
|
|
1335
|
+
for k in range(1, int(M - 1)):
|
|
1336
|
+
term = term * E
|
|
1337
|
+
c = base_ring(binomial(QQ((-1, 2)), k))
|
|
1338
|
+
F0 += (term * c).shift((M - k - 2) * p)
|
|
1339
|
+
|
|
1340
|
+
return F0, F0 * x_to_p, offset
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
def adjusted_prec(p, prec):
|
|
1344
|
+
r"""
|
|
1345
|
+
Compute how much precision is required in ``matrix_of_frobenius`` to
|
|
1346
|
+
get an answer correct to ``prec`` `p`-adic digits.
|
|
1347
|
+
|
|
1348
|
+
The issue is that the algorithm used in
|
|
1349
|
+
:func:`matrix_of_frobenius` sometimes performs divisions by `p`,
|
|
1350
|
+
so precision is lost during the algorithm.
|
|
1351
|
+
|
|
1352
|
+
The estimate returned by this function is based on Kedlaya's result
|
|
1353
|
+
(Lemmas 2 and 3 of [Ked2001]_),
|
|
1354
|
+
which implies that if we start with `M` `p`-adic
|
|
1355
|
+
digits, the total precision loss is at most
|
|
1356
|
+
`1 + \lfloor \log_p(2M-3) \rfloor` `p`-adic
|
|
1357
|
+
digits. (This estimate is somewhat less than the amount you would
|
|
1358
|
+
expect by naively counting the number of divisions by
|
|
1359
|
+
`p`.)
|
|
1360
|
+
|
|
1361
|
+
INPUT:
|
|
1362
|
+
|
|
1363
|
+
- ``p`` -- a prime ``p >= 5``
|
|
1364
|
+
|
|
1365
|
+
- ``prec`` -- integer; desired output precision, ``prec >= 1``
|
|
1366
|
+
|
|
1367
|
+
OUTPUT: adjusted precision (usually slightly more than ``prec``)
|
|
1368
|
+
|
|
1369
|
+
EXAMPLES::
|
|
1370
|
+
|
|
1371
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import adjusted_prec
|
|
1372
|
+
sage: adjusted_prec(5,2)
|
|
1373
|
+
3
|
|
1374
|
+
"""
|
|
1375
|
+
# initial estimate:
|
|
1376
|
+
if prec <= 2:
|
|
1377
|
+
defect = 0
|
|
1378
|
+
adjusted = 2
|
|
1379
|
+
else:
|
|
1380
|
+
defect = Integer(2 * prec - 3).exact_log(p)
|
|
1381
|
+
adjusted = prec + defect - 1
|
|
1382
|
+
|
|
1383
|
+
# increase it until we have enough
|
|
1384
|
+
while adjusted - defect - 1 < prec:
|
|
1385
|
+
adjusted += 1
|
|
1386
|
+
|
|
1387
|
+
return adjusted
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
def matrix_of_frobenius(Q, p, M, trace=None, compute_exact_forms=False):
|
|
1391
|
+
r"""
|
|
1392
|
+
Compute the matrix of Frobenius on Monsky-Washnitzer cohomology,
|
|
1393
|
+
with respect to the basis `(dx/y, x dx/y)`.
|
|
1394
|
+
|
|
1395
|
+
INPUT:
|
|
1396
|
+
|
|
1397
|
+
- ``Q`` -- cubic polynomial `Q(x) = x^3 + ax + b`
|
|
1398
|
+
defining an elliptic curve `E` by
|
|
1399
|
+
`y^2 = Q(x)`. The coefficient ring of `Q` should be a
|
|
1400
|
+
`\ZZ/(p^M)\ZZ`-algebra in which the matrix of
|
|
1401
|
+
frobenius will be constructed.
|
|
1402
|
+
|
|
1403
|
+
- ``p`` -- prime >= 5 for which E has good reduction
|
|
1404
|
+
|
|
1405
|
+
- ``M`` -- integer >= 2; `p` -adic precision of the coefficient ring
|
|
1406
|
+
|
|
1407
|
+
- ``trace`` -- (optional) the trace of the matrix, if
|
|
1408
|
+
known in advance. This is easy to compute because it is just the
|
|
1409
|
+
`a_p` of the curve. If the trace is supplied,
|
|
1410
|
+
matrix_of_frobenius will use it to speed the computation (i.e. we
|
|
1411
|
+
know the determinant is `p`, so we have two conditions, so
|
|
1412
|
+
really only column of the matrix needs to be computed. it is
|
|
1413
|
+
actually a little more complicated than that, but that's the basic
|
|
1414
|
+
idea.) If trace=None, then both columns will be computed
|
|
1415
|
+
independently, and you can get a strong indication of correctness
|
|
1416
|
+
by verifying the trace afterwards.
|
|
1417
|
+
|
|
1418
|
+
.. WARNING::
|
|
1419
|
+
|
|
1420
|
+
THE RESULT WILL NOT NECESSARILY BE CORRECT TO M p-ADIC
|
|
1421
|
+
DIGITS. If you want prec digits of precision, you need to use
|
|
1422
|
+
the function adjusted_prec(), and then you need to reduce the
|
|
1423
|
+
answer mod `p^{\mathrm{prec}}` at the end.
|
|
1424
|
+
|
|
1425
|
+
OUTPUT:
|
|
1426
|
+
|
|
1427
|
+
`2 \times 2` matrix of Frobenius acting on Monsky-Washnitzer cohomology,
|
|
1428
|
+
with entries in the coefficient ring of ``Q``.
|
|
1429
|
+
|
|
1430
|
+
EXAMPLES:
|
|
1431
|
+
|
|
1432
|
+
A simple example::
|
|
1433
|
+
|
|
1434
|
+
sage: p = 5
|
|
1435
|
+
sage: prec = 3
|
|
1436
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec); M
|
|
1437
|
+
4
|
|
1438
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1439
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1440
|
+
sage: A
|
|
1441
|
+
[340 62]
|
|
1442
|
+
[ 70 533]
|
|
1443
|
+
|
|
1444
|
+
But the result is only accurate to ``prec`` digits::
|
|
1445
|
+
|
|
1446
|
+
sage: B = A.change_ring(Integers(p**prec))
|
|
1447
|
+
sage: B
|
|
1448
|
+
[90 62]
|
|
1449
|
+
[70 33]
|
|
1450
|
+
|
|
1451
|
+
Check trace (123 = -2 mod 125) and determinant::
|
|
1452
|
+
|
|
1453
|
+
sage: B.det()
|
|
1454
|
+
5
|
|
1455
|
+
sage: B.trace()
|
|
1456
|
+
123
|
|
1457
|
+
sage: EllipticCurve([-1, 1/4]).ap(5)
|
|
1458
|
+
-2
|
|
1459
|
+
|
|
1460
|
+
Try using the trace to speed up the calculation::
|
|
1461
|
+
|
|
1462
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4),
|
|
1463
|
+
....: p, M, -2)
|
|
1464
|
+
sage: A
|
|
1465
|
+
[ 90 62]
|
|
1466
|
+
[320 533]
|
|
1467
|
+
|
|
1468
|
+
Hmmm... it looks different, but that's because the trace of our
|
|
1469
|
+
first answer was only -2 modulo `5^3`, not -2 modulo
|
|
1470
|
+
`5^5`. So the right answer is::
|
|
1471
|
+
|
|
1472
|
+
sage: A.change_ring(Integers(p**prec))
|
|
1473
|
+
[90 62]
|
|
1474
|
+
[70 33]
|
|
1475
|
+
|
|
1476
|
+
Check it works with only one digit of precision::
|
|
1477
|
+
|
|
1478
|
+
sage: p = 5
|
|
1479
|
+
sage: prec = 1
|
|
1480
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1481
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1482
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1483
|
+
sage: A.change_ring(Integers(p))
|
|
1484
|
+
[0 2]
|
|
1485
|
+
[0 3]
|
|
1486
|
+
|
|
1487
|
+
Here is an example that is particularly badly conditioned for
|
|
1488
|
+
using the trace trick::
|
|
1489
|
+
|
|
1490
|
+
sage: # needs sage.libs.pari
|
|
1491
|
+
sage: p = 11
|
|
1492
|
+
sage: prec = 3
|
|
1493
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1494
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1495
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 + 7*x + 8, p, M)
|
|
1496
|
+
sage: A.change_ring(Integers(p**prec))
|
|
1497
|
+
[1144 176]
|
|
1498
|
+
[ 847 185]
|
|
1499
|
+
|
|
1500
|
+
The problem here is that the top-right entry is divisible by 11,
|
|
1501
|
+
and the bottom-left entry is divisible by `11^2`. So when
|
|
1502
|
+
you apply the trace trick, neither `F(dx/y)` nor
|
|
1503
|
+
`F(x dx/y)` is enough to compute the whole matrix to the
|
|
1504
|
+
desired precision, even if you try increasing the target precision
|
|
1505
|
+
by one. Nevertheless, ``matrix_of_frobenius`` knows
|
|
1506
|
+
how to get the right answer by evaluating `F((x+1) dx/y)`
|
|
1507
|
+
instead::
|
|
1508
|
+
|
|
1509
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 + 7*x + 8, p, M, -2)
|
|
1510
|
+
sage: A.change_ring(Integers(p**prec))
|
|
1511
|
+
[1144 176]
|
|
1512
|
+
[ 847 185]
|
|
1513
|
+
|
|
1514
|
+
The running time is about ``O(p*prec**2)`` (times some logarithmic
|
|
1515
|
+
factors), so it is feasible to run on fairly large primes, or
|
|
1516
|
+
precision (or both?!?!)::
|
|
1517
|
+
|
|
1518
|
+
sage: # long time, needs sage.libs.pari
|
|
1519
|
+
sage: p = 10007
|
|
1520
|
+
sage: prec = 2
|
|
1521
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1522
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1523
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1524
|
+
sage: B = A.change_ring(Integers(p**prec)); B
|
|
1525
|
+
[74311982 57996908]
|
|
1526
|
+
[95877067 25828133]
|
|
1527
|
+
sage: B.det()
|
|
1528
|
+
10007
|
|
1529
|
+
sage: B.trace()
|
|
1530
|
+
66
|
|
1531
|
+
sage: EllipticCurve([-1, 1/4]).ap(10007)
|
|
1532
|
+
66
|
|
1533
|
+
|
|
1534
|
+
::
|
|
1535
|
+
|
|
1536
|
+
sage: # long time, needs sage.libs.pari
|
|
1537
|
+
sage: p = 5
|
|
1538
|
+
sage: prec = 300
|
|
1539
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1540
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1541
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1542
|
+
sage: B = A.change_ring(Integers(p**prec))
|
|
1543
|
+
sage: B.det()
|
|
1544
|
+
5
|
|
1545
|
+
sage: -B.trace()
|
|
1546
|
+
2
|
|
1547
|
+
sage: EllipticCurve([-1, 1/4]).ap(5)
|
|
1548
|
+
-2
|
|
1549
|
+
|
|
1550
|
+
Let us check consistency of the results for a range of precisions::
|
|
1551
|
+
|
|
1552
|
+
sage: # long time, needs sage.libs.pari
|
|
1553
|
+
sage: p = 5
|
|
1554
|
+
sage: max_prec = 60
|
|
1555
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, max_prec)
|
|
1556
|
+
sage: R.<x> = PolynomialRing(Integers(p**M))
|
|
1557
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1558
|
+
sage: A = A.change_ring(Integers(p**max_prec))
|
|
1559
|
+
sage: result = []
|
|
1560
|
+
sage: for prec in range(1, max_prec):
|
|
1561
|
+
....: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1562
|
+
....: R.<x> = PolynomialRing(Integers(p^M),'x')
|
|
1563
|
+
....: B = monsky_washnitzer.matrix_of_frobenius(x^3 - x + R(1/4), p, M)
|
|
1564
|
+
....: B = B.change_ring(Integers(p**prec))
|
|
1565
|
+
....: result.append(B == A.change_ring(Integers(p**prec)))
|
|
1566
|
+
sage: result == [True] * (max_prec - 1)
|
|
1567
|
+
True
|
|
1568
|
+
|
|
1569
|
+
The remaining examples discuss what happens when you take the
|
|
1570
|
+
coefficient ring to be a power series ring; i.e. in effect you're
|
|
1571
|
+
looking at a family of curves.
|
|
1572
|
+
|
|
1573
|
+
The code does in fact work...
|
|
1574
|
+
|
|
1575
|
+
::
|
|
1576
|
+
|
|
1577
|
+
sage: # needs sage.libs.pari
|
|
1578
|
+
sage: p = 11
|
|
1579
|
+
sage: prec = 3
|
|
1580
|
+
sage: M = monsky_washnitzer.adjusted_prec(p, prec)
|
|
1581
|
+
sage: S.<t> = PowerSeriesRing(Integers(p**M), default_prec=4)
|
|
1582
|
+
sage: a = 7 + t + 3*t^2
|
|
1583
|
+
sage: b = 8 - 6*t + 17*t^2
|
|
1584
|
+
sage: R.<x> = PolynomialRing(S)
|
|
1585
|
+
sage: Q = x**3 + a*x + b
|
|
1586
|
+
sage: A = monsky_washnitzer.matrix_of_frobenius(Q, p, M) # long time
|
|
1587
|
+
sage: B = A.change_ring(PowerSeriesRing(Integers(p**prec), 't', # long time
|
|
1588
|
+
....: default_prec=4)); B
|
|
1589
|
+
[1144 + 264*t + 841*t^2 + 1025*t^3 + O(t^4) 176 + 1052*t + 216*t^2 + 523*t^3 + O(t^4)]
|
|
1590
|
+
[ 847 + 668*t + 81*t^2 + 424*t^3 + O(t^4) 185 + 341*t + 171*t^2 + 642*t^3 + O(t^4)]
|
|
1591
|
+
|
|
1592
|
+
The trace trick should work for power series rings too, even in the
|
|
1593
|
+
badly-conditioned case. Unfortunately I do not know how to compute
|
|
1594
|
+
the trace in advance, so I am not sure exactly how this would help.
|
|
1595
|
+
Also, I suspect the running time will be dominated by the
|
|
1596
|
+
expansion, so the trace trick will not really speed things up anyway.
|
|
1597
|
+
Another problem is that the determinant is not always p::
|
|
1598
|
+
|
|
1599
|
+
sage: B.det() # long time
|
|
1600
|
+
11 + 484*t^2 + 451*t^3 + O(t^4)
|
|
1601
|
+
|
|
1602
|
+
However, it appears that the determinant always has the property
|
|
1603
|
+
that if you substitute t - 11t, you do get the constant series p
|
|
1604
|
+
(mod p\*\*prec). Similarly for the trace. And since the parameter
|
|
1605
|
+
only really makes sense when it is divisible by p anyway, perhaps
|
|
1606
|
+
this is not a problem after all.
|
|
1607
|
+
"""
|
|
1608
|
+
M = int(M)
|
|
1609
|
+
if M < 2:
|
|
1610
|
+
raise ValueError("M (=%s) must be at least 2" % M)
|
|
1611
|
+
|
|
1612
|
+
base_ring = Q.base_ring()
|
|
1613
|
+
|
|
1614
|
+
# Expand out frobenius of dx/y and x dx/y.
|
|
1615
|
+
# (You can substitute frobenius_expansion_by_series here, that will work
|
|
1616
|
+
# as well. See its docstring for some performance notes.)
|
|
1617
|
+
F0, F1, offset = frobenius_expansion_by_newton(Q, p, M)
|
|
1618
|
+
# F0, F1, offset = frobenius_expansion_by_series(Q, p, M)
|
|
1619
|
+
|
|
1620
|
+
if compute_exact_forms:
|
|
1621
|
+
# we need to do all the work to get the exact expressions f such that F(x^i dx/y) = df + \sum a_i x^i dx/y
|
|
1622
|
+
F0_coeffs = transpose_list(F0.coeffs())
|
|
1623
|
+
F0_reduced, f_0 = reduce_all(Q, p, F0_coeffs, offset, True)
|
|
1624
|
+
|
|
1625
|
+
F1_coeffs = transpose_list(F1.coeffs())
|
|
1626
|
+
F1_reduced, f_1 = reduce_all(Q, p, F1_coeffs, offset, True)
|
|
1627
|
+
|
|
1628
|
+
elif M == 2:
|
|
1629
|
+
# This implies that only one digit of precision is valid, so we only need
|
|
1630
|
+
# to reduce the second column. Also, the trace doesn't help at all.
|
|
1631
|
+
|
|
1632
|
+
F0_reduced = [base_ring(0), base_ring(0)]
|
|
1633
|
+
|
|
1634
|
+
F1_coeffs = transpose_list(F1.coeffs())
|
|
1635
|
+
F1_reduced = reduce_all(Q, p, F1_coeffs, offset)
|
|
1636
|
+
|
|
1637
|
+
elif trace is None:
|
|
1638
|
+
# No trace provided, just reduce F(dx/y) and F(x dx/y) separately.
|
|
1639
|
+
|
|
1640
|
+
F0_coeffs = transpose_list(F0.coeffs())
|
|
1641
|
+
F0_reduced = reduce_all(Q, p, F0_coeffs, offset)
|
|
1642
|
+
|
|
1643
|
+
F1_coeffs = transpose_list(F1.coeffs())
|
|
1644
|
+
F1_reduced = reduce_all(Q, p, F1_coeffs, offset)
|
|
1645
|
+
|
|
1646
|
+
else:
|
|
1647
|
+
# Trace has been provided.
|
|
1648
|
+
|
|
1649
|
+
# In most cases this can be used to quickly compute F(dx/y) from
|
|
1650
|
+
# F(x dx/y). However, if we're unlucky, the (dx/y)-component of
|
|
1651
|
+
# F(x dx/y) (i.e. the top-right corner of the matrix) may be divisible
|
|
1652
|
+
# by p, in which case there isn't enough information to get the
|
|
1653
|
+
# (x dx/y)-component of F(dx/y) to the desired precision. When this
|
|
1654
|
+
# happens, it turns out that F((x+1) dx/y) always *does* give enough
|
|
1655
|
+
# information (together with the trace) to get both columns to the
|
|
1656
|
+
# desired precision.
|
|
1657
|
+
|
|
1658
|
+
# First however we need a quick way of telling whether the top-right
|
|
1659
|
+
# corner is divisible by p, i.e. we want to compute the second column
|
|
1660
|
+
# of the matrix mod p. We could do this by just running the entire
|
|
1661
|
+
# algorithm with M = 2 (which assures precision 1). Luckily, we've
|
|
1662
|
+
# already done most of the work by computing F1 to high precision; so
|
|
1663
|
+
# all we need to do is extract the coefficients that would correspond
|
|
1664
|
+
# to the first term of the series, and run the reduction on them.
|
|
1665
|
+
|
|
1666
|
+
# todo: actually we only need to do this reduction step mod p^2, not
|
|
1667
|
+
# mod p^M, which is what the code currently does. If the base ring
|
|
1668
|
+
# is Integers(p^M), then it is easy. Otherwise it is tricky to construct
|
|
1669
|
+
# the right ring, I don't know how to do it.
|
|
1670
|
+
|
|
1671
|
+
F1_coeffs = transpose_list(F1.coeffs())
|
|
1672
|
+
F1_modp_coeffs = F1_coeffs[int((M-2)*p):]
|
|
1673
|
+
# make a copy, because reduce_all will destroy the coefficients:
|
|
1674
|
+
F1_modp_coeffs = [list(row) for row in F1_modp_coeffs]
|
|
1675
|
+
F1_modp_offset = offset - (M-2)*p
|
|
1676
|
+
F1_modp_reduced = reduce_all(Q, p, F1_modp_coeffs, F1_modp_offset)
|
|
1677
|
+
|
|
1678
|
+
if F1_modp_reduced[0].is_unit():
|
|
1679
|
+
# If the first entry is invertible mod p, then F(x dx/y) is sufficient
|
|
1680
|
+
# to get the whole matrix.
|
|
1681
|
+
|
|
1682
|
+
F1_reduced = reduce_all(Q, p, F1_coeffs, offset)
|
|
1683
|
+
|
|
1684
|
+
F0_reduced = [base_ring(trace) - F1_reduced[1], None]
|
|
1685
|
+
# using that the determinant is p:
|
|
1686
|
+
F0_reduced[1] = (F0_reduced[0] * F1_reduced[1] - base_ring(p)) \
|
|
1687
|
+
/ F1_reduced[0]
|
|
1688
|
+
|
|
1689
|
+
else:
|
|
1690
|
+
# If the first entry is zero mod p, then F((x+1) dx/y) will be sufficient
|
|
1691
|
+
# to get the whole matrix. (Here we are using the fact that the second
|
|
1692
|
+
# entry *cannot* be zero mod p. This is guaranteed by some results in
|
|
1693
|
+
# section 3.2 of ``Computation of p-adic Heights and Log Convergence''
|
|
1694
|
+
# by Mazur, Stein, Tate. But let's quickly check it anyway :-))
|
|
1695
|
+
msg = "The second entry in the second column "
|
|
1696
|
+
msg += "should be invertible mod p!"
|
|
1697
|
+
assert F1_modp_reduced[1].is_unit(), msg
|
|
1698
|
+
|
|
1699
|
+
G0_coeffs = transpose_list((F0 + F1).coeffs())
|
|
1700
|
+
G0_reduced = reduce_all(Q, p, G0_coeffs, offset)
|
|
1701
|
+
|
|
1702
|
+
# Now G0_reduced expresses F((x+1) dx/y) in terms of dx/y and x dx/y.
|
|
1703
|
+
# Re-express this in terms of (x+1) dx/y and x dx/y.
|
|
1704
|
+
H0_reduced = [G0_reduced[0], G0_reduced[1] - G0_reduced[0]]
|
|
1705
|
+
|
|
1706
|
+
# The thing we're about to divide by better be a unit.
|
|
1707
|
+
msg = "The second entry in this column "
|
|
1708
|
+
msg += "should be invertible mod p!"
|
|
1709
|
+
assert H0_reduced[1].is_unit(), msg
|
|
1710
|
+
|
|
1711
|
+
# Figure out the second column using the trace...
|
|
1712
|
+
H1_reduced = [None, base_ring(trace) - H0_reduced[0]]
|
|
1713
|
+
# ... and using that the determinant is p:
|
|
1714
|
+
H1_reduced[0] = (H0_reduced[0] * H1_reduced[1] - base_ring(p)) \
|
|
1715
|
+
/ H0_reduced[1]
|
|
1716
|
+
|
|
1717
|
+
# Finally, change back to the usual basis (dx/y, x dx/y)
|
|
1718
|
+
F1_reduced = [H1_reduced[0],
|
|
1719
|
+
H1_reduced[0] + H1_reduced[1]]
|
|
1720
|
+
F0_reduced = [H0_reduced[0] - F1_reduced[0],
|
|
1721
|
+
H0_reduced[0] + H0_reduced[1] - F1_reduced[1]]
|
|
1722
|
+
|
|
1723
|
+
# One more sanity check: our final result should be congruent mod p
|
|
1724
|
+
# to the approximation we used earlier.
|
|
1725
|
+
msg = "The output matrix is not congruent mod p "
|
|
1726
|
+
msg += "to the approximation found earlier!"
|
|
1727
|
+
assert not (
|
|
1728
|
+
(F1_reduced[0] - F1_modp_reduced[0]).is_unit() or
|
|
1729
|
+
(F1_reduced[1] - F1_modp_reduced[1]).is_unit() or
|
|
1730
|
+
F0_reduced[0].is_unit() or F0_reduced[1].is_unit()), msg
|
|
1731
|
+
|
|
1732
|
+
if compute_exact_forms:
|
|
1733
|
+
return matrix(base_ring, 2, 2, [F0_reduced[0], F1_reduced[0],
|
|
1734
|
+
F0_reduced[1], F1_reduced[1]]), f_0, f_1
|
|
1735
|
+
else:
|
|
1736
|
+
return matrix(base_ring, 2, 2, [F0_reduced[0], F1_reduced[0],
|
|
1737
|
+
F0_reduced[1], F1_reduced[1]])
|
|
1738
|
+
|
|
1739
|
+
|
|
1740
|
+
# ****************************************************************************
|
|
1741
|
+
# This is a generalization of the above functionality for hyperelliptic curves.
|
|
1742
|
+
#
|
|
1743
|
+
# THIS IS A WORK IN PROGRESS.
|
|
1744
|
+
#
|
|
1745
|
+
# I tried to embed must stuff into the rings themselves rather than
|
|
1746
|
+
# just extract and manipulate lists of coefficients. Hence the implementations
|
|
1747
|
+
# below are much less optimized, so are much slower, but should hopefully be
|
|
1748
|
+
# easier to follow. (E.g. one can print/make sense of intermediate results.)
|
|
1749
|
+
#
|
|
1750
|
+
# AUTHOR:
|
|
1751
|
+
# -- Robert Bradshaw (2007-04)
|
|
1752
|
+
#
|
|
1753
|
+
# ****************************************************************************
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
def matrix_of_frobenius_hyperelliptic(Q, p=None, prec=None, M=None):
|
|
1757
|
+
r"""
|
|
1758
|
+
Compute the matrix of Frobenius on Monsky-Washnitzer cohomology,
|
|
1759
|
+
with respect to the basis `(dx/2y, x dx/2y, ...x^{d-2} dx/2y)`, where
|
|
1760
|
+
`d` is the degree of `Q`.
|
|
1761
|
+
|
|
1762
|
+
INPUT:
|
|
1763
|
+
|
|
1764
|
+
- ``Q`` -- monic polynomial `Q(x)`
|
|
1765
|
+
|
|
1766
|
+
- ``p`` -- prime `\geq 5` for which `E` has good reduction
|
|
1767
|
+
|
|
1768
|
+
- ``prec`` -- (optional) `p`-adic precision of the coefficient ring
|
|
1769
|
+
|
|
1770
|
+
- ``M`` -- (optional) adjusted `p`-adic precision of the coefficient ring
|
|
1771
|
+
|
|
1772
|
+
OUTPUT:
|
|
1773
|
+
|
|
1774
|
+
`(d-1)` x `(d-1)` matrix `M` of Frobenius on Monsky-Washnitzer cohomology,
|
|
1775
|
+
and list of differentials \{f_i \} such that
|
|
1776
|
+
|
|
1777
|
+
.. MATH::
|
|
1778
|
+
|
|
1779
|
+
\phi^* (x^i dx/2y) = df_i + M[i]*vec(dx/2y, ..., x^{d-2} dx/2y)
|
|
1780
|
+
|
|
1781
|
+
EXAMPLES::
|
|
1782
|
+
|
|
1783
|
+
sage: # needs sage.rings.padics
|
|
1784
|
+
sage: p = 5
|
|
1785
|
+
sage: prec = 3
|
|
1786
|
+
sage: R.<x> = QQ['x']
|
|
1787
|
+
sage: A,f = monsky_washnitzer.matrix_of_frobenius_hyperelliptic(x^5 - 2*x + 3, p, prec)
|
|
1788
|
+
sage: A
|
|
1789
|
+
[ 4*5 + O(5^3) 5 + 2*5^2 + O(5^3) 2 + 3*5 + 2*5^2 + O(5^3) 2 + 5 + 5^2 + O(5^3)]
|
|
1790
|
+
[ 3*5 + 5^2 + O(5^3) 3*5 + O(5^3) 4*5 + O(5^3) 2 + 5^2 + O(5^3)]
|
|
1791
|
+
[ 4*5 + 4*5^2 + O(5^3) 3*5 + 2*5^2 + O(5^3) 5 + 3*5^2 + O(5^3) 2*5 + 2*5^2 + O(5^3)]
|
|
1792
|
+
[ 5^2 + O(5^3) 5 + 4*5^2 + O(5^3) 4*5 + 3*5^2 + O(5^3) 2*5 + O(5^3)]
|
|
1793
|
+
"""
|
|
1794
|
+
try:
|
|
1795
|
+
from sage.misc.profiler import Profiler
|
|
1796
|
+
except ImportError:
|
|
1797
|
+
def prof():
|
|
1798
|
+
pass
|
|
1799
|
+
else:
|
|
1800
|
+
prof = Profiler()
|
|
1801
|
+
|
|
1802
|
+
prof("setup")
|
|
1803
|
+
if p is None:
|
|
1804
|
+
try:
|
|
1805
|
+
K = Q.base_ring()
|
|
1806
|
+
p = K.prime()
|
|
1807
|
+
prec = K.precision_cap()
|
|
1808
|
+
except AttributeError:
|
|
1809
|
+
raise ValueError("p and prec must be specified if Q is not "
|
|
1810
|
+
"defined over a p-adic ring")
|
|
1811
|
+
if M is None:
|
|
1812
|
+
M = adjusted_prec(p, prec)
|
|
1813
|
+
extra_prec_ring = Integers(p**M)
|
|
1814
|
+
# extra_prec_ring = pAdicField(p, M) # SLOW!
|
|
1815
|
+
|
|
1816
|
+
real_prec_ring = pAdicField(p, prec) # pAdicField(p, prec) # To capped absolute?
|
|
1817
|
+
S = SpecialHyperellipticQuotientRing(Q, extra_prec_ring, True)
|
|
1818
|
+
MW = S.monsky_washnitzer()
|
|
1819
|
+
prof("frob basis elements")
|
|
1820
|
+
F = MW.frob_basis_elements(M, p)
|
|
1821
|
+
|
|
1822
|
+
prof("rationalize")
|
|
1823
|
+
# do reduction over Q in case we have non-integral entries (and it is so much faster than padics)
|
|
1824
|
+
rational_S = S.change_ring(QQ)
|
|
1825
|
+
# this is a hack until pAdics are fast
|
|
1826
|
+
# (They are in the latest development bundle, but its not standard and I'd need to merge.
|
|
1827
|
+
# (it will periodically cast into this ring to reduce coefficient size)
|
|
1828
|
+
rational_S._prec_cap = p**M
|
|
1829
|
+
rational_S._p = p
|
|
1830
|
+
# S._p = p
|
|
1831
|
+
# rational_S(F[0]).reduce_fast()
|
|
1832
|
+
# prof("reduce others")
|
|
1833
|
+
|
|
1834
|
+
# rational_S = S.change_ring(pAdicField(p, M))
|
|
1835
|
+
F = [rational_S(F_i) for F_i in F]
|
|
1836
|
+
|
|
1837
|
+
prof("reduce")
|
|
1838
|
+
reduced = [F_i.reduce_fast(True) for F_i in F]
|
|
1839
|
+
|
|
1840
|
+
# but the coeffs are WAY more precision than they need to be
|
|
1841
|
+
|
|
1842
|
+
prof("make matrix")
|
|
1843
|
+
# now take care of precision capping
|
|
1844
|
+
M = matrix(real_prec_ring, [a for f, a in reduced])
|
|
1845
|
+
for i in range(M.ncols()):
|
|
1846
|
+
for j in range(M.nrows()):
|
|
1847
|
+
M[i, j] = M[i, j].add_bigoh(prec)
|
|
1848
|
+
return M.transpose(), [f for f, a in reduced]
|
|
1849
|
+
|
|
1850
|
+
|
|
1851
|
+
class SpecialHyperellipticQuotientElement(ModuleElement):
|
|
1852
|
+
r"""
|
|
1853
|
+
Element in the Hyperelliptic quotient ring.
|
|
1854
|
+
|
|
1855
|
+
EXAMPLES::
|
|
1856
|
+
|
|
1857
|
+
sage: R.<x> = QQ['x']
|
|
1858
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1859
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1860
|
+
sage: MW = x.parent()
|
|
1861
|
+
sage: MW(x + x**2 + y - 77)
|
|
1862
|
+
-(77-y)*1 + x + x^2
|
|
1863
|
+
"""
|
|
1864
|
+
def __init__(self, parent, val=0, offset=0, check=True):
|
|
1865
|
+
"""
|
|
1866
|
+
Elements in the Hyperelliptic quotient ring.
|
|
1867
|
+
|
|
1868
|
+
EXAMPLES::
|
|
1869
|
+
|
|
1870
|
+
sage: R.<x> = QQ['x']
|
|
1871
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1872
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1873
|
+
sage: MW = x.parent()
|
|
1874
|
+
sage: elt = MW(x + x**2 + y - 77)
|
|
1875
|
+
sage: TestSuite(elt).run()
|
|
1876
|
+
"""
|
|
1877
|
+
ModuleElement.__init__(self, parent)
|
|
1878
|
+
if not check:
|
|
1879
|
+
self._f = parent._poly_ring(val, check=False)
|
|
1880
|
+
return
|
|
1881
|
+
if isinstance(val, SpecialHyperellipticQuotientElement):
|
|
1882
|
+
R = parent.base_ring()
|
|
1883
|
+
self._f = parent._poly_ring([a.change_ring(R) for a in val._f])
|
|
1884
|
+
return
|
|
1885
|
+
if isinstance(val, tuple):
|
|
1886
|
+
val, offset = val
|
|
1887
|
+
if isinstance(val, list) and val and isinstance(val[0], FreeModuleElement):
|
|
1888
|
+
val = transpose_list(val)
|
|
1889
|
+
self._f = parent._poly_ring(val)
|
|
1890
|
+
if offset != 0:
|
|
1891
|
+
self._f = self._f.parent()([a << offset for a in self._f], check=False)
|
|
1892
|
+
|
|
1893
|
+
def _richcmp_(self, other, op) -> bool:
|
|
1894
|
+
"""
|
|
1895
|
+
Compare the elements.
|
|
1896
|
+
|
|
1897
|
+
EXAMPLES::
|
|
1898
|
+
|
|
1899
|
+
sage: R.<x> = QQ['x']
|
|
1900
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1901
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1902
|
+
sage: x == x
|
|
1903
|
+
True
|
|
1904
|
+
sage: x > y
|
|
1905
|
+
True
|
|
1906
|
+
"""
|
|
1907
|
+
return richcmp(self._f, other._f, op)
|
|
1908
|
+
|
|
1909
|
+
def change_ring(self, R):
|
|
1910
|
+
"""
|
|
1911
|
+
Return the same element after changing the base ring to `R`.
|
|
1912
|
+
|
|
1913
|
+
EXAMPLES::
|
|
1914
|
+
|
|
1915
|
+
sage: R.<x> = QQ['x']
|
|
1916
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1917
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1918
|
+
sage: MW = x.parent()
|
|
1919
|
+
sage: z = MW(x + x**2 + y - 77)
|
|
1920
|
+
sage: z.change_ring(AA).parent() # needs sage.rings.number_field
|
|
1921
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 36*x + 1)
|
|
1922
|
+
over Algebraic Real Field
|
|
1923
|
+
"""
|
|
1924
|
+
return self.parent().change_ring(R)(self)
|
|
1925
|
+
|
|
1926
|
+
def __call__(self, *x):
|
|
1927
|
+
"""
|
|
1928
|
+
Evaluate ``self`` at given arguments.
|
|
1929
|
+
|
|
1930
|
+
EXAMPLES::
|
|
1931
|
+
|
|
1932
|
+
sage: R.<x> = QQ['x']
|
|
1933
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1934
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1935
|
+
sage: MW = x.parent()
|
|
1936
|
+
sage: z = MW(x + x**2 + y - 77); z
|
|
1937
|
+
-(77-y)*1 + x + x^2
|
|
1938
|
+
sage: z(66)
|
|
1939
|
+
4345 + y
|
|
1940
|
+
sage: z(5,4)
|
|
1941
|
+
-43
|
|
1942
|
+
"""
|
|
1943
|
+
return self._f(*x)
|
|
1944
|
+
|
|
1945
|
+
def __invert__(self):
|
|
1946
|
+
"""
|
|
1947
|
+
Return the inverse of ``self``.
|
|
1948
|
+
|
|
1949
|
+
The general element in our ring is not invertible, but `y` may
|
|
1950
|
+
be. We do not want to pass to the fraction field.
|
|
1951
|
+
|
|
1952
|
+
EXAMPLES::
|
|
1953
|
+
|
|
1954
|
+
sage: R.<x> = QQ['x']
|
|
1955
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
1956
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1957
|
+
sage: MW = x.parent()
|
|
1958
|
+
sage: z = y**(-1) # indirect doctest
|
|
1959
|
+
sage: z.parent()
|
|
1960
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 36*x + 1)
|
|
1961
|
+
over Rational Field
|
|
1962
|
+
|
|
1963
|
+
sage: z = (x+y)**(-1) # indirect doctest
|
|
1964
|
+
Traceback (most recent call last):
|
|
1965
|
+
...
|
|
1966
|
+
ZeroDivisionError: element not invertible
|
|
1967
|
+
"""
|
|
1968
|
+
if self._f.degree() == 0 and self._f[0].is_unit():
|
|
1969
|
+
P = self.parent()
|
|
1970
|
+
return P.element_class(P, ~self._f[0])
|
|
1971
|
+
else:
|
|
1972
|
+
raise ZeroDivisionError("element not invertible")
|
|
1973
|
+
|
|
1974
|
+
def __bool__(self):
|
|
1975
|
+
"""
|
|
1976
|
+
Return ``True`` iff ``self`` is not zero.
|
|
1977
|
+
|
|
1978
|
+
EXAMPLES::
|
|
1979
|
+
|
|
1980
|
+
sage: R.<x> = QQ['x']
|
|
1981
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
1982
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1983
|
+
sage: bool(x)
|
|
1984
|
+
True
|
|
1985
|
+
"""
|
|
1986
|
+
return bool(self._f)
|
|
1987
|
+
|
|
1988
|
+
def __eq__(self, other):
|
|
1989
|
+
"""
|
|
1990
|
+
Return ``True`` iff ``self`` is equal to ``other``.
|
|
1991
|
+
|
|
1992
|
+
EXAMPLES::
|
|
1993
|
+
|
|
1994
|
+
sage: R.<x> = QQ['x']
|
|
1995
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
1996
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
1997
|
+
sage: x == y # indirect doctest
|
|
1998
|
+
False
|
|
1999
|
+
"""
|
|
2000
|
+
if not isinstance(other, SpecialHyperellipticQuotientElement):
|
|
2001
|
+
other = self.parent()(other)
|
|
2002
|
+
return self._f == other._f
|
|
2003
|
+
|
|
2004
|
+
def _add_(self, other):
|
|
2005
|
+
"""
|
|
2006
|
+
Return the sum of two elements.
|
|
2007
|
+
|
|
2008
|
+
EXAMPLES::
|
|
2009
|
+
|
|
2010
|
+
sage: R.<x> = QQ['x']
|
|
2011
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
2012
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2013
|
+
sage: x + y
|
|
2014
|
+
y*1 + x
|
|
2015
|
+
"""
|
|
2016
|
+
P = self.parent()
|
|
2017
|
+
return P.element_class(P, self._f + other._f)
|
|
2018
|
+
|
|
2019
|
+
def _sub_(self, other):
|
|
2020
|
+
"""
|
|
2021
|
+
Return the difference of two elements.
|
|
2022
|
+
|
|
2023
|
+
EXAMPLES::
|
|
2024
|
+
|
|
2025
|
+
sage: R.<x> = QQ['x']
|
|
2026
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
2027
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2028
|
+
sage: y - x
|
|
2029
|
+
y*1 - x
|
|
2030
|
+
"""
|
|
2031
|
+
P = self.parent()
|
|
2032
|
+
return P.element_class(P, self._f - other._f)
|
|
2033
|
+
|
|
2034
|
+
def _mul_(self, other):
|
|
2035
|
+
"""
|
|
2036
|
+
Return the product of two elements.
|
|
2037
|
+
|
|
2038
|
+
EXAMPLES::
|
|
2039
|
+
|
|
2040
|
+
sage: R.<x> = QQ['x']
|
|
2041
|
+
sage: E = HyperellipticCurve(x^5 - 36*x + 1)
|
|
2042
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2043
|
+
sage: y*x
|
|
2044
|
+
y*x
|
|
2045
|
+
"""
|
|
2046
|
+
# over Laurent series, addition and subtraction can be
|
|
2047
|
+
# expensive, and the degree of this poly is small enough that
|
|
2048
|
+
# Karatsuba actually hurts significantly in some cases
|
|
2049
|
+
if self._f[0].valuation() + other._f[0].valuation() > -200:
|
|
2050
|
+
prod = self._f._mul_generic(other._f)
|
|
2051
|
+
else:
|
|
2052
|
+
prod = self._f * other._f
|
|
2053
|
+
v = prod.list()
|
|
2054
|
+
parent = self.parent()
|
|
2055
|
+
Q_coeffs = parent._Q_coeffs
|
|
2056
|
+
n = len(Q_coeffs) - 1
|
|
2057
|
+
y2 = self.parent()._series_ring_y << 1
|
|
2058
|
+
for i in range(len(v)-1, n-1, -1):
|
|
2059
|
+
for j in range(n):
|
|
2060
|
+
v[i-n+j] -= Q_coeffs[j] * v[i]
|
|
2061
|
+
v[i-n] += y2 * v[i]
|
|
2062
|
+
P = self.parent()
|
|
2063
|
+
return P.element_class(P, v[0:n])
|
|
2064
|
+
|
|
2065
|
+
def _rmul_(self, c):
|
|
2066
|
+
"""
|
|
2067
|
+
Return the product of ``self`` with `c` on the left.
|
|
2068
|
+
|
|
2069
|
+
EXAMPLES::
|
|
2070
|
+
|
|
2071
|
+
sage: R.<x> = QQ['x']
|
|
2072
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2073
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2074
|
+
sage: x._rmul_(y) # needs sage.rings.real_interval_field
|
|
2075
|
+
y*1*x
|
|
2076
|
+
"""
|
|
2077
|
+
P = self.parent()
|
|
2078
|
+
if not c:
|
|
2079
|
+
return P.zero()
|
|
2080
|
+
ret = [c*a for a in self._f.list(copy=False)]
|
|
2081
|
+
while ret and not ret[-1]: # strip off trailing 0s
|
|
2082
|
+
ret.pop()
|
|
2083
|
+
return P.element_class(P, ret, check=False)
|
|
2084
|
+
|
|
2085
|
+
def _lmul_(self, c):
|
|
2086
|
+
"""
|
|
2087
|
+
Return the product of ``self`` with `c` on the right.
|
|
2088
|
+
|
|
2089
|
+
EXAMPLES::
|
|
2090
|
+
|
|
2091
|
+
sage: R.<x> = QQ['x']
|
|
2092
|
+
sage: E = HyperellipticCurve(x^5-3*x+1)
|
|
2093
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2094
|
+
sage: x._lmul_(y) # needs sage.rings.real_interval_field
|
|
2095
|
+
y*1*x
|
|
2096
|
+
"""
|
|
2097
|
+
P = self.parent()
|
|
2098
|
+
if not c:
|
|
2099
|
+
return P.zero()
|
|
2100
|
+
ret = [a*c for a in self._f.list(copy=False)]
|
|
2101
|
+
while ret and not ret[-1]: # strip off trailing 0s
|
|
2102
|
+
ret.pop()
|
|
2103
|
+
return P.element_class(P, ret, check=False)
|
|
2104
|
+
|
|
2105
|
+
def __lshift__(self, k):
|
|
2106
|
+
"""
|
|
2107
|
+
Return the left shift of ``self`` by `k`.
|
|
2108
|
+
|
|
2109
|
+
EXAMPLES::
|
|
2110
|
+
|
|
2111
|
+
sage: R.<x> = QQ['x']
|
|
2112
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2113
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2114
|
+
sage: x.__lshift__(3)
|
|
2115
|
+
y^3*x
|
|
2116
|
+
"""
|
|
2117
|
+
coeffs = self._f.list(copy=False)
|
|
2118
|
+
P = self.parent()
|
|
2119
|
+
return P.element_class(P, [a << k for a in coeffs], check=False)
|
|
2120
|
+
|
|
2121
|
+
def __rshift__(self, k):
|
|
2122
|
+
"""
|
|
2123
|
+
Return the right shift of ``self`` by `k`.
|
|
2124
|
+
|
|
2125
|
+
EXAMPLES::
|
|
2126
|
+
|
|
2127
|
+
sage: R.<x> = QQ['x']
|
|
2128
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2129
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2130
|
+
sage: y.__rshift__(3)
|
|
2131
|
+
(y^-2)*1
|
|
2132
|
+
"""
|
|
2133
|
+
coeffs = self._f.list(copy=False)
|
|
2134
|
+
P = self.parent()
|
|
2135
|
+
return P.element_class(P, [a >> k for a in coeffs], check=False)
|
|
2136
|
+
|
|
2137
|
+
def truncate_neg(self, n):
|
|
2138
|
+
"""
|
|
2139
|
+
Return ``self`` minus its terms of degree less than `n` wrt `y`.
|
|
2140
|
+
|
|
2141
|
+
EXAMPLES::
|
|
2142
|
+
|
|
2143
|
+
sage: R.<x> = QQ['x']
|
|
2144
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2145
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2146
|
+
sage: (x + 3*y + 7*x*2*y**4).truncate_neg(1)
|
|
2147
|
+
3*y*1 + 14*y^4*x
|
|
2148
|
+
"""
|
|
2149
|
+
coeffs = self._f.list(copy=False)
|
|
2150
|
+
P = self.parent()
|
|
2151
|
+
return P.element_class(P, [a.truncate_neg(n) for a in coeffs], check=False)
|
|
2152
|
+
|
|
2153
|
+
def _repr_(self):
|
|
2154
|
+
"""
|
|
2155
|
+
Return a string representation of ``self``.
|
|
2156
|
+
|
|
2157
|
+
EXAMPLES::
|
|
2158
|
+
|
|
2159
|
+
sage: R.<x> = QQ['x']
|
|
2160
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2161
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2162
|
+
sage: (x + 3*y)._repr_()
|
|
2163
|
+
'3*y*1 + x'
|
|
2164
|
+
"""
|
|
2165
|
+
x = PolynomialRing(QQ, 'x').gen(0)
|
|
2166
|
+
coeffs = self._f.list()
|
|
2167
|
+
return repr_lincomb([(x**i, coeffs[i]) for i in range(len(coeffs))])
|
|
2168
|
+
|
|
2169
|
+
def _latex_(self):
|
|
2170
|
+
r"""
|
|
2171
|
+
Return a LateX string for ``self``.
|
|
2172
|
+
|
|
2173
|
+
EXAMPLES::
|
|
2174
|
+
|
|
2175
|
+
sage: R.<x> = QQ['x']
|
|
2176
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2177
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2178
|
+
sage: (x + 3*y)._latex_()
|
|
2179
|
+
'3y 1 + x'
|
|
2180
|
+
"""
|
|
2181
|
+
x = PolynomialRing(QQ, 'x').gen(0)
|
|
2182
|
+
coeffs = self._f.list()
|
|
2183
|
+
return repr_lincomb([(x**i, coeffs[i]) for i in range(len(coeffs))], is_latex=True)
|
|
2184
|
+
|
|
2185
|
+
def diff(self):
|
|
2186
|
+
"""
|
|
2187
|
+
Return the differential of ``self``.
|
|
2188
|
+
|
|
2189
|
+
EXAMPLES::
|
|
2190
|
+
|
|
2191
|
+
sage: R.<x> = QQ['x']
|
|
2192
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2193
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2194
|
+
sage: (x + 3*y).diff()
|
|
2195
|
+
(-(9-2*y)*1 + 15*x^4) dx/2y
|
|
2196
|
+
"""
|
|
2197
|
+
# try:
|
|
2198
|
+
# return self._diff_x
|
|
2199
|
+
# except AttributeError:
|
|
2200
|
+
# pass
|
|
2201
|
+
|
|
2202
|
+
# d(self) = A dx + B dy
|
|
2203
|
+
# = (2y A + BQ') dx/2y
|
|
2204
|
+
P = self.parent()
|
|
2205
|
+
R = P.base_ring()
|
|
2206
|
+
v = self._f.list()
|
|
2207
|
+
n = len(v)
|
|
2208
|
+
A = P([R(i) * v[i] for i in range(1, n)])
|
|
2209
|
+
B = P([a.derivative() for a in v])
|
|
2210
|
+
dQ = P._dQ
|
|
2211
|
+
return P._monsky_washnitzer((R(2) * A << 1) + dQ * B)
|
|
2212
|
+
# self._diff = self.parent()._monsky_washnitzer(two_y * A + dQ * B)
|
|
2213
|
+
# return self._diff
|
|
2214
|
+
|
|
2215
|
+
def extract_pow_y(self, k):
|
|
2216
|
+
r"""
|
|
2217
|
+
Return the coefficients of `y^k` in ``self`` as a list.
|
|
2218
|
+
|
|
2219
|
+
EXAMPLES::
|
|
2220
|
+
|
|
2221
|
+
sage: R.<x> = QQ['x']
|
|
2222
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2223
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2224
|
+
sage: (x + 3*y + 9*x*y).extract_pow_y(1)
|
|
2225
|
+
[3, 9, 0, 0, 0]
|
|
2226
|
+
"""
|
|
2227
|
+
v = [a[k] for a in self._f.list()]
|
|
2228
|
+
v += [ZZ.zero()] * (self.parent()._n - len(v))
|
|
2229
|
+
return v
|
|
2230
|
+
|
|
2231
|
+
def min_pow_y(self):
|
|
2232
|
+
r"""
|
|
2233
|
+
Return the minimal degree of ``self`` with respect to `y`.
|
|
2234
|
+
|
|
2235
|
+
EXAMPLES::
|
|
2236
|
+
|
|
2237
|
+
sage: R.<x> = QQ['x']
|
|
2238
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2239
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2240
|
+
sage: (x + 3*y).min_pow_y()
|
|
2241
|
+
0
|
|
2242
|
+
"""
|
|
2243
|
+
if self._f.degree() == -1:
|
|
2244
|
+
return ZZ.zero()
|
|
2245
|
+
return min([a.valuation() for a in self._f.list()])
|
|
2246
|
+
|
|
2247
|
+
def max_pow_y(self):
|
|
2248
|
+
r"""
|
|
2249
|
+
Return the maximal degree of ``self`` with respect to `y`.
|
|
2250
|
+
|
|
2251
|
+
EXAMPLES::
|
|
2252
|
+
|
|
2253
|
+
sage: R.<x> = QQ['x']
|
|
2254
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2255
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2256
|
+
sage: (x + 3*y).max_pow_y()
|
|
2257
|
+
1
|
|
2258
|
+
"""
|
|
2259
|
+
if self._f.degree() == -1:
|
|
2260
|
+
return ZZ.zero()
|
|
2261
|
+
return max([a.degree() for a in self._f.list()])
|
|
2262
|
+
|
|
2263
|
+
def coeffs(self, R=None):
|
|
2264
|
+
r"""
|
|
2265
|
+
Return the raw coefficients of this element.
|
|
2266
|
+
|
|
2267
|
+
INPUT:
|
|
2268
|
+
|
|
2269
|
+
- ``R`` -- an (optional) base-ring in which to cast the coefficients
|
|
2270
|
+
|
|
2271
|
+
OUTPUT:
|
|
2272
|
+
|
|
2273
|
+
- ``coeffs`` -- list of coefficients of powers of `x` for each power
|
|
2274
|
+
of `y`
|
|
2275
|
+
|
|
2276
|
+
- ``n`` -- an offset indicating the power of `y` of the first list
|
|
2277
|
+
element
|
|
2278
|
+
|
|
2279
|
+
EXAMPLES::
|
|
2280
|
+
|
|
2281
|
+
sage: R.<x> = QQ['x']
|
|
2282
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2283
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2284
|
+
sage: x.coeffs()
|
|
2285
|
+
([(0, 1, 0, 0, 0)], 0)
|
|
2286
|
+
sage: y.coeffs()
|
|
2287
|
+
([(0, 0, 0, 0, 0), (1, 0, 0, 0, 0)], 0)
|
|
2288
|
+
|
|
2289
|
+
sage: a = sum(n*x^n for n in range(5)); a
|
|
2290
|
+
x + 2*x^2 + 3*x^3 + 4*x^4
|
|
2291
|
+
sage: a.coeffs()
|
|
2292
|
+
([(0, 1, 2, 3, 4)], 0)
|
|
2293
|
+
sage: a.coeffs(Qp(7)) # needs sage.rings.padics
|
|
2294
|
+
([(0, 1 + O(7^20), 2 + O(7^20), 3 + O(7^20), 4 + O(7^20))], 0)
|
|
2295
|
+
sage: (a*y).coeffs()
|
|
2296
|
+
([(0, 0, 0, 0, 0), (0, 1, 2, 3, 4)], 0)
|
|
2297
|
+
sage: (a*y^-2).coeffs()
|
|
2298
|
+
([(0, 1, 2, 3, 4), (0, 0, 0, 0, 0), (0, 0, 0, 0, 0)], -2)
|
|
2299
|
+
|
|
2300
|
+
Note that the coefficient list is transposed compared to how they
|
|
2301
|
+
are stored and printed::
|
|
2302
|
+
|
|
2303
|
+
sage: a*y^-2
|
|
2304
|
+
(y^-2)*x + (2*y^-2)*x^2 + (3*y^-2)*x^3 + (4*y^-2)*x^4
|
|
2305
|
+
|
|
2306
|
+
A more complicated example::
|
|
2307
|
+
|
|
2308
|
+
sage: a = x^20*y^-3 - x^11*y^2; a
|
|
2309
|
+
(y^-3-4*y^-1+6*y-4*y^3+y^5)*1 - (12*y^-3-36*y^-1+36*y+y^2-12*y^3-2*y^4+y^6)*x
|
|
2310
|
+
+ (54*y^-3-108*y^-1+54*y+6*y^2-6*y^4)*x^2 - (108*y^-3-108*y^-1+9*y^2)*x^3
|
|
2311
|
+
+ (81*y^-3)*x^4
|
|
2312
|
+
sage: raw, offset = a.coeffs()
|
|
2313
|
+
sage: a.min_pow_y()
|
|
2314
|
+
-3
|
|
2315
|
+
sage: offset
|
|
2316
|
+
-3
|
|
2317
|
+
sage: raw
|
|
2318
|
+
[(1, -12, 54, -108, 81),
|
|
2319
|
+
(0, 0, 0, 0, 0),
|
|
2320
|
+
(-4, 36, -108, 108, 0),
|
|
2321
|
+
(0, 0, 0, 0, 0),
|
|
2322
|
+
(6, -36, 54, 0, 0),
|
|
2323
|
+
(0, -1, 6, -9, 0),
|
|
2324
|
+
(-4, 12, 0, 0, 0),
|
|
2325
|
+
(0, 2, -6, 0, 0),
|
|
2326
|
+
(1, 0, 0, 0, 0),
|
|
2327
|
+
(0, -1, 0, 0, 0)]
|
|
2328
|
+
sage: sum(c * x^i * y^(j+offset)
|
|
2329
|
+
....: for j, L in enumerate(raw) for i, c in enumerate(L)) == a
|
|
2330
|
+
True
|
|
2331
|
+
|
|
2332
|
+
Can also be used to construct elements::
|
|
2333
|
+
|
|
2334
|
+
sage: a.parent()(raw, offset) == a
|
|
2335
|
+
True
|
|
2336
|
+
"""
|
|
2337
|
+
zero = self.base_ring()(0) if R is None else R(0)
|
|
2338
|
+
y_offset = min(self.min_pow_y(), 0)
|
|
2339
|
+
y_degree = max(self.max_pow_y(), 0)
|
|
2340
|
+
coeffs = []
|
|
2341
|
+
n = y_degree - y_offset + 1
|
|
2342
|
+
for a in self._f.list():
|
|
2343
|
+
k = a.valuation()
|
|
2344
|
+
if k is Infinity:
|
|
2345
|
+
k = 0
|
|
2346
|
+
k -= y_offset
|
|
2347
|
+
z = a.list()
|
|
2348
|
+
coeffs.append([zero] * k + z + [zero]*(n - len(z) - k))
|
|
2349
|
+
while len(coeffs) < self.parent().degree():
|
|
2350
|
+
coeffs.append([zero] * n)
|
|
2351
|
+
V = FreeModule(self.base_ring() if R is None else R, self.parent().degree())
|
|
2352
|
+
coeffs = transpose_list(coeffs)
|
|
2353
|
+
return [V(a) for a in coeffs], y_offset
|
|
2354
|
+
|
|
2355
|
+
|
|
2356
|
+
class SpecialHyperellipticQuotientRing(UniqueRepresentation, Parent):
|
|
2357
|
+
"""
|
|
2358
|
+
The special hyperelliptic quotient ring.
|
|
2359
|
+
"""
|
|
2360
|
+
_p = None
|
|
2361
|
+
|
|
2362
|
+
def __init__(self, Q, R=None, invert_y=True):
|
|
2363
|
+
r"""
|
|
2364
|
+
Initialize ``self``.
|
|
2365
|
+
|
|
2366
|
+
TESTS::
|
|
2367
|
+
|
|
2368
|
+
sage: R.<x> = QQ['x']
|
|
2369
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2370
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing
|
|
2371
|
+
sage: HQR = SpecialHyperellipticQuotientRing(E)
|
|
2372
|
+
sage: TestSuite(HQR).run() # needs sage.rings.real_interval_field
|
|
2373
|
+
|
|
2374
|
+
Check that caching works::
|
|
2375
|
+
|
|
2376
|
+
sage: HQR is SpecialHyperellipticQuotientRing(E)
|
|
2377
|
+
True
|
|
2378
|
+
"""
|
|
2379
|
+
if R is None:
|
|
2380
|
+
R = Q.base_ring()
|
|
2381
|
+
|
|
2382
|
+
x = PolynomialRing(R, 'xx').gen()
|
|
2383
|
+
if isinstance(Q, EllipticCurve_generic):
|
|
2384
|
+
E = Q
|
|
2385
|
+
if E.a1() != 0 or E.a2() != 0:
|
|
2386
|
+
raise NotImplementedError("curve must be in Weierstrass normal form")
|
|
2387
|
+
Q = -E.change_ring(R).defining_polynomial()(x, 0, 1)
|
|
2388
|
+
self._curve = E
|
|
2389
|
+
|
|
2390
|
+
elif isinstance(Q, HyperellipticCurve_generic):
|
|
2391
|
+
C = Q
|
|
2392
|
+
if C.hyperelliptic_polynomials()[1] != 0:
|
|
2393
|
+
raise NotImplementedError("curve must be of form y^2 = Q(x)")
|
|
2394
|
+
Q = C.hyperelliptic_polynomials()[0].change_ring(R)
|
|
2395
|
+
self._curve = C
|
|
2396
|
+
|
|
2397
|
+
if isinstance(Q, Polynomial):
|
|
2398
|
+
self._Q = Q.change_ring(R)
|
|
2399
|
+
self._coeffs = self._Q.coefficients(sparse=False)
|
|
2400
|
+
if self._coeffs.pop() != 1:
|
|
2401
|
+
raise NotImplementedError("polynomial must be monic")
|
|
2402
|
+
if not hasattr(self, '_curve'):
|
|
2403
|
+
if self._Q.degree() == 3:
|
|
2404
|
+
ainvs = [0, self._Q[2], 0, self._Q[1], self._Q[0]]
|
|
2405
|
+
self._curve = EllipticCurve(ainvs)
|
|
2406
|
+
else:
|
|
2407
|
+
self._curve = HyperellipticCurve(self._Q, check_squarefree=R.is_field())
|
|
2408
|
+
|
|
2409
|
+
else:
|
|
2410
|
+
raise NotImplementedError("must be an elliptic curve or polynomial "
|
|
2411
|
+
"Q for y^2 = Q(x)\n(Got element of %s)" % Q.parent())
|
|
2412
|
+
|
|
2413
|
+
self._n = int(Q.degree())
|
|
2414
|
+
self._series_ring = (LaurentSeriesRing if invert_y else PolynomialRing)(R, 'y')
|
|
2415
|
+
self._series_ring_y = self._series_ring.gen(0)
|
|
2416
|
+
self._series_ring_0 = self._series_ring.zero()
|
|
2417
|
+
|
|
2418
|
+
Parent.__init__(self, base=R, category=Algebras(R).Commutative())
|
|
2419
|
+
|
|
2420
|
+
self._poly_ring = PolynomialRing(self._series_ring, 'x')
|
|
2421
|
+
|
|
2422
|
+
self._x = self.element_class(self, self._poly_ring.gen(0))
|
|
2423
|
+
self._y = self.element_class(self, self._series_ring.gen(0))
|
|
2424
|
+
|
|
2425
|
+
self._Q_coeffs = Q.change_ring(self._series_ring).list()
|
|
2426
|
+
self._dQ = Q.derivative().change_ring(self)(self._x)
|
|
2427
|
+
self._monsky_washnitzer = MonskyWashnitzerDifferentialRing(self)
|
|
2428
|
+
|
|
2429
|
+
self._monomial_diffs = {}
|
|
2430
|
+
self._monomial_diff_coeffs = {}
|
|
2431
|
+
|
|
2432
|
+
def _repr_(self) -> str:
|
|
2433
|
+
r"""
|
|
2434
|
+
String representation.
|
|
2435
|
+
|
|
2436
|
+
EXAMPLES::
|
|
2437
|
+
|
|
2438
|
+
sage: R.<x> = QQ['x']
|
|
2439
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2440
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2441
|
+
sage: x.parent() # indirect doctest
|
|
2442
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 3*x + 1) over Rational Field
|
|
2443
|
+
"""
|
|
2444
|
+
y_inverse = ",y^-1" if isinstance(self._series_ring, (LaurentSeriesRing, LazyLaurentSeriesRing)) else ""
|
|
2445
|
+
return "SpecialHyperellipticQuotientRing K[x,y%s] / (y^2 = %s) over %s" % (y_inverse, self._Q, self.base_ring())
|
|
2446
|
+
|
|
2447
|
+
def base_extend(self, R):
|
|
2448
|
+
r"""
|
|
2449
|
+
Return the base extension of ``self`` to the ring ``R`` if possible.
|
|
2450
|
+
|
|
2451
|
+
EXAMPLES::
|
|
2452
|
+
|
|
2453
|
+
sage: R.<x> = QQ['x']
|
|
2454
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2455
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2456
|
+
sage: x.parent().base_extend(UniversalCyclotomicField()) # needs sage.libs.gap
|
|
2457
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 3*x + 1)
|
|
2458
|
+
over Universal Cyclotomic Field
|
|
2459
|
+
sage: x.parent().base_extend(ZZ)
|
|
2460
|
+
Traceback (most recent call last):
|
|
2461
|
+
...
|
|
2462
|
+
TypeError: no such base extension
|
|
2463
|
+
"""
|
|
2464
|
+
if R.has_coerce_map_from(self.base_ring()):
|
|
2465
|
+
return self.change_ring(R)
|
|
2466
|
+
raise TypeError("no such base extension")
|
|
2467
|
+
|
|
2468
|
+
def change_ring(self, R):
|
|
2469
|
+
r"""
|
|
2470
|
+
Return the analog of ``self`` over the ring ``R``.
|
|
2471
|
+
|
|
2472
|
+
EXAMPLES::
|
|
2473
|
+
|
|
2474
|
+
sage: R.<x> = QQ['x']
|
|
2475
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2476
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2477
|
+
sage: x.parent().change_ring(ZZ)
|
|
2478
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 3*x + 1)
|
|
2479
|
+
over Integer Ring
|
|
2480
|
+
"""
|
|
2481
|
+
return SpecialHyperellipticQuotientRing(self._Q.change_ring(R), R,
|
|
2482
|
+
isinstance(self._series_ring, (LaurentSeriesRing, LazyLaurentSeriesRing)))
|
|
2483
|
+
|
|
2484
|
+
def _element_constructor_(self, val, offset=0, check=True):
|
|
2485
|
+
r"""
|
|
2486
|
+
Construct an element of ``self``.
|
|
2487
|
+
|
|
2488
|
+
EXAMPLES::
|
|
2489
|
+
|
|
2490
|
+
sage: R.<x> = QQ['x']
|
|
2491
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2492
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2493
|
+
sage: x.parent()(x^6)
|
|
2494
|
+
-(1-y^2)*x + 3*x^2
|
|
2495
|
+
"""
|
|
2496
|
+
if isinstance(val, SpecialHyperellipticQuotientElement) and val.parent() is self:
|
|
2497
|
+
if offset == 0:
|
|
2498
|
+
return val
|
|
2499
|
+
else:
|
|
2500
|
+
return val << offset
|
|
2501
|
+
elif isinstance(val, MonskyWashnitzerDifferential):
|
|
2502
|
+
return self._monsky_washnitzer(val)
|
|
2503
|
+
return self.element_class(self, val, offset, check)
|
|
2504
|
+
|
|
2505
|
+
@cached_method
|
|
2506
|
+
def one(self):
|
|
2507
|
+
"""
|
|
2508
|
+
Return the unit of ``self``.
|
|
2509
|
+
|
|
2510
|
+
EXAMPLES::
|
|
2511
|
+
|
|
2512
|
+
sage: R.<x> = QQ['x']
|
|
2513
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2514
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2515
|
+
sage: x.parent().one()
|
|
2516
|
+
1
|
|
2517
|
+
"""
|
|
2518
|
+
return self.element_class(self, self._poly_ring.one(), check=False)
|
|
2519
|
+
|
|
2520
|
+
@cached_method
|
|
2521
|
+
def zero(self):
|
|
2522
|
+
"""
|
|
2523
|
+
Return the zero of ``self``.
|
|
2524
|
+
|
|
2525
|
+
EXAMPLES::
|
|
2526
|
+
|
|
2527
|
+
sage: R.<x> = QQ['x']
|
|
2528
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2529
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2530
|
+
sage: x.parent().zero()
|
|
2531
|
+
0
|
|
2532
|
+
"""
|
|
2533
|
+
return self.element_class(self, self._poly_ring.zero(), check=False)
|
|
2534
|
+
|
|
2535
|
+
def gens(self) -> tuple:
|
|
2536
|
+
"""
|
|
2537
|
+
Return the generators of ``self``.
|
|
2538
|
+
|
|
2539
|
+
EXAMPLES::
|
|
2540
|
+
|
|
2541
|
+
sage: R.<x> = QQ['x']
|
|
2542
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2543
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2544
|
+
sage: x.parent().gens()
|
|
2545
|
+
(x, y*1)
|
|
2546
|
+
"""
|
|
2547
|
+
return (self._x, self._y)
|
|
2548
|
+
|
|
2549
|
+
def x(self):
|
|
2550
|
+
r"""
|
|
2551
|
+
Return the generator `x` of ``self``.
|
|
2552
|
+
|
|
2553
|
+
EXAMPLES::
|
|
2554
|
+
|
|
2555
|
+
sage: R.<x> = QQ['x']
|
|
2556
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2557
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2558
|
+
sage: x.parent().x()
|
|
2559
|
+
x
|
|
2560
|
+
"""
|
|
2561
|
+
return self._x
|
|
2562
|
+
|
|
2563
|
+
def y(self):
|
|
2564
|
+
r"""
|
|
2565
|
+
Return the generator `y` of ``self``.
|
|
2566
|
+
|
|
2567
|
+
EXAMPLES::
|
|
2568
|
+
|
|
2569
|
+
sage: R.<x> = QQ['x']
|
|
2570
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2571
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2572
|
+
sage: x.parent().y()
|
|
2573
|
+
y*1
|
|
2574
|
+
"""
|
|
2575
|
+
return self._y
|
|
2576
|
+
|
|
2577
|
+
def monomial(self, i, j, b=None):
|
|
2578
|
+
"""
|
|
2579
|
+
Return `b y^j x^i`, computed quickly.
|
|
2580
|
+
|
|
2581
|
+
EXAMPLES::
|
|
2582
|
+
|
|
2583
|
+
sage: R.<x> = QQ['x']
|
|
2584
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2585
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2586
|
+
sage: x.parent().monomial(4,5)
|
|
2587
|
+
y^5*x^4
|
|
2588
|
+
"""
|
|
2589
|
+
i = int(i)
|
|
2590
|
+
j = int(j)
|
|
2591
|
+
|
|
2592
|
+
if 0 < i < self._n:
|
|
2593
|
+
if b is None:
|
|
2594
|
+
by_to_j = self._series_ring_y << (j - 1)
|
|
2595
|
+
else:
|
|
2596
|
+
by_to_j = self._series_ring(b) << j
|
|
2597
|
+
v = [self._series_ring_0] * self._n
|
|
2598
|
+
v[i] = by_to_j
|
|
2599
|
+
return self.element_class(self, v)
|
|
2600
|
+
|
|
2601
|
+
if b is not None:
|
|
2602
|
+
b = self.base_ring()(b)
|
|
2603
|
+
return (self._x**i) << j if b is None else b * (self._x**i) << j
|
|
2604
|
+
|
|
2605
|
+
def monomial_diff_coeffs(self, i, j):
|
|
2606
|
+
r"""
|
|
2607
|
+
Compute coefficients of the basis representation of `d(x^iy^j)`.
|
|
2608
|
+
|
|
2609
|
+
The key here is that the formula for `d(x^iy^j)` is messy
|
|
2610
|
+
in terms of `i`, but varies nicely with `j`.
|
|
2611
|
+
|
|
2612
|
+
.. MATH::
|
|
2613
|
+
|
|
2614
|
+
d(x^iy^j) = y^{j-1} (2ix^{i-1}y^2 + j (A_i(x) + B_i(x)y^2)) \frac{dx}{2y},
|
|
2615
|
+
|
|
2616
|
+
where `A,B` have degree at most `n-1` for each
|
|
2617
|
+
`i`. Pre-compute `A_i, B_i` for each `i`
|
|
2618
|
+
the "hard" way, and the rest are easy.
|
|
2619
|
+
|
|
2620
|
+
EXAMPLES::
|
|
2621
|
+
|
|
2622
|
+
sage: R.<x> = QQ['x']
|
|
2623
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2624
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2625
|
+
sage: x.parent().monomial_diff_coeffs(2,3)
|
|
2626
|
+
((0, -15, 36, 0, 0), (0, 19, 0, 0, 0))
|
|
2627
|
+
"""
|
|
2628
|
+
try:
|
|
2629
|
+
return self._monomial_diff_coeffs[i, j]
|
|
2630
|
+
except KeyError:
|
|
2631
|
+
pass
|
|
2632
|
+
if i < self._n:
|
|
2633
|
+
try:
|
|
2634
|
+
A, B, two_i_x_to_i = self._precomputed_diff_coeffs[i]
|
|
2635
|
+
except AttributeError:
|
|
2636
|
+
self._precomputed_diff_coeffs = self._precompute_monomial_diffs()
|
|
2637
|
+
A, B, two_i_x_to_i = self._precomputed_diff_coeffs[i]
|
|
2638
|
+
if i == 0:
|
|
2639
|
+
return j*A, j*B
|
|
2640
|
+
else:
|
|
2641
|
+
return j*A, j*B + two_i_x_to_i
|
|
2642
|
+
else:
|
|
2643
|
+
dg = self.monomial(i, j).diff()
|
|
2644
|
+
coeffs = [dg.extract_pow_y(j-1), dg.extract_pow_y(j+1)]
|
|
2645
|
+
self._monomial_diff_coeffs[i, j] = coeffs
|
|
2646
|
+
return coeffs
|
|
2647
|
+
|
|
2648
|
+
def monomial_diff_coeffs_matrices(self):
|
|
2649
|
+
r"""
|
|
2650
|
+
Compute tables of coefficients of the basis representation
|
|
2651
|
+
of `d(x^iy^j)` for small `i`, `j`.
|
|
2652
|
+
|
|
2653
|
+
EXAMPLES::
|
|
2654
|
+
|
|
2655
|
+
sage: R.<x> = QQ['x']
|
|
2656
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2657
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2658
|
+
sage: x.parent().monomial_diff_coeffs_matrices()
|
|
2659
|
+
(
|
|
2660
|
+
[0 5 0 0 0] [0 2 0 0 0]
|
|
2661
|
+
[0 0 5 0 0] [0 0 4 0 0]
|
|
2662
|
+
[0 0 0 5 0] [0 0 0 6 0]
|
|
2663
|
+
[0 0 0 0 5] [0 0 0 0 8]
|
|
2664
|
+
[0 0 0 0 0], [0 0 0 0 0]
|
|
2665
|
+
)
|
|
2666
|
+
"""
|
|
2667
|
+
self.monomial_diff_coeffs(0, 0) # precompute stuff
|
|
2668
|
+
R = self.base_ring()
|
|
2669
|
+
mat_1 = matrix(R, self._n, self._n)
|
|
2670
|
+
mat_2 = matrix(R, self._n, self._n)
|
|
2671
|
+
for i in range(self._n):
|
|
2672
|
+
mat_1[i] = self._precomputed_diff_coeffs[i][1]
|
|
2673
|
+
mat_2[i] = self._precomputed_diff_coeffs[i][2]
|
|
2674
|
+
return mat_1.transpose(), mat_2.transpose()
|
|
2675
|
+
|
|
2676
|
+
def _precompute_monomial_diffs(self) -> list:
|
|
2677
|
+
r"""
|
|
2678
|
+
Precompute coefficients of the basis representation of `d(x^iy^j)`
|
|
2679
|
+
for small `i`, `j`.
|
|
2680
|
+
|
|
2681
|
+
EXAMPLES::
|
|
2682
|
+
|
|
2683
|
+
sage: R.<x> = QQ['x']
|
|
2684
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2685
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2686
|
+
sage: x.parent()._precompute_monomial_diffs()
|
|
2687
|
+
[((-3, 0, 0, 0, 5), (0, 0, 0, 0, 0), (0, 0, 0, 0, 0)),
|
|
2688
|
+
((-5, 12, 0, 0, 0), (5, 0, 0, 0, 0), (2, 0, 0, 0, 0)),
|
|
2689
|
+
((0, -5, 12, 0, 0), (0, 5, 0, 0, 0), (0, 4, 0, 0, 0)),
|
|
2690
|
+
((0, 0, -5, 12, 0), (0, 0, 5, 0, 0), (0, 0, 6, 0, 0)),
|
|
2691
|
+
((0, 0, 0, -5, 12), (0, 0, 0, 5, 0), (0, 0, 0, 8, 0))]
|
|
2692
|
+
"""
|
|
2693
|
+
x, y = self.gens()
|
|
2694
|
+
R = self.base_ring()
|
|
2695
|
+
V = FreeModule(R, self.degree())
|
|
2696
|
+
As = []
|
|
2697
|
+
for i in range(self.degree()):
|
|
2698
|
+
dg = self.monomial(i, 1).diff()
|
|
2699
|
+
two_i_x_to_i = R(2*i) * x**(i-1) * y*y if i > 0 else self(0)
|
|
2700
|
+
A = dg - self._monsky_washnitzer(two_i_x_to_i)
|
|
2701
|
+
As.append((V(A.extract_pow_y(0)), V(A.extract_pow_y(2)), V(two_i_x_to_i.extract_pow_y(2))))
|
|
2702
|
+
return As
|
|
2703
|
+
|
|
2704
|
+
def Q(self):
|
|
2705
|
+
"""
|
|
2706
|
+
Return the defining polynomial of the underlying hyperelliptic curve.
|
|
2707
|
+
|
|
2708
|
+
EXAMPLES::
|
|
2709
|
+
|
|
2710
|
+
sage: R.<x> = QQ['x']
|
|
2711
|
+
sage: E = HyperellipticCurve(x^5-2*x+1)
|
|
2712
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2713
|
+
sage: x.parent().Q()
|
|
2714
|
+
x^5 - 2*x + 1
|
|
2715
|
+
"""
|
|
2716
|
+
return self._Q
|
|
2717
|
+
|
|
2718
|
+
def curve(self):
|
|
2719
|
+
"""
|
|
2720
|
+
Return the underlying hyperelliptic curve.
|
|
2721
|
+
|
|
2722
|
+
EXAMPLES::
|
|
2723
|
+
|
|
2724
|
+
sage: R.<x> = QQ['x']
|
|
2725
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2726
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2727
|
+
sage: x.parent().curve()
|
|
2728
|
+
Hyperelliptic Curve over Rational Field defined by y^2 = x^5 - 3*x + 1
|
|
2729
|
+
"""
|
|
2730
|
+
return self._curve
|
|
2731
|
+
|
|
2732
|
+
def degree(self):
|
|
2733
|
+
"""
|
|
2734
|
+
Return the degree of the underlying hyperelliptic curve.
|
|
2735
|
+
|
|
2736
|
+
EXAMPLES::
|
|
2737
|
+
|
|
2738
|
+
sage: R.<x> = QQ['x']
|
|
2739
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2740
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2741
|
+
sage: x.parent().degree()
|
|
2742
|
+
5
|
|
2743
|
+
"""
|
|
2744
|
+
return Integer(self._n)
|
|
2745
|
+
|
|
2746
|
+
def prime(self):
|
|
2747
|
+
"""
|
|
2748
|
+
Return the stored prime number `p`.
|
|
2749
|
+
|
|
2750
|
+
EXAMPLES::
|
|
2751
|
+
|
|
2752
|
+
sage: R.<x> = QQ['x']
|
|
2753
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2754
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2755
|
+
sage: x.parent().prime() is None
|
|
2756
|
+
True
|
|
2757
|
+
"""
|
|
2758
|
+
return self._p
|
|
2759
|
+
|
|
2760
|
+
def monsky_washnitzer(self):
|
|
2761
|
+
"""
|
|
2762
|
+
Return the stored Monsky-Washnitzer differential ring.
|
|
2763
|
+
|
|
2764
|
+
EXAMPLES::
|
|
2765
|
+
|
|
2766
|
+
sage: R.<x> = QQ['x']
|
|
2767
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2768
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2769
|
+
sage: type(x.parent().monsky_washnitzer())
|
|
2770
|
+
<class 'sage.schemes.hyperelliptic_curves.monsky_washnitzer.MonskyWashnitzerDifferentialRing_with_category'>
|
|
2771
|
+
"""
|
|
2772
|
+
return self._monsky_washnitzer
|
|
2773
|
+
|
|
2774
|
+
def is_field(self, proof=True) -> bool:
|
|
2775
|
+
"""
|
|
2776
|
+
Return ``False`` as ``self`` is not a field.
|
|
2777
|
+
|
|
2778
|
+
EXAMPLES::
|
|
2779
|
+
|
|
2780
|
+
sage: R.<x> = QQ['x']
|
|
2781
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
2782
|
+
sage: x,y = E.monsky_washnitzer_gens()
|
|
2783
|
+
sage: x.parent().is_field()
|
|
2784
|
+
False
|
|
2785
|
+
"""
|
|
2786
|
+
return False
|
|
2787
|
+
|
|
2788
|
+
Element = SpecialHyperellipticQuotientElement
|
|
2789
|
+
|
|
2790
|
+
|
|
2791
|
+
SpecialHyperellipticQuotientRing_class = SpecialHyperellipticQuotientRing
|
|
2792
|
+
|
|
2793
|
+
|
|
2794
|
+
class MonskyWashnitzerDifferential(ModuleElement):
|
|
2795
|
+
r"""
|
|
2796
|
+
An element of the Monsky-Washnitzer ring of differentials, of
|
|
2797
|
+
the form `F dx/2y`.
|
|
2798
|
+
|
|
2799
|
+
EXAMPLES::
|
|
2800
|
+
|
|
2801
|
+
sage: R.<x> = QQ['x']
|
|
2802
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2803
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2804
|
+
sage: MW = C.invariant_differential().parent()
|
|
2805
|
+
sage: MW(x)
|
|
2806
|
+
x dx/2y
|
|
2807
|
+
sage: MW(y)
|
|
2808
|
+
y*1 dx/2y
|
|
2809
|
+
sage: MW(x, 10)
|
|
2810
|
+
y^10*x dx/2y
|
|
2811
|
+
"""
|
|
2812
|
+
def __init__(self, parent, val, offset=0):
|
|
2813
|
+
r"""
|
|
2814
|
+
Initialize ``self``.
|
|
2815
|
+
|
|
2816
|
+
INPUT:
|
|
2817
|
+
|
|
2818
|
+
- ``parent`` -- Monsky-Washnitzer differential ring (instance of class
|
|
2819
|
+
:class:`~MonskyWashnitzerDifferentialRing`
|
|
2820
|
+
|
|
2821
|
+
- ``val`` -- element of the base ring, or list of coefficients
|
|
2822
|
+
|
|
2823
|
+
- ``offset`` -- (default: 0) if nonzero, shift val by `y^\text{offset}`
|
|
2824
|
+
|
|
2825
|
+
EXAMPLES::
|
|
2826
|
+
|
|
2827
|
+
sage: R.<x> = QQ['x']
|
|
2828
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2829
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2830
|
+
sage: MW = C.invariant_differential().parent()
|
|
2831
|
+
sage: elt = MW(x)
|
|
2832
|
+
sage: TestSuite(elt).run()
|
|
2833
|
+
"""
|
|
2834
|
+
ModuleElement.__init__(self, parent)
|
|
2835
|
+
R = parent.base_ring()
|
|
2836
|
+
self._coeff = R._element_constructor_(val, offset)
|
|
2837
|
+
|
|
2838
|
+
def _add_(self, other):
|
|
2839
|
+
r"""
|
|
2840
|
+
Return the sum of ``self`` and ``other``, both elements of the
|
|
2841
|
+
Monsky-Washnitzer ring of differentials.
|
|
2842
|
+
|
|
2843
|
+
EXAMPLES::
|
|
2844
|
+
|
|
2845
|
+
sage: R.<x> = QQ['x']
|
|
2846
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2847
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2848
|
+
sage: w = C.invariant_differential()
|
|
2849
|
+
sage: w + w
|
|
2850
|
+
2*1 dx/2y
|
|
2851
|
+
sage: x*w + w
|
|
2852
|
+
(1 + x) dx/2y
|
|
2853
|
+
sage: x*w + y*w
|
|
2854
|
+
(y*1 + x) dx/2y
|
|
2855
|
+
"""
|
|
2856
|
+
P = self.parent()
|
|
2857
|
+
return P.element_class(P, self._coeff + other._coeff)
|
|
2858
|
+
|
|
2859
|
+
def _sub_(self, other):
|
|
2860
|
+
r"""
|
|
2861
|
+
Return the difference of ``self`` and ``other``, both elements of the
|
|
2862
|
+
Monsky-Washnitzer ring of differentials.
|
|
2863
|
+
|
|
2864
|
+
EXAMPLES::
|
|
2865
|
+
|
|
2866
|
+
sage: R.<x> = QQ['x']
|
|
2867
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2868
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2869
|
+
sage: w = C.invariant_differential()
|
|
2870
|
+
sage: w-w
|
|
2871
|
+
0 dx/2y
|
|
2872
|
+
sage: x*w-w
|
|
2873
|
+
(-1 + x) dx/2y
|
|
2874
|
+
sage: w - x*w - y*w
|
|
2875
|
+
((1-y)*1 - x) dx/2y
|
|
2876
|
+
"""
|
|
2877
|
+
P = self.parent()
|
|
2878
|
+
return P.element_class(P, self._coeff - other._coeff)
|
|
2879
|
+
|
|
2880
|
+
def __neg__(self):
|
|
2881
|
+
r"""
|
|
2882
|
+
Return the additive inverse of ``self``.
|
|
2883
|
+
|
|
2884
|
+
EXAMPLES::
|
|
2885
|
+
|
|
2886
|
+
sage: R.<x> = QQ['x']
|
|
2887
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2888
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2889
|
+
sage: w = C.invariant_differential()
|
|
2890
|
+
sage: -w
|
|
2891
|
+
-1 dx/2y
|
|
2892
|
+
sage: -((y-x)*w)
|
|
2893
|
+
(-y*1 + x) dx/2y
|
|
2894
|
+
"""
|
|
2895
|
+
P = self.parent()
|
|
2896
|
+
return P.element_class(P, -self._coeff)
|
|
2897
|
+
|
|
2898
|
+
def _lmul_(self, a):
|
|
2899
|
+
r"""
|
|
2900
|
+
Return `self * a`.
|
|
2901
|
+
|
|
2902
|
+
EXAMPLES::
|
|
2903
|
+
|
|
2904
|
+
sage: R.<x> = QQ['x']
|
|
2905
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2906
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2907
|
+
sage: w = C.invariant_differential()
|
|
2908
|
+
sage: w*x
|
|
2909
|
+
x dx/2y
|
|
2910
|
+
sage: (w*x)*2
|
|
2911
|
+
2*x dx/2y
|
|
2912
|
+
sage: w*y
|
|
2913
|
+
y*1 dx/2y
|
|
2914
|
+
sage: w*(x+y)
|
|
2915
|
+
(y*1 + x) dx/2y
|
|
2916
|
+
"""
|
|
2917
|
+
P = self.parent()
|
|
2918
|
+
return P.element_class(P, self._coeff * a)
|
|
2919
|
+
|
|
2920
|
+
def _rmul_(self, a):
|
|
2921
|
+
r"""
|
|
2922
|
+
Return `a * self`.
|
|
2923
|
+
|
|
2924
|
+
EXAMPLES::
|
|
2925
|
+
|
|
2926
|
+
sage: R.<x> = QQ['x']
|
|
2927
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2928
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2929
|
+
sage: w = C.invariant_differential()
|
|
2930
|
+
sage: x*w
|
|
2931
|
+
x dx/2y
|
|
2932
|
+
sage: 2*(x*w)
|
|
2933
|
+
2*x dx/2y
|
|
2934
|
+
sage: y*w
|
|
2935
|
+
y*1 dx/2y
|
|
2936
|
+
sage: (x+y)*w
|
|
2937
|
+
(y*1 + x) dx/2y
|
|
2938
|
+
"""
|
|
2939
|
+
P = self.parent()
|
|
2940
|
+
return P.element_class(P, a * self._coeff)
|
|
2941
|
+
|
|
2942
|
+
def coeff(self):
|
|
2943
|
+
r"""
|
|
2944
|
+
Return `A`, where this element is `A dx/2y`.
|
|
2945
|
+
|
|
2946
|
+
EXAMPLES::
|
|
2947
|
+
|
|
2948
|
+
sage: R.<x> = QQ['x']
|
|
2949
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2950
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2951
|
+
sage: w = C.invariant_differential()
|
|
2952
|
+
sage: w
|
|
2953
|
+
1 dx/2y
|
|
2954
|
+
sage: w.coeff()
|
|
2955
|
+
1
|
|
2956
|
+
sage: (x*y*w).coeff()
|
|
2957
|
+
y*x
|
|
2958
|
+
"""
|
|
2959
|
+
return self._coeff
|
|
2960
|
+
|
|
2961
|
+
def __bool__(self):
|
|
2962
|
+
r"""
|
|
2963
|
+
EXAMPLES::
|
|
2964
|
+
|
|
2965
|
+
sage: R.<x> = QQ['x']
|
|
2966
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2967
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2968
|
+
sage: w = C.invariant_differential()
|
|
2969
|
+
sage: not w
|
|
2970
|
+
False
|
|
2971
|
+
sage: not 0*w
|
|
2972
|
+
True
|
|
2973
|
+
sage: not x*y*w
|
|
2974
|
+
False
|
|
2975
|
+
"""
|
|
2976
|
+
return bool(self._coeff)
|
|
2977
|
+
|
|
2978
|
+
def _repr_(self):
|
|
2979
|
+
r"""
|
|
2980
|
+
EXAMPLES::
|
|
2981
|
+
|
|
2982
|
+
sage: R.<x> = QQ['x']
|
|
2983
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
2984
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
2985
|
+
sage: w = C.invariant_differential()
|
|
2986
|
+
sage: w
|
|
2987
|
+
1 dx/2y
|
|
2988
|
+
sage: (2*x+y)*w
|
|
2989
|
+
(y*1 + 2*x) dx/2y
|
|
2990
|
+
"""
|
|
2991
|
+
s = self._coeff._repr_()
|
|
2992
|
+
if s.find("+") != -1 or s.find("-") > 0:
|
|
2993
|
+
s = "(%s)" % s
|
|
2994
|
+
return s + " dx/2y"
|
|
2995
|
+
|
|
2996
|
+
def _latex_(self):
|
|
2997
|
+
r"""
|
|
2998
|
+
Return the latex representation of ``self``.
|
|
2999
|
+
|
|
3000
|
+
EXAMPLES::
|
|
3001
|
+
|
|
3002
|
+
sage: R.<x> = QQ['x']
|
|
3003
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3004
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3005
|
+
sage: w = C.invariant_differential()
|
|
3006
|
+
sage: latex(w)
|
|
3007
|
+
1 \frac{dx}{2y}
|
|
3008
|
+
sage: latex(x*w)
|
|
3009
|
+
x \frac{dx}{2y}
|
|
3010
|
+
"""
|
|
3011
|
+
s = self._coeff._latex_()
|
|
3012
|
+
if s.find("+") != -1 or s.find("-") > 0:
|
|
3013
|
+
s = "\\left(%s\\right)" % s
|
|
3014
|
+
return s + " \\frac{dx}{2y}"
|
|
3015
|
+
|
|
3016
|
+
def _richcmp_(self, other, op):
|
|
3017
|
+
"""
|
|
3018
|
+
Rich comparison of ``self`` to ``other``.
|
|
3019
|
+
|
|
3020
|
+
.. TODO::
|
|
3021
|
+
|
|
3022
|
+
This does not compare elements by any reduction;
|
|
3023
|
+
it only compares the coefficients. The comparison
|
|
3024
|
+
should be done against a normal form or possibly
|
|
3025
|
+
after some reduction steps.
|
|
3026
|
+
|
|
3027
|
+
EXAMPLES::
|
|
3028
|
+
|
|
3029
|
+
sage: R.<x> = QQ['x']
|
|
3030
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3031
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3032
|
+
sage: (y^-1).diff() == (y^-1).diff()
|
|
3033
|
+
True
|
|
3034
|
+
|
|
3035
|
+
This element is the zero differential in the ring, but it does
|
|
3036
|
+
not compare as equal because the representative is different::
|
|
3037
|
+
|
|
3038
|
+
sage: MW = C.invariant_differential().parent()
|
|
3039
|
+
sage: (y^-1).diff().reduce_neg_y()
|
|
3040
|
+
((y^-1)*1, 0 dx/2y)
|
|
3041
|
+
sage: (y^-1).diff() == MW.zero()
|
|
3042
|
+
False
|
|
3043
|
+
"""
|
|
3044
|
+
return richcmp(self._coeff, other._coeff, op)
|
|
3045
|
+
|
|
3046
|
+
def extract_pow_y(self, k):
|
|
3047
|
+
r"""
|
|
3048
|
+
Return the power of `y` in `A` where ``self`` is `A dx/2y`.
|
|
3049
|
+
|
|
3050
|
+
EXAMPLES::
|
|
3051
|
+
|
|
3052
|
+
sage: R.<x> = QQ['x']
|
|
3053
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3054
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3055
|
+
sage: A = y^5 - x*y^3
|
|
3056
|
+
sage: A.extract_pow_y(5)
|
|
3057
|
+
[1, 0, 0, 0, 0]
|
|
3058
|
+
sage: (A * C.invariant_differential()).extract_pow_y(5)
|
|
3059
|
+
[1, 0, 0, 0, 0]
|
|
3060
|
+
"""
|
|
3061
|
+
return self._coeff.extract_pow_y(k)
|
|
3062
|
+
|
|
3063
|
+
def min_pow_y(self):
|
|
3064
|
+
r"""
|
|
3065
|
+
Return the minimum power of `y` in `A` where ``self`` is `A dx/2y`.
|
|
3066
|
+
|
|
3067
|
+
EXAMPLES::
|
|
3068
|
+
|
|
3069
|
+
sage: R.<x> = QQ['x']
|
|
3070
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3071
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3072
|
+
sage: w = y^5 * C.invariant_differential()
|
|
3073
|
+
sage: w.min_pow_y()
|
|
3074
|
+
5
|
|
3075
|
+
sage: w = (x^2*y^4 + y^5) * C.invariant_differential()
|
|
3076
|
+
sage: w.min_pow_y()
|
|
3077
|
+
4
|
|
3078
|
+
"""
|
|
3079
|
+
return self._coeff.min_pow_y()
|
|
3080
|
+
|
|
3081
|
+
def max_pow_y(self):
|
|
3082
|
+
r"""
|
|
3083
|
+
Return the maximum power of `y` in `A` where ``self`` is `A dx/2y`.
|
|
3084
|
+
|
|
3085
|
+
EXAMPLES::
|
|
3086
|
+
|
|
3087
|
+
sage: R.<x> = QQ['x']
|
|
3088
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3089
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3090
|
+
sage: w = y^5 * C.invariant_differential()
|
|
3091
|
+
sage: w.max_pow_y()
|
|
3092
|
+
5
|
|
3093
|
+
sage: w = (x^2*y^4 + y^5) * C.invariant_differential()
|
|
3094
|
+
sage: w.max_pow_y()
|
|
3095
|
+
5
|
|
3096
|
+
"""
|
|
3097
|
+
return self._coeff.max_pow_y()
|
|
3098
|
+
|
|
3099
|
+
def reduce_neg_y(self):
|
|
3100
|
+
r"""
|
|
3101
|
+
Use homology relations to eliminate negative powers of `y`.
|
|
3102
|
+
|
|
3103
|
+
EXAMPLES::
|
|
3104
|
+
|
|
3105
|
+
sage: R.<x> = QQ['x']
|
|
3106
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3107
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3108
|
+
sage: (y^-1).diff().reduce_neg_y()
|
|
3109
|
+
((y^-1)*1, 0 dx/2y)
|
|
3110
|
+
sage: (y^-5*x^2+y^-1*x).diff().reduce_neg_y()
|
|
3111
|
+
((y^-1)*x + (y^-5)*x^2, 0 dx/2y)
|
|
3112
|
+
"""
|
|
3113
|
+
S = self.parent().base_ring()
|
|
3114
|
+
R = S.base_ring()
|
|
3115
|
+
M = self.parent().helper_matrix()
|
|
3116
|
+
p = S._p
|
|
3117
|
+
f = S.zero()
|
|
3118
|
+
reduced = self
|
|
3119
|
+
for j in range(self.min_pow_y()+1, 0):
|
|
3120
|
+
if p is not None and p.divides(j):
|
|
3121
|
+
cs = [a/j for a in reduced.extract_pow_y(j-1)]
|
|
3122
|
+
else:
|
|
3123
|
+
j_inverse = ~R(j)
|
|
3124
|
+
cs = [a*j_inverse for a in reduced.extract_pow_y(j-1)]
|
|
3125
|
+
lin_comb = M * vector(M.base_ring(), cs)
|
|
3126
|
+
if lin_comb.is_zero():
|
|
3127
|
+
continue
|
|
3128
|
+
g = S.sum(S.monomial(i, j, val) for i, val in enumerate(lin_comb) if val)
|
|
3129
|
+
if not g.is_zero():
|
|
3130
|
+
f += g
|
|
3131
|
+
reduced -= g.diff()
|
|
3132
|
+
|
|
3133
|
+
return f, reduced
|
|
3134
|
+
|
|
3135
|
+
def reduce_neg_y_fast(self, even_degree_only=False):
|
|
3136
|
+
r"""
|
|
3137
|
+
Use homology relations to eliminate negative powers of `y`.
|
|
3138
|
+
|
|
3139
|
+
EXAMPLES::
|
|
3140
|
+
|
|
3141
|
+
sage: R.<x> = QQ['x']
|
|
3142
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3143
|
+
sage: x, y = E.monsky_washnitzer_gens()
|
|
3144
|
+
sage: (y^-1).diff().reduce_neg_y_fast()
|
|
3145
|
+
((y^-1)*1, 0 dx/2y)
|
|
3146
|
+
sage: (y^-5*x^2+y^-1*x).diff().reduce_neg_y_fast()
|
|
3147
|
+
((y^-1)*x + (y^-5)*x^2, 0 dx/2y)
|
|
3148
|
+
|
|
3149
|
+
It leaves nonnegative powers of `y` alone::
|
|
3150
|
+
|
|
3151
|
+
sage: y.diff()
|
|
3152
|
+
(-3*1 + 5*x^4) dx/2y
|
|
3153
|
+
sage: y.diff().reduce_neg_y_fast()
|
|
3154
|
+
(0, (-3*1 + 5*x^4) dx/2y)
|
|
3155
|
+
"""
|
|
3156
|
+
# prof = Profiler()
|
|
3157
|
+
# prof("reduce setup")
|
|
3158
|
+
S = self.parent().base_ring()
|
|
3159
|
+
R = S.base_ring()
|
|
3160
|
+
M = self.parent().helper_matrix()
|
|
3161
|
+
|
|
3162
|
+
# prof("extract coeffs")
|
|
3163
|
+
coeffs, offset = self.coeffs(R)
|
|
3164
|
+
V = coeffs[0].parent()
|
|
3165
|
+
|
|
3166
|
+
if offset == 0:
|
|
3167
|
+
return S(0), self
|
|
3168
|
+
|
|
3169
|
+
# prof("loop %s"%self.min_pow_y())
|
|
3170
|
+
forms = []
|
|
3171
|
+
p = S._p
|
|
3172
|
+
for j in range(self.min_pow_y()+1, 0):
|
|
3173
|
+
if (even_degree_only and j % 2 == 0) or coeffs[j-offset-1].is_zero():
|
|
3174
|
+
forms.append(V(0))
|
|
3175
|
+
else:
|
|
3176
|
+
# this is a total hack to deal with the fact that we're using
|
|
3177
|
+
# rational numbers to approximate fixed precision p-adics
|
|
3178
|
+
if p is not None and j % 3 == 1:
|
|
3179
|
+
try:
|
|
3180
|
+
v = coeffs[j-offset-1]
|
|
3181
|
+
for kk in range(len(v)):
|
|
3182
|
+
a = v[kk]
|
|
3183
|
+
ppow = p**max(-a.valuation(S._p), 0)
|
|
3184
|
+
v[kk] = ((a * ppow) % S._prec_cap) / ppow
|
|
3185
|
+
except AttributeError:
|
|
3186
|
+
pass
|
|
3187
|
+
lin_comb = ~R(j) * (M * coeffs[j-offset-1])
|
|
3188
|
+
forms.append(lin_comb)
|
|
3189
|
+
for i in lin_comb.nonzero_positions():
|
|
3190
|
+
# g = lin_comb[i] x^i y^j
|
|
3191
|
+
# self -= dg
|
|
3192
|
+
coeffs[j-offset+1] -= lin_comb[i] * S.monomial_diff_coeffs(i, j)[1]
|
|
3193
|
+
|
|
3194
|
+
# prof("recreate forms")
|
|
3195
|
+
f = S(forms, offset+1)
|
|
3196
|
+
reduced = S._monsky_washnitzer(coeffs[-1-offset:], -1)
|
|
3197
|
+
return f, reduced
|
|
3198
|
+
|
|
3199
|
+
def reduce_neg_y_faster(self, even_degree_only=False):
|
|
3200
|
+
r"""
|
|
3201
|
+
Use homology relations to eliminate negative powers of `y`.
|
|
3202
|
+
|
|
3203
|
+
EXAMPLES::
|
|
3204
|
+
|
|
3205
|
+
sage: R.<x> = QQ['x']
|
|
3206
|
+
sage: C = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3207
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3208
|
+
sage: (y^-1).diff().reduce_neg_y()
|
|
3209
|
+
((y^-1)*1, 0 dx/2y)
|
|
3210
|
+
sage: (y^-5*x^2+y^-1*x).diff().reduce_neg_y_faster()
|
|
3211
|
+
((y^-1)*x + (y^-5)*x^2, 0 dx/2y)
|
|
3212
|
+
"""
|
|
3213
|
+
# Timings indicate that this is not any faster after all...
|
|
3214
|
+
|
|
3215
|
+
S = self.parent().base_ring()
|
|
3216
|
+
R = S.base_ring()
|
|
3217
|
+
M = self.parent().helper_matrix()
|
|
3218
|
+
|
|
3219
|
+
coeffs, offset = self.coeffs(R)
|
|
3220
|
+
V = coeffs[0].parent()
|
|
3221
|
+
zeroV = V(0)
|
|
3222
|
+
|
|
3223
|
+
if offset == 0:
|
|
3224
|
+
return S.zero(), self
|
|
3225
|
+
|
|
3226
|
+
# See monomial_diff_coeffs
|
|
3227
|
+
# this is the B_i and x_to_i contributions respectively for all i
|
|
3228
|
+
d_mat_1, d_mat_2 = S.monomial_diff_coeffs_matrices()
|
|
3229
|
+
|
|
3230
|
+
forms = []
|
|
3231
|
+
for j in range(self.min_pow_y()+1, 0):
|
|
3232
|
+
if coeffs[j-offset-1].is_zero():
|
|
3233
|
+
forms.append(zeroV)
|
|
3234
|
+
else:
|
|
3235
|
+
# this is a total hack to deal with the fact that we're using
|
|
3236
|
+
# rational numbers to approximate fixed precision p-adics
|
|
3237
|
+
if j % 3 == 0:
|
|
3238
|
+
try:
|
|
3239
|
+
v = coeffs[j-offset-1]
|
|
3240
|
+
for kk in range(len(v)):
|
|
3241
|
+
a = v[kk]
|
|
3242
|
+
ppow = S._p**max(-a.valuation(S._p), 0)
|
|
3243
|
+
v[kk] = ((a * ppow) % S._prec_cap) / ppow
|
|
3244
|
+
except AttributeError:
|
|
3245
|
+
pass
|
|
3246
|
+
j_inverse = ~R(j)
|
|
3247
|
+
lin_comb = (M * coeffs[j-offset-1])
|
|
3248
|
+
forms.append(j_inverse * lin_comb)
|
|
3249
|
+
coeffs[j-offset+1] -= (d_mat_1 + j_inverse * d_mat_2) * lin_comb
|
|
3250
|
+
|
|
3251
|
+
f = S(forms, offset + 1)
|
|
3252
|
+
reduced = S._monsky_washnitzer(coeffs[-1-offset:], -1)
|
|
3253
|
+
# reduced = self - f.diff()
|
|
3254
|
+
return f, reduced
|
|
3255
|
+
|
|
3256
|
+
def reduce_pos_y(self):
|
|
3257
|
+
r"""
|
|
3258
|
+
Use homology relations to eliminate positive powers of `y`.
|
|
3259
|
+
|
|
3260
|
+
EXAMPLES::
|
|
3261
|
+
|
|
3262
|
+
sage: R.<x> = QQ['x']
|
|
3263
|
+
sage: C = HyperellipticCurve(x^3-4*x+4)
|
|
3264
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3265
|
+
sage: (y^2).diff().reduce_pos_y()
|
|
3266
|
+
(y^2*1, 0 dx/2y)
|
|
3267
|
+
sage: (y^2*x).diff().reduce_pos_y()
|
|
3268
|
+
(y^2*x, 0 dx/2y)
|
|
3269
|
+
sage: (y^92*x).diff().reduce_pos_y()
|
|
3270
|
+
(y^92*x, 0 dx/2y)
|
|
3271
|
+
sage: w = (y^3 + x).diff()
|
|
3272
|
+
sage: w += w.parent()(x)
|
|
3273
|
+
sage: w.reduce_pos_y_fast()
|
|
3274
|
+
(y^3*1 + x, x dx/2y)
|
|
3275
|
+
"""
|
|
3276
|
+
S = self.parent().base_ring()
|
|
3277
|
+
n = S.Q().degree()
|
|
3278
|
+
f = S.zero()
|
|
3279
|
+
reduced = self
|
|
3280
|
+
for j in range(self.max_pow_y(), 0, -1):
|
|
3281
|
+
for i in range(n - 1, -1, -1):
|
|
3282
|
+
c = reduced.extract_pow_y(j)[i]
|
|
3283
|
+
if c:
|
|
3284
|
+
g = S.monomial(0, j+1) if i == n-1 else S.monomial(i+1, j-1)
|
|
3285
|
+
dg = g.diff()
|
|
3286
|
+
denom = dg.extract_pow_y(j)[i]
|
|
3287
|
+
c /= denom
|
|
3288
|
+
c = g.parent()(c)
|
|
3289
|
+
f += c * g
|
|
3290
|
+
reduced -= c * dg
|
|
3291
|
+
|
|
3292
|
+
return f, reduced
|
|
3293
|
+
|
|
3294
|
+
def reduce_pos_y_fast(self, even_degree_only=False):
|
|
3295
|
+
r"""
|
|
3296
|
+
Use homology relations to eliminate positive powers of `y`.
|
|
3297
|
+
|
|
3298
|
+
EXAMPLES::
|
|
3299
|
+
|
|
3300
|
+
sage: R.<x> = QQ['x']
|
|
3301
|
+
sage: E = HyperellipticCurve(x^3 - 4*x + 4)
|
|
3302
|
+
sage: x, y = E.monsky_washnitzer_gens()
|
|
3303
|
+
sage: y.diff().reduce_pos_y_fast()
|
|
3304
|
+
(y*1, 0 dx/2y)
|
|
3305
|
+
sage: (y^2).diff().reduce_pos_y_fast()
|
|
3306
|
+
(y^2*1, 0 dx/2y)
|
|
3307
|
+
sage: (y^2*x).diff().reduce_pos_y_fast()
|
|
3308
|
+
(y^2*x, 0 dx/2y)
|
|
3309
|
+
sage: (y^92*x).diff().reduce_pos_y_fast()
|
|
3310
|
+
(y^92*x, 0 dx/2y)
|
|
3311
|
+
sage: w = (y^3 + x).diff()
|
|
3312
|
+
sage: w += w.parent()(x)
|
|
3313
|
+
sage: w.reduce_pos_y_fast()
|
|
3314
|
+
(y^3*1 + x, x dx/2y)
|
|
3315
|
+
"""
|
|
3316
|
+
S = self.parent().base_ring()
|
|
3317
|
+
R = S.base_ring()
|
|
3318
|
+
n = S.Q().degree()
|
|
3319
|
+
|
|
3320
|
+
coeffs, offset = self.coeffs(R)
|
|
3321
|
+
V = coeffs[0].parent()
|
|
3322
|
+
zeroV = V(0)
|
|
3323
|
+
forms = [V(0), V(0)]
|
|
3324
|
+
|
|
3325
|
+
for j in range(self.max_pow_y(), -1, -1):
|
|
3326
|
+
|
|
3327
|
+
if (even_degree_only and j % 2) or (j > 0 and coeffs[j-offset].is_zero()):
|
|
3328
|
+
forms.append(zeroV)
|
|
3329
|
+
continue
|
|
3330
|
+
|
|
3331
|
+
form = V(0)
|
|
3332
|
+
i = n - 1
|
|
3333
|
+
c = coeffs[j-offset][i]
|
|
3334
|
+
if c:
|
|
3335
|
+
dg_coeffs = S.monomial_diff_coeffs(0, j+1)[0]
|
|
3336
|
+
c /= dg_coeffs[i]
|
|
3337
|
+
forms[len(forms)-2][0] = c
|
|
3338
|
+
# self -= c d(y^{j+1})
|
|
3339
|
+
coeffs[j-offset] -= c*dg_coeffs
|
|
3340
|
+
|
|
3341
|
+
if j == 0:
|
|
3342
|
+
# the others are basis elements
|
|
3343
|
+
break
|
|
3344
|
+
|
|
3345
|
+
for i in range(n-2, -1, -1):
|
|
3346
|
+
c = coeffs[j-offset][i]
|
|
3347
|
+
if c:
|
|
3348
|
+
dg_coeffs = S.monomial_diff_coeffs(i+1, j-1)
|
|
3349
|
+
denom = dg_coeffs[1][i]
|
|
3350
|
+
c /= denom
|
|
3351
|
+
form[i+1] = c
|
|
3352
|
+
# self -= c d(x^{i+1} y^{j-1})
|
|
3353
|
+
coeffs[j-offset] -= c*dg_coeffs[1]
|
|
3354
|
+
coeffs[j-offset-2] -= c*dg_coeffs[0]
|
|
3355
|
+
forms.append(form)
|
|
3356
|
+
|
|
3357
|
+
forms.reverse()
|
|
3358
|
+
f = S(forms)
|
|
3359
|
+
reduced = self.parent()(coeffs[:1-offset], offset)
|
|
3360
|
+
return f, reduced
|
|
3361
|
+
|
|
3362
|
+
def reduce(self):
|
|
3363
|
+
r"""
|
|
3364
|
+
Use homology relations to find `a` and `f` such that this element is
|
|
3365
|
+
equal to `a + df`, where `a` is given in terms of the `x^i dx/2y`.
|
|
3366
|
+
|
|
3367
|
+
EXAMPLES::
|
|
3368
|
+
|
|
3369
|
+
sage: R.<x> = QQ['x']
|
|
3370
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3371
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3372
|
+
sage: w = (y*x).diff()
|
|
3373
|
+
sage: w.reduce()
|
|
3374
|
+
(y*x, 0 dx/2y)
|
|
3375
|
+
|
|
3376
|
+
sage: w = x^4 * C.invariant_differential()
|
|
3377
|
+
sage: w.reduce()
|
|
3378
|
+
(1/5*y*1, 4/5*1 dx/2y)
|
|
3379
|
+
|
|
3380
|
+
sage: w = sum(QQ.random_element() * x^i * y^j
|
|
3381
|
+
....: for i in [0..4] for j in [-3..3]) * C.invariant_differential()
|
|
3382
|
+
sage: f, a = w.reduce()
|
|
3383
|
+
sage: f.diff() + a - w
|
|
3384
|
+
0 dx/2y
|
|
3385
|
+
"""
|
|
3386
|
+
n = self.parent().base_ring().Q().degree()
|
|
3387
|
+
f1, a = self.reduce_neg_y()
|
|
3388
|
+
f2, a = a.reduce_pos_y()
|
|
3389
|
+
f = f1 + f2
|
|
3390
|
+
|
|
3391
|
+
c = a.extract_pow_y(0)[n - 1]
|
|
3392
|
+
if c:
|
|
3393
|
+
x, y = self.parent().base_ring().gens()
|
|
3394
|
+
g = y
|
|
3395
|
+
dg = g.diff()
|
|
3396
|
+
c = g.parent()(c / dg.extract_pow_y(0)[n - 1])
|
|
3397
|
+
f += c * g
|
|
3398
|
+
a -= c * dg
|
|
3399
|
+
|
|
3400
|
+
return f, a
|
|
3401
|
+
|
|
3402
|
+
def reduce_fast(self, even_degree_only=False):
|
|
3403
|
+
r"""
|
|
3404
|
+
Use homology relations to find `a` and `f` such that this element is
|
|
3405
|
+
equal to `a + df`, where `a` is given in terms of the `x^i dx/2y`.
|
|
3406
|
+
|
|
3407
|
+
EXAMPLES::
|
|
3408
|
+
|
|
3409
|
+
sage: R.<x> = QQ['x']
|
|
3410
|
+
sage: E = HyperellipticCurve(x^3 - 4*x + 4)
|
|
3411
|
+
sage: x, y = E.monsky_washnitzer_gens()
|
|
3412
|
+
sage: x.diff().reduce_fast()
|
|
3413
|
+
(x, (0, 0))
|
|
3414
|
+
sage: y.diff().reduce_fast()
|
|
3415
|
+
(y*1, (0, 0))
|
|
3416
|
+
sage: (y^-1).diff().reduce_fast()
|
|
3417
|
+
((y^-1)*1, (0, 0))
|
|
3418
|
+
sage: (y^-11).diff().reduce_fast()
|
|
3419
|
+
((y^-11)*1, (0, 0))
|
|
3420
|
+
sage: (x*y^2).diff().reduce_fast()
|
|
3421
|
+
(y^2*x, (0, 0))
|
|
3422
|
+
"""
|
|
3423
|
+
f1, reduced = self.reduce_neg_y_fast(even_degree_only)
|
|
3424
|
+
f2, reduced = reduced.reduce_pos_y_fast(even_degree_only)
|
|
3425
|
+
# f1, reduced = self.reduce_neg_y()
|
|
3426
|
+
# f2, reduced = reduced.reduce_pos_y()
|
|
3427
|
+
v = reduced.extract_pow_y(0)
|
|
3428
|
+
v.pop()
|
|
3429
|
+
V = FreeModule(self.base_ring().base_ring(), len(v))
|
|
3430
|
+
return f1 + f2, V(v)
|
|
3431
|
+
|
|
3432
|
+
def coeffs(self, R=None):
|
|
3433
|
+
"""
|
|
3434
|
+
Used to obtain the raw coefficients of a differential, see
|
|
3435
|
+
:meth:`SpecialHyperellipticQuotientElement.coeffs`
|
|
3436
|
+
|
|
3437
|
+
INPUT:
|
|
3438
|
+
|
|
3439
|
+
- ``R`` -- an (optional) base ring in which to cast the coefficients
|
|
3440
|
+
|
|
3441
|
+
OUTPUT: the raw coefficients of `A` where ``self`` is `A dx/2y`
|
|
3442
|
+
|
|
3443
|
+
EXAMPLES::
|
|
3444
|
+
|
|
3445
|
+
sage: R.<x> = QQ['x']
|
|
3446
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3447
|
+
sage: x,y = C.monsky_washnitzer_gens()
|
|
3448
|
+
sage: w = C.invariant_differential()
|
|
3449
|
+
sage: w.coeffs()
|
|
3450
|
+
([(1, 0, 0, 0, 0)], 0)
|
|
3451
|
+
sage: (x*w).coeffs()
|
|
3452
|
+
([(0, 1, 0, 0, 0)], 0)
|
|
3453
|
+
sage: (y*w).coeffs()
|
|
3454
|
+
([(0, 0, 0, 0, 0), (1, 0, 0, 0, 0)], 0)
|
|
3455
|
+
sage: (y^-2*w).coeffs()
|
|
3456
|
+
([(1, 0, 0, 0, 0), (0, 0, 0, 0, 0), (0, 0, 0, 0, 0)], -2)
|
|
3457
|
+
"""
|
|
3458
|
+
return self._coeff.coeffs(R)
|
|
3459
|
+
|
|
3460
|
+
def coleman_integral(self, P, Q):
|
|
3461
|
+
r"""
|
|
3462
|
+
Compute the definite integral of ``self`` from `P` to `Q`.
|
|
3463
|
+
|
|
3464
|
+
INPUT:
|
|
3465
|
+
|
|
3466
|
+
- `P`, `Q` -- two points on the underlying curve
|
|
3467
|
+
|
|
3468
|
+
OUTPUT: `\int_P^Q \text{self}`
|
|
3469
|
+
|
|
3470
|
+
EXAMPLES::
|
|
3471
|
+
|
|
3472
|
+
sage: K = pAdicField(5,7)
|
|
3473
|
+
sage: E = EllipticCurve(K,[-31/3,-2501/108]) #11a
|
|
3474
|
+
sage: P = E(K(14/3), K(11/2))
|
|
3475
|
+
sage: w = E.invariant_differential()
|
|
3476
|
+
sage: w.coleman_integral(P, 2*P)
|
|
3477
|
+
O(5^6)
|
|
3478
|
+
|
|
3479
|
+
sage: Q = E([3,58332])
|
|
3480
|
+
sage: w.coleman_integral(P,Q)
|
|
3481
|
+
2*5 + 4*5^2 + 3*5^3 + 4*5^4 + 3*5^5 + O(5^6)
|
|
3482
|
+
sage: w.coleman_integral(2*P,Q)
|
|
3483
|
+
2*5 + 4*5^2 + 3*5^3 + 4*5^4 + 3*5^5 + O(5^6)
|
|
3484
|
+
sage: (2*w).coleman_integral(P, Q) == 2*(w.coleman_integral(P, Q))
|
|
3485
|
+
True
|
|
3486
|
+
"""
|
|
3487
|
+
return self.parent().base_ring().curve().coleman_integral(self, P, Q)
|
|
3488
|
+
|
|
3489
|
+
integrate = coleman_integral
|
|
3490
|
+
|
|
3491
|
+
|
|
3492
|
+
class MonskyWashnitzerDifferentialRing(UniqueRepresentation, Module):
|
|
3493
|
+
r"""
|
|
3494
|
+
A ring of Monsky--Washnitzer differentials over ``base_ring``.
|
|
3495
|
+
"""
|
|
3496
|
+
def __init__(self, base_ring):
|
|
3497
|
+
r"""
|
|
3498
|
+
Initialize ``self``.
|
|
3499
|
+
|
|
3500
|
+
TESTS::
|
|
3501
|
+
|
|
3502
|
+
sage: R.<x> = QQ['x']
|
|
3503
|
+
sage: E = HyperellipticCurve(x^5 - 3*x + 1)
|
|
3504
|
+
sage: from sage.schemes.hyperelliptic_curves.monsky_washnitzer import SpecialHyperellipticQuotientRing, MonskyWashnitzerDifferentialRing
|
|
3505
|
+
sage: S = SpecialHyperellipticQuotientRing(E)
|
|
3506
|
+
sage: DR = MonskyWashnitzerDifferentialRing(S)
|
|
3507
|
+
sage: TestSuite(DR).run() # needs sage.rings.real_interval_field
|
|
3508
|
+
|
|
3509
|
+
Check that caching works::
|
|
3510
|
+
|
|
3511
|
+
sage: DR is MonskyWashnitzerDifferentialRing(S)
|
|
3512
|
+
True
|
|
3513
|
+
"""
|
|
3514
|
+
Module.__init__(self, base_ring)
|
|
3515
|
+
|
|
3516
|
+
def invariant_differential(self):
|
|
3517
|
+
r"""
|
|
3518
|
+
Return `dx/2y` as an element of ``self``.
|
|
3519
|
+
|
|
3520
|
+
EXAMPLES::
|
|
3521
|
+
|
|
3522
|
+
sage: R.<x> = QQ['x']
|
|
3523
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3524
|
+
sage: MW = C.invariant_differential().parent()
|
|
3525
|
+
sage: MW.invariant_differential()
|
|
3526
|
+
1 dx/2y
|
|
3527
|
+
"""
|
|
3528
|
+
return self.element_class(self, 1)
|
|
3529
|
+
|
|
3530
|
+
def base_extend(self, R):
|
|
3531
|
+
"""
|
|
3532
|
+
Return a new differential ring which is ``self`` base-extended to `R`.
|
|
3533
|
+
|
|
3534
|
+
INPUT:
|
|
3535
|
+
|
|
3536
|
+
- ``R`` -- ring
|
|
3537
|
+
|
|
3538
|
+
OUTPUT:
|
|
3539
|
+
|
|
3540
|
+
Self, base-extended to `R`.
|
|
3541
|
+
|
|
3542
|
+
EXAMPLES::
|
|
3543
|
+
|
|
3544
|
+
sage: R.<x> = QQ['x']
|
|
3545
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3546
|
+
sage: MW = C.invariant_differential().parent()
|
|
3547
|
+
sage: MW.base_ring()
|
|
3548
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 4*x + 4)
|
|
3549
|
+
over Rational Field
|
|
3550
|
+
sage: MW.base_extend(Qp(5,5)).base_ring() # needs sage.rings.padics
|
|
3551
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = (1 + O(5^5))*x^5
|
|
3552
|
+
+ (1 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5))*x + 4 + O(5^5))
|
|
3553
|
+
over 5-adic Field with capped relative precision 5
|
|
3554
|
+
"""
|
|
3555
|
+
return MonskyWashnitzerDifferentialRing(self.base_ring().base_extend(R))
|
|
3556
|
+
|
|
3557
|
+
def change_ring(self, R):
|
|
3558
|
+
"""
|
|
3559
|
+
Return a new differential ring which is ``self`` with the coefficient
|
|
3560
|
+
ring changed to `R`.
|
|
3561
|
+
|
|
3562
|
+
INPUT:
|
|
3563
|
+
|
|
3564
|
+
- ``R`` -- ring of coefficients
|
|
3565
|
+
|
|
3566
|
+
OUTPUT: ``self`` with the coefficient ring changed to `R`
|
|
3567
|
+
|
|
3568
|
+
EXAMPLES::
|
|
3569
|
+
|
|
3570
|
+
sage: R.<x> = QQ['x']
|
|
3571
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3572
|
+
sage: MW = C.invariant_differential().parent()
|
|
3573
|
+
sage: MW.base_ring()
|
|
3574
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = x^5 - 4*x + 4)
|
|
3575
|
+
over Rational Field
|
|
3576
|
+
sage: MW.change_ring(Qp(5,5)).base_ring() # needs sage.rings.padics
|
|
3577
|
+
SpecialHyperellipticQuotientRing K[x,y,y^-1] / (y^2 = (1 + O(5^5))*x^5
|
|
3578
|
+
+ (1 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5))*x + 4 + O(5^5))
|
|
3579
|
+
over 5-adic Field with capped relative precision 5
|
|
3580
|
+
"""
|
|
3581
|
+
return MonskyWashnitzerDifferentialRing(self.base_ring().change_ring(R))
|
|
3582
|
+
|
|
3583
|
+
def degree(self):
|
|
3584
|
+
"""
|
|
3585
|
+
Return the degree of `Q(x)`, where the model of the underlying
|
|
3586
|
+
hyperelliptic curve of ``self`` is given by `y^2 = Q(x)`.
|
|
3587
|
+
|
|
3588
|
+
EXAMPLES::
|
|
3589
|
+
|
|
3590
|
+
sage: R.<x> = QQ['x']
|
|
3591
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3592
|
+
sage: MW = C.invariant_differential().parent()
|
|
3593
|
+
sage: MW.Q()
|
|
3594
|
+
x^5 - 4*x + 4
|
|
3595
|
+
sage: MW.degree()
|
|
3596
|
+
5
|
|
3597
|
+
"""
|
|
3598
|
+
return self.base_ring().degree()
|
|
3599
|
+
|
|
3600
|
+
def dimension(self):
|
|
3601
|
+
"""
|
|
3602
|
+
Return the dimension of ``self``.
|
|
3603
|
+
|
|
3604
|
+
EXAMPLES::
|
|
3605
|
+
|
|
3606
|
+
sage: # needs sage.rings.padics
|
|
3607
|
+
sage: R.<x> = QQ['x']
|
|
3608
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3609
|
+
sage: K = Qp(7,5)
|
|
3610
|
+
sage: CK = C.change_ring(K)
|
|
3611
|
+
sage: MW = CK.invariant_differential().parent()
|
|
3612
|
+
sage: MW.dimension()
|
|
3613
|
+
4
|
|
3614
|
+
"""
|
|
3615
|
+
return self.base_ring().degree() - 1
|
|
3616
|
+
|
|
3617
|
+
def Q(self):
|
|
3618
|
+
"""
|
|
3619
|
+
Return `Q(x)` where the model of the underlying hyperelliptic curve
|
|
3620
|
+
of ``self`` is given by `y^2 = Q(x)`.
|
|
3621
|
+
|
|
3622
|
+
EXAMPLES::
|
|
3623
|
+
|
|
3624
|
+
sage: R.<x> = QQ['x']
|
|
3625
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3626
|
+
sage: MW = C.invariant_differential().parent()
|
|
3627
|
+
sage: MW.Q()
|
|
3628
|
+
x^5 - 4*x + 4
|
|
3629
|
+
"""
|
|
3630
|
+
return self.base_ring().Q()
|
|
3631
|
+
|
|
3632
|
+
@cached_method
|
|
3633
|
+
def x_to_p(self, p):
|
|
3634
|
+
"""
|
|
3635
|
+
Return and cache `x^p`, reduced via the relations coming from the
|
|
3636
|
+
defining polynomial of the hyperelliptic curve.
|
|
3637
|
+
|
|
3638
|
+
EXAMPLES::
|
|
3639
|
+
|
|
3640
|
+
sage: R.<x> = QQ['x']
|
|
3641
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3642
|
+
sage: MW = C.invariant_differential().parent()
|
|
3643
|
+
sage: MW.x_to_p(3)
|
|
3644
|
+
x^3
|
|
3645
|
+
sage: MW.x_to_p(5)
|
|
3646
|
+
-(4-y^2)*1 + 4*x
|
|
3647
|
+
sage: MW.x_to_p(101) is MW.x_to_p(101)
|
|
3648
|
+
True
|
|
3649
|
+
"""
|
|
3650
|
+
return self.base_ring().x()**p
|
|
3651
|
+
|
|
3652
|
+
@cached_method
|
|
3653
|
+
def frob_Q(self, p):
|
|
3654
|
+
r"""
|
|
3655
|
+
Return and cache `Q(x^p)`, which is used in computing the image of
|
|
3656
|
+
`y` under a `p`-power lift of Frobenius to `A^{\dagger}`.
|
|
3657
|
+
|
|
3658
|
+
EXAMPLES::
|
|
3659
|
+
|
|
3660
|
+
sage: R.<x> = QQ['x']
|
|
3661
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3662
|
+
sage: MW = C.invariant_differential().parent()
|
|
3663
|
+
sage: MW.frob_Q(3)
|
|
3664
|
+
-(60-48*y^2+12*y^4-y^6)*1 + (192-96*y^2+12*y^4)*x - (192-48*y^2)*x^2 + 60*x^3
|
|
3665
|
+
sage: MW.Q()(MW.x_to_p(3)) # needs sage.rings.real_interval_field
|
|
3666
|
+
-(60-48*y^2+12*y^4-y^6)*1 + (192-96*y^2+12*y^4)*x - (192-48*y^2)*x^2 + 60*x^3
|
|
3667
|
+
sage: MW.frob_Q(11) is MW.frob_Q(11)
|
|
3668
|
+
True
|
|
3669
|
+
"""
|
|
3670
|
+
return self.base_ring()._Q.change_ring(self.base_ring())(self.x_to_p(p))
|
|
3671
|
+
|
|
3672
|
+
def frob_invariant_differential(self, prec, p):
|
|
3673
|
+
r"""
|
|
3674
|
+
Kedlaya's algorithm allows us to calculate the action of Frobenius on
|
|
3675
|
+
the Monsky-Washnitzer cohomology. First we lift `\phi` to `A^{\dagger}`
|
|
3676
|
+
by setting
|
|
3677
|
+
|
|
3678
|
+
.. MATH::
|
|
3679
|
+
|
|
3680
|
+
\phi(x) = x^p,
|
|
3681
|
+
\qquad\qquad
|
|
3682
|
+
\phi(y) = y^p \sqrt{1 + \frac{Q(x^p) - Q(x)^p}{Q(x)^p}}.
|
|
3683
|
+
|
|
3684
|
+
Pulling back the differential `dx/2y`, we get
|
|
3685
|
+
|
|
3686
|
+
.. MATH::
|
|
3687
|
+
|
|
3688
|
+
\phi^*(dx/2y) = px^{p-1} y(\phi(y))^{-1} dx/2y
|
|
3689
|
+
= px^{p-1} y^{1-p} \sqrt{1+ \frac{Q(x^p) - Q(x)^p}{Q(x)^p}} dx/2y.
|
|
3690
|
+
|
|
3691
|
+
Use Newton's method to calculate the square root.
|
|
3692
|
+
|
|
3693
|
+
EXAMPLES::
|
|
3694
|
+
|
|
3695
|
+
sage: R.<x> = QQ['x']
|
|
3696
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3697
|
+
sage: prec = 2
|
|
3698
|
+
sage: p = 7
|
|
3699
|
+
sage: MW = C.invariant_differential().parent()
|
|
3700
|
+
sage: MW.frob_invariant_differential(prec, p)
|
|
3701
|
+
((67894400*y^-20-81198880*y^-18+40140800*y^-16-10035200*y^-14+1254400*y^-12-62720*y^-10)*1
|
|
3702
|
+
- (119503944*y^-20-116064242*y^-18+43753472*y^-16-7426048*y^-14+514304*y^-12-12544*y^-10+1568*y^-8-70*y^-6-7*y^-4)*x
|
|
3703
|
+
+ (78905288*y^-20-61014016*y^-18+16859136*y^-16-2207744*y^-14+250880*y^-12-37632*y^-10+3136*y^-8-70*y^-6)*x^2
|
|
3704
|
+
- (39452448*y^-20-26148752*y^-18+8085490*y^-16-2007040*y^-14+376320*y^-12-37632*y^-10+1568*y^-8)*x^3
|
|
3705
|
+
+ (21102144*y^-20-18120592*y^-18+8028160*y^-16-2007040*y^-14+250880*y^-12-12544*y^-10)*x^4) dx/2y
|
|
3706
|
+
"""
|
|
3707
|
+
try:
|
|
3708
|
+
from sage.misc.profiler import Profiler
|
|
3709
|
+
except ImportError:
|
|
3710
|
+
def prof():
|
|
3711
|
+
pass
|
|
3712
|
+
else:
|
|
3713
|
+
prof = Profiler()
|
|
3714
|
+
prof("setup")
|
|
3715
|
+
# TODO, would it be useful to be able to take Frobenius of any element? Less efficient?
|
|
3716
|
+
x, y = self.base_ring().gens()
|
|
3717
|
+
prof("x_to_p")
|
|
3718
|
+
x_to_p_less_1 = x**(p-1)
|
|
3719
|
+
x_to_p = x*x_to_p_less_1
|
|
3720
|
+
|
|
3721
|
+
# cache for future use
|
|
3722
|
+
self.x_to_p.set_cache(p, x_to_p)
|
|
3723
|
+
|
|
3724
|
+
prof("frob_Q")
|
|
3725
|
+
a = self.frob_Q(p) >> 2*p # frobQ * y^{-2p}
|
|
3726
|
+
|
|
3727
|
+
prof("sqrt")
|
|
3728
|
+
|
|
3729
|
+
# Q = self.base_ring()._Q
|
|
3730
|
+
# three_halves = Q.parent().base_ring()(Rational((3,2)))
|
|
3731
|
+
# one_half = Q.parent().base_ring()(Rational((1,2)))
|
|
3732
|
+
three_halves = self.base_ring()._series_ring.base_ring()(Rational((3, 2)))
|
|
3733
|
+
one_half = self.base_ring()._series_ring.base_ring()(Rational((1, 2)))
|
|
3734
|
+
half_a = a._rmul_(one_half)
|
|
3735
|
+
|
|
3736
|
+
# We are solving for t = a^{-1/2} = (F_pQ y^{-p})^{-1/2}
|
|
3737
|
+
# Newton's method converges because we know the root is in the same residue class as 1.
|
|
3738
|
+
|
|
3739
|
+
# t = self.base_ring()(1)
|
|
3740
|
+
t = self.base_ring()(three_halves) - half_a
|
|
3741
|
+
# first iteration trivial, start with prec 2
|
|
3742
|
+
|
|
3743
|
+
from sage.misc.misc import newton_method_sizes
|
|
3744
|
+
|
|
3745
|
+
for cur_prec in newton_method_sizes(prec)[2:]:
|
|
3746
|
+
# newton_method_sizes = [1, 2, ...]
|
|
3747
|
+
y_prec = -(2*cur_prec-1)*p+1
|
|
3748
|
+
# binomial expansion is $\sum p^{k+1} y^{-(2k+1)p+1} f(x)$
|
|
3749
|
+
# so if we are only correct mod p^prec,
|
|
3750
|
+
# can ignore y powers less than y_prec
|
|
3751
|
+
t_cube = (t*t*t).truncate_neg(y_prec)
|
|
3752
|
+
t = t._rmul_(three_halves) - (half_a * t_cube).truncate_neg(y_prec)
|
|
3753
|
+
# t = (3/2) t - (1/2) a t^3
|
|
3754
|
+
|
|
3755
|
+
prof("compose")
|
|
3756
|
+
F_dx_y = (p * x_to_p_less_1 * t) >> (p-1) # px^{p-1} sqrt(a) * y^{-p+1}
|
|
3757
|
+
|
|
3758
|
+
prof("done")
|
|
3759
|
+
return MonskyWashnitzerDifferential(self, F_dx_y)
|
|
3760
|
+
|
|
3761
|
+
def frob_basis_elements(self, prec, p):
|
|
3762
|
+
r"""
|
|
3763
|
+
Return the action of a `p`-power lift of Frobenius on the basis.
|
|
3764
|
+
|
|
3765
|
+
.. MATH::
|
|
3766
|
+
|
|
3767
|
+
\{ dx/2y, x dx/2y, ..., x^{d-2} dx/2y \},
|
|
3768
|
+
|
|
3769
|
+
where `d` is the degree of the underlying hyperelliptic curve.
|
|
3770
|
+
|
|
3771
|
+
EXAMPLES::
|
|
3772
|
+
|
|
3773
|
+
sage: R.<x> = QQ['x']
|
|
3774
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3775
|
+
sage: prec = 1
|
|
3776
|
+
sage: p = 5
|
|
3777
|
+
sage: MW = C.invariant_differential().parent()
|
|
3778
|
+
sage: MW.frob_basis_elements(prec, p)
|
|
3779
|
+
[((92000*y^-14-74200*y^-12+32000*y^-10-8000*y^-8+1000*y^-6-50*y^-4)*1
|
|
3780
|
+
- (194400*y^-14-153600*y^-12+57600*y^-10-9600*y^-8+600*y^-6)*x
|
|
3781
|
+
+ (204800*y^-14-153600*y^-12+38400*y^-10-3200*y^-8)*x^2
|
|
3782
|
+
- (153600*y^-14-76800*y^-12+9600*y^-10)*x^3
|
|
3783
|
+
+ (63950*y^-14-18550*y^-12+1600*y^-10-400*y^-8+50*y^-6+5*y^-4)*x^4) dx/2y,
|
|
3784
|
+
(-(1391200*y^-14-941400*y^-12+302000*y^-10-76800*y^-8+14400*y^-6-1320*y^-4+30*y^-2)*1
|
|
3785
|
+
+ (2168800*y^-14-1402400*y^-12+537600*y^-10-134400*y^-8+16800*y^-6-720*y^-4)*x
|
|
3786
|
+
- (1596800*y^-14-1433600*y^-12+537600*y^-10-89600*y^-8+5600*y^-6)*x^2
|
|
3787
|
+
+ (1433600*y^-14-1075200*y^-12+268800*y^-10-22400*y^-8)*x^3
|
|
3788
|
+
- (870200*y^-14-445350*y^-12+63350*y^-10-3200*y^-8+600*y^-6-30*y^-4-5*y^-2)*x^4) dx/2y,
|
|
3789
|
+
((19488000*y^-14-15763200*y^-12+4944400*y^-10-913800*y^-8+156800*y^-6-22560*y^-4+1480*y^-2-10)*1
|
|
3790
|
+
- (28163200*y^-14-18669600*y^-12+5774400*y^-10-1433600*y^-8+268800*y^-6-25440*y^-4+760*y^-2)*x
|
|
3791
|
+
+ (15062400*y^-14-12940800*y^-12+5734400*y^-10-1433600*y^-8+179200*y^-6-8480*y^-4)*x^2
|
|
3792
|
+
- (12121600*y^-14-11468800*y^-12+4300800*y^-10-716800*y^-8+44800*y^-6)*x^3
|
|
3793
|
+
+ (9215200*y^-14-6952400*y^-12+1773950*y^-10-165750*y^-8+5600*y^-6-720*y^-4+10*y^-2+5)*x^4) dx/2y,
|
|
3794
|
+
(-(225395200*y^-14-230640000*y^-12+91733600*y^-10-18347400*y^-8+2293600*y^-6-280960*y^-4+31520*y^-2-1480-10*y^2)*1
|
|
3795
|
+
+ (338048000*y^-14-277132800*y^-12+89928000*y^-10-17816000*y^-8+3225600*y^-6-472320*y^-4+34560*y^-2-720)*x
|
|
3796
|
+
- (172902400*y^-14-141504000*y^-12+58976000*y^-10-17203200*y^-8+3225600*y^-6-314880*y^-4+11520*y^-2)*x^2
|
|
3797
|
+
+ (108736000*y^-14-109760000*y^-12+51609600*y^-10-12902400*y^-8+1612800*y^-6-78720*y^-4)*x^3
|
|
3798
|
+
- (85347200*y^-14-82900000*y^-12+31251400*y^-10-5304150*y^-8+367350*y^-6-8480*y^-4+760*y^-2+10-5*y^2)*x^4) dx/2y]
|
|
3799
|
+
"""
|
|
3800
|
+
F_i = self.frob_invariant_differential(prec, p)
|
|
3801
|
+
x_to_p = self.x_to_p(p)
|
|
3802
|
+
F = [F_i]
|
|
3803
|
+
for i in range(1, self.degree()-1):
|
|
3804
|
+
F_i *= x_to_p
|
|
3805
|
+
F.append(F_i)
|
|
3806
|
+
return F
|
|
3807
|
+
|
|
3808
|
+
@cached_method
|
|
3809
|
+
def helper_matrix(self):
|
|
3810
|
+
r"""
|
|
3811
|
+
We use this to solve for the linear combination of
|
|
3812
|
+
`x^i y^j` needed to clear all terms with `y^{j-1}`.
|
|
3813
|
+
|
|
3814
|
+
EXAMPLES::
|
|
3815
|
+
|
|
3816
|
+
sage: R.<x> = QQ['x']
|
|
3817
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3818
|
+
sage: MW = C.invariant_differential().parent()
|
|
3819
|
+
sage: MW.helper_matrix()
|
|
3820
|
+
[ 256/2101 320/2101 400/2101 500/2101 625/2101]
|
|
3821
|
+
[-625/8404 -64/2101 -80/2101 -100/2101 -125/2101]
|
|
3822
|
+
[-125/2101 -625/8404 -64/2101 -80/2101 -100/2101]
|
|
3823
|
+
[-100/2101 -125/2101 -625/8404 -64/2101 -80/2101]
|
|
3824
|
+
[ -80/2101 -100/2101 -125/2101 -625/8404 -64/2101]
|
|
3825
|
+
"""
|
|
3826
|
+
# The smallest y term of (1/j) d(x^i y^j) is constant for all j.
|
|
3827
|
+
x, y = self.base_ring().gens()
|
|
3828
|
+
n = self.degree()
|
|
3829
|
+
L = [(y * x**i).diff().extract_pow_y(0) for i in range(n)]
|
|
3830
|
+
A = matrix(L).transpose()
|
|
3831
|
+
if A.base_ring() not in IntegralDomains():
|
|
3832
|
+
# must be using integer_mod or something to approximate ?
|
|
3833
|
+
return (~A.change_ring(QQ)).change_ring(A.base_ring())
|
|
3834
|
+
|
|
3835
|
+
return ~A
|
|
3836
|
+
|
|
3837
|
+
def _element_constructor_(self, val=0, offset=0):
|
|
3838
|
+
r"""
|
|
3839
|
+
Construct an element of ``self``.
|
|
3840
|
+
|
|
3841
|
+
INPUT:
|
|
3842
|
+
|
|
3843
|
+
- ``parent`` -- Monsky-Washnitzer differential ring (instance of class
|
|
3844
|
+
:class:`~MonskyWashnitzerDifferentialRing`
|
|
3845
|
+
- ``val`` -- element of the base ring, or list of coefficients
|
|
3846
|
+
- ``offset`` -- (default: 0) if nonzero, shift val by `y^\text{offset}`
|
|
3847
|
+
|
|
3848
|
+
EXAMPLES::
|
|
3849
|
+
|
|
3850
|
+
sage: R.<x> = QQ['x']
|
|
3851
|
+
sage: C = HyperellipticCurve(x^5 - 4*x + 4)
|
|
3852
|
+
sage: MW = C.invariant_differential().parent()
|
|
3853
|
+
sage: MW(3)
|
|
3854
|
+
3*1 dx/2y
|
|
3855
|
+
"""
|
|
3856
|
+
if isinstance(val, MonskyWashnitzerDifferential):
|
|
3857
|
+
val = val._coeff
|
|
3858
|
+
return self.element_class(self, val, offset)
|
|
3859
|
+
|
|
3860
|
+
Element = MonskyWashnitzerDifferential
|
|
3861
|
+
|
|
3862
|
+
|
|
3863
|
+
MonskyWashnitzerDifferentialRing_class = MonskyWashnitzerDifferentialRing
|