passagemath-schemes 10.6.40__cp314-cp314-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.
Potentially problematic release.
This version of passagemath-schemes might be problematic. Click here for more details.
- 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.6.40.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.40.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.40.dist-info/RECORD +314 -0
- passagemath_schemes-10.6.40.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.40.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 +9558 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.cpython-314-darwin.so +0 -0
- sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +301 -0
- sage/dynamics/arithmetic_dynamics/wehlerK3.py +2576 -0
- sage/lfunctions/all.py +18 -0
- sage/lfunctions/dokchitser.py +745 -0
- sage/lfunctions/pari.py +818 -0
- sage/lfunctions/zero_sums.cpython-314-darwin.so +0 -0
- sage/lfunctions/zero_sums.pyx +1847 -0
- sage/modular/abvar/abvar.py +5135 -0
- sage/modular/abvar/abvar_ambient_jacobian.py +413 -0
- sage/modular/abvar/abvar_newform.py +244 -0
- sage/modular/abvar/all.py +8 -0
- sage/modular/abvar/constructor.py +186 -0
- sage/modular/abvar/cuspidal_subgroup.py +371 -0
- sage/modular/abvar/finite_subgroup.py +896 -0
- sage/modular/abvar/homology.py +720 -0
- sage/modular/abvar/homspace.py +998 -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 +740 -0
- sage/modular/all.py +43 -0
- sage/modular/arithgroup/all.py +20 -0
- sage/modular/arithgroup/arithgroup_element.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/arithgroup_element.pyx +474 -0
- sage/modular/arithgroup/arithgroup_generic.py +1402 -0
- sage/modular/arithgroup/arithgroup_perm.py +2692 -0
- sage/modular/arithgroup/congroup.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/congroup.pyx +334 -0
- sage/modular/arithgroup/congroup_gamma.py +363 -0
- sage/modular/arithgroup/congroup_gamma0.py +692 -0
- sage/modular/arithgroup/congroup_gamma1.py +653 -0
- sage/modular/arithgroup/congroup_gammaH.py +1469 -0
- sage/modular/arithgroup/congroup_generic.py +628 -0
- sage/modular/arithgroup/congroup_sl2z.py +267 -0
- sage/modular/arithgroup/farey_symbol.cpython-314-darwin.so +0 -0
- sage/modular/arithgroup/farey_symbol.pyx +1066 -0
- sage/modular/arithgroup/tests.py +418 -0
- sage/modular/btquotients/all.py +4 -0
- sage/modular/btquotients/btquotient.py +3753 -0
- sage/modular/btquotients/pautomorphicform.py +2570 -0
- sage/modular/buzzard.py +100 -0
- sage/modular/congroup.py +29 -0
- sage/modular/congroup_element.py +13 -0
- sage/modular/cusps.py +1109 -0
- sage/modular/cusps_nf.py +1270 -0
- sage/modular/dims.py +569 -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 +1065 -0
- sage/modular/hecke/algebra.py +746 -0
- sage/modular/hecke/all.py +20 -0
- sage/modular/hecke/ambient_module.py +1019 -0
- sage/modular/hecke/degenmap.py +119 -0
- sage/modular/hecke/element.py +325 -0
- sage/modular/hecke/hecke_operator.py +780 -0
- sage/modular/hecke/homspace.py +206 -0
- sage/modular/hecke/module.py +1767 -0
- sage/modular/hecke/morphism.py +174 -0
- sage/modular/hecke/submodule.py +989 -0
- sage/modular/hypergeometric_misc.cpython-314-darwin.so +0 -0
- sage/modular/hypergeometric_misc.pxd +4 -0
- sage/modular/hypergeometric_misc.pyx +166 -0
- sage/modular/hypergeometric_motive.py +2017 -0
- sage/modular/local_comp/all.py +2 -0
- sage/modular/local_comp/liftings.py +292 -0
- sage/modular/local_comp/local_comp.py +1071 -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 +815 -0
- sage/modular/modform/ambient_R.py +177 -0
- sage/modular/modform/ambient_eps.py +306 -0
- sage/modular/modform/ambient_g0.py +124 -0
- sage/modular/modform/ambient_g1.py +204 -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 +505 -0
- sage/modular/modform/eisenstein_submodule.py +663 -0
- sage/modular/modform/element.py +4131 -0
- sage/modular/modform/find_generators.py +59 -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 +133 -0
- sage/modular/modform/l_series_gross_zagier_coeffs.cpython-314-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 +1860 -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 +381 -0
- sage/modular/modform/weight1.py +220 -0
- sage/modular/modform_hecketriangle/abstract_ring.py +1932 -0
- sage/modular/modform_hecketriangle/abstract_space.py +2528 -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 +3352 -0
- sage/modular/modform_hecketriangle/hecke_triangle_groups.py +1432 -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 +3846 -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-314-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 +1295 -0
- sage/modular/modsym/modsym.py +400 -0
- sage/modular/modsym/modular_symbols.py +384 -0
- sage/modular/modsym/p1list.cpython-314-darwin.so +0 -0
- sage/modular/modsym/p1list.pxd +29 -0
- sage/modular/modsym/p1list.pyx +1372 -0
- sage/modular/modsym/p1list_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-314-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 +375 -0
- sage/modular/multiple_zeta.py +2632 -0
- sage/modular/multiple_zeta_F_algebra.py +786 -0
- sage/modular/overconvergent/all.py +6 -0
- sage/modular/overconvergent/genus0.py +1878 -0
- sage/modular/overconvergent/hecke_series.py +1187 -0
- sage/modular/overconvergent/weightspace.py +778 -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 +859 -0
- sage/modular/pollack_stevens/modsym.py +1593 -0
- sage/modular/pollack_stevens/padic_lseries.py +417 -0
- sage/modular/pollack_stevens/sigma0.py +534 -0
- sage/modular/pollack_stevens/space.py +1076 -0
- sage/modular/quasimodform/all.py +3 -0
- sage/modular/quasimodform/element.py +845 -0
- sage/modular/quasimodform/ring.py +828 -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 +748 -0
- sage/schemes/curves/affine_curve.py +2928 -0
- sage/schemes/curves/all.py +33 -0
- sage/schemes/curves/closed_point.py +434 -0
- sage/schemes/curves/constructor.py +381 -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 +3026 -0
- sage/schemes/curves/zariski_vankampen.py +1932 -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 +1036 -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 +1102 -0
- sage/schemes/elliptic_curves/constructor.py +1552 -0
- sage/schemes/elliptic_curves/ec_database.py +175 -0
- sage/schemes/elliptic_curves/ell_curve_isogeny.py +3972 -0
- sage/schemes/elliptic_curves/ell_egros.py +459 -0
- sage/schemes/elliptic_curves/ell_field.py +2836 -0
- sage/schemes/elliptic_curves/ell_finite_field.py +3359 -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 +4787 -0
- sage/schemes/elliptic_curves/ell_rational_field.py +7368 -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 +1669 -0
- sage/schemes/elliptic_curves/gp_simon.py +152 -0
- sage/schemes/elliptic_curves/heegner.py +7335 -0
- sage/schemes/elliptic_curves/height.py +2109 -0
- sage/schemes/elliptic_curves/hom.py +1406 -0
- sage/schemes/elliptic_curves/hom_composite.py +934 -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 +682 -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 +1521 -0
- sage/schemes/elliptic_curves/isogeny_small_degree.py +2797 -0
- sage/schemes/elliptic_curves/jacobian.py +237 -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 +943 -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-314-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-314-darwin.so +0 -0
- sage/schemes/elliptic_curves/period_lattice_region.pyx +722 -0
- sage/schemes/elliptic_curves/saturation.py +715 -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 +291 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +1914 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_g2.py +192 -0
- sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +954 -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 +315 -0
- sage/schemes/hyperelliptic_curves/jacobian_g2.py +32 -0
- sage/schemes/hyperelliptic_curves/jacobian_generic.py +419 -0
- sage/schemes/hyperelliptic_curves/jacobian_homset.py +186 -0
- sage/schemes/hyperelliptic_curves/jacobian_morphism.py +875 -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 +3871 -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 +580 -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 +73 -0
- sage/schemes/riemann_surfaces/all.py +1 -0
- sage/schemes/riemann_surfaces/riemann_surface.py +4117 -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,715 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
# sage.doctest: needs sage.rings.finite_rings sage.rings.number_field
|
|
3
|
+
r"""
|
|
4
|
+
Saturation of Mordell-Weil groups of elliptic curves over number fields
|
|
5
|
+
|
|
6
|
+
Points `P_1`, `\dots`, `P_r` in `E(K)`, where `E` is an elliptic curve
|
|
7
|
+
over a number field `K`, are said to be `p`-saturated if no linear
|
|
8
|
+
combination `\sum n_iP_i` is divisible by `p` in `E(K)` except
|
|
9
|
+
trivially when all `n_i` are multiples of `p`. The points are said to
|
|
10
|
+
be saturated if they are `p`-saturated at all primes; this is always
|
|
11
|
+
true for all but finitely many primes since `E(K)` is a
|
|
12
|
+
finitely-generated Abelian group.
|
|
13
|
+
|
|
14
|
+
The process of `p`-saturating a given set of points is implemented
|
|
15
|
+
here. The naive algorithm simply checks all `(p^r-1)/(p-1)`
|
|
16
|
+
projective combinations of the points, testing each to see if it can
|
|
17
|
+
be divided by `p`. If this occurs then we replace one of the points
|
|
18
|
+
and continue. The function :meth:`p_saturation` does one step of
|
|
19
|
+
this, while :meth:`full_p_saturation` repeats until the points are
|
|
20
|
+
`p`-saturated. A more sophisticated algorithm for `p`-saturation is
|
|
21
|
+
implemented which is much more efficient for large `p` and `r`, and
|
|
22
|
+
involves computing the reduction of the points modulo auxiliary primes
|
|
23
|
+
to obtain linear conditions modulo `p` which must be satisfied by the
|
|
24
|
+
coefficients `a_i` of any nontrivial relation. When the points are
|
|
25
|
+
already `p`-saturated this sieving technique can prove their
|
|
26
|
+
saturation quickly.
|
|
27
|
+
|
|
28
|
+
The method :meth:`saturation` of the class :class:`EllipticCurve_number_field`
|
|
29
|
+
applies full `p`-saturation at any given set of primes, or can compute
|
|
30
|
+
a bound on the primes `p` at which the given points may not be
|
|
31
|
+
`p`-saturated. This involves computing a lower bound for the
|
|
32
|
+
canonical height of points of infinite order, together with estimates
|
|
33
|
+
from the geometry of numbers.
|
|
34
|
+
|
|
35
|
+
AUTHORS:
|
|
36
|
+
|
|
37
|
+
- Robert Bradshaw
|
|
38
|
+
|
|
39
|
+
- John Cremona
|
|
40
|
+
"""
|
|
41
|
+
#*****************************************************************************
|
|
42
|
+
# Copyright (C) 2017 Robert Bradshaw <robertwb@math.washington.edu>
|
|
43
|
+
# John Cremona <john.cremona@gmail.com>
|
|
44
|
+
# William Stein <wstein@gmail.com>
|
|
45
|
+
#
|
|
46
|
+
# This program is free software: you can redistribute it and/or modify
|
|
47
|
+
# it under the terms of the GNU General Public License as published by
|
|
48
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
49
|
+
# (at your option) any later version.
|
|
50
|
+
# http://www.gnu.org/licenses/
|
|
51
|
+
#*****************************************************************************
|
|
52
|
+
|
|
53
|
+
from sage.rings.finite_rings.finite_field_constructor import GF
|
|
54
|
+
from sage.rings.integer_ring import ZZ
|
|
55
|
+
from sage.arith.misc import kronecker as kro
|
|
56
|
+
from sage.structure.sage_object import SageObject
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def reduce_mod_q(x, amodq):
|
|
60
|
+
r"""The reduction of ``x`` modulo the prime ideal defined by ``amodq``.
|
|
61
|
+
|
|
62
|
+
INPUT:
|
|
63
|
+
|
|
64
|
+
- ``x`` -- an element of a number field `K`
|
|
65
|
+
|
|
66
|
+
- ``amodq`` -- an element of `GF(q)` which is a root mod `q` of
|
|
67
|
+
the defining polynomial of `K`. This defines a degree 1 prime
|
|
68
|
+
ideal `Q=(q,\alpha-a)` of `K=\QQ(\alpha)`, where `a \bmod q` =
|
|
69
|
+
``amodq``.
|
|
70
|
+
|
|
71
|
+
OUTPUT: the image of ``x`` in the residue field of `K` at the prime `Q`
|
|
72
|
+
|
|
73
|
+
EXAMPLES::
|
|
74
|
+
|
|
75
|
+
sage: from sage.schemes.elliptic_curves.saturation import reduce_mod_q
|
|
76
|
+
sage: x = polygen(QQ)
|
|
77
|
+
sage: pol = x^3 - x^2 - 3*x + 1
|
|
78
|
+
sage: K.<a> = NumberField(pol)
|
|
79
|
+
sage: [(q, [(amodq, reduce_mod_q(1 - a + a^4, amodq))
|
|
80
|
+
....: for amodq in sorted(pol.roots(GF(q), multiplicities=False))])
|
|
81
|
+
....: for q in primes(50, 70)]
|
|
82
|
+
[(53, []),
|
|
83
|
+
(59, [(36, 28)]),
|
|
84
|
+
(61, [(40, 35)]),
|
|
85
|
+
(67, [(10, 8), (62, 28), (63, 60)])]
|
|
86
|
+
"""
|
|
87
|
+
Fq = amodq.parent()
|
|
88
|
+
try:
|
|
89
|
+
return x.lift().change_ring(Fq)(amodq)
|
|
90
|
+
except AttributeError: # in case x is in QQ
|
|
91
|
+
return Fq(x)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class EllipticCurveSaturator(SageObject):
|
|
95
|
+
r"""
|
|
96
|
+
Class for saturating points on an elliptic curve over a number field.
|
|
97
|
+
|
|
98
|
+
INPUT:
|
|
99
|
+
|
|
100
|
+
- ``E`` -- an elliptic curve defined over a number field, or `\QQ`
|
|
101
|
+
|
|
102
|
+
- ``verbose`` -- boolean (default: ``False``); verbosity flag
|
|
103
|
+
|
|
104
|
+
.. NOTE::
|
|
105
|
+
|
|
106
|
+
This function is not normally called directly by users, who
|
|
107
|
+
may access the data via methods of the EllipticCurve
|
|
108
|
+
classes.
|
|
109
|
+
"""
|
|
110
|
+
def __init__(self, E, verbose=False):
|
|
111
|
+
r"""
|
|
112
|
+
Initialize the saturator.
|
|
113
|
+
|
|
114
|
+
INPUT:
|
|
115
|
+
|
|
116
|
+
- ``E`` -- an elliptic curve defined over a number field
|
|
117
|
+
|
|
118
|
+
- ``verbose`` -- boolean (default: ``False``); verbosity flag
|
|
119
|
+
"""
|
|
120
|
+
self._verbose = verbose
|
|
121
|
+
self._curve = E
|
|
122
|
+
self._N = E.discriminant().norm()
|
|
123
|
+
self._field = K = E.base_field()
|
|
124
|
+
if K.absolute_degree() == 1:
|
|
125
|
+
from sage.rings.rational_field import QQ
|
|
126
|
+
from sage.rings.polynomial.polynomial_ring import polygen
|
|
127
|
+
self._Kpol = polygen(QQ)
|
|
128
|
+
else:
|
|
129
|
+
self._Kpol = K.defining_polynomial()
|
|
130
|
+
self._D = self._Kpol.discriminant()
|
|
131
|
+
self._reductions = {}
|
|
132
|
+
self._lincombs = {}
|
|
133
|
+
self._torsion_gens = [t.element() for t in E.torsion_subgroup().gens()]
|
|
134
|
+
self._reductions = {}
|
|
135
|
+
# This will hold a dictionary with keys (q,aq) with q prime
|
|
136
|
+
# and aq a root of K's defining polynomial mod q, and values
|
|
137
|
+
# (n,gens) where n is the cardinality of the reduction of E
|
|
138
|
+
# and gens are generators of that reduction.
|
|
139
|
+
|
|
140
|
+
def add_reductions(self, q):
|
|
141
|
+
r"""
|
|
142
|
+
Add reduction data at primes above ``q`` if not already there.
|
|
143
|
+
|
|
144
|
+
INPUT:
|
|
145
|
+
|
|
146
|
+
- ``q`` -- a prime number not dividing the defining polynomial
|
|
147
|
+
of ``self.__field``
|
|
148
|
+
|
|
149
|
+
OUTPUT:
|
|
150
|
+
|
|
151
|
+
Returns nothing, but updates self._reductions dictionary for
|
|
152
|
+
key ``q`` to a dict whose keys are the roots of the defining
|
|
153
|
+
polynomial mod ``q`` and values tuples (``nq``, ``Eq``) where
|
|
154
|
+
``Eq`` is an elliptic curve over `GF(q)` and ``nq`` its
|
|
155
|
+
cardinality. If ``q`` divides the conductor norm or order
|
|
156
|
+
discriminant nothing is added.
|
|
157
|
+
|
|
158
|
+
EXAMPLES:
|
|
159
|
+
|
|
160
|
+
Over `\QQ`::
|
|
161
|
+
|
|
162
|
+
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator
|
|
163
|
+
|
|
164
|
+
sage: # needs database_cremona_mini_ellcurve
|
|
165
|
+
sage: E = EllipticCurve('11a1')
|
|
166
|
+
sage: saturator = EllipticCurveSaturator(E)
|
|
167
|
+
sage: saturator._reductions
|
|
168
|
+
{}
|
|
169
|
+
sage: saturator.add_reductions(19)
|
|
170
|
+
sage: saturator._reductions
|
|
171
|
+
{19: {0: (20, Elliptic Curve defined by y^2 + y = x^3 + 18*x^2 + 9*x + 18
|
|
172
|
+
over Finite Field of size 19)}}
|
|
173
|
+
|
|
174
|
+
Over a number field::
|
|
175
|
+
|
|
176
|
+
sage: x = polygen(QQ); K.<a> = NumberField(x^2 + 2)
|
|
177
|
+
sage: E = EllipticCurve(K, [0,1,0,a,a])
|
|
178
|
+
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator
|
|
179
|
+
sage: saturator = EllipticCurveSaturator(E)
|
|
180
|
+
sage: for q in primes(20):
|
|
181
|
+
....: saturator.add_reductions(q)
|
|
182
|
+
sage: saturator._reductions
|
|
183
|
+
{2: {},
|
|
184
|
+
3: {},
|
|
185
|
+
5: {},
|
|
186
|
+
7: {},
|
|
187
|
+
11: {3: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 3
|
|
188
|
+
over Finite Field of size 11),
|
|
189
|
+
8: (8, Elliptic Curve defined by y^2 = x^3 + x^2 + 8*x + 8
|
|
190
|
+
over Finite Field of size 11)},
|
|
191
|
+
13: {},
|
|
192
|
+
17: {7: (20, Elliptic Curve defined by y^2 = x^3 + x^2 + 7*x + 7
|
|
193
|
+
over Finite Field of size 17),
|
|
194
|
+
10: (18, Elliptic Curve defined by y^2 = x^3 + x^2 + 10*x + 10
|
|
195
|
+
over Finite Field of size 17)},
|
|
196
|
+
19: {6: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 6*x + 6
|
|
197
|
+
over Finite Field of size 19),
|
|
198
|
+
13: (12, Elliptic Curve defined by y^2 = x^3 + x^2 + 13*x + 13
|
|
199
|
+
over Finite Field of size 19)}}
|
|
200
|
+
"""
|
|
201
|
+
if q in self._reductions:
|
|
202
|
+
return
|
|
203
|
+
self._reductions[q] = redmodq = {}
|
|
204
|
+
if q.divides(self._N) or q.divides(self._D):
|
|
205
|
+
return
|
|
206
|
+
from sage.schemes.elliptic_curves.constructor import EllipticCurve
|
|
207
|
+
for amodq in sorted(self._Kpol.roots(GF(q), multiplicities=False)):
|
|
208
|
+
Eq = EllipticCurve([reduce_mod_q(ai, amodq) for ai in self._curve.ainvs()])
|
|
209
|
+
nq = Eq.cardinality()
|
|
210
|
+
redmodq[amodq] = (nq, Eq)
|
|
211
|
+
|
|
212
|
+
def full_p_saturation(self, Plist, p):
|
|
213
|
+
r"""Full `p`-saturation of ``Plist``.
|
|
214
|
+
|
|
215
|
+
INPUT:
|
|
216
|
+
|
|
217
|
+
- ``Plist`` -- list of independent points on one elliptic curve
|
|
218
|
+
|
|
219
|
+
- ``p`` -- integer; a prime number
|
|
220
|
+
|
|
221
|
+
OUTPUT:
|
|
222
|
+
|
|
223
|
+
(``newPlist``, ``exponent``) where ``newPlist`` has the same
|
|
224
|
+
length as ``Plist`` and spans the `p`-saturation of the span
|
|
225
|
+
of ``Plist``, which contains that span with index
|
|
226
|
+
``p**exponent``.
|
|
227
|
+
|
|
228
|
+
EXAMPLES::
|
|
229
|
+
|
|
230
|
+
sage: # needs database_cremona_mini_ellcurve
|
|
231
|
+
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator
|
|
232
|
+
sage: E = EllipticCurve('389a')
|
|
233
|
+
sage: K.<i> = QuadraticField(-1)
|
|
234
|
+
sage: EK = E.change_ring(K)
|
|
235
|
+
sage: P = EK(1 + i, -1 - 2*i)
|
|
236
|
+
sage: saturator = EllipticCurveSaturator(EK, verbose=True)
|
|
237
|
+
sage: saturator.full_p_saturation([8*P], 2)
|
|
238
|
+
--starting full 2-saturation
|
|
239
|
+
Points were not 2-saturated, exponent was 3
|
|
240
|
+
([(i + 1 : -2*i - 1 : 1)], 3)
|
|
241
|
+
sage: Q = EK(0, 0)
|
|
242
|
+
sage: R = EK(-1, 1)
|
|
243
|
+
sage: saturator = EllipticCurveSaturator(EK, verbose=False)
|
|
244
|
+
sage: saturator.full_p_saturation([P, Q, R], 3)
|
|
245
|
+
([(i + 1 : -2*i - 1 : 1), (0 : 0 : 1), (-1 : 1 : 1)], 0)
|
|
246
|
+
|
|
247
|
+
An example where the points are not 7-saturated and we gain
|
|
248
|
+
index exponent 1. Running this example with verbose=True
|
|
249
|
+
would show that it uses the code for when the reduction has
|
|
250
|
+
`p`-rank 2 (which occurs for the reduction modulo `(16-5i)`),
|
|
251
|
+
which uses the Weil pairing::
|
|
252
|
+
|
|
253
|
+
sage: saturator.full_p_saturation([P, Q + 3*R, Q - 4*R], 7) # needs database_cremona_mini_ellcurve
|
|
254
|
+
([(i + 1 : -2*i - 1 : 1),
|
|
255
|
+
(2869/676 : 154413/17576 : 1),
|
|
256
|
+
(-7095/502681 : -366258864/356400829 : 1)], 1)
|
|
257
|
+
"""
|
|
258
|
+
if not Plist:
|
|
259
|
+
return Plist, ZZ.zero()
|
|
260
|
+
|
|
261
|
+
exponent = ZZ(0)
|
|
262
|
+
|
|
263
|
+
# To handle p-torsion, we must add any torsion generators whose
|
|
264
|
+
# order is divisible by p to the list of points. Note that it is
|
|
265
|
+
# not correct to add generators of the p-torsion here, we actually
|
|
266
|
+
# need generators of p-cotorsion. If there are any of these, we
|
|
267
|
+
# cannot use the supplied dict of linear combinations, so we reset
|
|
268
|
+
# this. The torsion points are removed before returning the
|
|
269
|
+
# saturated list.
|
|
270
|
+
|
|
271
|
+
verbose = self._verbose
|
|
272
|
+
if verbose:
|
|
273
|
+
print(" --starting full %s-saturation" % p)
|
|
274
|
+
|
|
275
|
+
n = len(Plist) # number of points supplied & to be returned
|
|
276
|
+
Plist = Plist + [T for T in self._torsion_gens if p.divides(T.order())]
|
|
277
|
+
nx = len(Plist) # number of points including relevant torsion
|
|
278
|
+
extra_torsion = nx-n
|
|
279
|
+
if extra_torsion:
|
|
280
|
+
if verbose:
|
|
281
|
+
print("Adding {} torsion generators before {}-saturation".format(extra_torsion,p))
|
|
282
|
+
|
|
283
|
+
res = self.p_saturation(Plist, p)
|
|
284
|
+
while res: # res is either False or (i, newP)
|
|
285
|
+
exponent += 1
|
|
286
|
+
Plist[res[0]] = res[1]
|
|
287
|
+
res = self.p_saturation(Plist, p)
|
|
288
|
+
|
|
289
|
+
if extra_torsion:
|
|
290
|
+
# remove the torsion points
|
|
291
|
+
if verbose:
|
|
292
|
+
print("Removing the torsion generators after %s-saturation" % p)
|
|
293
|
+
Plist = Plist[:n]
|
|
294
|
+
|
|
295
|
+
if verbose:
|
|
296
|
+
if exponent:
|
|
297
|
+
print("Points were not %s-saturated, exponent was %s" % (p,exponent))
|
|
298
|
+
else:
|
|
299
|
+
print("Points were %s-saturated" % p)
|
|
300
|
+
|
|
301
|
+
return (Plist, exponent)
|
|
302
|
+
|
|
303
|
+
def p_saturation(self, Plist, p, sieve=True):
|
|
304
|
+
r"""Checks whether the list of points is `p`-saturated.
|
|
305
|
+
|
|
306
|
+
INPUT:
|
|
307
|
+
|
|
308
|
+
- ``Plist`` -- list of independent points on one elliptic curve
|
|
309
|
+
|
|
310
|
+
- ``p`` -- integer; a prime number
|
|
311
|
+
|
|
312
|
+
- ``sieve`` -- boolean; if ``True``, use a sieve (when there are at
|
|
313
|
+
least 2 points), otherwise test all combinations
|
|
314
|
+
|
|
315
|
+
.. NOTE::
|
|
316
|
+
|
|
317
|
+
The sieve is much more efficient when the points are
|
|
318
|
+
saturated and the number of points or the prime are large.
|
|
319
|
+
|
|
320
|
+
OUTPUT:
|
|
321
|
+
|
|
322
|
+
Either ``False`` if the points are `p`-saturated, or (``i``,
|
|
323
|
+
``newP``) if they are not `p`-saturated, in which case after
|
|
324
|
+
replacing the i'th point with ``newP``, the subgroup generated
|
|
325
|
+
contains that generated by ``Plist`` with index `p`.
|
|
326
|
+
|
|
327
|
+
EXAMPLES::
|
|
328
|
+
|
|
329
|
+
sage: # needs database_cremona_mini_ellcurve
|
|
330
|
+
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator
|
|
331
|
+
sage: E = EllipticCurve('389a')
|
|
332
|
+
sage: K.<i> = QuadraticField(-1)
|
|
333
|
+
sage: EK = E.change_ring(K)
|
|
334
|
+
sage: P = EK(1 + i, -1 - 2*i)
|
|
335
|
+
sage: saturator = EllipticCurveSaturator(EK)
|
|
336
|
+
sage: saturator.p_saturation([P], 2)
|
|
337
|
+
False
|
|
338
|
+
sage: saturator.p_saturation([2*P], 2)
|
|
339
|
+
(0, (i + 1 : -2*i - 1 : 1))
|
|
340
|
+
sage: Q = EK(0, 0)
|
|
341
|
+
sage: R = EK(-1, 1)
|
|
342
|
+
sage: saturator.p_saturation([P, Q, R], 3)
|
|
343
|
+
False
|
|
344
|
+
|
|
345
|
+
Here we see an example where 19-saturation is proved, with the
|
|
346
|
+
verbose flag set to ``True`` so that we can see what is going on::
|
|
347
|
+
|
|
348
|
+
sage: # needs database_cremona_mini_ellcurve
|
|
349
|
+
sage: saturator = EllipticCurveSaturator(EK, verbose=True)
|
|
350
|
+
sage: saturator.p_saturation([P, Q, R], 19)
|
|
351
|
+
Using sieve method to saturate...
|
|
352
|
+
E has 19-torsion over Finite Field of size 197, projecting points
|
|
353
|
+
--> [(15 : 168 : 1), (0 : 0 : 1), (196 : 1 : 1)]
|
|
354
|
+
--rank is now 1
|
|
355
|
+
E has 19-torsion over Finite Field of size 197, projecting points
|
|
356
|
+
--> [(184 : 27 : 1), (0 : 0 : 1), (196 : 1 : 1)]
|
|
357
|
+
--rank is now 2
|
|
358
|
+
E has 19-torsion over Finite Field of size 293, projecting points
|
|
359
|
+
--> [(139 : 16 : 1), (0 : 0 : 1), (292 : 1 : 1)]
|
|
360
|
+
--rank is now 3
|
|
361
|
+
Reached full rank: points were 19-saturated
|
|
362
|
+
False
|
|
363
|
+
|
|
364
|
+
An example where the points are not 11-saturated::
|
|
365
|
+
|
|
366
|
+
sage: # needs database_cremona_mini_ellcurve
|
|
367
|
+
sage: saturator = EllipticCurveSaturator(EK, verbose=False)
|
|
368
|
+
sage: res = saturator.p_saturation([P + 5*Q, P - 6*Q, R], 11); res
|
|
369
|
+
(0, (-5783311/14600041*i + 1396143/14600041
|
|
370
|
+
: 37679338314/55786756661*i + 3813624227/55786756661 : 1))
|
|
371
|
+
|
|
372
|
+
That means that the 0'th point may be replaced by the displayed
|
|
373
|
+
point to achieve an index gain of 11::
|
|
374
|
+
|
|
375
|
+
sage: saturator.p_saturation([res[1], P - 6*Q, R], 11) # needs database_cremona_mini_ellcurve
|
|
376
|
+
False
|
|
377
|
+
|
|
378
|
+
TESTS:
|
|
379
|
+
|
|
380
|
+
See :issue:`27387`::
|
|
381
|
+
|
|
382
|
+
sage: x = polygen(QQ)
|
|
383
|
+
sage: K.<a> = NumberField(x^2 - x - 26)
|
|
384
|
+
sage: E = EllipticCurve([a, 1 - a, 0, 93 - 16*a, 3150 - 560*a])
|
|
385
|
+
sage: P = E([65 - 35*a/3, (959*a-5377)/9])
|
|
386
|
+
sage: T = E.torsion_points()[0]
|
|
387
|
+
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator
|
|
388
|
+
sage: saturator = EllipticCurveSaturator(E, True)
|
|
389
|
+
sage: saturator.p_saturation([P, T], 2)
|
|
390
|
+
Using sieve method to saturate...
|
|
391
|
+
...
|
|
392
|
+
-- points were not 2-saturated, gaining index 2
|
|
393
|
+
(1, (0 : 1 : 0))
|
|
394
|
+
|
|
395
|
+
A CM example where large sieving primes are needed (LMFDB
|
|
396
|
+
label 2.0.3.1-50625.1-CMb2)::
|
|
397
|
+
|
|
398
|
+
sage: x = polygen(QQ)
|
|
399
|
+
sage: K.<a> = NumberField(x^2 - x + 1)
|
|
400
|
+
sage: E = EllipticCurve(K, [0, 0, 1, -750, 7906])
|
|
401
|
+
sage: E.has_rational_cm()
|
|
402
|
+
True
|
|
403
|
+
sage: E.cm_discriminant()
|
|
404
|
+
-27
|
|
405
|
+
sage: points = [E([10, -38]), E([15/49*a + 760/49, 675/343*a - 884/343])]
|
|
406
|
+
sage: E.saturation(points, verbose=True) # long time (17s)
|
|
407
|
+
Computing lower height bound..
|
|
408
|
+
..done: 7.168735020029907e-06
|
|
409
|
+
p-saturating for primes p < 132
|
|
410
|
+
...
|
|
411
|
+
Saturating at p=131
|
|
412
|
+
--starting full 131-saturation
|
|
413
|
+
Using sieve method to saturate...
|
|
414
|
+
E has 131-torsion over Finite Field of size 617011, projecting points
|
|
415
|
+
--> [(10 : 616973 : 1), (592083 : 192224 : 1)]
|
|
416
|
+
--rank is now 1
|
|
417
|
+
--rank is now 2
|
|
418
|
+
Reached full rank: points were 131-saturated
|
|
419
|
+
Points were 131-saturated
|
|
420
|
+
--already 131-saturated
|
|
421
|
+
([(10 : -38 : 1), (15/49*a + 760/49 : 675/343*a - 884/343 : 1)],
|
|
422
|
+
1,
|
|
423
|
+
0.123378097374749)
|
|
424
|
+
"""
|
|
425
|
+
verbose = self._verbose
|
|
426
|
+
# This code does a lot of elliptic curve group structure
|
|
427
|
+
# computations, and would benefit immensely if those were sped
|
|
428
|
+
# up.
|
|
429
|
+
n = len(Plist)
|
|
430
|
+
if n == 0:
|
|
431
|
+
return False
|
|
432
|
+
|
|
433
|
+
if n == 1 and p == 2:
|
|
434
|
+
pts = Plist[0].division_points(p)
|
|
435
|
+
if pts:
|
|
436
|
+
return (0, pts[0])
|
|
437
|
+
else:
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
E = self._curve
|
|
441
|
+
|
|
442
|
+
if not sieve:
|
|
443
|
+
from sage.groups.generic import multiples
|
|
444
|
+
from sage.schemes.projective.projective_space import ProjectiveSpace
|
|
445
|
+
|
|
446
|
+
mults = [list(multiples(P, p)) for P in Plist[:-1]] + [list(multiples(Plist[-1],2))]
|
|
447
|
+
E0 = E(0)
|
|
448
|
+
|
|
449
|
+
for v in ProjectiveSpace(GF(p),n-1): # an iterator
|
|
450
|
+
w = tuple(int(x) for x in v)
|
|
451
|
+
P = sum([m[c] for m,c in zip(mults,w)],E0)
|
|
452
|
+
pts = P.division_points(p)
|
|
453
|
+
if pts:
|
|
454
|
+
if verbose:
|
|
455
|
+
print(" points not saturated at {}, increasing index by {}".format(p,p))
|
|
456
|
+
# w will certainly have a coordinate equal to 1
|
|
457
|
+
return (w.index(1), pts[0])
|
|
458
|
+
# we only get here if no linear combination is divisible by p,
|
|
459
|
+
# so the points are p-saturated:
|
|
460
|
+
return False
|
|
461
|
+
|
|
462
|
+
# Now we use the more sophisticated sieve to either prove
|
|
463
|
+
# p-saturation, or compute a much smaller list of possible points
|
|
464
|
+
# to test for p-divisibility:
|
|
465
|
+
|
|
466
|
+
if verbose:
|
|
467
|
+
print("Using sieve method to saturate...")
|
|
468
|
+
from sage.matrix.constructor import matrix
|
|
469
|
+
from sage.sets.primes import Primes
|
|
470
|
+
|
|
471
|
+
A = matrix(GF(p), 0, n)
|
|
472
|
+
rankA = 0
|
|
473
|
+
count = 0
|
|
474
|
+
|
|
475
|
+
# We reduce the curve modulo primes Q of degree 1 above
|
|
476
|
+
# rational primes q chosen as follows: we avoid primes of bad
|
|
477
|
+
# reduction and primes dividing the discriminant of the
|
|
478
|
+
# defining polynomial of the field, so that we can avoid
|
|
479
|
+
# constructing number field primes entirely and just look for
|
|
480
|
+
# roots amodq of the defining polynomial mod q, then reduction
|
|
481
|
+
# of the curve is easy.
|
|
482
|
+
|
|
483
|
+
# We also avoid primes dividing the denominators of any of the
|
|
484
|
+
# points: the points would reduce to 0 modulo such primes
|
|
485
|
+
# anyway, and this way reduction of the points is also easy.
|
|
486
|
+
|
|
487
|
+
# Lastly, there is a special case when E has rational CM by
|
|
488
|
+
# some discriminant D. In the non-split case (kro(D,p)=-1)
|
|
489
|
+
# the image of the mod-p Galois representation is cyclic of
|
|
490
|
+
# order p^2-1 and there will be no p-torsion mod Q unless
|
|
491
|
+
# Frob(Q) has trivial image; a necessary condition for this is
|
|
492
|
+
# q=1 (mod p) so we restrict to such q. That way the density
|
|
493
|
+
# of q for which there is a point of order p is 1/(p+1)
|
|
494
|
+
# instead of 1/(p^2-1). This test was put in after running
|
|
495
|
+
# many tests: for example, LMFDB curve 2.0.3.1-50625.1-CMb2
|
|
496
|
+
# has saturation index bound 132 and to saturate at p=131
|
|
497
|
+
# requires q=617011. (In the split case the density is 1/(p-1)
|
|
498
|
+
# and there is no simple test.)
|
|
499
|
+
|
|
500
|
+
avoid = [self._N, self._D] + [P[0].denominator_ideal().norm() for P in Plist]
|
|
501
|
+
cm_test = E.has_rational_cm() and kro(E.cm_discriminant(), p) == -1
|
|
502
|
+
for q in Primes():
|
|
503
|
+
if any(q.divides(m) for m in avoid):
|
|
504
|
+
continue
|
|
505
|
+
if cm_test and not p.divides(q-1):
|
|
506
|
+
continue
|
|
507
|
+
self.add_reductions(q) # does nothing if key q is already there
|
|
508
|
+
for amodq in self._reductions[q]:
|
|
509
|
+
(nq, Eq) = self._reductions[q][amodq]
|
|
510
|
+
if not p.divides(nq):
|
|
511
|
+
continue
|
|
512
|
+
if verbose:
|
|
513
|
+
print("E has %s-torsion over %s, projecting points" % (p,GF(q)))
|
|
514
|
+
projPlist = [Eq([reduce_mod_q(c, amodq) for c in pt]) for pt in Plist]
|
|
515
|
+
if verbose:
|
|
516
|
+
print(" --> %s" % projPlist)
|
|
517
|
+
try:
|
|
518
|
+
vecs = p_projections(Eq, projPlist, p)
|
|
519
|
+
except ValueError:
|
|
520
|
+
vecs = []
|
|
521
|
+
for v in vecs:
|
|
522
|
+
A = matrix(A.rows()+[v])
|
|
523
|
+
newrank = A.rank()
|
|
524
|
+
if verbose:
|
|
525
|
+
print(" --rank is now %s" % newrank)
|
|
526
|
+
if newrank == n:
|
|
527
|
+
if verbose:
|
|
528
|
+
print("Reached full rank: points were %s-saturated" % p)
|
|
529
|
+
return False
|
|
530
|
+
if newrank == rankA:
|
|
531
|
+
count += 1
|
|
532
|
+
if count == 15:
|
|
533
|
+
if verbose:
|
|
534
|
+
print("! rank same for 15 steps, checking kernel...")
|
|
535
|
+
# no increase in rank for the last 15 primes Q
|
|
536
|
+
# find the points in the kernel and call the no-sieve version
|
|
537
|
+
vecs = A.right_kernel().basis()
|
|
538
|
+
if verbose:
|
|
539
|
+
print("kernel vectors: %s" % vecs)
|
|
540
|
+
Rlist = [sum([int(vi)*Pi for vi,Pi in zip(v,Plist)],E(0))
|
|
541
|
+
for v in vecs]
|
|
542
|
+
if verbose:
|
|
543
|
+
print("points generating kernel: %s" % Rlist)
|
|
544
|
+
|
|
545
|
+
# If the nullity of A were 1 (the usual
|
|
546
|
+
# case) we take any nonzero vector in its
|
|
547
|
+
# kernel and compute that linear
|
|
548
|
+
# combination of the points, giving a
|
|
549
|
+
# point which is certainly a p-multiple
|
|
550
|
+
# modulo 15 primes Q, and we test if it
|
|
551
|
+
# actually is a p-multiple:
|
|
552
|
+
if len(Rlist) == 1:
|
|
553
|
+
R = Rlist[0]
|
|
554
|
+
pts = R.division_points(p)
|
|
555
|
+
if pts:
|
|
556
|
+
pt = pts[0]
|
|
557
|
+
v = vecs[0]
|
|
558
|
+
# replace one of the original
|
|
559
|
+
# points with this one; we can
|
|
560
|
+
# replace any for which the
|
|
561
|
+
# coefficient of v is nonzero
|
|
562
|
+
if verbose:
|
|
563
|
+
print("-- points were not {}-saturated, gaining index {}".format(p,p))
|
|
564
|
+
j = next(i for i,x in enumerate(v) if x)
|
|
565
|
+
return (j, pt)
|
|
566
|
+
else: # R is not a p-multiple so the
|
|
567
|
+
# points were p-saturated
|
|
568
|
+
return False
|
|
569
|
+
|
|
570
|
+
# Else we call the non-sieve version with
|
|
571
|
+
# a list of points which are all
|
|
572
|
+
# p-multiples modulo 15 primes, and we
|
|
573
|
+
# will just try to divide all linear
|
|
574
|
+
# combinations of them
|
|
575
|
+
res = self.p_saturation(Rlist, p, sieve=False)
|
|
576
|
+
if res:
|
|
577
|
+
jj, R = res
|
|
578
|
+
v = vecs[jj]
|
|
579
|
+
# The v-linear combination of Rlist
|
|
580
|
+
# really is p*R. Now to enlarge the
|
|
581
|
+
# span, we may replce the j'th point
|
|
582
|
+
# in Plist with R, where v[j] is
|
|
583
|
+
# nonzero.
|
|
584
|
+
if verbose:
|
|
585
|
+
print("-- points were not {}-saturated, gaining index {}".format(p,p))
|
|
586
|
+
j = next(i for i,x in enumerate(v) if x)
|
|
587
|
+
return (j, R)
|
|
588
|
+
else:
|
|
589
|
+
# points really were saturated
|
|
590
|
+
if verbose:
|
|
591
|
+
print("-- points were %s-saturated" % p)
|
|
592
|
+
return False
|
|
593
|
+
else: # rank went up but is <n; carry on using more Qs
|
|
594
|
+
rankA = newrank
|
|
595
|
+
count = 0
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def p_projections(Eq, Plist, p, debug=False):
|
|
599
|
+
r"""
|
|
600
|
+
|
|
601
|
+
INPUT:
|
|
602
|
+
|
|
603
|
+
- ``Eq`` -- an elliptic curve over a finite field
|
|
604
|
+
|
|
605
|
+
- ``Plist`` -- list of points on `Eq`
|
|
606
|
+
|
|
607
|
+
- ``p`` -- a prime number
|
|
608
|
+
|
|
609
|
+
OUTPUT:
|
|
610
|
+
|
|
611
|
+
A list of `r\le2` vectors in `\GF{p^n}`, the images of the points in
|
|
612
|
+
`G \otimes \GF{p}`, where `r` is the number of vectors is the
|
|
613
|
+
`p`-rank of `Eq`.
|
|
614
|
+
|
|
615
|
+
ALGORITHM:
|
|
616
|
+
|
|
617
|
+
First project onto the `p`-primary part of `Eq`. If that has
|
|
618
|
+
`p`-rank 1 (i.e. is cyclic), use discrete logs there to define a
|
|
619
|
+
map to `\GF{p}`, otherwise use the Weil pairing to define two
|
|
620
|
+
independent maps to `\GF{p}`.
|
|
621
|
+
|
|
622
|
+
EXAMPLES:
|
|
623
|
+
|
|
624
|
+
This curve has three independent rational points::
|
|
625
|
+
|
|
626
|
+
sage: E = EllipticCurve([0,0,1,-7,6])
|
|
627
|
+
|
|
628
|
+
We reduce modulo `409` where its order is `3^2\cdot7^2`; the
|
|
629
|
+
`3`-primary part is non-cyclic while the `7`-primary part is
|
|
630
|
+
cyclic of order `49`::
|
|
631
|
+
|
|
632
|
+
sage: F = GF(409)
|
|
633
|
+
sage: EF = E.change_ring(F)
|
|
634
|
+
sage: G = EF.abelian_group()
|
|
635
|
+
sage: G
|
|
636
|
+
Additive abelian group isomorphic to Z/147 + Z/3
|
|
637
|
+
embedded in Abelian group of points on Elliptic Curve
|
|
638
|
+
defined by y^2 + y = x^3 + 402*x + 6 over Finite Field of size 409
|
|
639
|
+
sage: G.order().factor()
|
|
640
|
+
3^2 * 7^2
|
|
641
|
+
|
|
642
|
+
We construct three points and project them to the `p`-primary
|
|
643
|
+
parts for `p=2,3,5,7`, yielding 0,2,0,1 vectors of length 3 modulo
|
|
644
|
+
`p` respectively. The exact vectors output depend on the computed
|
|
645
|
+
generators of `G`::
|
|
646
|
+
|
|
647
|
+
sage: Plist = [EF([-2,3]), EF([0,2]), EF([1,0])]
|
|
648
|
+
sage: from sage.schemes.elliptic_curves.saturation import p_projections
|
|
649
|
+
sage: [(p, p_projections(EF, Plist, p)) for p in primes(11)] # random
|
|
650
|
+
[(2, []), (3, [(0, 2, 2), (2, 2, 1)]), (5, []), (7, [(5, 1, 1)])]
|
|
651
|
+
sage: [(p, len(p_projections(EF, Plist, p))) for p in primes(11)]
|
|
652
|
+
[(2, 0), (3, 2), (5, 0), (7, 1)]
|
|
653
|
+
"""
|
|
654
|
+
if debug:
|
|
655
|
+
print("In p_projections(Eq,Plist,p) with Eq = {}, Plist = {}, p = {}".format(Eq,Plist,p))
|
|
656
|
+
n = Eq.cardinality()
|
|
657
|
+
m = n.prime_to_m_part(p) # prime-to-p part of order
|
|
658
|
+
if debug:
|
|
659
|
+
print("m={}, n={}".format(m,n))
|
|
660
|
+
if m == n: # p-primary part trivial, nothing to do
|
|
661
|
+
return []
|
|
662
|
+
G = Eq.abelian_group()
|
|
663
|
+
if debug:
|
|
664
|
+
print("gens = {}".format(G.gens()))
|
|
665
|
+
|
|
666
|
+
# project onto p-primary part
|
|
667
|
+
|
|
668
|
+
pts = [m*pt for pt in Plist]
|
|
669
|
+
gens = [m*g.element() for g in G.gens()]
|
|
670
|
+
gens = [g for g in gens if g]
|
|
671
|
+
if debug:
|
|
672
|
+
print("gens for {}-primary part of G: {}".format(p, gens))
|
|
673
|
+
print("{}*points: {}".format(m,pts))
|
|
674
|
+
from sage.groups.generic import discrete_log as dlog
|
|
675
|
+
from sage.modules.free_module_element import vector
|
|
676
|
+
Fp = GF(p)
|
|
677
|
+
|
|
678
|
+
# If the p-primary part is cyclic we use elliptic discrete logs directly:
|
|
679
|
+
|
|
680
|
+
if len(gens) == 1:
|
|
681
|
+
g = gens[0]
|
|
682
|
+
pp = g.order()
|
|
683
|
+
if debug:
|
|
684
|
+
print("Cyclic case, taking dlogs to base {} of order {}".format(g,pp))
|
|
685
|
+
# logs are well-defined mod pp, hence mod p
|
|
686
|
+
v = [dlog(pt, g, ord=pp, operation='+') for pt in pts]
|
|
687
|
+
if debug:
|
|
688
|
+
print("dlogs: {}".format(v))
|
|
689
|
+
return [vector(Fp, v)]
|
|
690
|
+
|
|
691
|
+
# We make no assumption about which generator order divides the
|
|
692
|
+
# other, since conventions differ!
|
|
693
|
+
|
|
694
|
+
orders = [g.order() for g in gens]
|
|
695
|
+
p1, p2 = min(orders), max(orders)
|
|
696
|
+
g1, g2 = gens
|
|
697
|
+
if debug:
|
|
698
|
+
print("Non-cyclic case, orders = {}, p1={}, p2={}, g1={}, g2={}".format(orders,p1,p2,g1,g2))
|
|
699
|
+
|
|
700
|
+
# Now the p-primary part of the reduction is non-cyclic of
|
|
701
|
+
# exponent p2, and we use the Weil pairing, whose values are p1'th
|
|
702
|
+
# roots of unity with p1|p2, together with discrete log in the
|
|
703
|
+
# multiplicative group.
|
|
704
|
+
|
|
705
|
+
zeta = g1.weil_pairing(g2, p2) # a primitive p1'th root of unity
|
|
706
|
+
if debug:
|
|
707
|
+
print("wp of gens = {} with order {}".format(zeta, zeta.multiplicative_order()))
|
|
708
|
+
assert zeta.multiplicative_order() == p1, "Weil pairing error during saturation: p={}, G={}, Plist={}".format(p, G, Plist)
|
|
709
|
+
|
|
710
|
+
# logs are well-defined mod p1, hence mod p
|
|
711
|
+
|
|
712
|
+
return [vector(Fp, [dlog(pt.weil_pairing(g1, p2), zeta,
|
|
713
|
+
ord=p1, operation='*') for pt in pts]),
|
|
714
|
+
vector(Fp, [dlog(pt.weil_pairing(g2, p2), zeta,
|
|
715
|
+
ord=p1, operation='*') for pt in pts])]
|