passagemath-schemes 10.6.47__cp312-cp312-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.6.47.dist-info/METADATA +204 -0
- passagemath_schemes-10.6.47.dist-info/METADATA.bak +205 -0
- passagemath_schemes-10.6.47.dist-info/RECORD +311 -0
- passagemath_schemes-10.6.47.dist-info/WHEEL +6 -0
- passagemath_schemes-10.6.47.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-312-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-312-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-312-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-312-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-312-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-312-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-312-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-312-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_nf.py +1241 -0
- sage/modular/modsym/relation_matrix.py +591 -0
- sage/modular/modsym/relation_matrix_pyx.cpython-312-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-312-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-312-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,3972 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-schemes
|
|
2
|
+
r"""
|
|
3
|
+
Isogenies
|
|
4
|
+
|
|
5
|
+
An isogeny `\varphi: E_1\to E_2` between two elliptic curves `E_1` and
|
|
6
|
+
`E_2` is a morphism of curves that sends the origin of `E_1` to the
|
|
7
|
+
origin of `E_2`. Such a morphism is automatically a morphism of group
|
|
8
|
+
schemes and the kernel is a finite subgroup scheme of `E_1`. Such a
|
|
9
|
+
subscheme can either be given by a list of generators, which have to
|
|
10
|
+
be torsion points, or by a polynomial in the coordinate `x` of the
|
|
11
|
+
Weierstrass equation of `E_1`.
|
|
12
|
+
|
|
13
|
+
The usual way to create and work with isogenies is illustrated with
|
|
14
|
+
the following example::
|
|
15
|
+
|
|
16
|
+
sage: k = GF(11)
|
|
17
|
+
sage: E = EllipticCurve(k, [1,1])
|
|
18
|
+
sage: Q = E(6,5)
|
|
19
|
+
sage: phi = E.isogeny(Q)
|
|
20
|
+
sage: phi
|
|
21
|
+
Isogeny of degree 7
|
|
22
|
+
from Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11
|
|
23
|
+
to Elliptic Curve defined by y^2 = x^3 + 7*x + 8
|
|
24
|
+
over Finite Field of size 11
|
|
25
|
+
sage: P = E(4,5)
|
|
26
|
+
sage: phi(P)
|
|
27
|
+
(10 : 0 : 1)
|
|
28
|
+
sage: phi.codomain()
|
|
29
|
+
Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11
|
|
30
|
+
sage: phi.rational_maps()
|
|
31
|
+
((x^7 + 4*x^6 - 3*x^5 - 2*x^4
|
|
32
|
+
- 3*x^3 + 3*x^2 + x - 2)/(x^6 + 4*x^5 - 4*x^4 - 5*x^3 + 5*x^2),
|
|
33
|
+
(x^9*y - 5*x^8*y - x^7*y + x^5*y - x^4*y
|
|
34
|
+
- 5*x^3*y - 5*x^2*y - 2*x*y - 5*y)/(x^9 - 5*x^8 + 4*x^6 - 3*x^4 + 2*x^3))
|
|
35
|
+
|
|
36
|
+
The methods directly accessible from an elliptic curve ``E`` over a
|
|
37
|
+
field are
|
|
38
|
+
:meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.isogeny`
|
|
39
|
+
and
|
|
40
|
+
:meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.isogeny_codomain`.
|
|
41
|
+
|
|
42
|
+
The most useful methods that apply to isogenies are:
|
|
43
|
+
|
|
44
|
+
- ``.domain()``
|
|
45
|
+
- ``.codomain()``
|
|
46
|
+
- :meth:`~EllipticCurveHom.degree`
|
|
47
|
+
- :meth:`~EllipticCurveIsogeny.dual`
|
|
48
|
+
- :meth:`~EllipticCurveIsogeny.rational_maps`
|
|
49
|
+
- :meth:`~EllipticCurveIsogeny.kernel_polynomial`
|
|
50
|
+
|
|
51
|
+
.. WARNING::
|
|
52
|
+
|
|
53
|
+
This class only implements separable isogenies. When using Kohel's
|
|
54
|
+
algorithm, only cyclic isogenies can be computed (except for `[2]`).
|
|
55
|
+
|
|
56
|
+
Working with other kinds of isogenies may be possible using other
|
|
57
|
+
child classes of :class:`EllipticCurveHom`.
|
|
58
|
+
|
|
59
|
+
Some algorithms may need the isogeny to be normalized.
|
|
60
|
+
|
|
61
|
+
AUTHORS:
|
|
62
|
+
|
|
63
|
+
- Daniel Shumow <shumow@gmail.com>: 2009-04-19: initial version
|
|
64
|
+
|
|
65
|
+
- Chris Wuthrich: 7/09: add check of input, not the full list is needed.
|
|
66
|
+
10/09: eliminating some bugs.
|
|
67
|
+
|
|
68
|
+
- John Cremona 2014-08-08: tidying of code and docstrings, systematic
|
|
69
|
+
use of univariate vs. bivariate polynomials and rational functions.
|
|
70
|
+
|
|
71
|
+
- Lorenz Panny (2022-04): major cleanup of code and documentation
|
|
72
|
+
|
|
73
|
+
- Lorenz Panny (2022): inseparable duals
|
|
74
|
+
|
|
75
|
+
- Rémy Oudompheng (2023): implementation of the BMSS algorithm
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# ****************************************************************************
|
|
79
|
+
# Copyright (C) 2009 Daniel Shumow <shumow@gmail.com>
|
|
80
|
+
#
|
|
81
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
82
|
+
# https://www.gnu.org/licenses/
|
|
83
|
+
# ****************************************************************************
|
|
84
|
+
|
|
85
|
+
from copy import copy, deepcopy
|
|
86
|
+
|
|
87
|
+
from sage.structure.sequence import Sequence
|
|
88
|
+
|
|
89
|
+
from sage.schemes.elliptic_curves.hom import EllipticCurveHom
|
|
90
|
+
|
|
91
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
92
|
+
from sage.rings.integer import Integer
|
|
93
|
+
from sage.rings.laurent_series_ring import LaurentSeriesRing
|
|
94
|
+
from sage.rings.polynomial.polynomial_element import Polynomial
|
|
95
|
+
from sage.rings.fraction_field import FractionField
|
|
96
|
+
|
|
97
|
+
from sage.schemes.elliptic_curves.constructor import EllipticCurve
|
|
98
|
+
from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic
|
|
99
|
+
|
|
100
|
+
from sage.schemes.elliptic_curves.weierstrass_morphism \
|
|
101
|
+
import WeierstrassIsomorphism, _isomorphisms, baseWI, negation_morphism
|
|
102
|
+
|
|
103
|
+
#
|
|
104
|
+
# Private function for parsing input to determine the type of
|
|
105
|
+
# algorithm
|
|
106
|
+
#
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _isogeny_determine_algorithm(E, kernel):
|
|
110
|
+
r"""
|
|
111
|
+
Helper function to infer the algorithm to be used from the
|
|
112
|
+
parameters passed to the various isogeny functions.
|
|
113
|
+
|
|
114
|
+
If ``kernel`` is a list of points on the elliptic curve `E`,
|
|
115
|
+
we will try to use Vélu's algorithm.
|
|
116
|
+
|
|
117
|
+
If ``kernel`` is a list of coefficients or a univariate
|
|
118
|
+
polynomial, we will try to use the Kohel's algorithms.
|
|
119
|
+
|
|
120
|
+
INPUT:
|
|
121
|
+
|
|
122
|
+
- ``E`` -- domain elliptic curve
|
|
123
|
+
|
|
124
|
+
- ``kernel`` -- either a list of points on ``E``, or a univariate
|
|
125
|
+
polynomial or list of coefficients of a univariate polynomial
|
|
126
|
+
|
|
127
|
+
OUTPUT: string; either ``'velu'`` or ``'kohel'``.
|
|
128
|
+
|
|
129
|
+
EXAMPLES:
|
|
130
|
+
|
|
131
|
+
This helper function will be implicitly called by the following examples::
|
|
132
|
+
|
|
133
|
+
sage: R.<x> = GF(5)[]
|
|
134
|
+
sage: E = EllipticCurve(GF(5), [0,0,0,1,0]) # indirect doctest
|
|
135
|
+
|
|
136
|
+
We can construct the same isogeny from a kernel polynomial::
|
|
137
|
+
|
|
138
|
+
sage: phi = EllipticCurveIsogeny(E, x + 3) # indirect doctest
|
|
139
|
+
|
|
140
|
+
or from a list of coefficients of a kernel polynomial::
|
|
141
|
+
|
|
142
|
+
sage: phi == EllipticCurveIsogeny(E, [3,1]) # indirect doctest
|
|
143
|
+
True
|
|
144
|
+
|
|
145
|
+
or from a rational point which generates the kernel::
|
|
146
|
+
|
|
147
|
+
sage: phi == EllipticCurveIsogeny(E, E((2,0))) # indirect doctest
|
|
148
|
+
True
|
|
149
|
+
|
|
150
|
+
In the first two cases, Kohel's algorithm will be used, while in
|
|
151
|
+
the third case it is Vélu::
|
|
152
|
+
|
|
153
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import _isogeny_determine_algorithm
|
|
154
|
+
sage: _isogeny_determine_algorithm(E, x + 3)
|
|
155
|
+
'kohel'
|
|
156
|
+
sage: _isogeny_determine_algorithm(E, [3, 1])
|
|
157
|
+
'kohel'
|
|
158
|
+
sage: _isogeny_determine_algorithm(E, E((2,0)))
|
|
159
|
+
'velu'
|
|
160
|
+
"""
|
|
161
|
+
kernel_is_list = isinstance(kernel, list)
|
|
162
|
+
|
|
163
|
+
if not kernel_is_list and kernel in E:
|
|
164
|
+
kernel = [kernel]
|
|
165
|
+
kernel_is_list = True
|
|
166
|
+
|
|
167
|
+
if isinstance(kernel, Polynomial) or (kernel_is_list and kernel[0] in E.base_ring()):
|
|
168
|
+
return "kohel"
|
|
169
|
+
|
|
170
|
+
if kernel_is_list and kernel[0] in E:
|
|
171
|
+
# note that if kernel[0] is on an extension of E this
|
|
172
|
+
# condition will be false
|
|
173
|
+
return "velu"
|
|
174
|
+
|
|
175
|
+
raise ValueError("invalid parameters to EllipticCurveIsogeny constructor")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def isogeny_codomain_from_kernel(E, kernel):
|
|
179
|
+
r"""
|
|
180
|
+
Compute the isogeny codomain given a kernel.
|
|
181
|
+
|
|
182
|
+
INPUT:
|
|
183
|
+
|
|
184
|
+
- ``E`` -- domain elliptic curve
|
|
185
|
+
|
|
186
|
+
- ``kernel`` -- either a list of points in the kernel of the isogeny,
|
|
187
|
+
or a kernel polynomial (specified as either a
|
|
188
|
+
univariate polynomial or a coefficient list)
|
|
189
|
+
|
|
190
|
+
OUTPUT: elliptic curve) The codomain of the separable normalized isogeny
|
|
191
|
+
defined by this kernel.
|
|
192
|
+
|
|
193
|
+
EXAMPLES::
|
|
194
|
+
|
|
195
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import isogeny_codomain_from_kernel
|
|
196
|
+
sage: E = EllipticCurve(GF(7), [1,0,1,0,1])
|
|
197
|
+
sage: R.<x> = GF(7)[]
|
|
198
|
+
sage: isogeny_codomain_from_kernel(E, [4,1])
|
|
199
|
+
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6
|
|
200
|
+
over Finite Field of size 7
|
|
201
|
+
sage: (EllipticCurveIsogeny(E, [4,1]).codomain()
|
|
202
|
+
....: == isogeny_codomain_from_kernel(E, [4,1]))
|
|
203
|
+
True
|
|
204
|
+
sage: isogeny_codomain_from_kernel(E, x^3 + x^2 + 4*x + 3)
|
|
205
|
+
Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 6
|
|
206
|
+
over Finite Field of size 7
|
|
207
|
+
sage: isogeny_codomain_from_kernel(E, x^3 + 2*x^2 + 4*x + 3)
|
|
208
|
+
Elliptic Curve defined by y^2 + x*y + y = x^3 + 5*x + 2
|
|
209
|
+
over Finite Field of size 7
|
|
210
|
+
|
|
211
|
+
sage: # needs sage.rings.finite_rings
|
|
212
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
213
|
+
sage: kernel_list = [E((15,10)), E((10,3)), E((6,5))]
|
|
214
|
+
sage: isogeny_codomain_from_kernel(E, kernel_list)
|
|
215
|
+
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15
|
|
216
|
+
over Finite Field of size 19
|
|
217
|
+
"""
|
|
218
|
+
algorithm = _isogeny_determine_algorithm(E, kernel)
|
|
219
|
+
|
|
220
|
+
if algorithm == 'velu':
|
|
221
|
+
# if we are using Velu's formula, just instantiate the isogeny
|
|
222
|
+
# and return the codomain
|
|
223
|
+
return EllipticCurveIsogeny(E, kernel).codomain()
|
|
224
|
+
|
|
225
|
+
if algorithm == 'kohel':
|
|
226
|
+
return compute_codomain_kohel(E, kernel)
|
|
227
|
+
|
|
228
|
+
raise NotImplementedError
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def compute_codomain_formula(E, v, w):
|
|
232
|
+
r"""
|
|
233
|
+
Compute the codomain curve given parameters `v` and `w` (as in
|
|
234
|
+
Vélu/Kohel/etc. formulas).
|
|
235
|
+
|
|
236
|
+
INPUT:
|
|
237
|
+
|
|
238
|
+
- ``E`` -- an elliptic curve
|
|
239
|
+
|
|
240
|
+
- ``v``, ``w`` -- elements of the base field of ``E``
|
|
241
|
+
|
|
242
|
+
OUTPUT:
|
|
243
|
+
|
|
244
|
+
The elliptic curve with invariants
|
|
245
|
+
`[a_1,a_2,a_3,a_4-5v,a_6-(a_1^2+4a_2)v-7w]` where
|
|
246
|
+
`E = [a_1,a_2,a_3,a_4,a_6]`.
|
|
247
|
+
|
|
248
|
+
EXAMPLES:
|
|
249
|
+
|
|
250
|
+
This formula is used by every invocation of the
|
|
251
|
+
:class:`EllipticCurveIsogeny` constructor::
|
|
252
|
+
|
|
253
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
254
|
+
sage: phi = EllipticCurveIsogeny(E, E((1,2)) )
|
|
255
|
+
sage: phi.codomain()
|
|
256
|
+
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 13
|
|
257
|
+
over Finite Field of size 19
|
|
258
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_formula
|
|
259
|
+
sage: v = phi._EllipticCurveIsogeny__v
|
|
260
|
+
sage: w = phi._EllipticCurveIsogeny__w
|
|
261
|
+
sage: compute_codomain_formula(E, v, w) == phi.codomain()
|
|
262
|
+
True
|
|
263
|
+
"""
|
|
264
|
+
a1, a2, a3, a4, a6 = E.a_invariants()
|
|
265
|
+
|
|
266
|
+
A4 = a4 - 5*v
|
|
267
|
+
A6 = a6 - (a1**2 + 4*a2)*v - 7*w
|
|
268
|
+
|
|
269
|
+
return EllipticCurve([a1, a2, a3, A4, A6])
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4):
|
|
273
|
+
r"""
|
|
274
|
+
Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of
|
|
275
|
+
degree exactly divisible by `2`.
|
|
276
|
+
|
|
277
|
+
INPUT:
|
|
278
|
+
|
|
279
|
+
- ``x0``, ``y0`` -- coordinates of a 2-torsion point on an elliptic curve `E`
|
|
280
|
+
|
|
281
|
+
- ``a1``, ``a2``, ``a4`` -- invariants of `E`
|
|
282
|
+
|
|
283
|
+
OUTPUT: Vélu's isogeny parameters `(v,w)`.
|
|
284
|
+
|
|
285
|
+
EXAMPLES:
|
|
286
|
+
|
|
287
|
+
This function will be implicitly called by the following example::
|
|
288
|
+
|
|
289
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
290
|
+
sage: phi = EllipticCurveIsogeny(E, [9,1]); phi
|
|
291
|
+
Isogeny of degree 2
|
|
292
|
+
from Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5
|
|
293
|
+
over Finite Field of size 19
|
|
294
|
+
to Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 8
|
|
295
|
+
over Finite Field of size 19
|
|
296
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg1
|
|
297
|
+
sage: a1,a2,a3,a4,a6 = E.a_invariants()
|
|
298
|
+
sage: x0 = -9
|
|
299
|
+
sage: y0 = -(a1*x0 + a3)/2
|
|
300
|
+
sage: compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
|
|
301
|
+
(18, 9)
|
|
302
|
+
"""
|
|
303
|
+
v = 3*x0**2 + 2*a2*x0 + a4 - a1*y0
|
|
304
|
+
w = x0 * v
|
|
305
|
+
return v, w
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3):
|
|
309
|
+
r"""
|
|
310
|
+
Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of
|
|
311
|
+
degree divisible by `4`.
|
|
312
|
+
|
|
313
|
+
INPUT:
|
|
314
|
+
|
|
315
|
+
- ``b2``, ``b4`` -- invariants of an elliptic curve `E`
|
|
316
|
+
|
|
317
|
+
- ``s1``, ``s2``, ``s3`` -- signed coefficients of the 2-division
|
|
318
|
+
polynomial of `E`
|
|
319
|
+
|
|
320
|
+
OUTPUT: Vélu's isogeny parameters `(v,w)`.
|
|
321
|
+
|
|
322
|
+
EXAMPLES:
|
|
323
|
+
|
|
324
|
+
This function will be implicitly called by the following example::
|
|
325
|
+
|
|
326
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
327
|
+
sage: R.<x> = GF(19)[]
|
|
328
|
+
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12); phi
|
|
329
|
+
Isogeny of degree 4
|
|
330
|
+
from Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5
|
|
331
|
+
over Finite Field of size 19
|
|
332
|
+
to Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15
|
|
333
|
+
over Finite Field of size 19
|
|
334
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_even_deg3
|
|
335
|
+
sage: b2,b4 = E.b2(), E.b4()
|
|
336
|
+
sage: s1, s2, s3 = -7, 15, -12
|
|
337
|
+
sage: compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
|
|
338
|
+
(4, 7)
|
|
339
|
+
"""
|
|
340
|
+
temp1 = s1**2 - 2*s2
|
|
341
|
+
v = 3*temp1 + (b2*s1 + 3*b4)/2
|
|
342
|
+
w = 3*(s1**3 - 3*s1*s2 + 3*s3) + (b2*temp1 + b4*s1)/2
|
|
343
|
+
return v, w
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n):
|
|
347
|
+
r"""
|
|
348
|
+
Compute Vélu's `(v,w)` using Kohel's formulas for isogenies of odd
|
|
349
|
+
degree.
|
|
350
|
+
|
|
351
|
+
INPUT:
|
|
352
|
+
|
|
353
|
+
- ``b2``, ``b4``, ``b6`` -- invariants of an elliptic curve `E`
|
|
354
|
+
|
|
355
|
+
- ``s1``, ``s2``, ``s3`` -- signed coefficients of lowest powers
|
|
356
|
+
of `x` in the kernel polynomial
|
|
357
|
+
|
|
358
|
+
- ``n`` -- integer; the degree
|
|
359
|
+
|
|
360
|
+
OUTPUT: Vélu's isogeny parameters `(v,w)`
|
|
361
|
+
|
|
362
|
+
EXAMPLES:
|
|
363
|
+
|
|
364
|
+
This function will be implicitly called by the following example::
|
|
365
|
+
|
|
366
|
+
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
|
|
367
|
+
sage: R.<x> = GF(19)[]
|
|
368
|
+
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11); phi
|
|
369
|
+
Isogeny of degree 7
|
|
370
|
+
from Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 15*x + 14
|
|
371
|
+
over Finite Field of size 19
|
|
372
|
+
to Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 18*x + 18
|
|
373
|
+
over Finite Field of size 19
|
|
374
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_vw_kohel_odd
|
|
375
|
+
sage: b2,b4,b6 = E.b2(), E.b4(), E.b6()
|
|
376
|
+
sage: s1,s2,s3 = -14,3,-11
|
|
377
|
+
sage: compute_vw_kohel_odd(b2,b4,b6,s1,s2,s3,3)
|
|
378
|
+
(7, 1)
|
|
379
|
+
"""
|
|
380
|
+
v = 6*(s1**2 - 2*s2) + b2*s1 + n*b4
|
|
381
|
+
w = 10*(s1**3 - 3*s1*s2 + 3*s3) + 2*b2*(s1**2 - 2*s2) + 3*b4*s1 + n*b6
|
|
382
|
+
return v, w
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def compute_codomain_kohel(E, kernel):
|
|
386
|
+
r"""
|
|
387
|
+
Compute the codomain from the kernel polynomial using Kohel's
|
|
388
|
+
formulas.
|
|
389
|
+
|
|
390
|
+
INPUT:
|
|
391
|
+
|
|
392
|
+
- ``E`` -- domain elliptic curve
|
|
393
|
+
|
|
394
|
+
- ``kernel`` -- polynomial or list; the kernel polynomial, or a
|
|
395
|
+
list of its coefficients
|
|
396
|
+
|
|
397
|
+
OUTPUT: elliptic curve; the codomain elliptic curve of the isogeny
|
|
398
|
+
defined by ``kernel``
|
|
399
|
+
|
|
400
|
+
EXAMPLES::
|
|
401
|
+
|
|
402
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_codomain_kohel
|
|
403
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
404
|
+
sage: phi = EllipticCurveIsogeny(E, [9,1])
|
|
405
|
+
sage: phi.codomain() == isogeny_codomain_from_kernel(E, [9,1])
|
|
406
|
+
True
|
|
407
|
+
sage: compute_codomain_kohel(E, [9,1])
|
|
408
|
+
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 9*x + 8
|
|
409
|
+
over Finite Field of size 19
|
|
410
|
+
sage: R.<x> = GF(19)[]
|
|
411
|
+
sage: E = EllipticCurve(GF(19), [18,17,16,15,14])
|
|
412
|
+
sage: phi = EllipticCurveIsogeny(E, x^3 + 14*x^2 + 3*x + 11)
|
|
413
|
+
sage: phi.codomain() == isogeny_codomain_from_kernel(E, x^3 + 14*x^2 + 3*x + 11)
|
|
414
|
+
True
|
|
415
|
+
sage: compute_codomain_kohel(E, x^3 + 14*x^2 + 3*x + 11)
|
|
416
|
+
Elliptic Curve defined by y^2 + 18*x*y + 16*y = x^3 + 17*x^2 + 18*x + 18
|
|
417
|
+
over Finite Field of size 19
|
|
418
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
419
|
+
sage: phi = EllipticCurveIsogeny(E, x^3 + 7*x^2 + 15*x + 12)
|
|
420
|
+
sage: isogeny_codomain_from_kernel(E, x^3 + 7*x^2 + 15*x + 12) == phi.codomain()
|
|
421
|
+
True
|
|
422
|
+
sage: compute_codomain_kohel(E, x^3 + 7*x^2 + 15*x + 12)
|
|
423
|
+
Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 3*x + 15
|
|
424
|
+
over Finite Field of size 19
|
|
425
|
+
|
|
426
|
+
ALGORITHM:
|
|
427
|
+
|
|
428
|
+
This function uses the formulas of Section 2.4 of [Koh1996]_.
|
|
429
|
+
"""
|
|
430
|
+
# First set up the polynomial ring
|
|
431
|
+
base_field = E.base_ring()
|
|
432
|
+
poly_ring = PolynomialRing(base_field,'x')
|
|
433
|
+
|
|
434
|
+
try:
|
|
435
|
+
psi = poly_ring(kernel)
|
|
436
|
+
except TypeError:
|
|
437
|
+
raise ValueError("invalid input to compute_codomain_kohel")
|
|
438
|
+
|
|
439
|
+
# next determine the even / odd part of the isogeny
|
|
440
|
+
psi_2tor = two_torsion_part(E, psi)
|
|
441
|
+
|
|
442
|
+
if psi_2tor.degree() != 0: # even degree case
|
|
443
|
+
|
|
444
|
+
psi_quo = psi//psi_2tor
|
|
445
|
+
|
|
446
|
+
if psi_quo.degree() != 0:
|
|
447
|
+
raise NotImplementedError("Kohel's algorithm currently only supports cyclic isogenies (except for [2])")
|
|
448
|
+
|
|
449
|
+
n = psi_2tor.degree()
|
|
450
|
+
|
|
451
|
+
if n == 1: # degree divisible exactly by 2
|
|
452
|
+
|
|
453
|
+
a1, a2, a3, a4, a6 = E.a_invariants()
|
|
454
|
+
|
|
455
|
+
x0 = -psi_2tor.constant_coefficient()
|
|
456
|
+
|
|
457
|
+
# determine y0
|
|
458
|
+
if base_field.characteristic() == 2:
|
|
459
|
+
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
|
|
460
|
+
else:
|
|
461
|
+
y0 = -(a1*x0 + a3)/2
|
|
462
|
+
|
|
463
|
+
# now (x0,y0) is the 2-torsion point in the kernel
|
|
464
|
+
|
|
465
|
+
v, w = compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
|
|
466
|
+
|
|
467
|
+
elif n == 3: # psi_2tor is the full 2-division polynomial
|
|
468
|
+
|
|
469
|
+
b2, b4, _, _ = E.b_invariants()
|
|
470
|
+
|
|
471
|
+
s1 = -psi_2tor[n - 1]
|
|
472
|
+
s2 = psi_2tor[n - 2]
|
|
473
|
+
s3 = -psi_2tor[n - 3]
|
|
474
|
+
|
|
475
|
+
v, w = compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
|
|
476
|
+
|
|
477
|
+
else: # odd degree case
|
|
478
|
+
|
|
479
|
+
n = psi.degree()
|
|
480
|
+
|
|
481
|
+
b2, b4, b6, _ = E.b_invariants()
|
|
482
|
+
|
|
483
|
+
s1 = -psi[n - 1] if n >= 1 else 0
|
|
484
|
+
s2 = psi[n - 2] if n >= 2 else 0
|
|
485
|
+
s3 = -psi[n - 3] if n >= 3 else 0
|
|
486
|
+
|
|
487
|
+
# initializing these allows us to calculate E2.
|
|
488
|
+
v, w = compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n)
|
|
489
|
+
|
|
490
|
+
return compute_codomain_formula(E, v, w)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def two_torsion_part(E, psi):
|
|
494
|
+
r"""
|
|
495
|
+
Return the greatest common divisor of ``psi`` and the 2-torsion
|
|
496
|
+
polynomial of `E`.
|
|
497
|
+
|
|
498
|
+
INPUT:
|
|
499
|
+
|
|
500
|
+
- ``E`` -- an elliptic curve
|
|
501
|
+
|
|
502
|
+
- ``psi`` -- a univariate polynomial over the base field of ``E``
|
|
503
|
+
|
|
504
|
+
OUTPUT: polynomial; the `\gcd` of ``psi`` and the 2-torsion polynomial of ``E``
|
|
505
|
+
|
|
506
|
+
EXAMPLES:
|
|
507
|
+
|
|
508
|
+
Every function that computes the kernel polynomial via Kohel's
|
|
509
|
+
formulas will call this function::
|
|
510
|
+
|
|
511
|
+
sage: E = EllipticCurve(GF(19), [1,2,3,4,5])
|
|
512
|
+
sage: R.<x> = GF(19)[]
|
|
513
|
+
sage: phi = EllipticCurveIsogeny(E, x + 13)
|
|
514
|
+
sage: isogeny_codomain_from_kernel(E, x + 13) == phi.codomain()
|
|
515
|
+
True
|
|
516
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
|
|
517
|
+
sage: two_torsion_part(E, x + 13)
|
|
518
|
+
x + 13
|
|
519
|
+
"""
|
|
520
|
+
x = psi.parent().gen() # NB psi is univariate but could be constant
|
|
521
|
+
psi_2 = E.two_division_polynomial(x)
|
|
522
|
+
return psi.gcd(psi_2)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class EllipticCurveIsogeny(EllipticCurveHom):
|
|
526
|
+
r"""
|
|
527
|
+
This class implements separable isogenies of elliptic curves.
|
|
528
|
+
|
|
529
|
+
Several different algorithms for computing isogenies are
|
|
530
|
+
available. These include:
|
|
531
|
+
|
|
532
|
+
- Vélu's Formulas: Vélu's original formulas for computing
|
|
533
|
+
isogenies. This algorithm is selected by giving as the
|
|
534
|
+
``kernel`` parameter a single point, or a list of points,
|
|
535
|
+
generating a finite subgroup.
|
|
536
|
+
|
|
537
|
+
- Kohel's Formulas: Kohel's original formulas for computing
|
|
538
|
+
isogenies. This algorithm is selected by giving as the
|
|
539
|
+
``kernel`` parameter a monic polynomial (or a coefficient list)
|
|
540
|
+
which will define the kernel of the isogeny.
|
|
541
|
+
Kohel's algorithm is currently only implemented for cyclic
|
|
542
|
+
isogenies, with the exception of `[2]`.
|
|
543
|
+
|
|
544
|
+
INPUT:
|
|
545
|
+
|
|
546
|
+
- ``E`` -- an elliptic curve; the domain of the isogeny to initialize
|
|
547
|
+
|
|
548
|
+
- ``kernel`` -- a kernel; either a point on ``E``, a list of
|
|
549
|
+
points on ``E``, a monic kernel polynomial, or ``None``.
|
|
550
|
+
If initializing from a domain/codomain, this must be ``None``.
|
|
551
|
+
|
|
552
|
+
- ``codomain`` -- an elliptic curve (default: ``None``)
|
|
553
|
+
|
|
554
|
+
- If ``kernel`` is ``None``, then ``degree`` must be given as well
|
|
555
|
+
and the given ``codomain`` must be the codomain of a cyclic,
|
|
556
|
+
separable, normalized isogeny of the given degree.
|
|
557
|
+
|
|
558
|
+
- If ``kernel`` is not ``None``, then this must be isomorphic to
|
|
559
|
+
the codomain of the separable isogeny defined by ``kernel``; in
|
|
560
|
+
this case, the isogeny is post-composed with an isomorphism so
|
|
561
|
+
that the codomain equals the given curve.
|
|
562
|
+
|
|
563
|
+
- ``degree`` -- integer (default: ``None``)
|
|
564
|
+
|
|
565
|
+
- If ``kernel`` is ``None``, then this is the degree of the isogeny
|
|
566
|
+
from ``E`` to ``codomain``.
|
|
567
|
+
|
|
568
|
+
- If ``kernel`` is not ``None``, then this is used to determine
|
|
569
|
+
whether or not to skip a `\gcd` of the given kernel polynomial
|
|
570
|
+
with the two-torsion polynomial of ``E``.
|
|
571
|
+
|
|
572
|
+
- ``model`` -- string (default: ``None``); supported values
|
|
573
|
+
(cf. :func:`~sage.schemes.elliptic_curves.ell_field.compute_model`):
|
|
574
|
+
|
|
575
|
+
- ``'minimal'`` -- if ``E`` is a curve over the rationals or
|
|
576
|
+
over a number field, then the codomain is a global minimal
|
|
577
|
+
model where this exists.
|
|
578
|
+
|
|
579
|
+
- ``'short_weierstrass'`` -- the codomain is a short Weierstrass curve,
|
|
580
|
+
assuming one exists.
|
|
581
|
+
|
|
582
|
+
- ``'montgomery'`` -- the codomain is an (untwisted) Montgomery
|
|
583
|
+
curve, assuming one exists over this field.
|
|
584
|
+
|
|
585
|
+
- ``check`` -- boolean (default: ``True``); check whether the input is valid.
|
|
586
|
+
Setting this to ``False`` can lead to significant speedups.
|
|
587
|
+
|
|
588
|
+
EXAMPLES:
|
|
589
|
+
|
|
590
|
+
A simple example of creating an isogeny of a field of small
|
|
591
|
+
characteristic::
|
|
592
|
+
|
|
593
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,1,0])
|
|
594
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)) ); phi
|
|
595
|
+
Isogeny of degree 2
|
|
596
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7
|
|
597
|
+
to Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
|
|
598
|
+
sage: phi.degree() == 2
|
|
599
|
+
True
|
|
600
|
+
sage: phi.kernel_polynomial()
|
|
601
|
+
x
|
|
602
|
+
sage: phi.rational_maps()
|
|
603
|
+
((x^2 + 1)/x, (x^2*y - y)/x^2)
|
|
604
|
+
sage: phi == loads(dumps(phi)) # known bug
|
|
605
|
+
True
|
|
606
|
+
|
|
607
|
+
A more complicated example of a characteristic-2 field::
|
|
608
|
+
|
|
609
|
+
sage: # needs sage.rings.finite_rings
|
|
610
|
+
sage: E = EllipticCurve(GF(2^4,'alpha'), [0,0,1,0,1])
|
|
611
|
+
sage: P = E((1,1))
|
|
612
|
+
sage: phi_v = EllipticCurveIsogeny(E, P); phi_v
|
|
613
|
+
Isogeny of degree 3
|
|
614
|
+
from Elliptic Curve defined by y^2 + y = x^3 + 1
|
|
615
|
+
over Finite Field in alpha of size 2^4
|
|
616
|
+
to Elliptic Curve defined by y^2 + y = x^3
|
|
617
|
+
over Finite Field in alpha of size 2^4
|
|
618
|
+
sage: phi_ker_poly = phi_v.kernel_polynomial()
|
|
619
|
+
sage: phi_ker_poly
|
|
620
|
+
x + 1
|
|
621
|
+
sage: phi_k = EllipticCurveIsogeny(E, phi_ker_poly)
|
|
622
|
+
sage: phi_k == phi_v
|
|
623
|
+
True
|
|
624
|
+
sage: phi_k.rational_maps()
|
|
625
|
+
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
|
|
626
|
+
sage: phi_v.rational_maps()
|
|
627
|
+
((x^3 + x + 1)/(x^2 + 1), (x^3*y + x^2*y + x*y + x + y)/(x^3 + x^2 + x + 1))
|
|
628
|
+
sage: phi_k.degree() == phi_v.degree() == 3
|
|
629
|
+
True
|
|
630
|
+
sage: phi_k.is_separable()
|
|
631
|
+
True
|
|
632
|
+
sage: phi_v(E(0))
|
|
633
|
+
(0 : 1 : 0)
|
|
634
|
+
sage: alpha = E.base_field().gen()
|
|
635
|
+
sage: Q = E((0, alpha*(alpha + 1)))
|
|
636
|
+
sage: phi_v(Q)
|
|
637
|
+
(1 : alpha^2 + alpha : 1)
|
|
638
|
+
sage: phi_v(P) == phi_k(P)
|
|
639
|
+
True
|
|
640
|
+
sage: phi_k(P) == phi_v.codomain()(0)
|
|
641
|
+
True
|
|
642
|
+
|
|
643
|
+
We can create an isogeny whose kernel equals the full 2-torsion::
|
|
644
|
+
|
|
645
|
+
sage: # needs sage.rings.finite_rings
|
|
646
|
+
sage: E = EllipticCurve(GF((3,2)), [0,0,0,1,1])
|
|
647
|
+
sage: ker_poly = E.division_polynomial(2)
|
|
648
|
+
sage: phi = EllipticCurveIsogeny(E, ker_poly); phi
|
|
649
|
+
Isogeny of degree 4
|
|
650
|
+
from Elliptic Curve defined by y^2 = x^3 + x + 1
|
|
651
|
+
over Finite Field in z2 of size 3^2
|
|
652
|
+
to Elliptic Curve defined by y^2 = x^3 + x + 1
|
|
653
|
+
over Finite Field in z2 of size 3^2
|
|
654
|
+
sage: P1,P2,P3 = filter(bool, E(0).division_points(2))
|
|
655
|
+
sage: phi(P1)
|
|
656
|
+
(0 : 1 : 0)
|
|
657
|
+
sage: phi(P2)
|
|
658
|
+
(0 : 1 : 0)
|
|
659
|
+
sage: phi(P3)
|
|
660
|
+
(0 : 1 : 0)
|
|
661
|
+
sage: phi.degree()
|
|
662
|
+
4
|
|
663
|
+
|
|
664
|
+
We can also create trivial isogenies with the trivial kernel::
|
|
665
|
+
|
|
666
|
+
sage: E = EllipticCurve(GF(17), [11, 11, 4, 12, 10])
|
|
667
|
+
sage: phi_v = EllipticCurveIsogeny(E, E(0))
|
|
668
|
+
sage: phi_v.degree()
|
|
669
|
+
1
|
|
670
|
+
sage: phi_v.rational_maps()
|
|
671
|
+
(x, y)
|
|
672
|
+
sage: E == phi_v.codomain()
|
|
673
|
+
True
|
|
674
|
+
sage: P = E.random_point()
|
|
675
|
+
sage: phi_v(P) == P
|
|
676
|
+
True
|
|
677
|
+
|
|
678
|
+
sage: E = EllipticCurve(GF(31), [23, 1, 22, 7, 18])
|
|
679
|
+
sage: phi_k = EllipticCurveIsogeny(E, [1]); phi_k
|
|
680
|
+
Isogeny of degree 1
|
|
681
|
+
from Elliptic Curve defined by y^2 + 23*x*y + 22*y = x^3 + x^2 + 7*x + 18
|
|
682
|
+
over Finite Field of size 31
|
|
683
|
+
to Elliptic Curve defined by y^2 + 23*x*y + 22*y = x^3 + x^2 + 7*x + 18
|
|
684
|
+
over Finite Field of size 31
|
|
685
|
+
sage: phi_k.degree()
|
|
686
|
+
1
|
|
687
|
+
sage: phi_k.rational_maps()
|
|
688
|
+
(x, y)
|
|
689
|
+
sage: phi_k.codomain() == E
|
|
690
|
+
True
|
|
691
|
+
sage: phi_k.kernel_polynomial()
|
|
692
|
+
1
|
|
693
|
+
sage: P = E.random_point(); P == phi_k(P)
|
|
694
|
+
True
|
|
695
|
+
|
|
696
|
+
Vélu and Kohel also work in characteristic `0`::
|
|
697
|
+
|
|
698
|
+
sage: E = EllipticCurve(QQ, [0,0,0,3,4])
|
|
699
|
+
sage: P_list = E.torsion_points()
|
|
700
|
+
sage: phi = EllipticCurveIsogeny(E, P_list); phi
|
|
701
|
+
Isogeny of degree 2
|
|
702
|
+
from Elliptic Curve defined by y^2 = x^3 + 3*x + 4 over Rational Field
|
|
703
|
+
to Elliptic Curve defined by y^2 = x^3 - 27*x + 46 over Rational Field
|
|
704
|
+
sage: P = E((0,2))
|
|
705
|
+
sage: phi(P)
|
|
706
|
+
(6 : -10 : 1)
|
|
707
|
+
sage: phi_ker_poly = phi.kernel_polynomial()
|
|
708
|
+
sage: phi_ker_poly
|
|
709
|
+
x + 1
|
|
710
|
+
sage: phi_k = EllipticCurveIsogeny(E, phi_ker_poly); phi_k
|
|
711
|
+
Isogeny of degree 2
|
|
712
|
+
from Elliptic Curve defined by y^2 = x^3 + 3*x + 4 over Rational Field
|
|
713
|
+
to Elliptic Curve defined by y^2 = x^3 - 27*x + 46 over Rational Field
|
|
714
|
+
sage: phi_k(P) == phi(P)
|
|
715
|
+
True
|
|
716
|
+
sage: phi_k == phi
|
|
717
|
+
True
|
|
718
|
+
sage: phi_k.degree()
|
|
719
|
+
2
|
|
720
|
+
sage: phi_k.is_separable()
|
|
721
|
+
True
|
|
722
|
+
|
|
723
|
+
A more complicated example over the rationals (of odd degree)::
|
|
724
|
+
|
|
725
|
+
sage: E = EllipticCurve('11a1')
|
|
726
|
+
sage: P_list = E.torsion_points()
|
|
727
|
+
sage: phi_v = EllipticCurveIsogeny(E, P_list); phi_v
|
|
728
|
+
Isogeny of degree 5
|
|
729
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
|
|
730
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
|
|
731
|
+
sage: P = E((16,-61))
|
|
732
|
+
sage: phi_v(P)
|
|
733
|
+
(0 : 1 : 0)
|
|
734
|
+
sage: ker_poly = phi_v.kernel_polynomial(); ker_poly
|
|
735
|
+
x^2 - 21*x + 80
|
|
736
|
+
sage: phi_k = EllipticCurveIsogeny(E, ker_poly); phi_k
|
|
737
|
+
Isogeny of degree 5
|
|
738
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
|
|
739
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
|
|
740
|
+
sage: phi_k == phi_v
|
|
741
|
+
True
|
|
742
|
+
sage: phi_v(P) == phi_k(P)
|
|
743
|
+
True
|
|
744
|
+
sage: phi_k.is_separable()
|
|
745
|
+
True
|
|
746
|
+
|
|
747
|
+
We can also do this same example over the number field defined by
|
|
748
|
+
the irreducible two-torsion polynomial of `E`::
|
|
749
|
+
|
|
750
|
+
sage: # needs sage.rings.number_field
|
|
751
|
+
sage: E = EllipticCurve('11a1')
|
|
752
|
+
sage: P_list = E.torsion_points()
|
|
753
|
+
sage: x = polygen(ZZ, 'x')
|
|
754
|
+
sage: K.<alpha> = NumberField(x^3 - 2* x^2 - 40*x - 158)
|
|
755
|
+
sage: EK = E.change_ring(K)
|
|
756
|
+
sage: P_list = [EK(P) for P in P_list]
|
|
757
|
+
sage: phi_v = EllipticCurveIsogeny(EK, P_list); phi_v
|
|
758
|
+
Isogeny of degree 5
|
|
759
|
+
from Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-10)*x + (-20)
|
|
760
|
+
over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
|
|
761
|
+
to Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-7820)*x + (-263580)
|
|
762
|
+
over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
|
|
763
|
+
sage: P = EK((alpha/2,-1/2))
|
|
764
|
+
sage: phi_v(P)
|
|
765
|
+
(122/121*alpha^2 + 1633/242*alpha - 3920/121 : -1/2 : 1)
|
|
766
|
+
sage: ker_poly = phi_v.kernel_polynomial()
|
|
767
|
+
sage: ker_poly
|
|
768
|
+
x^2 - 21*x + 80
|
|
769
|
+
sage: phi_k = EllipticCurveIsogeny(EK, ker_poly); phi_k
|
|
770
|
+
Isogeny of degree 5
|
|
771
|
+
from Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-10)*x + (-20)
|
|
772
|
+
over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
|
|
773
|
+
to Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-7820)*x + (-263580)
|
|
774
|
+
over Number Field in alpha with defining polynomial x^3 - 2*x^2 - 40*x - 158
|
|
775
|
+
sage: phi_v == phi_k
|
|
776
|
+
True
|
|
777
|
+
sage: phi_k(P) == phi_v(P)
|
|
778
|
+
True
|
|
779
|
+
sage: phi_k == phi_v
|
|
780
|
+
True
|
|
781
|
+
sage: phi_k.degree()
|
|
782
|
+
5
|
|
783
|
+
sage: phi_v.is_separable()
|
|
784
|
+
True
|
|
785
|
+
|
|
786
|
+
The following example shows how to specify an isogeny from domain
|
|
787
|
+
and codomain::
|
|
788
|
+
|
|
789
|
+
sage: E = EllipticCurve('11a1')
|
|
790
|
+
sage: R.<x> = QQ[]
|
|
791
|
+
sage: f = x^2 - 21*x + 80
|
|
792
|
+
sage: phi = E.isogeny(f)
|
|
793
|
+
sage: E2 = phi.codomain()
|
|
794
|
+
sage: phi_s = EllipticCurveIsogeny(E, None, E2, 5); phi_s
|
|
795
|
+
Isogeny of degree 5
|
|
796
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
|
|
797
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
|
|
798
|
+
sage: phi_s == phi
|
|
799
|
+
True
|
|
800
|
+
sage: phi_s.rational_maps() == phi.rational_maps()
|
|
801
|
+
True
|
|
802
|
+
|
|
803
|
+
However, only cyclic normalized isogenies can be constructed this way.
|
|
804
|
+
The non-cyclic multiplication-by-`3` isogeny won't be found::
|
|
805
|
+
|
|
806
|
+
sage: E.isogeny(None, codomain=E, degree=9)
|
|
807
|
+
Traceback (most recent call last):
|
|
808
|
+
...
|
|
809
|
+
ValueError: the two curves are not linked by a cyclic normalized isogeny of degree 9
|
|
810
|
+
|
|
811
|
+
Non-normalized isogeny also won't be found::
|
|
812
|
+
|
|
813
|
+
sage: E2.isogeny(None, codomain=E, degree=5)
|
|
814
|
+
Traceback (most recent call last):
|
|
815
|
+
...
|
|
816
|
+
ValueError: the two curves are not linked by a cyclic normalized isogeny of degree 5
|
|
817
|
+
sage: phihat = phi.dual(); phihat
|
|
818
|
+
Isogeny of degree 5
|
|
819
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580
|
|
820
|
+
over Rational Field
|
|
821
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
|
|
822
|
+
sage: phihat.is_normalized()
|
|
823
|
+
False
|
|
824
|
+
|
|
825
|
+
Here an example of a construction of a endomorphisms with cyclic
|
|
826
|
+
kernel on a CM-curve::
|
|
827
|
+
|
|
828
|
+
sage: # needs sage.rings.number_field
|
|
829
|
+
sage: K.<i> = NumberField(x^2 + 1)
|
|
830
|
+
sage: E = EllipticCurve(K, [1,0])
|
|
831
|
+
sage: RK.<X> = K[]
|
|
832
|
+
sage: f = X^2 - 2/5*i + 1/5
|
|
833
|
+
sage: phi= E.isogeny(f)
|
|
834
|
+
sage: isom = phi.codomain().isomorphism_to(E)
|
|
835
|
+
sage: phi = isom * phi
|
|
836
|
+
sage: phi.codomain() == phi.domain()
|
|
837
|
+
True
|
|
838
|
+
sage: phi.rational_maps()
|
|
839
|
+
(((4/25*i + 3/25)*x^5 + (4/5*i - 2/5)*x^3 - x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)),
|
|
840
|
+
((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y + (141/125*i + 162/125)*x^2*y + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125)))
|
|
841
|
+
|
|
842
|
+
TESTS:
|
|
843
|
+
|
|
844
|
+
Domain and codomain tests (see :issue:`12880`)::
|
|
845
|
+
|
|
846
|
+
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
|
|
847
|
+
sage: phi = EllipticCurveIsogeny(E, E(0,0))
|
|
848
|
+
sage: phi.domain() == E
|
|
849
|
+
True
|
|
850
|
+
sage: phi.codomain()
|
|
851
|
+
Elliptic Curve defined by y^2 = x^3 - 4*x over Rational Field
|
|
852
|
+
|
|
853
|
+
sage: E = EllipticCurve(GF(31), [1,0,0,1,2])
|
|
854
|
+
sage: phi = EllipticCurveIsogeny(E, [17, 1])
|
|
855
|
+
sage: phi.domain()
|
|
856
|
+
Elliptic Curve defined by y^2 + x*y = x^3 + x + 2 over Finite Field of size 31
|
|
857
|
+
sage: phi.codomain()
|
|
858
|
+
Elliptic Curve defined by y^2 + x*y = x^3 + 24*x + 6 over Finite Field of size 31
|
|
859
|
+
|
|
860
|
+
Composition tests (see :issue:`16245`, cf. :issue:`34410`)::
|
|
861
|
+
|
|
862
|
+
sage: E = EllipticCurve(j=GF(7)(0))
|
|
863
|
+
sage: phi = E.isogeny([E(0), E((0,1)), E((0,-1))]); phi
|
|
864
|
+
Composite morphism of degree 3:
|
|
865
|
+
From: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7
|
|
866
|
+
To: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7
|
|
867
|
+
sage: phi2 = phi * phi; phi2
|
|
868
|
+
Composite morphism of degree 9 = 3^2:
|
|
869
|
+
From: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7
|
|
870
|
+
To: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7
|
|
871
|
+
|
|
872
|
+
Examples over relative number fields used not to work (see :issue:`16779`)::
|
|
873
|
+
|
|
874
|
+
sage: # long time, needs sage.rings.number_field
|
|
875
|
+
sage: pol26 = hilbert_class_polynomial(-4*26)
|
|
876
|
+
sage: F = NumberField(pol26,'a')
|
|
877
|
+
sage: pol = F.optimized_representation()[0].polynomial()
|
|
878
|
+
sage: K.<a> = NumberField(pol)
|
|
879
|
+
sage: j = pol26.roots(K)[0][0]
|
|
880
|
+
sage: E = EllipticCurve(j=j)
|
|
881
|
+
sage: L.<b> = K.extension(x^2 + 26)
|
|
882
|
+
sage: EL = E.change_ring(L)
|
|
883
|
+
sage: iso2 = EL.isogenies_prime_degree(2); len(iso2)
|
|
884
|
+
1
|
|
885
|
+
sage: iso3 = EL.isogenies_prime_degree(3); len(iso3)
|
|
886
|
+
2
|
|
887
|
+
|
|
888
|
+
Examples over function fields used not to work (see :issue:`11327`)::
|
|
889
|
+
|
|
890
|
+
sage: F.<t> = FunctionField(QQ)
|
|
891
|
+
sage: E = EllipticCurve([0,0,0,-t^2,0])
|
|
892
|
+
sage: isogs = E.isogenies_prime_degree(2)
|
|
893
|
+
sage: isogs[0]
|
|
894
|
+
Isogeny of degree 2
|
|
895
|
+
from Elliptic Curve defined by y^2 = x^3 + (-t^2)*x
|
|
896
|
+
over Rational function field in t over Rational Field
|
|
897
|
+
to Elliptic Curve defined by y^2 = x^3 + 4*t^2*x
|
|
898
|
+
over Rational function field in t over Rational Field
|
|
899
|
+
sage: isogs[0].rational_maps()
|
|
900
|
+
((x^2 - t^2)/x, (x^2*y + t^2*y)/x^2)
|
|
901
|
+
sage: duals = [phi.dual() for phi in isogs]
|
|
902
|
+
sage: duals[0]
|
|
903
|
+
Isogeny of degree 2
|
|
904
|
+
from Elliptic Curve defined by y^2 = x^3 + 4*t^2*x
|
|
905
|
+
over Rational function field in t over Rational Field
|
|
906
|
+
to Elliptic Curve defined by y^2 = x^3 + (-t^2)*x
|
|
907
|
+
over Rational function field in t over Rational Field
|
|
908
|
+
sage: duals[0].rational_maps()
|
|
909
|
+
((1/4*x^2 + t^2)/x, (1/8*x^2*y + (-1/2*t^2)*y)/x^2)
|
|
910
|
+
sage: duals[0]
|
|
911
|
+
Isogeny of degree 2
|
|
912
|
+
from Elliptic Curve defined by y^2 = x^3 + 4*t^2*x
|
|
913
|
+
over Rational function field in t over Rational Field
|
|
914
|
+
to Elliptic Curve defined by y^2 = x^3 + (-t^2)*x
|
|
915
|
+
over Rational function field in t over Rational Field
|
|
916
|
+
"""
|
|
917
|
+
|
|
918
|
+
####################
|
|
919
|
+
# member variables
|
|
920
|
+
####################
|
|
921
|
+
|
|
922
|
+
#
|
|
923
|
+
# variables common to all algorithms
|
|
924
|
+
#
|
|
925
|
+
_domain = None
|
|
926
|
+
_codomain = None
|
|
927
|
+
|
|
928
|
+
_degree = None
|
|
929
|
+
|
|
930
|
+
__algorithm = None
|
|
931
|
+
|
|
932
|
+
__check = None
|
|
933
|
+
|
|
934
|
+
#
|
|
935
|
+
# pre-isomorphism
|
|
936
|
+
#
|
|
937
|
+
__pre_isomorphism = None
|
|
938
|
+
__prei_ratl_maps = None
|
|
939
|
+
|
|
940
|
+
#
|
|
941
|
+
# post-isomorphism
|
|
942
|
+
#
|
|
943
|
+
__post_isomorphism = None
|
|
944
|
+
__posti_ratl_maps = None
|
|
945
|
+
|
|
946
|
+
#
|
|
947
|
+
# algebraic structs
|
|
948
|
+
#
|
|
949
|
+
__base_field = None
|
|
950
|
+
__poly_ring = None # univariate in x over __base_field
|
|
951
|
+
__mpoly_ring = None # __base_field[x][y], internal use only
|
|
952
|
+
|
|
953
|
+
#
|
|
954
|
+
# Rational Maps
|
|
955
|
+
#
|
|
956
|
+
__ratl_maps = None
|
|
957
|
+
|
|
958
|
+
#
|
|
959
|
+
# The dual
|
|
960
|
+
#
|
|
961
|
+
__dual = None
|
|
962
|
+
|
|
963
|
+
#
|
|
964
|
+
# Kernel Data
|
|
965
|
+
#
|
|
966
|
+
|
|
967
|
+
__kernel_list = None # list of elements in the kernel
|
|
968
|
+
|
|
969
|
+
__kernel_polynomial = None # polynomial with roots at x values for x-coordinate of points in the kernel
|
|
970
|
+
|
|
971
|
+
__inner_kernel_polynomial = None # the inner kernel polynomial (ignoring preisomorphism)
|
|
972
|
+
|
|
973
|
+
#
|
|
974
|
+
# member variables common to Velu's formula
|
|
975
|
+
#
|
|
976
|
+
|
|
977
|
+
# a full set of representatives of the kernel subgroup modulo negation
|
|
978
|
+
__kernel_mod_sign = None
|
|
979
|
+
|
|
980
|
+
# variables used in Velu's formula (as well as Kohel's variant)
|
|
981
|
+
__v = None
|
|
982
|
+
__w = None
|
|
983
|
+
|
|
984
|
+
#
|
|
985
|
+
# member variables specific to Kohel's algorithm.
|
|
986
|
+
#
|
|
987
|
+
__psi = None # psi polynomial
|
|
988
|
+
__phi = None # phi polynomial
|
|
989
|
+
__omega = None # omega polynomial, an element of k[x][y]
|
|
990
|
+
|
|
991
|
+
#
|
|
992
|
+
# Python Special Functions
|
|
993
|
+
#
|
|
994
|
+
|
|
995
|
+
def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True):
|
|
996
|
+
r"""
|
|
997
|
+
Constructor for ``EllipticCurveIsogeny`` class.
|
|
998
|
+
|
|
999
|
+
EXAMPLES::
|
|
1000
|
+
|
|
1001
|
+
sage: E = EllipticCurve(GF(2), [0,0,1,0,1])
|
|
1002
|
+
sage: phi = EllipticCurveIsogeny(E, [1,1]); phi
|
|
1003
|
+
Isogeny of degree 3
|
|
1004
|
+
from Elliptic Curve defined by y^2 + y = x^3 + 1 over Finite Field of size 2
|
|
1005
|
+
to Elliptic Curve defined by y^2 + y = x^3 over Finite Field of size 2
|
|
1006
|
+
|
|
1007
|
+
sage: E = EllipticCurve(GF(31), [0,0,0,1,0])
|
|
1008
|
+
sage: P = E((2,17))
|
|
1009
|
+
sage: phi = EllipticCurveIsogeny(E, P); phi
|
|
1010
|
+
Isogeny of degree 8
|
|
1011
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 31
|
|
1012
|
+
to Elliptic Curve defined by y^2 = x^3 + 10*x + 28 over Finite Field of size 31
|
|
1013
|
+
|
|
1014
|
+
sage: E = EllipticCurve('17a1')
|
|
1015
|
+
sage: phi = EllipticCurveIsogeny(E, [41/3, -55, -1, -1, 1]); phi
|
|
1016
|
+
Isogeny of degree 9
|
|
1017
|
+
from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - x - 14
|
|
1018
|
+
over Rational Field
|
|
1019
|
+
to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 56*x - 10124
|
|
1020
|
+
over Rational Field
|
|
1021
|
+
|
|
1022
|
+
sage: E = EllipticCurve('37a1')
|
|
1023
|
+
sage: triv = EllipticCurveIsogeny(E, E(0)); triv
|
|
1024
|
+
Isogeny of degree 1
|
|
1025
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
|
|
1026
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
|
|
1027
|
+
sage: triv.rational_maps()
|
|
1028
|
+
(x, y)
|
|
1029
|
+
|
|
1030
|
+
sage: E = EllipticCurve('49a3')
|
|
1031
|
+
sage: R.<X> = QQ[]
|
|
1032
|
+
sage: EllipticCurveIsogeny(E, X^3 - 13*X^2 - 58*X + 503, check=False)
|
|
1033
|
+
Isogeny of degree 7
|
|
1034
|
+
from Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 107*x + 552
|
|
1035
|
+
over Rational Field
|
|
1036
|
+
to Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 5252*x - 178837
|
|
1037
|
+
over Rational Field
|
|
1038
|
+
"""
|
|
1039
|
+
if not isinstance(E, EllipticCurve_generic):
|
|
1040
|
+
raise ValueError("given E is not an elliptic curve")
|
|
1041
|
+
|
|
1042
|
+
if not isinstance(kernel, list) and kernel in E:
|
|
1043
|
+
# a single point was given, we put it in a list
|
|
1044
|
+
# the first condition assures that [1,1] is treated as x+1
|
|
1045
|
+
kernel = [kernel]
|
|
1046
|
+
|
|
1047
|
+
# if the kernel is None and the codomain isn't
|
|
1048
|
+
# calculate the kernel polynomial
|
|
1049
|
+
pre_isom = None
|
|
1050
|
+
post_isom = None
|
|
1051
|
+
|
|
1052
|
+
self.__check = check
|
|
1053
|
+
|
|
1054
|
+
if kernel is None and codomain is not None:
|
|
1055
|
+
|
|
1056
|
+
if degree is None:
|
|
1057
|
+
raise ValueError("degree must be given when specifying isogeny by domain and codomain")
|
|
1058
|
+
|
|
1059
|
+
# save the codomain: really used now (trac #7096)
|
|
1060
|
+
old_codomain = codomain
|
|
1061
|
+
|
|
1062
|
+
pre_isom, post_isom, E, codomain, kernel = compute_sequence_of_maps(E, codomain, degree)
|
|
1063
|
+
|
|
1064
|
+
self.__init_algebraic_structs(E)
|
|
1065
|
+
|
|
1066
|
+
algorithm = _isogeny_determine_algorithm(E, kernel)
|
|
1067
|
+
|
|
1068
|
+
self.__algorithm = algorithm
|
|
1069
|
+
|
|
1070
|
+
if algorithm == 'velu':
|
|
1071
|
+
self.__init_from_kernel_gens(kernel)
|
|
1072
|
+
elif algorithm == 'kohel':
|
|
1073
|
+
self.__init_from_kernel_polynomial(kernel)
|
|
1074
|
+
else:
|
|
1075
|
+
raise NotImplementedError
|
|
1076
|
+
|
|
1077
|
+
self.__compute_codomain()
|
|
1078
|
+
|
|
1079
|
+
self.__setup_post_isomorphism(codomain, model)
|
|
1080
|
+
|
|
1081
|
+
if pre_isom is not None:
|
|
1082
|
+
self._set_pre_isomorphism(pre_isom)
|
|
1083
|
+
|
|
1084
|
+
if post_isom is not None:
|
|
1085
|
+
self.__set_post_isomorphism(old_codomain, post_isom) #(trac #7096)
|
|
1086
|
+
|
|
1087
|
+
# Inheritance house keeping
|
|
1088
|
+
self.__perform_inheritance_housekeeping()
|
|
1089
|
+
|
|
1090
|
+
def _eval(self, P):
|
|
1091
|
+
r"""
|
|
1092
|
+
Less strict evaluation method for internal use.
|
|
1093
|
+
|
|
1094
|
+
In particular, this can be used to evaluate ``self`` at a
|
|
1095
|
+
point defined over an extension field.
|
|
1096
|
+
|
|
1097
|
+
INPUT:
|
|
1098
|
+
|
|
1099
|
+
- ``P`` -- a sequence of 3 coordinates defining a point on ``self``
|
|
1100
|
+
|
|
1101
|
+
OUTPUT: the result of evaluating ``self`` at the given point
|
|
1102
|
+
|
|
1103
|
+
EXAMPLES::
|
|
1104
|
+
|
|
1105
|
+
sage: E = EllipticCurve([1,0]); E
|
|
1106
|
+
Elliptic Curve defined by y^2 = x^3 + x over Rational Field
|
|
1107
|
+
sage: phi = E.isogeny(E(0,0))
|
|
1108
|
+
sage: P = E.change_ring(QQbar).lift_x(QQbar.random_element()) # needs sage.rings.number_field
|
|
1109
|
+
sage: phi._eval(P).curve() # needs sage.rings.number_field
|
|
1110
|
+
Elliptic Curve defined by y^2 = x^3 + (-4)*x over Algebraic Field
|
|
1111
|
+
|
|
1112
|
+
::
|
|
1113
|
+
|
|
1114
|
+
sage: E = EllipticCurve(j=Mod(0,419))
|
|
1115
|
+
sage: K = next(filter(bool, E(0).division_points(5)))
|
|
1116
|
+
sage: psi = E.isogeny(K)
|
|
1117
|
+
sage: Ps = E.change_ring(GF(419**2))(0).division_points(5) # needs sage.rings.number_field
|
|
1118
|
+
sage: {psi._eval(P).curve() for P in Ps} # needs sage.rings.number_field
|
|
1119
|
+
{Elliptic Curve defined by y^2 = x^3 + 140*x + 214 over Finite Field in z2 of size 419^2}
|
|
1120
|
+
"""
|
|
1121
|
+
if self._domain.defining_polynomial()(*P):
|
|
1122
|
+
raise ValueError(f"{P} not on {self._domain}")
|
|
1123
|
+
|
|
1124
|
+
k = Sequence(P).universe()
|
|
1125
|
+
|
|
1126
|
+
if not P:
|
|
1127
|
+
return self._codomain(0).change_ring(k)
|
|
1128
|
+
|
|
1129
|
+
Q = P.xy()
|
|
1130
|
+
|
|
1131
|
+
if self.__pre_isomorphism is not None:
|
|
1132
|
+
Q = baseWI.__call__(self.__pre_isomorphism, Q)
|
|
1133
|
+
|
|
1134
|
+
if self.__algorithm == "velu":
|
|
1135
|
+
compute = self.__compute_via_velu
|
|
1136
|
+
elif self.__algorithm == "kohel":
|
|
1137
|
+
compute = self.__compute_via_kohel
|
|
1138
|
+
else:
|
|
1139
|
+
raise NotImplementedError
|
|
1140
|
+
|
|
1141
|
+
try:
|
|
1142
|
+
Q = compute(*Q)
|
|
1143
|
+
except ZeroDivisionError:
|
|
1144
|
+
Q = (0, 1, 0)
|
|
1145
|
+
|
|
1146
|
+
if self.__post_isomorphism is not None:
|
|
1147
|
+
Q = baseWI.__call__(self.__post_isomorphism, Q)
|
|
1148
|
+
|
|
1149
|
+
return self._codomain.base_extend(k).point(Q)
|
|
1150
|
+
|
|
1151
|
+
def _call_(self, P):
|
|
1152
|
+
r"""
|
|
1153
|
+
Implement evaluation of elliptic-curve isogenies using the
|
|
1154
|
+
function-call syntax.
|
|
1155
|
+
|
|
1156
|
+
EXAMPLES::
|
|
1157
|
+
|
|
1158
|
+
sage: E = EllipticCurve(GF(17), [1, 9, 5, 4, 3])
|
|
1159
|
+
sage: phi = EllipticCurveIsogeny(E, [6,13,1])
|
|
1160
|
+
sage: phi(E((1,0)))
|
|
1161
|
+
(15 : 13 : 1)
|
|
1162
|
+
|
|
1163
|
+
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
|
|
1164
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1165
|
+
sage: phi(E((1,5)))
|
|
1166
|
+
(2 : 0 : 1)
|
|
1167
|
+
|
|
1168
|
+
sage: E = EllipticCurve(QQ, [0,0,0,3,0])
|
|
1169
|
+
sage: P = E((1,2))
|
|
1170
|
+
sage: phi = EllipticCurveIsogeny(E, [0,1])
|
|
1171
|
+
sage: phi(P)
|
|
1172
|
+
(4 : -4 : 1)
|
|
1173
|
+
sage: phi(-P)
|
|
1174
|
+
(4 : 4 : 1)
|
|
1175
|
+
|
|
1176
|
+
sage: E = EllipticCurve(GF(17), [0,-1,0,-3,-1])
|
|
1177
|
+
sage: Q = E((16,0))
|
|
1178
|
+
sage: tau = E.isogeny([Q], E)
|
|
1179
|
+
sage: tau(Q)
|
|
1180
|
+
(0 : 1 : 0)
|
|
1181
|
+
|
|
1182
|
+
TESTS:
|
|
1183
|
+
|
|
1184
|
+
Tests for :issue:`10888`::
|
|
1185
|
+
|
|
1186
|
+
sage: # needs sage.rings.number_field
|
|
1187
|
+
sage: x = polygen(ZZ, 'x')
|
|
1188
|
+
sage: K.<th> = NumberField(x^2 + 3)
|
|
1189
|
+
sage: E = EllipticCurve(K, [7,0])
|
|
1190
|
+
sage: phi = E.isogeny(E(0,0))
|
|
1191
|
+
sage: P = E(-3,4*th)
|
|
1192
|
+
sage: phi(P)
|
|
1193
|
+
(-16/3 : 8/9*th : 1)
|
|
1194
|
+
sage: Q = phi(P)
|
|
1195
|
+
sage: phihat = phi.dual()
|
|
1196
|
+
sage: phihat(Q)
|
|
1197
|
+
(-1/48 : 127/576*th : 1)
|
|
1198
|
+
|
|
1199
|
+
Call a composed isogeny (added for :issue:`16238`)::
|
|
1200
|
+
|
|
1201
|
+
sage: E = EllipticCurve(j=GF(7)(0))
|
|
1202
|
+
sage: phi = E.isogeny([E(0), E((0,1)), E((0,-1))])
|
|
1203
|
+
sage: phi(E.points()[0])
|
|
1204
|
+
(0 : 1 : 0)
|
|
1205
|
+
sage: phi2 = phi * phi
|
|
1206
|
+
sage: phi2(E.points()[0])
|
|
1207
|
+
(0 : 1 : 0)
|
|
1208
|
+
|
|
1209
|
+
Coercion works fine with :meth:`_call_` (added for :issue:`16238`)::
|
|
1210
|
+
|
|
1211
|
+
sage: # needs sage.rings.number_field
|
|
1212
|
+
sage: K.<th> = NumberField(x^2 + 3)
|
|
1213
|
+
sage: E = EllipticCurve(K, [7,0])
|
|
1214
|
+
sage: E2 = EllipticCurve(K, [5,0])
|
|
1215
|
+
sage: phi = E.isogeny(E(0))
|
|
1216
|
+
sage: phi(E2(0))
|
|
1217
|
+
(0 : 1 : 0)
|
|
1218
|
+
sage: E2(20,90)
|
|
1219
|
+
(20 : 90 : 1)
|
|
1220
|
+
sage: phi(E2(20,90))
|
|
1221
|
+
Traceback (most recent call last):
|
|
1222
|
+
...
|
|
1223
|
+
TypeError: (20 : 90 : 1) fails to convert into the map's domain
|
|
1224
|
+
Elliptic Curve defined by y^2 = x^3 + 7*x over
|
|
1225
|
+
Number Field in th with defining polynomial x^2 + 3,
|
|
1226
|
+
but a `pushforward` method is not properly implemented
|
|
1227
|
+
|
|
1228
|
+
Check that copying the order over works::
|
|
1229
|
+
|
|
1230
|
+
sage: # needs sage.rings.finite_rings
|
|
1231
|
+
sage: E = EllipticCurve(GF(431), [1,0])
|
|
1232
|
+
sage: P, = E.gens()
|
|
1233
|
+
sage: Q = 2^99*P; Q.order()
|
|
1234
|
+
27
|
|
1235
|
+
sage: phi = E.isogeny(3^99*P)
|
|
1236
|
+
sage: phi(Q)._order
|
|
1237
|
+
27
|
|
1238
|
+
|
|
1239
|
+
Test for :issue:`35983`::
|
|
1240
|
+
|
|
1241
|
+
sage: E = EllipticCurve([1,0,0,-1,0])
|
|
1242
|
+
sage: P = E([1,0])
|
|
1243
|
+
sage: P.order()
|
|
1244
|
+
+Infinity
|
|
1245
|
+
sage: phi = E.isogenies_prime_degree(2)[0]
|
|
1246
|
+
sage: Q = phi(P); Q
|
|
1247
|
+
(0 : 1 : 1)
|
|
1248
|
+
sage: Q.order()
|
|
1249
|
+
+Infinity
|
|
1250
|
+
"""
|
|
1251
|
+
if P.is_zero():
|
|
1252
|
+
return self._codomain(0)
|
|
1253
|
+
|
|
1254
|
+
xP, yP = P.xy()
|
|
1255
|
+
|
|
1256
|
+
# if there is a pre-isomorphism, apply it
|
|
1257
|
+
if self.__pre_isomorphism is not None:
|
|
1258
|
+
yP = self.__prei_ratl_maps[1](xP, yP)
|
|
1259
|
+
xP = self.__prei_ratl_maps[0](xP)
|
|
1260
|
+
|
|
1261
|
+
if self.__algorithm == 'velu':
|
|
1262
|
+
outP = self.__compute_via_velu_numeric(xP, yP)
|
|
1263
|
+
elif self.__algorithm == 'kohel':
|
|
1264
|
+
outP = self.__compute_via_kohel_numeric(xP, yP)
|
|
1265
|
+
else:
|
|
1266
|
+
raise NotImplementedError
|
|
1267
|
+
|
|
1268
|
+
# the intermediate functions return the empty tuple ()
|
|
1269
|
+
# if the input point is in the kernel
|
|
1270
|
+
if outP == ():
|
|
1271
|
+
return self._codomain(0)
|
|
1272
|
+
|
|
1273
|
+
xP, yP = outP
|
|
1274
|
+
|
|
1275
|
+
# if there is a post-isomorphism, apply it
|
|
1276
|
+
if self.__post_isomorphism is not None:
|
|
1277
|
+
yP = self.__posti_ratl_maps[1](xP, yP)
|
|
1278
|
+
xP = self.__posti_ratl_maps[0](xP)
|
|
1279
|
+
|
|
1280
|
+
Q = self._codomain(xP, yP)
|
|
1281
|
+
if hasattr(P, '_order'):
|
|
1282
|
+
if P.has_infinite_order() or P._order.gcd(self._degree).is_one():
|
|
1283
|
+
Q._order = P._order
|
|
1284
|
+
# TODO: For non-coprime degree, the order of the point
|
|
1285
|
+
# may get reduced by a divisor of the degree when passing
|
|
1286
|
+
# through the isogeny. We could run something along the
|
|
1287
|
+
# lines of order_from_multiple() to determine the new
|
|
1288
|
+
# order, but this probably shouldn't happen by default
|
|
1289
|
+
# as it'll be detrimental to performance in some cases.
|
|
1290
|
+
return Q
|
|
1291
|
+
|
|
1292
|
+
def __getitem__(self, i):
|
|
1293
|
+
r"""
|
|
1294
|
+
Return one of the rational-map components.
|
|
1295
|
+
|
|
1296
|
+
.. NOTE::
|
|
1297
|
+
|
|
1298
|
+
Both components are returned as elements of the function
|
|
1299
|
+
field `F(x,y)` in two variables over the base field `F`,
|
|
1300
|
+
though the first only involves `x`. To obtain the
|
|
1301
|
+
`x`-coordinate function as a rational function in `F(x)`,
|
|
1302
|
+
use :meth:`x_rational_map`.
|
|
1303
|
+
|
|
1304
|
+
EXAMPLES::
|
|
1305
|
+
|
|
1306
|
+
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
|
|
1307
|
+
sage: phi = EllipticCurveIsogeny(E, [1])
|
|
1308
|
+
sage: phi[0]
|
|
1309
|
+
x
|
|
1310
|
+
sage: phi[1]
|
|
1311
|
+
y
|
|
1312
|
+
|
|
1313
|
+
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
|
|
1314
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1315
|
+
sage: phi[0]
|
|
1316
|
+
(x^2 + 3)/x
|
|
1317
|
+
sage: phi[1]
|
|
1318
|
+
(x^2*y - 3*y)/x^2
|
|
1319
|
+
"""
|
|
1320
|
+
return self.rational_maps()[i]
|
|
1321
|
+
|
|
1322
|
+
def __iter__(self):
|
|
1323
|
+
r"""
|
|
1324
|
+
Return an iterator through the rational-map components.
|
|
1325
|
+
|
|
1326
|
+
EXAMPLES::
|
|
1327
|
+
|
|
1328
|
+
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
|
|
1329
|
+
sage: phi = EllipticCurveIsogeny(E, [1])
|
|
1330
|
+
sage: for c in phi: print(c)
|
|
1331
|
+
x
|
|
1332
|
+
y
|
|
1333
|
+
|
|
1334
|
+
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
|
|
1335
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1336
|
+
sage: for c in phi: print(c)
|
|
1337
|
+
(x^2 + 3)/x
|
|
1338
|
+
(x^2*y - 3*y)/x^2
|
|
1339
|
+
"""
|
|
1340
|
+
return iter(self.rational_maps())
|
|
1341
|
+
|
|
1342
|
+
def __neg__(self):
|
|
1343
|
+
r"""
|
|
1344
|
+
Return a copy of the isogeny that has been negated.
|
|
1345
|
+
|
|
1346
|
+
This implements the unary `-` operator.
|
|
1347
|
+
|
|
1348
|
+
EXAMPLES:
|
|
1349
|
+
|
|
1350
|
+
The following examples inherently exercise this function::
|
|
1351
|
+
|
|
1352
|
+
sage: E = EllipticCurve(j=GF(17)(0))
|
|
1353
|
+
sage: phi = EllipticCurveIsogeny(E, E((-1,0)) )
|
|
1354
|
+
sage: negphi = -phi
|
|
1355
|
+
sage: phi(E((0,1))) + negphi(E((0,1))) == 0
|
|
1356
|
+
True
|
|
1357
|
+
|
|
1358
|
+
sage: E = EllipticCurve(j=GF(19)(1728))
|
|
1359
|
+
sage: R.<x> = GF(19)[]
|
|
1360
|
+
sage: phi = EllipticCurveIsogeny(E, x)
|
|
1361
|
+
sage: negphi = -phi
|
|
1362
|
+
sage: phi(E((3,7))) + negphi(E((3,12))) == phi(2*E((3,7)))
|
|
1363
|
+
True
|
|
1364
|
+
sage: negphi(E((18,6)))
|
|
1365
|
+
(17 : 0 : 1)
|
|
1366
|
+
|
|
1367
|
+
sage: R.<x> = QQ[]
|
|
1368
|
+
sage: E = EllipticCurve('17a1')
|
|
1369
|
+
sage: R.<x> = QQ[]
|
|
1370
|
+
sage: f = x - 11/4
|
|
1371
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
1372
|
+
sage: negphi = -phi
|
|
1373
|
+
sage: phi.rational_maps()[0] == negphi.rational_maps()[0]
|
|
1374
|
+
True
|
|
1375
|
+
sage: P = E((7,13))
|
|
1376
|
+
sage: phi(P) + negphi(P) == 0
|
|
1377
|
+
True
|
|
1378
|
+
|
|
1379
|
+
sage: E = EllipticCurve(GF(23), [0,0,0,1,0])
|
|
1380
|
+
sage: f = E.torsion_polynomial(3)/3
|
|
1381
|
+
sage: phi = EllipticCurveIsogeny(E, f, E)
|
|
1382
|
+
sage: phi.rational_maps() == E.multiplication_by_m(3)
|
|
1383
|
+
False
|
|
1384
|
+
sage: negphi = -phi
|
|
1385
|
+
sage: negphi.rational_maps() == E.multiplication_by_m(3)
|
|
1386
|
+
True
|
|
1387
|
+
|
|
1388
|
+
sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11])
|
|
1389
|
+
sage: R.<x> = GF(17)[]
|
|
1390
|
+
sage: f = x+6
|
|
1391
|
+
sage: phi = EllipticCurveIsogeny(E, f); phi
|
|
1392
|
+
Isogeny of degree 2
|
|
1393
|
+
from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17
|
|
1394
|
+
to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17
|
|
1395
|
+
sage: phi.rational_maps()
|
|
1396
|
+
((x^2 + 6*x + 4)/(x + 6), (x^2*y - 5*x*y + 8*x - 2*y)/(x^2 - 5*x + 2))
|
|
1397
|
+
sage: negphi = -phi
|
|
1398
|
+
sage: negphi
|
|
1399
|
+
Isogeny of degree 2
|
|
1400
|
+
from Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 7*x + 6 over Finite Field of size 17
|
|
1401
|
+
to Elliptic Curve defined by y^2 + 15*x*y + 12*y = x^3 + 3*x^2 + 4*x + 8 over Finite Field of size 17
|
|
1402
|
+
sage: negphi.rational_maps()
|
|
1403
|
+
((x^2 + 6*x + 4)/(x + 6),
|
|
1404
|
+
(2*x^3 - x^2*y - 5*x^2 + 5*x*y - 4*x + 2*y + 7)/(x^2 - 5*x + 2))
|
|
1405
|
+
|
|
1406
|
+
sage: E = EllipticCurve('11a1')
|
|
1407
|
+
sage: R.<x> = QQ[]
|
|
1408
|
+
sage: f = x^2 - 21*x + 80
|
|
1409
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
1410
|
+
sage: (xmap1, ymap1) = phi.rational_maps()
|
|
1411
|
+
sage: negphi = -phi
|
|
1412
|
+
sage: (xmap2, ymap2) = negphi.rational_maps()
|
|
1413
|
+
sage: xmap1 == xmap2
|
|
1414
|
+
True
|
|
1415
|
+
sage: ymap1 == -ymap2 - E.a1()*xmap2 - E.a3()
|
|
1416
|
+
True
|
|
1417
|
+
|
|
1418
|
+
sage: # needs sage.rings.number_field
|
|
1419
|
+
sage: K.<a> = NumberField(x^2 + 1)
|
|
1420
|
+
sage: E = EllipticCurve(K, [0,0,0,1,0])
|
|
1421
|
+
sage: R.<x> = K[]
|
|
1422
|
+
sage: phi = EllipticCurveIsogeny(E, x - a)
|
|
1423
|
+
sage: phi.rational_maps()
|
|
1424
|
+
((x^2 + (-a)*x - 2)/(x + (-a)), (x^2*y + (-2*a)*x*y + y)/(x^2 + (-2*a)*x - 1))
|
|
1425
|
+
sage: negphi = -phi
|
|
1426
|
+
sage: negphi.rational_maps()
|
|
1427
|
+
((x^2 + (-a)*x - 2)/(x + (-a)), (-x^2*y + (2*a)*x*y - y)/(x^2 + (-2*a)*x - 1))
|
|
1428
|
+
"""
|
|
1429
|
+
output = deepcopy(self)
|
|
1430
|
+
output._set_post_isomorphism(negation_morphism(output._codomain))
|
|
1431
|
+
return output
|
|
1432
|
+
|
|
1433
|
+
#
|
|
1434
|
+
# Sage Special Functions
|
|
1435
|
+
#
|
|
1436
|
+
|
|
1437
|
+
def _repr_(self):
|
|
1438
|
+
r"""
|
|
1439
|
+
Return basic information about the isogeny as a string.
|
|
1440
|
+
|
|
1441
|
+
EXAMPLES::
|
|
1442
|
+
|
|
1443
|
+
sage: E = EllipticCurve(GF(31), [1,0,1,1,0])
|
|
1444
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
|
|
1445
|
+
sage: phi._repr_()
|
|
1446
|
+
'Isogeny of degree 17 from Elliptic Curve defined by y^2 + x*y + y = x^3 + x over Finite Field of size 31 to Elliptic Curve defined by y^2 + x*y + y = x^3 + 14*x + 9 over Finite Field of size 31'
|
|
1447
|
+
|
|
1448
|
+
sage: E = EllipticCurve(QQ, [1,0,0,1,9])
|
|
1449
|
+
sage: phi = EllipticCurveIsogeny(E, [2,1])
|
|
1450
|
+
sage: phi._repr_()
|
|
1451
|
+
'Isogeny of degree 2 from Elliptic Curve defined by y^2 + x*y = x^3 + x + 9 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - 59*x + 165 over Rational Field'
|
|
1452
|
+
"""
|
|
1453
|
+
return f'Isogeny of degree {self._degree} from {self._domain} to {self._codomain}'
|
|
1454
|
+
|
|
1455
|
+
def _latex_(self):
|
|
1456
|
+
r"""
|
|
1457
|
+
Return the rational maps of the isogeny as a LaTeX string.
|
|
1458
|
+
|
|
1459
|
+
This function returns a latex string representing the isogeny
|
|
1460
|
+
``self`` as the `x` and `y` coordinate rational functions.
|
|
1461
|
+
|
|
1462
|
+
EXAMPLES::
|
|
1463
|
+
|
|
1464
|
+
sage: E = EllipticCurve(QQ, [0,0,0,1,-1])
|
|
1465
|
+
sage: phi = EllipticCurveIsogeny(E, E(0))
|
|
1466
|
+
sage: phi._latex_()
|
|
1467
|
+
'\\left( x , y \\right)'
|
|
1468
|
+
|
|
1469
|
+
sage: E = EllipticCurve(GF(17), [0,0,0,1,-1])
|
|
1470
|
+
sage: R.<X> = GF(17)[]
|
|
1471
|
+
sage: phi = EllipticCurveIsogeny(E, X + 11)
|
|
1472
|
+
sage: phi._latex_()
|
|
1473
|
+
'\\left( \\frac{x^{2} + 11 x + 7}{x + 11} , \\frac{x^{2} y + 5 x y + 12 y}{x^{2} + 5 x + 2} \\right)'
|
|
1474
|
+
"""
|
|
1475
|
+
fx,fy = self.rational_maps()
|
|
1476
|
+
return fr'\left( {fx._latex_()} , {fy._latex_()} \right)'
|
|
1477
|
+
|
|
1478
|
+
###########################
|
|
1479
|
+
# Private Common Functions
|
|
1480
|
+
###########################
|
|
1481
|
+
|
|
1482
|
+
def __clear_cached_values(self):
|
|
1483
|
+
r"""
|
|
1484
|
+
A private function to clear the cache if the codomain has been
|
|
1485
|
+
modified by a pre or post-isomorphism.
|
|
1486
|
+
|
|
1487
|
+
EXAMPLES::
|
|
1488
|
+
|
|
1489
|
+
sage: R.<x> = QQ[]
|
|
1490
|
+
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
|
|
1491
|
+
sage: phi = EllipticCurveIsogeny(E, x)
|
|
1492
|
+
sage: old_ratl_maps = phi.rational_maps()
|
|
1493
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism
|
|
1494
|
+
sage: phi._set_post_isomorphism(negation_morphism(phi.codomain()))
|
|
1495
|
+
sage: old_ratl_maps == phi.rational_maps()
|
|
1496
|
+
False
|
|
1497
|
+
sage: old_ratl_maps[1] == -phi.rational_maps()[1]
|
|
1498
|
+
True
|
|
1499
|
+
|
|
1500
|
+
sage: F = GF(127); R.<x> = F[]
|
|
1501
|
+
sage: E = EllipticCurve(j=F(1728))
|
|
1502
|
+
sage: f = x^5 + 43*x^4 + 97*x^3 + 81*x^2 + 42*x + 82
|
|
1503
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
1504
|
+
sage: old_ratl_maps = phi.rational_maps()
|
|
1505
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
1506
|
+
sage: phi._set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(),
|
|
1507
|
+
....: (-13,13,-13,13)))
|
|
1508
|
+
sage: old_ratl_maps == phi.rational_maps()
|
|
1509
|
+
False
|
|
1510
|
+
sage: phi._EllipticCurveIsogeny__clear_cached_values()
|
|
1511
|
+
"""
|
|
1512
|
+
self.__ratl_maps = None
|
|
1513
|
+
self.__dual = None
|
|
1514
|
+
|
|
1515
|
+
def __perform_inheritance_housekeeping(self):
|
|
1516
|
+
r"""
|
|
1517
|
+
Internal helper function, sets values on the super classes of
|
|
1518
|
+
this class.
|
|
1519
|
+
|
|
1520
|
+
EXAMPLES:
|
|
1521
|
+
|
|
1522
|
+
The following examples will implicitly exercise this
|
|
1523
|
+
function::
|
|
1524
|
+
|
|
1525
|
+
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
|
|
1526
|
+
sage: R.<x> = GF(43)[]; f = x + 42
|
|
1527
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
1528
|
+
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
|
|
1529
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
1530
|
+
sage: E2 = phi.codomain()
|
|
1531
|
+
sage: post_isom = WeierstrassIsomorphism(E2, (41, 37, 31, 29))
|
|
1532
|
+
sage: phi._set_post_isomorphism(post_isom)
|
|
1533
|
+
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain()
|
|
1534
|
+
sage: pre_isom = E1pr.isomorphism_to(E)
|
|
1535
|
+
sage: phi._set_pre_isomorphism(pre_isom)
|
|
1536
|
+
"""
|
|
1537
|
+
EllipticCurveHom.__init__(self, self._domain, self._codomain)
|
|
1538
|
+
|
|
1539
|
+
def __init_algebraic_structs(self, E):
|
|
1540
|
+
r"""
|
|
1541
|
+
An internal function for EllipticCurveIsogeny objects that
|
|
1542
|
+
sets up the member variables necessary for algebra.
|
|
1543
|
+
|
|
1544
|
+
EXAMPLES::
|
|
1545
|
+
|
|
1546
|
+
sage: E = EllipticCurve(j=GF(17)(0))
|
|
1547
|
+
sage: phi = EllipticCurveIsogeny(E, E((-1,0)))
|
|
1548
|
+
|
|
1549
|
+
The constructor calls this function itself, so the fields it
|
|
1550
|
+
sets are already defined::
|
|
1551
|
+
|
|
1552
|
+
sage: phi._domain
|
|
1553
|
+
Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 17
|
|
1554
|
+
sage: phi._EllipticCurveIsogeny__base_field
|
|
1555
|
+
Finite Field of size 17
|
|
1556
|
+
sage: phi._EllipticCurveIsogeny__poly_ring
|
|
1557
|
+
Univariate Polynomial Ring in x over Finite Field of size 17
|
|
1558
|
+
sage: phi._EllipticCurveIsogeny__mpoly_ring
|
|
1559
|
+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
|
|
1560
|
+
|
|
1561
|
+
Now, calling the initialization function does nothing more::
|
|
1562
|
+
|
|
1563
|
+
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
|
|
1564
|
+
sage: phi._domain
|
|
1565
|
+
Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 17
|
|
1566
|
+
sage: phi._EllipticCurveIsogeny__base_field
|
|
1567
|
+
Finite Field of size 17
|
|
1568
|
+
sage: phi._EllipticCurveIsogeny__poly_ring
|
|
1569
|
+
Univariate Polynomial Ring in x over Finite Field of size 17
|
|
1570
|
+
sage: phi._EllipticCurveIsogeny__mpoly_ring
|
|
1571
|
+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
|
|
1572
|
+
|
|
1573
|
+
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
|
|
1574
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1575
|
+
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
|
|
1576
|
+
sage: phi._domain
|
|
1577
|
+
Elliptic Curve defined by y^2 = x^3 + x over Rational Field
|
|
1578
|
+
sage: phi._EllipticCurveIsogeny__base_field
|
|
1579
|
+
Rational Field
|
|
1580
|
+
sage: phi._EllipticCurveIsogeny__poly_ring
|
|
1581
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
1582
|
+
sage: phi._EllipticCurveIsogeny__mpoly_ring
|
|
1583
|
+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field
|
|
1584
|
+
|
|
1585
|
+
sage: F = GF(19); R.<x> = F[]
|
|
1586
|
+
sage: E = EllipticCurve(j=GF(19)(0))
|
|
1587
|
+
sage: phi = EllipticCurveIsogeny(E, x)
|
|
1588
|
+
sage: phi._EllipticCurveIsogeny__init_algebraic_structs(E)
|
|
1589
|
+
sage: phi._domain
|
|
1590
|
+
Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 19
|
|
1591
|
+
sage: phi._EllipticCurveIsogeny__base_field
|
|
1592
|
+
Finite Field of size 19
|
|
1593
|
+
sage: phi._EllipticCurveIsogeny__poly_ring
|
|
1594
|
+
Univariate Polynomial Ring in x over Finite Field of size 19
|
|
1595
|
+
sage: phi._EllipticCurveIsogeny__mpoly_ring
|
|
1596
|
+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 19
|
|
1597
|
+
"""
|
|
1598
|
+
self._domain = E
|
|
1599
|
+
self.__base_field = E.base_ring()
|
|
1600
|
+
self.__poly_ring = PolynomialRing(self.__base_field, ['x'])
|
|
1601
|
+
self.__mpoly_ring = PolynomialRing(self.__poly_ring, ['y'])
|
|
1602
|
+
# The fraction fields are implicitly part of the public API, being the parents
|
|
1603
|
+
# of the rational maps.
|
|
1604
|
+
self.__xfield = FractionField(self.__poly_ring)
|
|
1605
|
+
self.__xyfield = FractionField(PolynomialRing(self.__base_field, ['x', 'y']))
|
|
1606
|
+
|
|
1607
|
+
def __compute_codomain(self):
|
|
1608
|
+
r"""
|
|
1609
|
+
Private function that computes and sets the isogeny codomain.
|
|
1610
|
+
|
|
1611
|
+
EXAMPLES:
|
|
1612
|
+
|
|
1613
|
+
These examples inherently exercise this function::
|
|
1614
|
+
|
|
1615
|
+
sage: E = EllipticCurve(j=GF(7)(1728))
|
|
1616
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1617
|
+
sage: phi.codomain()
|
|
1618
|
+
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
|
|
1619
|
+
sage: phi._EllipticCurveIsogeny__compute_codomain()
|
|
1620
|
+
|
|
1621
|
+
sage: R.<x> = GF(7)[]
|
|
1622
|
+
sage: phi = EllipticCurveIsogeny(E, x)
|
|
1623
|
+
sage: phi.codomain()
|
|
1624
|
+
Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 7
|
|
1625
|
+
sage: phi._EllipticCurveIsogeny__compute_codomain()
|
|
1626
|
+
"""
|
|
1627
|
+
if self.__algorithm == 'velu':
|
|
1628
|
+
self._codomain = self.__compute_codomain_via_velu()
|
|
1629
|
+
elif self.__algorithm == 'kohel':
|
|
1630
|
+
self._codomain = self.__compute_codomain_via_kohel()
|
|
1631
|
+
else:
|
|
1632
|
+
raise NotImplementedError
|
|
1633
|
+
|
|
1634
|
+
def __initialize_rational_maps(self, precomputed_maps=None):
|
|
1635
|
+
r"""
|
|
1636
|
+
Private function that computes and initializes the rational
|
|
1637
|
+
maps.
|
|
1638
|
+
|
|
1639
|
+
INPUT:
|
|
1640
|
+
|
|
1641
|
+
- ``precomputed_maps`` -- (default: ``None``) tuple `(X,Y)`
|
|
1642
|
+
of rational functions in `x,y`
|
|
1643
|
+
|
|
1644
|
+
EXAMPLES:
|
|
1645
|
+
|
|
1646
|
+
The following examples inherently exercise this function::
|
|
1647
|
+
|
|
1648
|
+
sage: E = EllipticCurve(j=GF(7)(1728))
|
|
1649
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1650
|
+
sage: phi.rational_maps() # implicit doctest
|
|
1651
|
+
((x^2 + 1)/x, (x^2*y - y)/x^2)
|
|
1652
|
+
|
|
1653
|
+
sage: R.<x> = GF(7)[]
|
|
1654
|
+
sage: phi = EllipticCurveIsogeny(E, x)
|
|
1655
|
+
sage: phi.rational_maps() # implicit doctest
|
|
1656
|
+
((x^2 + 1)/x, (x^2*y - y)/x^2)
|
|
1657
|
+
|
|
1658
|
+
sage: E = EllipticCurve([1,2,3,4,5])
|
|
1659
|
+
sage: Eshort = E.short_weierstrass_model()
|
|
1660
|
+
sage: phi = E.isogeny(E(0), Eshort)
|
|
1661
|
+
sage: phiX, phiY = phi.rational_maps() # implicit doctest
|
|
1662
|
+
sage: phiX(1,2), phiY(1,2)
|
|
1663
|
+
(63, 864)
|
|
1664
|
+
"""
|
|
1665
|
+
if self.__ratl_maps is not None:
|
|
1666
|
+
return
|
|
1667
|
+
|
|
1668
|
+
if precomputed_maps is None:
|
|
1669
|
+
if self.__algorithm == 'velu':
|
|
1670
|
+
X_map, Y_map = self.__initialize_rational_maps_via_velu()
|
|
1671
|
+
elif self.__algorithm == 'kohel':
|
|
1672
|
+
X_map, Y_map = self.__initialize_rational_maps_via_kohel()
|
|
1673
|
+
else:
|
|
1674
|
+
raise NotImplementedError
|
|
1675
|
+
|
|
1676
|
+
else:
|
|
1677
|
+
X_map, Y_map = precomputed_maps
|
|
1678
|
+
# cannot coerce directly in xfield for some reason
|
|
1679
|
+
X_map = self.__poly_ring(X_map.numerator()) \
|
|
1680
|
+
/ self.__poly_ring(X_map.denominator())
|
|
1681
|
+
|
|
1682
|
+
if self.__prei_ratl_maps is not None:
|
|
1683
|
+
prei_X_map, prei_Y_map = self.__prei_ratl_maps
|
|
1684
|
+
X_map = X_map(prei_X_map)
|
|
1685
|
+
Y_map = Y_map([prei_X_map, prei_Y_map])
|
|
1686
|
+
|
|
1687
|
+
if self.__posti_ratl_maps is not None:
|
|
1688
|
+
posti_X_map, posti_Y_map = self.__posti_ratl_maps
|
|
1689
|
+
# Do not reverse the order here!
|
|
1690
|
+
Y_map = posti_Y_map([X_map, Y_map])
|
|
1691
|
+
X_map = posti_X_map(X_map)
|
|
1692
|
+
|
|
1693
|
+
self.__ratl_maps = self.__xfield(X_map), self.__xyfield(Y_map)
|
|
1694
|
+
|
|
1695
|
+
def __init_kernel_polynomial(self):
|
|
1696
|
+
r"""
|
|
1697
|
+
Private function that initializes the kernel polynomial (if
|
|
1698
|
+
the algorithm does not take it as a parameter).
|
|
1699
|
+
|
|
1700
|
+
EXAMPLES:
|
|
1701
|
+
|
|
1702
|
+
The following examples inherently exercise this function::
|
|
1703
|
+
|
|
1704
|
+
sage: E = EllipticCurve(j=GF(7)(1728))
|
|
1705
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1706
|
+
sage: phi.kernel_polynomial() # implicit doctest
|
|
1707
|
+
x
|
|
1708
|
+
"""
|
|
1709
|
+
if self.__kernel_polynomial is None:
|
|
1710
|
+
if self.__algorithm == 'velu':
|
|
1711
|
+
self.__init_kernel_polynomial_velu()
|
|
1712
|
+
else:
|
|
1713
|
+
assert False, "the kernel polynomial should already be defined!"
|
|
1714
|
+
|
|
1715
|
+
def __set_pre_isomorphism(self, domain, isomorphism):
|
|
1716
|
+
r"""
|
|
1717
|
+
Private function to set the pre-isomorphism and domain (and
|
|
1718
|
+
keep track of the domain of the isogeny).
|
|
1719
|
+
|
|
1720
|
+
EXAMPLES::
|
|
1721
|
+
|
|
1722
|
+
sage: E = EllipticCurve(GF(43), [2,3,5,7,11])
|
|
1723
|
+
sage: R.<x> = GF(43)[]; f = x + 42
|
|
1724
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
1725
|
+
sage: phi._EllipticCurveIsogeny__perform_inheritance_housekeeping()
|
|
1726
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
1727
|
+
sage: E1pr = WeierstrassIsomorphism(E, (-1, 2, -3, 4)).codomain()
|
|
1728
|
+
sage: pre_isom = E1pr.isomorphism_to(E)
|
|
1729
|
+
sage: phi._set_pre_isomorphism(pre_isom)
|
|
1730
|
+
sage: phi._EllipticCurveIsogeny__set_pre_isomorphism(E, WeierstrassIsomorphism(E, (-1, 3, -3, 4)))
|
|
1731
|
+
sage: E == phi.domain()
|
|
1732
|
+
True
|
|
1733
|
+
"""
|
|
1734
|
+
self._domain = domain
|
|
1735
|
+
self.__pre_isomorphism = isomorphism
|
|
1736
|
+
|
|
1737
|
+
# calculate the isomorphism as a rational map.
|
|
1738
|
+
|
|
1739
|
+
u, r, s, t = (self.__base_field(c) for c in isomorphism.tuple())
|
|
1740
|
+
uinv = 1/u
|
|
1741
|
+
uinv2 = uinv**2
|
|
1742
|
+
uinv3 = uinv*uinv2
|
|
1743
|
+
|
|
1744
|
+
x = self.__poly_ring.gen()
|
|
1745
|
+
y = self.__xyfield.gen(1) # not mpoly_ring.gen(1) else we end
|
|
1746
|
+
# up in K(x)[y] and trouble ensues
|
|
1747
|
+
|
|
1748
|
+
self.__prei_ratl_maps = (x - r) * uinv2, (y - s*(x-r) - t) * uinv3
|
|
1749
|
+
|
|
1750
|
+
if self.__kernel_polynomial is not None:
|
|
1751
|
+
ker_poly = self.__kernel_polynomial
|
|
1752
|
+
ker_poly = ker_poly(self.__prei_ratl_maps[0])
|
|
1753
|
+
self.__kernel_polynomial = ker_poly.monic()
|
|
1754
|
+
|
|
1755
|
+
self.__perform_inheritance_housekeeping()
|
|
1756
|
+
|
|
1757
|
+
def __set_post_isomorphism(self, codomain, isomorphism):
|
|
1758
|
+
r"""
|
|
1759
|
+
Private function to set the post-isomorphism and codomain (and
|
|
1760
|
+
keep track of the codomain of the isogeny).
|
|
1761
|
+
|
|
1762
|
+
EXAMPLES:
|
|
1763
|
+
|
|
1764
|
+
The following examples inherently exercise this function::
|
|
1765
|
+
|
|
1766
|
+
sage: E = EllipticCurve(j=GF(7)(1728))
|
|
1767
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
1768
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
1769
|
+
sage: E2 = phi.codomain()
|
|
1770
|
+
sage: isom = WeierstrassIsomorphism(E2, (-1,2,-3,4))
|
|
1771
|
+
sage: phi._set_post_isomorphism(isom)
|
|
1772
|
+
sage: phi._EllipticCurveIsogeny__set_post_isomorphism(E2, WeierstrassIsomorphism(phi.codomain(), (1,-2,3,-4)))
|
|
1773
|
+
sage: E2 == phi.codomain()
|
|
1774
|
+
True
|
|
1775
|
+
"""
|
|
1776
|
+
self._codomain = codomain
|
|
1777
|
+
self.__post_isomorphism = isomorphism
|
|
1778
|
+
|
|
1779
|
+
# calculate the isomorphism as a rational map.
|
|
1780
|
+
|
|
1781
|
+
u, r, s, t = (self.__base_field(c) for c in isomorphism.tuple())
|
|
1782
|
+
uinv = 1/u
|
|
1783
|
+
uinv2 = uinv**2
|
|
1784
|
+
uinv3 = uinv*uinv2
|
|
1785
|
+
|
|
1786
|
+
x = self.__poly_ring.gen()
|
|
1787
|
+
y = self.__xyfield.gen(1)
|
|
1788
|
+
|
|
1789
|
+
self.__posti_ratl_maps = (x - r) * uinv2, (y - s*(x-r) - t) * uinv3
|
|
1790
|
+
|
|
1791
|
+
self.__perform_inheritance_housekeeping()
|
|
1792
|
+
|
|
1793
|
+
def __setup_post_isomorphism(self, codomain, model):
|
|
1794
|
+
r"""
|
|
1795
|
+
Private function to set up the post-isomorphism given the
|
|
1796
|
+
codomain.
|
|
1797
|
+
|
|
1798
|
+
EXAMPLES:
|
|
1799
|
+
|
|
1800
|
+
The following examples inherently exercise this function::
|
|
1801
|
+
|
|
1802
|
+
sage: E = EllipticCurve(j=GF(7)(1728))
|
|
1803
|
+
sage: E2 = EllipticCurve(GF(7), [0,0,0,5,0])
|
|
1804
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)), E2); phi
|
|
1805
|
+
Isogeny of degree 2
|
|
1806
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7
|
|
1807
|
+
to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 7
|
|
1808
|
+
sage: E3 = EllipticCurve(GF(7), [0,0,0,6,0])
|
|
1809
|
+
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(E3, None)
|
|
1810
|
+
sage: phi
|
|
1811
|
+
Isogeny of degree 2
|
|
1812
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7
|
|
1813
|
+
to Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
1814
|
+
|
|
1815
|
+
sage: EllipticCurveIsogeny(E, E(0,0), model='montgomery')
|
|
1816
|
+
Isogeny of degree 2
|
|
1817
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7
|
|
1818
|
+
to Elliptic Curve defined by y^2 = x^3 + x^2 + x over Finite Field of size 7
|
|
1819
|
+
|
|
1820
|
+
sage: R.<x> = QQ[]
|
|
1821
|
+
sage: E = EllipticCurve(j=1728)
|
|
1822
|
+
sage: f = x^3 - x
|
|
1823
|
+
sage: phi = EllipticCurveIsogeny(E, f, model='minimal'); phi
|
|
1824
|
+
Isogeny of degree 4
|
|
1825
|
+
from Elliptic Curve defined by y^2 = x^3 - x over Rational Field
|
|
1826
|
+
to Elliptic Curve defined by y^2 = x^3 - x over Rational Field
|
|
1827
|
+
|
|
1828
|
+
sage: phi = EllipticCurveIsogeny(E, f, model=None)
|
|
1829
|
+
sage: phi._EllipticCurveIsogeny__setup_post_isomorphism(None, 'minimal')
|
|
1830
|
+
sage: phi
|
|
1831
|
+
Isogeny of degree 4
|
|
1832
|
+
from Elliptic Curve defined by y^2 = x^3 - x over Rational Field
|
|
1833
|
+
to Elliptic Curve defined by y^2 = x^3 - x over Rational Field
|
|
1834
|
+
"""
|
|
1835
|
+
if model is codomain is None:
|
|
1836
|
+
return
|
|
1837
|
+
|
|
1838
|
+
oldE2 = self._codomain
|
|
1839
|
+
|
|
1840
|
+
if model is not None:
|
|
1841
|
+
if codomain is not None:
|
|
1842
|
+
raise ValueError("cannot specify a codomain curve and model name simultaneously")
|
|
1843
|
+
|
|
1844
|
+
from sage.schemes.elliptic_curves.ell_field import compute_model
|
|
1845
|
+
codomain = compute_model(oldE2, model)
|
|
1846
|
+
|
|
1847
|
+
else: # codomain is not None
|
|
1848
|
+
if not isinstance(codomain, EllipticCurve_generic):
|
|
1849
|
+
raise ValueError("given codomain is not an elliptic curve")
|
|
1850
|
+
|
|
1851
|
+
if not oldE2.is_isomorphic(codomain):
|
|
1852
|
+
raise ValueError("given codomain is not isomorphic to the computed codomain")
|
|
1853
|
+
|
|
1854
|
+
post_isom = oldE2.isomorphism_to(codomain)
|
|
1855
|
+
self.__set_post_isomorphism(codomain, post_isom)
|
|
1856
|
+
|
|
1857
|
+
###########################
|
|
1858
|
+
# Velu's Formula Functions
|
|
1859
|
+
###########################
|
|
1860
|
+
|
|
1861
|
+
#
|
|
1862
|
+
# Setup function for Velu's formula
|
|
1863
|
+
#
|
|
1864
|
+
|
|
1865
|
+
def __init_from_kernel_gens(self, kernel_gens):
|
|
1866
|
+
r"""
|
|
1867
|
+
Private function that initializes the isogeny from a list of
|
|
1868
|
+
points which generate the kernel (For Vélu's formulas.)
|
|
1869
|
+
|
|
1870
|
+
EXAMPLES:
|
|
1871
|
+
|
|
1872
|
+
The following example inherently exercises this function::
|
|
1873
|
+
|
|
1874
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
1875
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0))); phi
|
|
1876
|
+
Isogeny of degree 2
|
|
1877
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
1878
|
+
to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
|
|
1879
|
+
sage: phi._EllipticCurveIsogeny__init_from_kernel_gens([E(0), E((0,0))])
|
|
1880
|
+
|
|
1881
|
+
The following example demonstrates the necessity of avoiding any calls
|
|
1882
|
+
to P.order(), since such calls involve factoring the group order which
|
|
1883
|
+
could take a long time. ::
|
|
1884
|
+
|
|
1885
|
+
sage: # needs sage.rings.finite_rings
|
|
1886
|
+
sage: p = 12 * next_prime(2^180) * next_prime(2^194) - 1
|
|
1887
|
+
sage: F = FiniteField(p, proof=False)
|
|
1888
|
+
sage: E = EllipticCurve([F(1), F(0)])
|
|
1889
|
+
sage: P = E(0).division_points(3)[1]
|
|
1890
|
+
sage: EllipticCurveIsogeny(E, P)
|
|
1891
|
+
Isogeny of degree 3
|
|
1892
|
+
from Elliptic Curve defined by y^2 = x^3 + x
|
|
1893
|
+
over Finite Field of size 461742260113997803268895001173557974278278194575766957660028841364655249961609425998827452443620996655395008156411
|
|
1894
|
+
to Elliptic Curve defined by y^2 = x^3 + 80816485163488178037199320944019099858815874115367810482828676054000067654558381377552245721755005198633191074893*x + 301497584865165444049833326660609767433467459033532853758006118022998267706948164646650354324860226263546558337993
|
|
1895
|
+
over Finite Field of size 461742260113997803268895001173557974278278194575766957660028841364655249961609425998827452443620996655395008156411
|
|
1896
|
+
"""
|
|
1897
|
+
if self.__check:
|
|
1898
|
+
for P in kernel_gens:
|
|
1899
|
+
if not P.has_finite_order():
|
|
1900
|
+
raise ValueError("given kernel contains point of infinite order")
|
|
1901
|
+
|
|
1902
|
+
self.__kernel_mod_sign = {}
|
|
1903
|
+
self.__v = self.__w = 0
|
|
1904
|
+
|
|
1905
|
+
# Fast path: The kernel is given by a single generating point.
|
|
1906
|
+
if len(kernel_gens) == 1 and kernel_gens[0]:
|
|
1907
|
+
self.__init_from_kernel_point(kernel_gens[0])
|
|
1908
|
+
return
|
|
1909
|
+
|
|
1910
|
+
# Compute a list of points in the subgroup generated by the
|
|
1911
|
+
# points in kernel_gens. This is very naive: when finite
|
|
1912
|
+
# subgroups are implemented better, this could be simplified,
|
|
1913
|
+
# but it won't speed things up too much.
|
|
1914
|
+
|
|
1915
|
+
def all_multiples(itr, terminal):
|
|
1916
|
+
mult_list = [terminal]
|
|
1917
|
+
R = terminal + itr
|
|
1918
|
+
while R != terminal:
|
|
1919
|
+
mult_list.append(R)
|
|
1920
|
+
R += itr
|
|
1921
|
+
return mult_list
|
|
1922
|
+
|
|
1923
|
+
kernel_set = {self._domain(0)}
|
|
1924
|
+
for P in kernel_gens:
|
|
1925
|
+
kernel_set.update(R for Q in tuple(kernel_set)
|
|
1926
|
+
for R in all_multiples(P,Q))
|
|
1927
|
+
|
|
1928
|
+
self._degree = Integer(len(kernel_set))
|
|
1929
|
+
self.__kernel_list = list(kernel_set)
|
|
1930
|
+
self.__init_from_kernel_list()
|
|
1931
|
+
|
|
1932
|
+
#
|
|
1933
|
+
# Precompute the values in Velu's Formula.
|
|
1934
|
+
#
|
|
1935
|
+
def __update_kernel_data(self, xQ, yQ):
|
|
1936
|
+
r"""
|
|
1937
|
+
Internal helper function to update some data coming from the
|
|
1938
|
+
kernel points of this isogeny when using Vélu's formulas.
|
|
1939
|
+
|
|
1940
|
+
TESTS:
|
|
1941
|
+
|
|
1942
|
+
The following example inherently exercises this function::
|
|
1943
|
+
|
|
1944
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
1945
|
+
sage: P = E((4,2))
|
|
1946
|
+
sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest
|
|
1947
|
+
Isogeny of degree 4
|
|
1948
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
1949
|
+
to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
1950
|
+
"""
|
|
1951
|
+
a1, a2, a3, a4, _ = self._domain.a_invariants()
|
|
1952
|
+
|
|
1953
|
+
gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ
|
|
1954
|
+
gyQ = -2*yQ - a1*xQ - a3
|
|
1955
|
+
|
|
1956
|
+
uQ = gyQ**2
|
|
1957
|
+
|
|
1958
|
+
if 2*yQ == -a1*xQ - a3: # Q is 2-torsion
|
|
1959
|
+
vQ = gxQ
|
|
1960
|
+
else: # Q is not 2-torsion
|
|
1961
|
+
vQ = 2*gxQ - a1*gyQ
|
|
1962
|
+
|
|
1963
|
+
self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ
|
|
1964
|
+
|
|
1965
|
+
self.__v += vQ
|
|
1966
|
+
self.__w += uQ + xQ*vQ
|
|
1967
|
+
|
|
1968
|
+
def __init_from_kernel_point(self, ker):
|
|
1969
|
+
r"""
|
|
1970
|
+
Private function with functionality equivalent to
|
|
1971
|
+
:meth:`__init_from_kernel_list`, but optimized for when
|
|
1972
|
+
the kernel is given by a single point.
|
|
1973
|
+
|
|
1974
|
+
TESTS:
|
|
1975
|
+
|
|
1976
|
+
The following example inherently exercises this function::
|
|
1977
|
+
|
|
1978
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
1979
|
+
sage: P = E((4,2))
|
|
1980
|
+
sage: phi = EllipticCurveIsogeny(E, P); phi # implicit doctest
|
|
1981
|
+
Isogeny of degree 4
|
|
1982
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
1983
|
+
to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
1984
|
+
|
|
1985
|
+
We check that the result is the same as for :meth:`__init_from_kernel_list`::
|
|
1986
|
+
|
|
1987
|
+
sage: psi = EllipticCurveIsogeny(E, [P, P]); psi
|
|
1988
|
+
Isogeny of degree 4
|
|
1989
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
1990
|
+
to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
1991
|
+
sage: phi == psi
|
|
1992
|
+
True
|
|
1993
|
+
"""
|
|
1994
|
+
self._degree = Integer(1)
|
|
1995
|
+
|
|
1996
|
+
Q, prevQ = ker, self._domain(0)
|
|
1997
|
+
|
|
1998
|
+
while Q and Q != -prevQ:
|
|
1999
|
+
self.__update_kernel_data(*Q.xy())
|
|
2000
|
+
|
|
2001
|
+
if Q == -Q:
|
|
2002
|
+
self._degree += 1
|
|
2003
|
+
break
|
|
2004
|
+
|
|
2005
|
+
prevQ = Q
|
|
2006
|
+
Q += ker
|
|
2007
|
+
self._degree += 2
|
|
2008
|
+
|
|
2009
|
+
def __init_from_kernel_list(self):
|
|
2010
|
+
r"""
|
|
2011
|
+
Private function that sorts the list of points in the kernel
|
|
2012
|
+
(For Vélu's formulas). Sorts out the 2-torsion points, and
|
|
2013
|
+
puts them in a dictionary.
|
|
2014
|
+
|
|
2015
|
+
EXAMPLES:
|
|
2016
|
+
|
|
2017
|
+
The following example inherently exercises this function::
|
|
2018
|
+
|
|
2019
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2020
|
+
sage: P = E((4,2))
|
|
2021
|
+
sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest
|
|
2022
|
+
Isogeny of degree 4
|
|
2023
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
2024
|
+
to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
2025
|
+
"""
|
|
2026
|
+
for Q in self.__kernel_list:
|
|
2027
|
+
|
|
2028
|
+
if Q.is_zero():
|
|
2029
|
+
continue
|
|
2030
|
+
|
|
2031
|
+
xQ, yQ = Q.xy()
|
|
2032
|
+
|
|
2033
|
+
if xQ in self.__kernel_mod_sign:
|
|
2034
|
+
continue
|
|
2035
|
+
|
|
2036
|
+
self.__update_kernel_data(xQ, yQ)
|
|
2037
|
+
|
|
2038
|
+
#
|
|
2039
|
+
# Velu's formula computing the codomain curve
|
|
2040
|
+
#
|
|
2041
|
+
def __compute_codomain_via_velu(self):
|
|
2042
|
+
r"""
|
|
2043
|
+
Private function that computes the codomain via Vélu's
|
|
2044
|
+
formulas.
|
|
2045
|
+
|
|
2046
|
+
EXAMPLES:
|
|
2047
|
+
|
|
2048
|
+
The following example inherently exercises this function::
|
|
2049
|
+
|
|
2050
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2051
|
+
sage: P = E((4,2))
|
|
2052
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2053
|
+
sage: phi.codomain()
|
|
2054
|
+
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
2055
|
+
sage: phi._EllipticCurveIsogeny__compute_codomain_via_velu()
|
|
2056
|
+
Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7
|
|
2057
|
+
"""
|
|
2058
|
+
return compute_codomain_formula(self._domain, self.__v, self.__w)
|
|
2059
|
+
|
|
2060
|
+
@staticmethod
|
|
2061
|
+
def __velu_sum_helper(xQ, Qvalues, a1, a3, x, y):
|
|
2062
|
+
r"""
|
|
2063
|
+
Private function for Vélu's formulas, helper function to help
|
|
2064
|
+
perform the summation.
|
|
2065
|
+
|
|
2066
|
+
EXAMPLES:
|
|
2067
|
+
|
|
2068
|
+
The following example inherently exercises this function::
|
|
2069
|
+
|
|
2070
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2071
|
+
sage: P = E((4,2))
|
|
2072
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2073
|
+
sage: Q = E((0,0)); phi(Q)
|
|
2074
|
+
(0 : 0 : 1)
|
|
2075
|
+
sage: phi.rational_maps()
|
|
2076
|
+
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2), (x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
|
|
2077
|
+
|
|
2078
|
+
sage: F = GF(7)
|
|
2079
|
+
sage: E = EllipticCurve(F, [0,0,0,1,0])
|
|
2080
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)) )
|
|
2081
|
+
sage: Qvals = phi._EllipticCurveIsogeny__kernel_mod_sign[0]
|
|
2082
|
+
sage: phi._EllipticCurveIsogeny__velu_sum_helper(0, Qvals, 0, 0, F(5), F(5))
|
|
2083
|
+
(3, 3)
|
|
2084
|
+
sage: R.<x,y> = GF(7)[]
|
|
2085
|
+
sage: phi._EllipticCurveIsogeny__velu_sum_helper(0, Qvals, 0, 0, x, y)
|
|
2086
|
+
(1/x, y/x^2)
|
|
2087
|
+
"""
|
|
2088
|
+
yQ, gxQ, gyQ, vQ, uQ = Qvalues
|
|
2089
|
+
|
|
2090
|
+
t1 = x - xQ
|
|
2091
|
+
inv_t1 = t1**-1
|
|
2092
|
+
inv_t1_2 = inv_t1**2
|
|
2093
|
+
inv_t1_3 = inv_t1_2*inv_t1
|
|
2094
|
+
|
|
2095
|
+
tX = vQ*inv_t1 + uQ*(inv_t1_2)
|
|
2096
|
+
|
|
2097
|
+
tY0 = uQ*(2*y + a1*x + a3)
|
|
2098
|
+
tY1 = vQ*(a1*t1 + y - yQ)
|
|
2099
|
+
tY2 = a1*uQ - gxQ*gyQ
|
|
2100
|
+
|
|
2101
|
+
# Without this explicit coercion, tY ends up in K(x)[y]
|
|
2102
|
+
# instead of K(x,y), and trouble ensues!
|
|
2103
|
+
F = FractionField(y.parent())
|
|
2104
|
+
tY = tY0*F(inv_t1_3) + (tY1 + tY2)*F(inv_t1_2)
|
|
2105
|
+
|
|
2106
|
+
return tX, tY
|
|
2107
|
+
|
|
2108
|
+
def __compute_via_velu_numeric(self, xP, yP):
|
|
2109
|
+
r"""
|
|
2110
|
+
Private function that sorts the list of points in the kernel
|
|
2111
|
+
(for Vélu's formulas). Sorts out the 2-torsion points, and
|
|
2112
|
+
puts them in a dictionary.
|
|
2113
|
+
|
|
2114
|
+
EXAMPLES:
|
|
2115
|
+
|
|
2116
|
+
The following example inherently exercises this function::
|
|
2117
|
+
|
|
2118
|
+
sage: F = GF(7)
|
|
2119
|
+
sage: E = EllipticCurve(F, [0,0,0,-1,0])
|
|
2120
|
+
sage: P = E((4,2))
|
|
2121
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2122
|
+
sage: Q = E((0,0)); phi(Q)
|
|
2123
|
+
(0 : 0 : 1)
|
|
2124
|
+
sage: Q = E((-1,0)); phi(Q)
|
|
2125
|
+
(0 : 0 : 1)
|
|
2126
|
+
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(F(0), F(0))
|
|
2127
|
+
(0, 0)
|
|
2128
|
+
sage: phi._EllipticCurveIsogeny__compute_via_velu_numeric(F(-1), F(0))
|
|
2129
|
+
(0, 0)
|
|
2130
|
+
"""
|
|
2131
|
+
# first check if the point is in the kernel
|
|
2132
|
+
if xP in self.__kernel_mod_sign:
|
|
2133
|
+
return ()
|
|
2134
|
+
|
|
2135
|
+
return self.__compute_via_velu(xP,yP)
|
|
2136
|
+
|
|
2137
|
+
def __compute_via_velu(self, xP, yP):
|
|
2138
|
+
r"""
|
|
2139
|
+
Private function for Vélu's formulas, to perform the summation.
|
|
2140
|
+
|
|
2141
|
+
EXAMPLES:
|
|
2142
|
+
|
|
2143
|
+
The following example inherently exercises this function::
|
|
2144
|
+
|
|
2145
|
+
sage: F = GF(7)
|
|
2146
|
+
sage: E = EllipticCurve(F, [0,0,0,-1,0])
|
|
2147
|
+
sage: P = E((4,2))
|
|
2148
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2149
|
+
sage: Q = E((0,0)); phi(Q)
|
|
2150
|
+
(0 : 0 : 1)
|
|
2151
|
+
sage: phi.rational_maps()
|
|
2152
|
+
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2), (x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
|
|
2153
|
+
sage: phi._EllipticCurveIsogeny__compute_via_velu(F(0), F(0))
|
|
2154
|
+
(0, 0)
|
|
2155
|
+
sage: R.<x,y> = GF(7)[]
|
|
2156
|
+
sage: phi._EllipticCurveIsogeny__compute_via_velu(x, y)
|
|
2157
|
+
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2),
|
|
2158
|
+
(x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
|
|
2159
|
+
|
|
2160
|
+
TESTS:
|
|
2161
|
+
|
|
2162
|
+
Check for :issue:`33214`::
|
|
2163
|
+
|
|
2164
|
+
sage: # needs sage.rings.finite_rings
|
|
2165
|
+
sage: z2 = GF(71^2).gen()
|
|
2166
|
+
sage: E = EllipticCurve(GF(71^2), [5,5])
|
|
2167
|
+
sage: phi = E.isogeny(E.lift_x(0))
|
|
2168
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
2169
|
+
sage: pre = WeierstrassIsomorphism(None, (z2,7,8,9), E)
|
|
2170
|
+
sage: phi = phi * pre
|
|
2171
|
+
sage: P = phi.domain()(1, 46*z2+49)
|
|
2172
|
+
sage: phi(P) # indirect doctest
|
|
2173
|
+
(33 : 61*z2 + 10 : 1)
|
|
2174
|
+
|
|
2175
|
+
The rational maps are also computed via this code path; check
|
|
2176
|
+
that they are plausible (this failed prior to :issue:`33214`)::
|
|
2177
|
+
|
|
2178
|
+
sage: # needs sage.rings.finite_rings
|
|
2179
|
+
sage: fx,fy = phi.rational_maps() # indirect doctest
|
|
2180
|
+
sage: R.<x,y> = GF(71^2)[]
|
|
2181
|
+
sage: E0, E2 = phi.domain(), phi.codomain()
|
|
2182
|
+
sage: eqs = [EE.defining_polynomial()(x,y,1) for EE in (E0,E2)]
|
|
2183
|
+
sage: eqs[1](fx,fy).numerator() % eqs[0]
|
|
2184
|
+
0
|
|
2185
|
+
"""
|
|
2186
|
+
if self.__pre_isomorphism is None:
|
|
2187
|
+
E = self._domain
|
|
2188
|
+
else:
|
|
2189
|
+
E = self.__pre_isomorphism.codomain()
|
|
2190
|
+
|
|
2191
|
+
a1 = E.a1()
|
|
2192
|
+
a3 = E.a3()
|
|
2193
|
+
|
|
2194
|
+
X = xP
|
|
2195
|
+
Y = yP
|
|
2196
|
+
|
|
2197
|
+
for xQ, Qvalues in self.__kernel_mod_sign.items():
|
|
2198
|
+
tX, tY = self.__velu_sum_helper(xQ, Qvalues, a1, a3, xP, yP)
|
|
2199
|
+
X += tX
|
|
2200
|
+
Y -= tY
|
|
2201
|
+
|
|
2202
|
+
return X, Y
|
|
2203
|
+
|
|
2204
|
+
def __initialize_rational_maps_via_velu(self):
|
|
2205
|
+
r"""
|
|
2206
|
+
Private function for Vélu's formulas, helper function to
|
|
2207
|
+
initialize the rational maps.
|
|
2208
|
+
|
|
2209
|
+
EXAMPLES:
|
|
2210
|
+
|
|
2211
|
+
The following example inherently exercises this function::
|
|
2212
|
+
|
|
2213
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2214
|
+
sage: P = E((4,2))
|
|
2215
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2216
|
+
sage: phi.rational_maps()
|
|
2217
|
+
((x^4 - 2*x^3 + x^2 - 3*x)/(x^3 - 2*x^2 + 3*x - 2), (x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
|
|
2218
|
+
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_velu()
|
|
2219
|
+
((x^4 + 5*x^3 + x^2 + 4*x)/(x^3 + 5*x^2 + 3*x + 5), (x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
|
|
2220
|
+
"""
|
|
2221
|
+
x = self.__poly_ring.gen()
|
|
2222
|
+
y = self.__xyfield.gen(1)
|
|
2223
|
+
return self.__compute_via_velu(x,y)
|
|
2224
|
+
|
|
2225
|
+
def __init_kernel_polynomial_velu(self):
|
|
2226
|
+
r"""
|
|
2227
|
+
Private function for Vélu's formulas, helper function to
|
|
2228
|
+
initialize the rational maps.
|
|
2229
|
+
|
|
2230
|
+
EXAMPLES:
|
|
2231
|
+
|
|
2232
|
+
The following example inherently exercises this function::
|
|
2233
|
+
|
|
2234
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2235
|
+
sage: P = E((4,2))
|
|
2236
|
+
sage: phi = EllipticCurveIsogeny(E, P)
|
|
2237
|
+
sage: phi.kernel_polynomial() # implicit doctest
|
|
2238
|
+
x^2 + 2*x + 4
|
|
2239
|
+
"""
|
|
2240
|
+
poly_ring, x = self.__poly_ring.objgen()
|
|
2241
|
+
|
|
2242
|
+
if self.__pre_isomorphism is not None:
|
|
2243
|
+
pre_isom = self.__pre_isomorphism
|
|
2244
|
+
invX = pre_isom.u**2 * x + pre_isom.r
|
|
2245
|
+
else:
|
|
2246
|
+
invX = x
|
|
2247
|
+
|
|
2248
|
+
psi = poly_ring.one()
|
|
2249
|
+
for xQ in self.__kernel_mod_sign.keys():
|
|
2250
|
+
psi *= x - invX(xQ)
|
|
2251
|
+
|
|
2252
|
+
self.__kernel_polynomial = psi
|
|
2253
|
+
|
|
2254
|
+
###################################
|
|
2255
|
+
# Kohel's Variant of Velu's Formula
|
|
2256
|
+
###################################
|
|
2257
|
+
|
|
2258
|
+
def __init_from_kernel_polynomial(self, kernel_polynomial):
|
|
2259
|
+
r"""
|
|
2260
|
+
Private function that initializes the isogeny from a kernel
|
|
2261
|
+
polynomial.
|
|
2262
|
+
|
|
2263
|
+
EXAMPLES:
|
|
2264
|
+
|
|
2265
|
+
These examples inherently exercise this private function::
|
|
2266
|
+
|
|
2267
|
+
sage: R.<x> = GF(7)[]
|
|
2268
|
+
sage: E = EllipticCurve(GF(7), [0,0,0,-1,0])
|
|
2269
|
+
sage: phi = EllipticCurveIsogeny(E, x);phi
|
|
2270
|
+
Isogeny of degree 2
|
|
2271
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
2272
|
+
to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
|
|
2273
|
+
|
|
2274
|
+
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x)
|
|
2275
|
+
|
|
2276
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2277
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
|
|
2278
|
+
Isogeny of degree 3
|
|
2279
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
|
|
2280
|
+
to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
|
|
2281
|
+
|
|
2282
|
+
sage: phi._EllipticCurveIsogeny__init_from_kernel_polynomial(x+6)
|
|
2283
|
+
"""
|
|
2284
|
+
poly_ring = self.__poly_ring
|
|
2285
|
+
E = self._domain
|
|
2286
|
+
|
|
2287
|
+
# Convert to a univariate polynomial, even if it had a
|
|
2288
|
+
# bivariate parent, or was given as a list:
|
|
2289
|
+
psi = poly_ring(kernel_polynomial)
|
|
2290
|
+
|
|
2291
|
+
self.__kernel_polynomial = psi
|
|
2292
|
+
|
|
2293
|
+
if psi.leading_coefficient() != 1:
|
|
2294
|
+
raise ValueError("given kernel polynomial is not monic")
|
|
2295
|
+
|
|
2296
|
+
#
|
|
2297
|
+
# Determine if kernel polynomial is entirely 2-torsion
|
|
2298
|
+
#
|
|
2299
|
+
psi_G = two_torsion_part(E, psi).monic()
|
|
2300
|
+
|
|
2301
|
+
if psi_G.degree() != 0: # even degree case
|
|
2302
|
+
|
|
2303
|
+
psi_quo = psi//psi_G
|
|
2304
|
+
|
|
2305
|
+
if psi_quo.degree() != 0:
|
|
2306
|
+
raise NotImplementedError("Kohel's algorithm currently only supports cyclic isogenies (except for [2])")
|
|
2307
|
+
|
|
2308
|
+
phi, omega, v, w, _, d = self.__init_even_kernel_polynomial(E, psi_G)
|
|
2309
|
+
|
|
2310
|
+
else: # odd degree case
|
|
2311
|
+
|
|
2312
|
+
phi, omega, v, w, _, d = self.__init_odd_kernel_polynomial(E, psi)
|
|
2313
|
+
|
|
2314
|
+
#
|
|
2315
|
+
# Set up the necessary instance variables
|
|
2316
|
+
#
|
|
2317
|
+
|
|
2318
|
+
self.__kernel_polynomial = psi
|
|
2319
|
+
self.__inner_kernel_polynomial = psi
|
|
2320
|
+
|
|
2321
|
+
self._degree = Integer(d) # degree of the isogeny
|
|
2322
|
+
|
|
2323
|
+
# As a rational map, the isogeny maps (x,y) to (X,Y), where
|
|
2324
|
+
# X=phi(x)/psi(x)^2 and Y=omega(x,y)/psi(x)^3. Both phi and
|
|
2325
|
+
# psi are univariate polynomials in x, while omega is a
|
|
2326
|
+
# bivariate polynomial in x, y. The names are compatible so
|
|
2327
|
+
# that univariate polynomials automatically coerce into the
|
|
2328
|
+
# bivariate polynomial ring.
|
|
2329
|
+
|
|
2330
|
+
self.__psi = psi
|
|
2331
|
+
self.__phi = phi
|
|
2332
|
+
self.__omega = omega
|
|
2333
|
+
|
|
2334
|
+
self.__v = v
|
|
2335
|
+
self.__w = w
|
|
2336
|
+
|
|
2337
|
+
def __init_even_kernel_polynomial(self, E, psi_G):
|
|
2338
|
+
r"""
|
|
2339
|
+
Return the isogeny parameters for the 2-part of an isogeny.
|
|
2340
|
+
|
|
2341
|
+
INPUT:
|
|
2342
|
+
|
|
2343
|
+
- ``E`` -- an elliptic curve
|
|
2344
|
+
|
|
2345
|
+
- ``psi_G`` -- a univariate polynomial over the base field of
|
|
2346
|
+
``E`` of degree 1 or 3 dividing its 2-division polynomial
|
|
2347
|
+
|
|
2348
|
+
OUTPUT:
|
|
2349
|
+
|
|
2350
|
+
A tuple (``phi``, ``omega``, ``v``, ``w``, ``n``, ``d``) where:
|
|
2351
|
+
|
|
2352
|
+
- ``phi`` is a univariate polynomial, the numerator of the
|
|
2353
|
+
`X`-coordinate of the isogeny;
|
|
2354
|
+
|
|
2355
|
+
- ``omega`` is a bivariate polynomial, the numerator of the
|
|
2356
|
+
`Y`-coordinate of the isogeny;
|
|
2357
|
+
|
|
2358
|
+
- ``v``, ``w`` are the Vélu parameters of the isogeny;
|
|
2359
|
+
|
|
2360
|
+
- ``n`` is the degree of ``psi``;
|
|
2361
|
+
|
|
2362
|
+
- ``d`` is the degree of the isogeny.
|
|
2363
|
+
|
|
2364
|
+
EXAMPLES:
|
|
2365
|
+
|
|
2366
|
+
These examples inherently exercise this private function::
|
|
2367
|
+
|
|
2368
|
+
sage: R.<x> = GF(7)[]
|
|
2369
|
+
sage: E = EllipticCurve(GF(7), [-1,0])
|
|
2370
|
+
sage: phi = EllipticCurveIsogeny(E, x); phi
|
|
2371
|
+
Isogeny of degree 2
|
|
2372
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7
|
|
2373
|
+
to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7
|
|
2374
|
+
|
|
2375
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
|
|
2376
|
+
sage: psig = two_torsion_part(E,x)
|
|
2377
|
+
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
|
|
2378
|
+
(x^3 + 6*x, (x^3 + x)*y, 6, 0, 1, 2)
|
|
2379
|
+
|
|
2380
|
+
sage: F = GF(2^4, 'alpha'); R.<x> = F[] # needs sage.rings.finite_rings
|
|
2381
|
+
sage: E = EllipticCurve(F, [1,1,0,1,0]) # needs sage.rings.finite_rings
|
|
2382
|
+
sage: phi = EllipticCurveIsogeny(E, x); phi # needs sage.rings.finite_rings
|
|
2383
|
+
Isogeny of degree 2
|
|
2384
|
+
from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + x over Finite Field in alpha of size 2^4
|
|
2385
|
+
to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 1 over Finite Field in alpha of size 2^4
|
|
2386
|
+
|
|
2387
|
+
sage: psig = two_torsion_part(E,x) # needs sage.rings.finite_rings
|
|
2388
|
+
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig) # needs sage.rings.finite_rings
|
|
2389
|
+
(x^3 + x, (x^3 + x)*y + x^2, 1, 0, 1, 2)
|
|
2390
|
+
|
|
2391
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2392
|
+
sage: R.<x> = GF(7)[]
|
|
2393
|
+
sage: f = x^3 + 6*x^2 + 1
|
|
2394
|
+
sage: phi = EllipticCurveIsogeny(E, f); phi
|
|
2395
|
+
Isogeny of degree 4
|
|
2396
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
|
|
2397
|
+
to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 2*x + 5 over Finite Field of size 7
|
|
2398
|
+
sage: psig = two_torsion_part(E,f)
|
|
2399
|
+
sage: psig = two_torsion_part(E,f)
|
|
2400
|
+
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
|
|
2401
|
+
(x^7 + 5*x^6 + 2*x^5 + 6*x^4 + 3*x^3 + 5*x^2 + 6*x + 3, (x^9 + 4*x^8 + 2*x^7 + 4*x^3 + 2*x^2 + x + 6)*y, 1, 6, 3, 4)
|
|
2402
|
+
"""
|
|
2403
|
+
# check if the polynomial really divides the two_torsion_polynomial
|
|
2404
|
+
if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0:
|
|
2405
|
+
raise ValueError(f"the polynomial {psi_G} does not define a finite subgroup of {E}")
|
|
2406
|
+
|
|
2407
|
+
n = psi_G.degree() # 1 or 3
|
|
2408
|
+
d = n+1 # 2 or 4
|
|
2409
|
+
|
|
2410
|
+
a1, a2, a3, a4, a6 = E.a_invariants()
|
|
2411
|
+
b2, b4, _, _ = E.b_invariants()
|
|
2412
|
+
x = self.__poly_ring.gen()
|
|
2413
|
+
y = self.__mpoly_ring.gen()
|
|
2414
|
+
|
|
2415
|
+
if n == 1:
|
|
2416
|
+
x0 = -psi_G.constant_coefficient()
|
|
2417
|
+
|
|
2418
|
+
# determine y0
|
|
2419
|
+
if self.__base_field.characteristic() == 2:
|
|
2420
|
+
y0 = (x0**3 + a2*x0**2 + a4*x0 + a6).sqrt()
|
|
2421
|
+
else:
|
|
2422
|
+
y0 = -(a1*x0 + a3)/2
|
|
2423
|
+
|
|
2424
|
+
v,w = compute_vw_kohel_even_deg1(x0, y0, a1, a2, a4)
|
|
2425
|
+
|
|
2426
|
+
phi = (x*psi_G + v)*psi_G
|
|
2427
|
+
omega = (y*psi_G**2 - v*(a1*psi_G + (y - y0)))*psi_G
|
|
2428
|
+
|
|
2429
|
+
elif n == 3:
|
|
2430
|
+
s1 = -psi_G[n - 1]
|
|
2431
|
+
s2 = psi_G[n - 2]
|
|
2432
|
+
s3 = -psi_G[n - 3]
|
|
2433
|
+
|
|
2434
|
+
psi_G_pr = psi_G.derivative()
|
|
2435
|
+
psi_G_prpr = psi_G_pr.derivative()
|
|
2436
|
+
|
|
2437
|
+
phi = (psi_G_pr**2) + (-2*psi_G_prpr + (4*x - s1))*psi_G
|
|
2438
|
+
phi_pr = phi.derivative(x)
|
|
2439
|
+
|
|
2440
|
+
psi_2 = 2*y + a1*x + a3
|
|
2441
|
+
|
|
2442
|
+
omega = (psi_2*(phi_pr*psi_G - phi*psi_G_pr) - (a1*phi + a3*psi_G)*psi_G)/2
|
|
2443
|
+
|
|
2444
|
+
phi *= psi_G
|
|
2445
|
+
omega *= psi_G
|
|
2446
|
+
|
|
2447
|
+
v,w = compute_vw_kohel_even_deg3(b2, b4, s1, s2, s3)
|
|
2448
|
+
|
|
2449
|
+
else:
|
|
2450
|
+
raise ValueError(f"input polynomial must have degree 1 or 3, not {n}")
|
|
2451
|
+
|
|
2452
|
+
return phi, omega, v, w, n, d
|
|
2453
|
+
|
|
2454
|
+
def __init_odd_kernel_polynomial(self, E, psi):
|
|
2455
|
+
r"""
|
|
2456
|
+
Return the isogeny parameters for a cyclic isogeny of odd degree.
|
|
2457
|
+
|
|
2458
|
+
INPUT:
|
|
2459
|
+
|
|
2460
|
+
- ``E`` -- an elliptic curve
|
|
2461
|
+
|
|
2462
|
+
- ``psi`` -- a univariate polynomial over the base field of
|
|
2463
|
+
``E``, assumed to be a kernel polynomial
|
|
2464
|
+
|
|
2465
|
+
OUTPUT:
|
|
2466
|
+
|
|
2467
|
+
A tuple (``phi``, ``omega``, ``v``, ``w``, ``n``, ``d``) where:
|
|
2468
|
+
|
|
2469
|
+
- ``phi`` is a univariate polynomial, the numerator of the
|
|
2470
|
+
`X`-coordinate of the isogeny;
|
|
2471
|
+
|
|
2472
|
+
- ``omega`` is a bivariate polynomial, the numerator of the
|
|
2473
|
+
`Y`-coordinate of the isogeny;
|
|
2474
|
+
|
|
2475
|
+
- ``v``, ``w`` are the Vélu parameters of the isogeny;
|
|
2476
|
+
|
|
2477
|
+
- ``n`` is the degree of ``psi``;
|
|
2478
|
+
|
|
2479
|
+
- ``d`` is the degree of the isogeny.
|
|
2480
|
+
|
|
2481
|
+
EXAMPLES:
|
|
2482
|
+
|
|
2483
|
+
These examples inherently exercise this private function::
|
|
2484
|
+
|
|
2485
|
+
sage: R.<x> = GF(7)[]
|
|
2486
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2487
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
|
|
2488
|
+
Isogeny of degree 3
|
|
2489
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
|
|
2490
|
+
to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
|
|
2491
|
+
|
|
2492
|
+
sage: R.<x> = GF(7)[]
|
|
2493
|
+
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6)
|
|
2494
|
+
(x^3 + 5*x^2 + 3*x + 2, (x^3 + 4*x^2 + x)*y, 2, 6, 1, 3)
|
|
2495
|
+
|
|
2496
|
+
sage: # needs sage.rings.finite_rings
|
|
2497
|
+
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
|
|
2498
|
+
sage: alpha = F.gen()
|
|
2499
|
+
sage: E = EllipticCurve(F, [1,1,F.gen(),F.gen()^2+1,1])
|
|
2500
|
+
sage: f = x + alpha^2 + 1
|
|
2501
|
+
sage: phi = EllipticCurveIsogeny(E, f); phi
|
|
2502
|
+
Isogeny of degree 3
|
|
2503
|
+
from Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + (alpha^2+1)*x + 1 over Finite Field in alpha of size 2^4
|
|
2504
|
+
to Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + alpha*x + alpha^3 over Finite Field in alpha of size 2^4
|
|
2505
|
+
|
|
2506
|
+
sage: R.<x> = F[] # needs sage.rings.finite_rings
|
|
2507
|
+
sage: f = x + alpha^2 + 1 # needs sage.rings.finite_rings
|
|
2508
|
+
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f) # needs sage.rings.finite_rings
|
|
2509
|
+
(x^3 + (alpha^2 + 1)*x + alpha^3 + alpha^2 + alpha, (x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha, alpha^2 + alpha + 1, alpha^3 + alpha^2 + alpha, 1, 3)
|
|
2510
|
+
|
|
2511
|
+
sage: E = EllipticCurve(j=-262537412640768000)
|
|
2512
|
+
sage: f = E.isogenies_prime_degree()[0].kernel_polynomial()
|
|
2513
|
+
sage: f.degree()
|
|
2514
|
+
81
|
|
2515
|
+
sage: E.isogeny(kernel=f, check=False)
|
|
2516
|
+
Isogeny of degree 163
|
|
2517
|
+
from Elliptic Curve defined by y^2 + y = x^3 - 2174420*x + 1234136692 over Rational Field
|
|
2518
|
+
to Elliptic Curve defined by y^2 + y = x^3 - 57772164980*x - 5344733777551611 over Rational Field
|
|
2519
|
+
"""
|
|
2520
|
+
n = psi.degree()
|
|
2521
|
+
d = 2*n + 1
|
|
2522
|
+
|
|
2523
|
+
# check if the polynomial really divides the torsion polynomial :
|
|
2524
|
+
if self.__check:
|
|
2525
|
+
from .isogeny_small_degree import is_kernel_polynomial
|
|
2526
|
+
if not is_kernel_polynomial(E, d, psi):
|
|
2527
|
+
raise ValueError(f"the polynomial {psi} does not define a finite subgroup of {E}")
|
|
2528
|
+
|
|
2529
|
+
b2, b4, b6, _ = E.b_invariants()
|
|
2530
|
+
|
|
2531
|
+
s1 = s2 = s3 = 0
|
|
2532
|
+
if 1 <= n:
|
|
2533
|
+
s1 = -psi[n-1]
|
|
2534
|
+
if 2 <= n:
|
|
2535
|
+
s2 = psi[n-2]
|
|
2536
|
+
if 3 <= n:
|
|
2537
|
+
s3 = -psi[n-3]
|
|
2538
|
+
|
|
2539
|
+
# initializing these allows us to calculate E2.
|
|
2540
|
+
v, w = compute_vw_kohel_odd(b2, b4, b6, s1, s2, s3, n)
|
|
2541
|
+
|
|
2542
|
+
# initialize the polynomial temporary variables
|
|
2543
|
+
|
|
2544
|
+
psi_pr = psi.derivative()
|
|
2545
|
+
psi_prpr = psi_pr.derivative()
|
|
2546
|
+
|
|
2547
|
+
x = self.__poly_ring.gen()
|
|
2548
|
+
|
|
2549
|
+
phi = (4*x**3 + b2*x**2 + 2*b4*x + b6)*(psi_pr**2 - psi_prpr*psi) \
|
|
2550
|
+
- (6*x**2 + b2*x + b4)*psi_pr*psi + (d*x - 2*s1)*psi**2
|
|
2551
|
+
|
|
2552
|
+
phi_pr = phi.derivative(x)
|
|
2553
|
+
|
|
2554
|
+
if self.__base_field.characteristic() != 2:
|
|
2555
|
+
omega = self.__compute_omega_fast(E, psi, psi_pr, phi, phi_pr)
|
|
2556
|
+
else:
|
|
2557
|
+
omega = self.__compute_omega_general(E, psi, psi_pr, phi, phi_pr)
|
|
2558
|
+
|
|
2559
|
+
return phi, omega, v, w, n, d
|
|
2560
|
+
|
|
2561
|
+
#
|
|
2562
|
+
# This is the fast omega computation that works when characteristic is not 2
|
|
2563
|
+
#
|
|
2564
|
+
def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
|
|
2565
|
+
r"""
|
|
2566
|
+
Return omega from phi, psi and their derivatives, used when
|
|
2567
|
+
the characteristic field is not 2.
|
|
2568
|
+
|
|
2569
|
+
INPUT:
|
|
2570
|
+
|
|
2571
|
+
- ``E`` -- an elliptic curve
|
|
2572
|
+
|
|
2573
|
+
- ``psi``, ``psi_pr``, ``phi``, ``phi_pr`` -- univariate
|
|
2574
|
+
polynomials over the base field of ``E``, where ``psi``
|
|
2575
|
+
is the kernel polynomial and ``phi`` the numerator of
|
|
2576
|
+
the `X`-coordinate of the isogeny, together with their
|
|
2577
|
+
derivatives
|
|
2578
|
+
|
|
2579
|
+
OUTPUT:
|
|
2580
|
+
|
|
2581
|
+
A bivariate polynomial ``omega`` giving the numerator of
|
|
2582
|
+
the `Y`-coordinate of the isogeny.
|
|
2583
|
+
|
|
2584
|
+
EXAMPLES:
|
|
2585
|
+
|
|
2586
|
+
These examples inherently exercise this private function::
|
|
2587
|
+
|
|
2588
|
+
sage: R.<x> = GF(7)[]
|
|
2589
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2590
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3); phi
|
|
2591
|
+
Isogeny of degree 3
|
|
2592
|
+
from Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 1 over Finite Field of size 7
|
|
2593
|
+
to Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
|
|
2594
|
+
|
|
2595
|
+
sage: R.<x,y> = GF(7)[]
|
|
2596
|
+
sage: psi = phi._EllipticCurveIsogeny__psi
|
|
2597
|
+
sage: psi_pr = psi.derivative()
|
|
2598
|
+
sage: fi = phi._EllipticCurveIsogeny__phi
|
|
2599
|
+
sage: fi_pr = fi.derivative()
|
|
2600
|
+
sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr)
|
|
2601
|
+
(x^3 + 4*x^2 + x)*y
|
|
2602
|
+
"""
|
|
2603
|
+
a1 = E.a1()
|
|
2604
|
+
a3 = E.a3()
|
|
2605
|
+
x = self.__poly_ring.gen()
|
|
2606
|
+
y = self.__mpoly_ring.gen()
|
|
2607
|
+
|
|
2608
|
+
psi_2 = 2*y + a1*x + a3
|
|
2609
|
+
|
|
2610
|
+
# The formula in Kohel's thesis has some typos:
|
|
2611
|
+
# Notably, the first plus sign should be a minus
|
|
2612
|
+
# as it is below.
|
|
2613
|
+
return phi_pr*psi*psi_2/2 - phi*psi_pr*psi_2 - (a1*phi + a3*psi**2)*psi/2
|
|
2614
|
+
|
|
2615
|
+
def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
|
|
2616
|
+
r"""
|
|
2617
|
+
Return omega from phi, psi and their derivatives, in any
|
|
2618
|
+
characteristic.
|
|
2619
|
+
|
|
2620
|
+
INPUT:
|
|
2621
|
+
|
|
2622
|
+
- ``E`` -- an elliptic curve
|
|
2623
|
+
|
|
2624
|
+
- ``psi``, ``psi_pr``, ``phi``, ``phi_pr`` -- univariate polynomials
|
|
2625
|
+
over the base field of ``E``, where ``psi`` is the kernel
|
|
2626
|
+
polynomial and ``phi`` the numerator of the `X`-coordinate
|
|
2627
|
+
of the isogeny, together with their derivatives
|
|
2628
|
+
|
|
2629
|
+
OUTPUT:
|
|
2630
|
+
|
|
2631
|
+
A bivariate polynomial ``omega`` giving the numerator of
|
|
2632
|
+
the `Y`-coordinate of the isogeny.
|
|
2633
|
+
|
|
2634
|
+
EXAMPLES:
|
|
2635
|
+
|
|
2636
|
+
These examples inherently exercise this private function::
|
|
2637
|
+
|
|
2638
|
+
sage: # needs sage.rings.finite_rings
|
|
2639
|
+
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
|
|
2640
|
+
sage: alpha = F.gen()
|
|
2641
|
+
sage: E = EllipticCurve(F, [1, 1, F.gen(), F.gen()^2+1, 1])
|
|
2642
|
+
sage: f = x + alpha^2 + 1
|
|
2643
|
+
sage: phi = EllipticCurveIsogeny(E, f); phi
|
|
2644
|
+
Isogeny of degree 3
|
|
2645
|
+
from Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + (alpha^2+1)*x + 1 over Finite Field in alpha of size 2^4
|
|
2646
|
+
to Elliptic Curve defined by y^2 + x*y + alpha*y = x^3 + x^2 + alpha*x + alpha^3 over Finite Field in alpha of size 2^4
|
|
2647
|
+
|
|
2648
|
+
sage: # needs sage.rings.finite_rings
|
|
2649
|
+
sage: R.<x,y> = F[]
|
|
2650
|
+
sage: psi = phi._EllipticCurveIsogeny__psi
|
|
2651
|
+
sage: psi_pr = psi.derivative()
|
|
2652
|
+
sage: fi = phi._EllipticCurveIsogeny__phi
|
|
2653
|
+
sage: fi_pr = fi.derivative()
|
|
2654
|
+
sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr)
|
|
2655
|
+
(x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha
|
|
2656
|
+
|
|
2657
|
+
A bug fixed in :issue:`7907`::
|
|
2658
|
+
|
|
2659
|
+
sage: # needs sage.rings.finite_rings
|
|
2660
|
+
sage: F = GF(128,'a')
|
|
2661
|
+
sage: a = F.gen()
|
|
2662
|
+
sage: E = EllipticCurve([1,0,0,0,(a**6+a**4+a**2+a)])
|
|
2663
|
+
sage: x = polygen(F)
|
|
2664
|
+
sage: ker = (x^6 + (a^6 + a^5 + a^4 + a^3 + a^2 + a)*x^5 + (a^6 + a^5 + a^2 + 1)*x^4
|
|
2665
|
+
....: + (a^6 + a^5 + a^4 + a^3 + a^2 + 1)*x^3 + (a^6 + a^3 + a)*x^2 + (a^4 + a^3 + 1)*x + a^5 + a^4 + a)
|
|
2666
|
+
sage: E.isogeny(ker)
|
|
2667
|
+
Isogeny of degree 13
|
|
2668
|
+
from Elliptic Curve defined by y^2 + x*y = x^3 + (a^6+a^4+a^2+a) over Finite Field in a of size 2^7
|
|
2669
|
+
to Elliptic Curve defined by y^2 + x*y = x^3 + (a^6+a^5+a^4+a^3+a^2+a)*x + (a^5+a^3) over Finite Field in a of size 2^7
|
|
2670
|
+
"""
|
|
2671
|
+
a1, a2, a3, a4, a6 = E.a_invariants()
|
|
2672
|
+
b2, b4, _, _ = E.b_invariants()
|
|
2673
|
+
x = self.__poly_ring.gen()
|
|
2674
|
+
y = self.__mpoly_ring.gen()
|
|
2675
|
+
|
|
2676
|
+
n = psi.degree()
|
|
2677
|
+
d = 2 * n + 1
|
|
2678
|
+
|
|
2679
|
+
s1 = -psi[n-1] if n > 0 else 0
|
|
2680
|
+
|
|
2681
|
+
psi_prpr = 0
|
|
2682
|
+
cur_x_pow = 1
|
|
2683
|
+
|
|
2684
|
+
# Note: we now get the "derivatives" of psi
|
|
2685
|
+
# these are not actually the derivatives
|
|
2686
|
+
# furthermore, the formulas in Kohel's
|
|
2687
|
+
# thesis are wrong, the correct formulas
|
|
2688
|
+
# are coded below
|
|
2689
|
+
|
|
2690
|
+
from sage.arith.misc import binomial
|
|
2691
|
+
|
|
2692
|
+
for j in range(n - 1):
|
|
2693
|
+
psi_prpr += binomial(j+2, 2) * psi[j+2] * cur_x_pow
|
|
2694
|
+
cur_x_pow = x * cur_x_pow
|
|
2695
|
+
|
|
2696
|
+
psi_prprpr = 0
|
|
2697
|
+
cur_x_pow = 1
|
|
2698
|
+
|
|
2699
|
+
for j in range(n - 2):
|
|
2700
|
+
psi_prprpr += (3 * binomial(j+3, 3)) * psi[j+3] * cur_x_pow
|
|
2701
|
+
cur_x_pow = x * cur_x_pow
|
|
2702
|
+
|
|
2703
|
+
psi_2 = 2 * y + a1 * x + a3
|
|
2704
|
+
|
|
2705
|
+
omega = phi_pr*psi*y - phi*psi_pr*psi_2 \
|
|
2706
|
+
+ ((a1*x + a3)*(psi_2**2)*(psi_prpr*psi_pr-psi_prprpr*psi)
|
|
2707
|
+
+ (a1*psi_2**2 - 3*(a1*x + a3)*(6*x**2 + b2*x + b4))*psi_prpr*psi
|
|
2708
|
+
+ (a1*x**3 + 3*a3*x**2 + (2*a2*a3 - a1*a4)*x + (a3*a4 - 2*a1*a6))*psi_pr**2
|
|
2709
|
+
+ (-(3*a1*x**2 + 6*a3*x + (-a1*a4 + 2*a2*a3))
|
|
2710
|
+
+ (a1*x + a3)*(d*x - 2*s1) )*psi_pr*psi + (a1*s1 + a3*n)*psi**2) * psi
|
|
2711
|
+
|
|
2712
|
+
return omega
|
|
2713
|
+
|
|
2714
|
+
def __compute_via_kohel_numeric(self, xP, yP):
|
|
2715
|
+
r"""
|
|
2716
|
+
Private function that computes the image of a point under this
|
|
2717
|
+
isogeny, using Kohel's formulas.
|
|
2718
|
+
|
|
2719
|
+
EXAMPLES:
|
|
2720
|
+
|
|
2721
|
+
These examples inherently exercise this private function::
|
|
2722
|
+
|
|
2723
|
+
sage: R.<x> = GF(7)[]
|
|
2724
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2725
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
|
|
2726
|
+
sage: P = E((0,1)); phi(P)
|
|
2727
|
+
(2 : 0 : 1)
|
|
2728
|
+
sage: P = E((1,1)); phi(P)
|
|
2729
|
+
(0 : 1 : 0)
|
|
2730
|
+
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(0, 1)
|
|
2731
|
+
(2, 0)
|
|
2732
|
+
sage: phi._EllipticCurveIsogeny__compute_via_kohel_numeric(1, 1)
|
|
2733
|
+
()
|
|
2734
|
+
"""
|
|
2735
|
+
# first check if the point is in the kernel
|
|
2736
|
+
if self.__inner_kernel_polynomial(xP) == 0:
|
|
2737
|
+
return ()
|
|
2738
|
+
|
|
2739
|
+
return self.__compute_via_kohel(xP, yP)
|
|
2740
|
+
|
|
2741
|
+
def __compute_via_kohel(self, xP, yP):
|
|
2742
|
+
r"""
|
|
2743
|
+
Private function that applies Kohel's formulas.
|
|
2744
|
+
|
|
2745
|
+
EXAMPLES:
|
|
2746
|
+
|
|
2747
|
+
These examples inherently exercise this private function::
|
|
2748
|
+
|
|
2749
|
+
sage: R.<x> = GF(7)[]
|
|
2750
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2751
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
|
|
2752
|
+
sage: P = E((0,1)); phi(P)
|
|
2753
|
+
(2 : 0 : 1)
|
|
2754
|
+
sage: phi.rational_maps()
|
|
2755
|
+
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1), (x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
|
|
2756
|
+
sage: phi._EllipticCurveIsogeny__compute_via_kohel(0,1)
|
|
2757
|
+
(2, 0)
|
|
2758
|
+
sage: R.<x,y> = GF(7)[]
|
|
2759
|
+
sage: phi._EllipticCurveIsogeny__compute_via_kohel(x,y)
|
|
2760
|
+
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1), (x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
|
|
2761
|
+
"""
|
|
2762
|
+
a = self.__phi(xP)
|
|
2763
|
+
omega0 = self.__omega[0]
|
|
2764
|
+
omega1 = self.__omega[1]
|
|
2765
|
+
b = omega0(xP) + omega1(xP)*yP
|
|
2766
|
+
c = self.__psi(xP)
|
|
2767
|
+
return a/c**2, b/c**3
|
|
2768
|
+
|
|
2769
|
+
def __initialize_rational_maps_via_kohel(self):
|
|
2770
|
+
r"""
|
|
2771
|
+
Private function that computes and initializes the rational
|
|
2772
|
+
maps of this isogeny.
|
|
2773
|
+
|
|
2774
|
+
EXAMPLES:
|
|
2775
|
+
|
|
2776
|
+
These examples inherently exercise this private function::
|
|
2777
|
+
|
|
2778
|
+
sage: R.<x> = GF(7)[]
|
|
2779
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2780
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
|
|
2781
|
+
sage: phi.rational_maps()
|
|
2782
|
+
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1), (x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
|
|
2783
|
+
sage: phi._EllipticCurveIsogeny__initialize_rational_maps_via_kohel()
|
|
2784
|
+
((x^3 + 5*x^2 + 3*x + 2)/(x^2 + 5*x + 1), (x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
|
|
2785
|
+
"""
|
|
2786
|
+
x = self.__poly_ring.gen()
|
|
2787
|
+
y = self.__xyfield.gen(1)
|
|
2788
|
+
return self.__compute_via_kohel(x, y)
|
|
2789
|
+
|
|
2790
|
+
#
|
|
2791
|
+
# Kohel's formula computing the codomain curve
|
|
2792
|
+
#
|
|
2793
|
+
def __compute_codomain_via_kohel(self):
|
|
2794
|
+
r"""
|
|
2795
|
+
Private function that computes and initializes the codomain of
|
|
2796
|
+
the isogeny (via Kohel's.)
|
|
2797
|
+
|
|
2798
|
+
EXAMPLES:
|
|
2799
|
+
|
|
2800
|
+
These examples inherently exercise this private function::
|
|
2801
|
+
|
|
2802
|
+
sage: R.<x> = GF(7)[]
|
|
2803
|
+
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
|
|
2804
|
+
sage: phi = EllipticCurveIsogeny(E, x+6, degree=3)
|
|
2805
|
+
sage: phi.codomain()
|
|
2806
|
+
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
|
|
2807
|
+
sage: phi._EllipticCurveIsogeny__compute_codomain_via_kohel()
|
|
2808
|
+
Elliptic Curve defined by y^2 = x^3 + 6*x^2 + 4*x + 2 over Finite Field of size 7
|
|
2809
|
+
"""
|
|
2810
|
+
return compute_codomain_formula(self._domain, self.__v, self.__w)
|
|
2811
|
+
|
|
2812
|
+
#
|
|
2813
|
+
# public isogeny methods
|
|
2814
|
+
#
|
|
2815
|
+
|
|
2816
|
+
def rational_maps(self):
|
|
2817
|
+
r"""
|
|
2818
|
+
Return the pair of rational maps defining this isogeny.
|
|
2819
|
+
|
|
2820
|
+
.. NOTE::
|
|
2821
|
+
|
|
2822
|
+
Both components are returned as elements of the function
|
|
2823
|
+
field `F(x,y)` in two variables over the base field `F`,
|
|
2824
|
+
though the first only involves `x`. To obtain the
|
|
2825
|
+
`x`-coordinate function as a rational function in `F(x)`,
|
|
2826
|
+
use :meth:`x_rational_map`.
|
|
2827
|
+
|
|
2828
|
+
EXAMPLES::
|
|
2829
|
+
|
|
2830
|
+
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
|
|
2831
|
+
sage: phi = EllipticCurveIsogeny(E, [1])
|
|
2832
|
+
sage: phi.rational_maps()
|
|
2833
|
+
(x, y)
|
|
2834
|
+
|
|
2835
|
+
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
|
|
2836
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
2837
|
+
sage: phi.rational_maps()
|
|
2838
|
+
((x^2 + 3)/x, (x^2*y - 3*y)/x^2)
|
|
2839
|
+
"""
|
|
2840
|
+
self.__initialize_rational_maps()
|
|
2841
|
+
return tuple(self.__xyfield(f) for f in self.__ratl_maps)
|
|
2842
|
+
|
|
2843
|
+
def x_rational_map(self):
|
|
2844
|
+
r"""
|
|
2845
|
+
Return the rational map giving the `x`-coordinate of this isogeny.
|
|
2846
|
+
|
|
2847
|
+
.. NOTE::
|
|
2848
|
+
|
|
2849
|
+
This function returns the `x`-coordinate component of the
|
|
2850
|
+
isogeny as a rational function in `F(x)`, where `F` is the
|
|
2851
|
+
base field. To obtain both coordinate functions as
|
|
2852
|
+
elements of the function field `F(x,y)` in two variables,
|
|
2853
|
+
use :meth:`rational_maps`.
|
|
2854
|
+
|
|
2855
|
+
EXAMPLES::
|
|
2856
|
+
|
|
2857
|
+
sage: E = EllipticCurve(QQ, [0,2,0,1,-1])
|
|
2858
|
+
sage: phi = EllipticCurveIsogeny(E, [1])
|
|
2859
|
+
sage: phi.x_rational_map()
|
|
2860
|
+
x
|
|
2861
|
+
|
|
2862
|
+
sage: E = EllipticCurve(GF(17), [0,0,0,3,0])
|
|
2863
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
2864
|
+
sage: phi.x_rational_map()
|
|
2865
|
+
(x^2 + 3)/x
|
|
2866
|
+
"""
|
|
2867
|
+
self.__initialize_rational_maps()
|
|
2868
|
+
return self.__ratl_maps[0]
|
|
2869
|
+
|
|
2870
|
+
def scaling_factor(self):
|
|
2871
|
+
r"""
|
|
2872
|
+
Return the Weierstrass scaling factor associated to this
|
|
2873
|
+
elliptic-curve isogeny.
|
|
2874
|
+
|
|
2875
|
+
The scaling factor is the constant `u` (in the base field)
|
|
2876
|
+
such that `\varphi^* \omega_2 = u \omega_1`, where
|
|
2877
|
+
`\varphi: E_1\to E_2` is this isogeny and `\omega_i` are
|
|
2878
|
+
the standard Weierstrass differentials on `E_i` defined by
|
|
2879
|
+
`\mathrm dx/(2y+a_1x+a_3)`.
|
|
2880
|
+
|
|
2881
|
+
EXAMPLES::
|
|
2882
|
+
|
|
2883
|
+
sage: # needs sage.rings.finite_rings
|
|
2884
|
+
sage: E = EllipticCurve(GF(257^2), [0,1])
|
|
2885
|
+
sage: phi = E.isogeny(E.lift_x(240))
|
|
2886
|
+
sage: phi.degree()
|
|
2887
|
+
43
|
|
2888
|
+
sage: phi.scaling_factor()
|
|
2889
|
+
1
|
|
2890
|
+
sage: phi.dual().scaling_factor()
|
|
2891
|
+
43
|
|
2892
|
+
|
|
2893
|
+
TESTS:
|
|
2894
|
+
|
|
2895
|
+
Check for :issue:`36638`::
|
|
2896
|
+
|
|
2897
|
+
sage: phi.scaling_factor().parent() # needs sage.rings.finite_rings
|
|
2898
|
+
Finite Field in z2 of size 257^2
|
|
2899
|
+
|
|
2900
|
+
ALGORITHM: The "inner" isogeny is normalized by construction,
|
|
2901
|
+
so we only need to account for the scaling factors of a pre-
|
|
2902
|
+
and post-isomorphism.
|
|
2903
|
+
"""
|
|
2904
|
+
sc = self.__base_field.one()
|
|
2905
|
+
if self.__pre_isomorphism is not None:
|
|
2906
|
+
sc *= self.__pre_isomorphism.scaling_factor()
|
|
2907
|
+
if self.__post_isomorphism is not None:
|
|
2908
|
+
sc *= self.__post_isomorphism.scaling_factor()
|
|
2909
|
+
return sc
|
|
2910
|
+
|
|
2911
|
+
def kernel_polynomial(self):
|
|
2912
|
+
r"""
|
|
2913
|
+
Return the kernel polynomial of this isogeny.
|
|
2914
|
+
|
|
2915
|
+
EXAMPLES::
|
|
2916
|
+
|
|
2917
|
+
sage: E = EllipticCurve(QQ, [0,0,0,2,0])
|
|
2918
|
+
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
|
|
2919
|
+
sage: phi.kernel_polynomial()
|
|
2920
|
+
x
|
|
2921
|
+
|
|
2922
|
+
sage: E = EllipticCurve('11a1')
|
|
2923
|
+
sage: phi = EllipticCurveIsogeny(E, E.torsion_points())
|
|
2924
|
+
sage: phi.kernel_polynomial()
|
|
2925
|
+
x^2 - 21*x + 80
|
|
2926
|
+
|
|
2927
|
+
sage: E = EllipticCurve(GF(17), [1,-1,1,-1,1])
|
|
2928
|
+
sage: phi = EllipticCurveIsogeny(E, [1])
|
|
2929
|
+
sage: phi.kernel_polynomial()
|
|
2930
|
+
1
|
|
2931
|
+
|
|
2932
|
+
sage: E = EllipticCurve(GF(31), [0,0,0,3,0])
|
|
2933
|
+
sage: phi = EllipticCurveIsogeny(E, [0,3,0,1])
|
|
2934
|
+
sage: phi.kernel_polynomial()
|
|
2935
|
+
x^3 + 3*x
|
|
2936
|
+
"""
|
|
2937
|
+
if self.__kernel_polynomial is None:
|
|
2938
|
+
self.__init_kernel_polynomial()
|
|
2939
|
+
return self.__kernel_polynomial
|
|
2940
|
+
|
|
2941
|
+
def inseparable_degree(self):
|
|
2942
|
+
r"""
|
|
2943
|
+
Return the inseparable degree of this isogeny.
|
|
2944
|
+
|
|
2945
|
+
Since this class only implements separable isogenies,
|
|
2946
|
+
this method always returns one.
|
|
2947
|
+
|
|
2948
|
+
TESTS::
|
|
2949
|
+
|
|
2950
|
+
sage: EllipticCurveIsogeny.inseparable_degree(None)
|
|
2951
|
+
1
|
|
2952
|
+
"""
|
|
2953
|
+
return Integer(1)
|
|
2954
|
+
|
|
2955
|
+
def _set_pre_isomorphism(self, preWI):
|
|
2956
|
+
"""
|
|
2957
|
+
Modify this isogeny by pre-composing with a
|
|
2958
|
+
:class:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`.
|
|
2959
|
+
|
|
2960
|
+
For internal use only.
|
|
2961
|
+
|
|
2962
|
+
TESTS::
|
|
2963
|
+
|
|
2964
|
+
sage: E = EllipticCurve(GF(31), [1,1,0,1,-1])
|
|
2965
|
+
sage: R.<x> = GF(31)[]
|
|
2966
|
+
sage: f = x^3 + 9*x^2 + x + 30
|
|
2967
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
2968
|
+
sage: Epr = E.short_weierstrass_model()
|
|
2969
|
+
sage: isom = Epr.isomorphism_to(E)
|
|
2970
|
+
sage: phi._set_pre_isomorphism(isom)
|
|
2971
|
+
sage: phi.rational_maps()
|
|
2972
|
+
((-6*x^4 - 3*x^3 + 12*x^2 + 10*x - 1)/(x^3 + x - 12),
|
|
2973
|
+
(3*x^7 + x^6*y - 14*x^6 - 3*x^5 + 5*x^4*y + 7*x^4 + 8*x^3*y - 8*x^3 - 5*x^2*y + 5*x^2 - 14*x*y + 14*x - 6*y - 6)/(x^6 + 2*x^4 + 7*x^3 + x^2 + 7*x - 11))
|
|
2974
|
+
sage: phi(Epr((0,22)))
|
|
2975
|
+
(13 : 21 : 1)
|
|
2976
|
+
sage: phi(Epr((3,7)))
|
|
2977
|
+
(14 : 17 : 1)
|
|
2978
|
+
|
|
2979
|
+
sage: E = EllipticCurve(GF(29), [0,0,0,1,0])
|
|
2980
|
+
sage: R.<x> = GF(29)[]
|
|
2981
|
+
sage: f = x^2 + 5
|
|
2982
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
2983
|
+
sage: phi
|
|
2984
|
+
Isogeny of degree 5
|
|
2985
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 29
|
|
2986
|
+
to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
|
|
2987
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
2988
|
+
sage: inv_isom = WeierstrassIsomorphism(E, (1,-2,5,10))
|
|
2989
|
+
sage: Epr = inv_isom.codomain()
|
|
2990
|
+
sage: isom = Epr.isomorphism_to(E)
|
|
2991
|
+
sage: phi._set_pre_isomorphism(isom)
|
|
2992
|
+
sage: phi
|
|
2993
|
+
Isogeny of degree 5
|
|
2994
|
+
from Elliptic Curve defined by y^2 + 10*x*y + 20*y = x^3 + 27*x^2 + 6 over Finite Field of size 29
|
|
2995
|
+
to Elliptic Curve defined by y^2 = x^3 + 20*x over Finite Field of size 29
|
|
2996
|
+
sage: phi(Epr((12,1)))
|
|
2997
|
+
(26 : 0 : 1)
|
|
2998
|
+
sage: phi(Epr((2,9)))
|
|
2999
|
+
(0 : 0 : 1)
|
|
3000
|
+
sage: phi(Epr((21,12)))
|
|
3001
|
+
(3 : 0 : 1)
|
|
3002
|
+
sage: phi.rational_maps()[0]
|
|
3003
|
+
(x^5 - 10*x^4 - 6*x^3 - 7*x^2 - x + 3)/(x^4 - 8*x^3 + 5*x^2 - 14*x - 6)
|
|
3004
|
+
|
|
3005
|
+
sage: E = EllipticCurve('11a1')
|
|
3006
|
+
sage: R.<x> = QQ[]
|
|
3007
|
+
sage: f = x^2 - 21*x + 80
|
|
3008
|
+
sage: phi = EllipticCurveIsogeny(E, f); phi
|
|
3009
|
+
Isogeny of degree 5
|
|
3010
|
+
from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
|
|
3011
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
|
|
3012
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
3013
|
+
sage: Epr = E.short_weierstrass_model()
|
|
3014
|
+
sage: isom = Epr.isomorphism_to(E)
|
|
3015
|
+
sage: phi._set_pre_isomorphism(isom)
|
|
3016
|
+
sage: phi
|
|
3017
|
+
Isogeny of degree 5
|
|
3018
|
+
from Elliptic Curve defined by y^2 = x^3 - 13392*x - 1080432 over Rational Field
|
|
3019
|
+
to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field
|
|
3020
|
+
sage: phi(Epr((168,1188)))
|
|
3021
|
+
(0 : 1 : 0)
|
|
3022
|
+
"""
|
|
3023
|
+
WIdom = preWI.domain()
|
|
3024
|
+
WIcod = preWI.codomain()
|
|
3025
|
+
|
|
3026
|
+
if not isinstance(preWI, WeierstrassIsomorphism):
|
|
3027
|
+
raise ValueError("invalid parameter: isomorphism must be a WeierstrassIsomorphism")
|
|
3028
|
+
|
|
3029
|
+
if self._domain != WIcod:
|
|
3030
|
+
raise ValueError("invalid parameter: isomorphism must have codomain curve equal to this isogenies' domain")
|
|
3031
|
+
|
|
3032
|
+
if self.__pre_isomorphism is None:
|
|
3033
|
+
isom = preWI
|
|
3034
|
+
domain = WIdom
|
|
3035
|
+
else:
|
|
3036
|
+
isom = self.__pre_isomorphism*preWI
|
|
3037
|
+
domain = WIdom
|
|
3038
|
+
|
|
3039
|
+
self.__clear_cached_values()
|
|
3040
|
+
|
|
3041
|
+
self.__set_pre_isomorphism(domain, isom)
|
|
3042
|
+
|
|
3043
|
+
def _set_post_isomorphism(self, postWI):
|
|
3044
|
+
"""
|
|
3045
|
+
Modify this isogeny by post-composing with a
|
|
3046
|
+
:class:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism`.
|
|
3047
|
+
|
|
3048
|
+
For internal use only.
|
|
3049
|
+
|
|
3050
|
+
TESTS::
|
|
3051
|
+
|
|
3052
|
+
sage: E = EllipticCurve(j=GF(31)(0))
|
|
3053
|
+
sage: R.<x> = GF(31)[]
|
|
3054
|
+
sage: phi = EllipticCurveIsogeny(E, x+18)
|
|
3055
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
3056
|
+
sage: phi._set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (6,8,10,12)))
|
|
3057
|
+
sage: phi
|
|
3058
|
+
Isogeny of degree 3
|
|
3059
|
+
from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 31
|
|
3060
|
+
to Elliptic Curve defined by y^2 + 24*x*y + 7*y = x^3 + 22*x^2 + 16*x + 20 over Finite Field of size 31
|
|
3061
|
+
|
|
3062
|
+
sage: E = EllipticCurve(j=GF(47)(0))
|
|
3063
|
+
sage: f = E.torsion_polynomial(3)/3
|
|
3064
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3065
|
+
sage: E2 = phi.codomain()
|
|
3066
|
+
sage: post_isom = E2.isomorphism_to(E)
|
|
3067
|
+
sage: phi._set_post_isomorphism(post_isom)
|
|
3068
|
+
sage: phi.rational_maps() == E.multiplication_by_m(3)
|
|
3069
|
+
False
|
|
3070
|
+
sage: phi = -phi
|
|
3071
|
+
sage: phi.rational_maps() == E.multiplication_by_m(3)
|
|
3072
|
+
True
|
|
3073
|
+
|
|
3074
|
+
sage: R.<x> = QQ[]
|
|
3075
|
+
sage: K.<a> = NumberField(x^2 + 2)
|
|
3076
|
+
sage: E = EllipticCurve(j=K(1728))
|
|
3077
|
+
sage: ker_list = E.torsion_points()
|
|
3078
|
+
sage: phi = EllipticCurveIsogeny(E, ker_list)
|
|
3079
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
3080
|
+
sage: post_isom = WeierstrassIsomorphism(phi.codomain(), (a,2,3,5))
|
|
3081
|
+
sage: phi
|
|
3082
|
+
Isogeny of degree 4
|
|
3083
|
+
from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^2 + 2
|
|
3084
|
+
to Elliptic Curve defined by y^2 = x^3 + (-44)*x + 112 over Number Field in a with defining polynomial x^2 + 2
|
|
3085
|
+
"""
|
|
3086
|
+
WIdom = postWI.domain()
|
|
3087
|
+
WIcod = postWI.codomain()
|
|
3088
|
+
|
|
3089
|
+
if not isinstance(postWI, WeierstrassIsomorphism):
|
|
3090
|
+
raise ValueError("invalid parameter: isomorphism must be a WeierstrassIsomorphism")
|
|
3091
|
+
|
|
3092
|
+
if self._codomain != WIdom:
|
|
3093
|
+
raise ValueError("invalid parameter: isomorphism must have domain curve equal to this isogenies' codomain")
|
|
3094
|
+
|
|
3095
|
+
if self.__post_isomorphism is None:
|
|
3096
|
+
isom = postWI
|
|
3097
|
+
codomain = WIcod
|
|
3098
|
+
else:
|
|
3099
|
+
isom = postWI*self.__post_isomorphism
|
|
3100
|
+
codomain = WIcod
|
|
3101
|
+
|
|
3102
|
+
self.__clear_cached_values()
|
|
3103
|
+
|
|
3104
|
+
self.__set_post_isomorphism(codomain, isom)
|
|
3105
|
+
|
|
3106
|
+
def dual(self):
|
|
3107
|
+
r"""
|
|
3108
|
+
Return the isogeny dual to this isogeny.
|
|
3109
|
+
|
|
3110
|
+
.. NOTE::
|
|
3111
|
+
|
|
3112
|
+
If `\varphi\colon E \to E'` is the given isogeny and `n`
|
|
3113
|
+
is its degree, then the dual is by definition the unique
|
|
3114
|
+
isogeny `\hat\varphi\colon E'\to E` such that the
|
|
3115
|
+
compositions `\hat\varphi\circ\varphi` and
|
|
3116
|
+
`\varphi\circ\hat\varphi` are the multiplication-by-`n`
|
|
3117
|
+
maps on `E` and `E'`, respectively.
|
|
3118
|
+
|
|
3119
|
+
EXAMPLES::
|
|
3120
|
+
|
|
3121
|
+
sage: E = EllipticCurve('11a1')
|
|
3122
|
+
sage: R.<x> = QQ[]
|
|
3123
|
+
sage: f = x^2 - 21*x + 80
|
|
3124
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3125
|
+
sage: phi_hat = phi.dual()
|
|
3126
|
+
sage: phi_hat.domain() == phi.codomain()
|
|
3127
|
+
True
|
|
3128
|
+
sage: phi_hat.codomain() == phi.domain()
|
|
3129
|
+
True
|
|
3130
|
+
sage: (X, Y) = phi.rational_maps()
|
|
3131
|
+
sage: (Xhat, Yhat) = phi_hat.rational_maps()
|
|
3132
|
+
sage: Xm = Xhat.subs(x=X, y=Y)
|
|
3133
|
+
sage: Ym = Yhat.subs(x=X, y=Y)
|
|
3134
|
+
sage: (Xm, Ym) == E.multiplication_by_m(5)
|
|
3135
|
+
True
|
|
3136
|
+
|
|
3137
|
+
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
|
|
3138
|
+
sage: R.<x> = GF(37)[]
|
|
3139
|
+
sage: f = x^3 + x^2 + 28*x + 33
|
|
3140
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3141
|
+
sage: phi_hat = phi.dual()
|
|
3142
|
+
sage: phi_hat.codomain() == phi.domain()
|
|
3143
|
+
True
|
|
3144
|
+
sage: phi_hat.domain() == phi.codomain()
|
|
3145
|
+
True
|
|
3146
|
+
sage: (X, Y) = phi.rational_maps()
|
|
3147
|
+
sage: (Xhat, Yhat) = phi_hat.rational_maps()
|
|
3148
|
+
sage: Xm = Xhat.subs(x=X, y=Y)
|
|
3149
|
+
sage: Ym = Yhat.subs(x=X, y=Y)
|
|
3150
|
+
sage: (Xm, Ym) == E.multiplication_by_m(7)
|
|
3151
|
+
True
|
|
3152
|
+
|
|
3153
|
+
sage: E = EllipticCurve(GF(31), [0,0,0,1,8])
|
|
3154
|
+
sage: R.<x> = GF(31)[]
|
|
3155
|
+
sage: f = x^2 + 17*x + 29
|
|
3156
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3157
|
+
sage: phi_hat = phi.dual()
|
|
3158
|
+
sage: phi_hat.codomain() == phi.domain()
|
|
3159
|
+
True
|
|
3160
|
+
sage: phi_hat.domain() == phi.codomain()
|
|
3161
|
+
True
|
|
3162
|
+
sage: (X, Y) = phi.rational_maps()
|
|
3163
|
+
sage: (Xhat, Yhat) = phi_hat.rational_maps()
|
|
3164
|
+
sage: Xm = Xhat.subs(x=X, y=Y)
|
|
3165
|
+
sage: Ym = Yhat.subs(x=X, y=Y)
|
|
3166
|
+
sage: (Xm, Ym) == E.multiplication_by_m(5)
|
|
3167
|
+
True
|
|
3168
|
+
|
|
3169
|
+
Inseparable duals should be computed correctly::
|
|
3170
|
+
|
|
3171
|
+
sage: # needs sage.rings.finite_rings
|
|
3172
|
+
sage: z2 = GF(71^2).gen()
|
|
3173
|
+
sage: E = EllipticCurve(j=57*z2+51)
|
|
3174
|
+
sage: E.isogeny(3*E.lift_x(0)).dual()
|
|
3175
|
+
Composite morphism of degree 71 = 71*1^2:
|
|
3176
|
+
From: Elliptic Curve defined by y^2 = x^3 + (32*z2+67)*x + (24*z2+37)
|
|
3177
|
+
over Finite Field in z2 of size 71^2
|
|
3178
|
+
To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42)
|
|
3179
|
+
over Finite Field in z2 of size 71^2
|
|
3180
|
+
sage: E.isogeny(E.lift_x(0)).dual()
|
|
3181
|
+
Composite morphism of degree 213 = 71*3:
|
|
3182
|
+
From: Elliptic Curve defined by y^2 = x^3 + (58*z2+31)*x + (34*z2+58)
|
|
3183
|
+
over Finite Field in z2 of size 71^2
|
|
3184
|
+
To: Elliptic Curve defined by y^2 = x^3 + (41*z2+56)*x + (18*z2+42)
|
|
3185
|
+
over Finite Field in z2 of size 71^2
|
|
3186
|
+
|
|
3187
|
+
...even if pre- or post-isomorphisms are present::
|
|
3188
|
+
|
|
3189
|
+
sage: # needs sage.rings.finite_rings
|
|
3190
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
3191
|
+
sage: phi = E.isogeny(E.lift_x(0))
|
|
3192
|
+
sage: pre = ~WeierstrassIsomorphism(phi.domain(), (z2,2,3,4))
|
|
3193
|
+
sage: post = WeierstrassIsomorphism(phi.codomain(), (5,6,7,8))
|
|
3194
|
+
sage: phi = post * phi * pre
|
|
3195
|
+
sage: phi.dual()
|
|
3196
|
+
Composite morphism of degree 213 = 71*3:
|
|
3197
|
+
From: Elliptic Curve defined
|
|
3198
|
+
by y^2 + 17*x*y + 45*y = x^3 + 30*x^2 + (6*z2+64)*x + (48*z2+65)
|
|
3199
|
+
over Finite Field in z2 of size 71^2
|
|
3200
|
+
To: Elliptic Curve defined
|
|
3201
|
+
by y^2 + (60*z2+22)*x*y + (69*z2+37)*y = x^3 + (32*z2+48)*x^2
|
|
3202
|
+
+ (19*z2+58)*x + (56*z2+22)
|
|
3203
|
+
over Finite Field in z2 of size 71^2
|
|
3204
|
+
|
|
3205
|
+
TESTS:
|
|
3206
|
+
|
|
3207
|
+
Test for :issue:`23928`::
|
|
3208
|
+
|
|
3209
|
+
sage: E = EllipticCurve(j=GF(431**2)(4)) # needs sage.rings.finite_rings
|
|
3210
|
+
sage: phi = E.isogeny(E.lift_x(0)) # needs sage.rings.finite_rings
|
|
3211
|
+
sage: phi.dual() # needs sage.rings.finite_rings
|
|
3212
|
+
Isogeny of degree 2
|
|
3213
|
+
from Elliptic Curve defined by y^2 = x^3 + 427*x over Finite Field in z2 of size 431^2
|
|
3214
|
+
to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 431^2
|
|
3215
|
+
|
|
3216
|
+
Test (for :issue:`7096`)::
|
|
3217
|
+
|
|
3218
|
+
sage: E = EllipticCurve('11a1')
|
|
3219
|
+
sage: phi = E.isogeny(E(5,5))
|
|
3220
|
+
sage: phi.dual().dual() == phi
|
|
3221
|
+
True
|
|
3222
|
+
|
|
3223
|
+
sage: k = GF(103)
|
|
3224
|
+
sage: E = EllipticCurve(k,[11,11])
|
|
3225
|
+
sage: phi = E.isogeny(E(4,4))
|
|
3226
|
+
sage: phi
|
|
3227
|
+
Isogeny of degree 5
|
|
3228
|
+
from Elliptic Curve defined by y^2 = x^3 + 11*x + 11 over Finite Field of size 103
|
|
3229
|
+
to Elliptic Curve defined by y^2 = x^3 + 25*x + 80 over Finite Field of size 103
|
|
3230
|
+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
|
|
3231
|
+
sage: phi = WeierstrassIsomorphism(phi.codomain(),(5,0,1,2)) * phi
|
|
3232
|
+
sage: phi.dual().dual() == phi
|
|
3233
|
+
True
|
|
3234
|
+
|
|
3235
|
+
sage: E = EllipticCurve(GF(103),[1,0,0,1,-1])
|
|
3236
|
+
sage: phi = E.isogeny(E(60,85))
|
|
3237
|
+
sage: phi.dual()
|
|
3238
|
+
Isogeny of degree 7
|
|
3239
|
+
from Elliptic Curve defined by y^2 + x*y = x^3 + 84*x + 34 over Finite Field of size 103
|
|
3240
|
+
to Elliptic Curve defined by y^2 + x*y = x^3 + x + 102 over Finite Field of size 103
|
|
3241
|
+
|
|
3242
|
+
Check that :issue:`17293` is fixed::
|
|
3243
|
+
|
|
3244
|
+
sage: # needs sage.rings.number_field
|
|
3245
|
+
sage: k.<s> = QuadraticField(2)
|
|
3246
|
+
sage: E = EllipticCurve(k, [-3*s*(4 + 5*s), 2*s*(2 + 14*s + 11*s^2)])
|
|
3247
|
+
sage: phi = E.isogenies_prime_degree(3)[0]
|
|
3248
|
+
sage: (-phi).dual() == -phi.dual()
|
|
3249
|
+
True
|
|
3250
|
+
sage: phi._EllipticCurveIsogeny__clear_cached_values() # forget the dual
|
|
3251
|
+
sage: -phi.dual() == (-phi).dual()
|
|
3252
|
+
True
|
|
3253
|
+
|
|
3254
|
+
Check that :issue:`37168` is fixed::
|
|
3255
|
+
|
|
3256
|
+
sage: R.<x> = GF(23)[]
|
|
3257
|
+
sage: F.<a> = FiniteField(23^2, modulus=x^2-x+1)
|
|
3258
|
+
sage: E0 = EllipticCurve(F, (0, 1))
|
|
3259
|
+
sage: E1 = EllipticCurve(F, (8, 1))
|
|
3260
|
+
sage: phi = E0.isogeny(kernel=E0((a, 0)), codomain=E1)
|
|
3261
|
+
sage: phi.dual()
|
|
3262
|
+
Isogeny of degree 2
|
|
3263
|
+
from Elliptic Curve defined by y^2 = x^3 + 8*x + 1 over Finite Field in a of size 23^2
|
|
3264
|
+
to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 23^2
|
|
3265
|
+
"""
|
|
3266
|
+
if self.__base_field.characteristic() in (2, 3):
|
|
3267
|
+
raise NotImplementedError("computation of dual isogenies not yet implemented in characteristics 2 and 3")
|
|
3268
|
+
|
|
3269
|
+
if self.__dual is not None:
|
|
3270
|
+
return self.__dual
|
|
3271
|
+
|
|
3272
|
+
# trac 7096
|
|
3273
|
+
E1, E2pr, _, _ = compute_intermediate_curves(self.codomain(), self.domain())
|
|
3274
|
+
|
|
3275
|
+
F = self.__base_field
|
|
3276
|
+
d = self._degree
|
|
3277
|
+
|
|
3278
|
+
from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
|
|
3279
|
+
|
|
3280
|
+
if F(d) == 0: # inseparable dual!
|
|
3281
|
+
p = F.characteristic()
|
|
3282
|
+
k = d.valuation(p)
|
|
3283
|
+
|
|
3284
|
+
from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius
|
|
3285
|
+
frob = EllipticCurveHom_frobenius(self._codomain, k)
|
|
3286
|
+
|
|
3287
|
+
dsep = d // p**k
|
|
3288
|
+
if dsep > 1:
|
|
3289
|
+
#TODO: We could also use resultants here; this is much
|
|
3290
|
+
# faster in some cases (but seems worse in general).
|
|
3291
|
+
# Presumably there should be a wrapper function that
|
|
3292
|
+
# decides on the fly which method to use.
|
|
3293
|
+
# Eventually this should become a .separable_part() method.
|
|
3294
|
+
|
|
3295
|
+
f = self.kernel_polynomial()
|
|
3296
|
+
|
|
3297
|
+
psi = self._domain.division_polynomial(p)
|
|
3298
|
+
mu_num = self._domain._multiple_x_numerator(p)
|
|
3299
|
+
mu_den = self._domain._multiple_x_denominator(p)
|
|
3300
|
+
|
|
3301
|
+
for _ in range(k):
|
|
3302
|
+
f //= f.gcd(psi)
|
|
3303
|
+
S = f.parent().quotient_ring(f)
|
|
3304
|
+
mu = S(mu_num) / S(mu_den)
|
|
3305
|
+
f = mu.minpoly()
|
|
3306
|
+
|
|
3307
|
+
sep = self._domain.isogeny(f, codomain=frob.codomain()).dual()
|
|
3308
|
+
|
|
3309
|
+
else:
|
|
3310
|
+
sep = frob.codomain().isomorphism_to(self._domain)
|
|
3311
|
+
|
|
3312
|
+
phi_hat = EllipticCurveHom_composite.from_factors([frob, sep])
|
|
3313
|
+
|
|
3314
|
+
from sage.schemes.elliptic_curves.hom import find_post_isomorphism
|
|
3315
|
+
mult = self._domain.scalar_multiplication(d)
|
|
3316
|
+
rhs = phi_hat * self
|
|
3317
|
+
corr = find_post_isomorphism(mult, rhs)
|
|
3318
|
+
self.__dual = corr * phi_hat
|
|
3319
|
+
return self.__dual
|
|
3320
|
+
|
|
3321
|
+
else:
|
|
3322
|
+
# trac 7096
|
|
3323
|
+
# this should take care of the case when the isogeny is
|
|
3324
|
+
# not normalized.
|
|
3325
|
+
u = self.scaling_factor()
|
|
3326
|
+
E2 = E2pr.change_weierstrass_model(u/F(d), 0, 0, 0)
|
|
3327
|
+
|
|
3328
|
+
phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
|
|
3329
|
+
# assert phi_hat.scaling_factor() == 1
|
|
3330
|
+
|
|
3331
|
+
for pre_iso in self._codomain.isomorphisms(E1):
|
|
3332
|
+
for post_iso in E2.isomorphisms(self._domain):
|
|
3333
|
+
sc = u * pre_iso.scaling_factor() * post_iso.scaling_factor()
|
|
3334
|
+
if sc == d:
|
|
3335
|
+
break
|
|
3336
|
+
else:
|
|
3337
|
+
continue
|
|
3338
|
+
break
|
|
3339
|
+
else:
|
|
3340
|
+
raise RuntimeError("bug in dual()")
|
|
3341
|
+
|
|
3342
|
+
phi_hat._set_pre_isomorphism(pre_iso)
|
|
3343
|
+
phi_hat._set_post_isomorphism(post_iso)
|
|
3344
|
+
phi_hat.__perform_inheritance_housekeeping()
|
|
3345
|
+
return phi_hat
|
|
3346
|
+
|
|
3347
|
+
@staticmethod
|
|
3348
|
+
def _composition_impl(left, right):
|
|
3349
|
+
r"""
|
|
3350
|
+
Return the composition of an ``EllipticCurveIsogeny``
|
|
3351
|
+
with another elliptic-curve morphism.
|
|
3352
|
+
|
|
3353
|
+
Called by :meth:`EllipticCurveHom._composition_`.
|
|
3354
|
+
|
|
3355
|
+
EXAMPLES::
|
|
3356
|
+
|
|
3357
|
+
sage: E = EllipticCurve(GF(127), [5,2])
|
|
3358
|
+
sage: phi = E.isogeny(E.lift_x(47)); E2 = phi.codomain()
|
|
3359
|
+
sage: iso1 = E.change_weierstrass_model(1,1,1,1).isomorphism_to(E)
|
|
3360
|
+
sage: iso2 = E2.isomorphism_to(E2.change_weierstrass_model(39,0,0,0))
|
|
3361
|
+
sage: phi * iso1 # indirect doctest
|
|
3362
|
+
Isogeny of degree 11
|
|
3363
|
+
from Elliptic Curve defined by y^2 + 2*x*y + 2*y = x^3 + 2*x^2 + 6*x + 7 over Finite Field of size 127
|
|
3364
|
+
to Elliptic Curve defined by y^2 = x^3 + 37*x + 85 over Finite Field of size 127
|
|
3365
|
+
sage: iso2 * phi # indirect doctest
|
|
3366
|
+
Isogeny of degree 11
|
|
3367
|
+
from Elliptic Curve defined by y^2 = x^3 + 5*x + 2 over Finite Field of size 127
|
|
3368
|
+
to Elliptic Curve defined by y^2 = x^3 + 117*x + 58 over Finite Field of size 127
|
|
3369
|
+
sage: iso2 * phi * iso1 # indirect doctest
|
|
3370
|
+
Isogeny of degree 11
|
|
3371
|
+
from Elliptic Curve defined by y^2 + 2*x*y + 2*y = x^3 + 2*x^2 + 6*x + 7 over Finite Field of size 127
|
|
3372
|
+
to Elliptic Curve defined by y^2 = x^3 + 117*x + 58 over Finite Field of size 127
|
|
3373
|
+
|
|
3374
|
+
TESTS:
|
|
3375
|
+
|
|
3376
|
+
We should return ``NotImplemented`` when passed a combination of
|
|
3377
|
+
elliptic-curve morphism types that we don't handle here::
|
|
3378
|
+
|
|
3379
|
+
sage: phi._composition_impl(iso1, iso1**-1)
|
|
3380
|
+
NotImplemented
|
|
3381
|
+
"""
|
|
3382
|
+
if isinstance(left, WeierstrassIsomorphism) and isinstance(right, EllipticCurveIsogeny):
|
|
3383
|
+
result = deepcopy(right)
|
|
3384
|
+
result._set_post_isomorphism(left)
|
|
3385
|
+
return result
|
|
3386
|
+
|
|
3387
|
+
if isinstance(left, EllipticCurveIsogeny) and isinstance(right, WeierstrassIsomorphism):
|
|
3388
|
+
assert isinstance(left, EllipticCurveIsogeny)
|
|
3389
|
+
result = deepcopy(left)
|
|
3390
|
+
result._set_pre_isomorphism(right)
|
|
3391
|
+
return result
|
|
3392
|
+
|
|
3393
|
+
return NotImplemented
|
|
3394
|
+
|
|
3395
|
+
|
|
3396
|
+
def compute_isogeny_bmss(E1, E2, l):
|
|
3397
|
+
r"""
|
|
3398
|
+
Compute the kernel polynomial of the unique normalized isogeny
|
|
3399
|
+
of degree ``l`` between ``E1`` and ``E2``.
|
|
3400
|
+
|
|
3401
|
+
Both curves must be given in short Weierstrass form, and the
|
|
3402
|
+
characteristic must be either `0` or no smaller than `4l+4`.
|
|
3403
|
+
|
|
3404
|
+
ALGORITHM: [BMSS2006]_, algorithm *fastElkies'*.
|
|
3405
|
+
|
|
3406
|
+
EXAMPLES::
|
|
3407
|
+
|
|
3408
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_bmss
|
|
3409
|
+
sage: E1 = EllipticCurve(GF(167), [153, 112])
|
|
3410
|
+
sage: E2 = EllipticCurve(GF(167), [56, 40])
|
|
3411
|
+
sage: compute_isogeny_bmss(E1, E2, 13)
|
|
3412
|
+
x^6 + 139*x^5 + 73*x^4 + 139*x^3 + 120*x^2 + 88*x
|
|
3413
|
+
"""
|
|
3414
|
+
# Original author of this function: Rémy Oudompheng.
|
|
3415
|
+
# https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/isogeny_weber/isogenies.py#L272-L332
|
|
3416
|
+
# Released under the MIT license: https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/LICENSE
|
|
3417
|
+
# Slightly adjusted for inclusion in the Sage library.
|
|
3418
|
+
if E1.a1() or E1.a2() or E1.a3():
|
|
3419
|
+
raise ValueError('E1 must be a short Weierstrass curve')
|
|
3420
|
+
if E2.a1() or E2.a2() or E2.a3():
|
|
3421
|
+
raise ValueError('E2 must be a short Weierstrass curve')
|
|
3422
|
+
char = E1.base_ring().characteristic()
|
|
3423
|
+
if char != 0 and char < 4*l + 4:
|
|
3424
|
+
raise ValueError('characteristic must be at least 4*degree+4')
|
|
3425
|
+
Rx, x = E1.base_ring()["x"].objgen()
|
|
3426
|
+
# Compute C = 1/(1 + Ax^4 + Bx^6) mod x^4l
|
|
3427
|
+
A, B = E1.a4(), E1.a6()
|
|
3428
|
+
C = (1 + A * x**4 + B * x**6).inverse_series_trunc(4 * l)
|
|
3429
|
+
# Solve differential equation
|
|
3430
|
+
# The number of terms doubles at each iteration.
|
|
3431
|
+
# S'^2 = G(x,S) = (1 + A2 S^4 + B2 S^6) / (1 + Ax^4 + Bx^6)
|
|
3432
|
+
# S = x + O(x^2)
|
|
3433
|
+
A2, B2 = E2.a4(), E2.a6()
|
|
3434
|
+
S = x + (A2 - A) / 10 * x**5 + (B2 - B) / 14 * x**7
|
|
3435
|
+
sprec = 8
|
|
3436
|
+
while sprec < 4 * l:
|
|
3437
|
+
assert sprec % 2 == 0
|
|
3438
|
+
sprec = min(sprec, 2 * l)
|
|
3439
|
+
# s1 => s1 + x^k s2
|
|
3440
|
+
# 2 s1' s2' - dG/dS(x, s1) s2 = G(x, s1) - s1'2
|
|
3441
|
+
s1 = S
|
|
3442
|
+
ds1 = s1.derivative()
|
|
3443
|
+
s1pows = [1, s1]
|
|
3444
|
+
while len(s1pows) < 7:
|
|
3445
|
+
s1pows.append(s1._mul_trunc_(s1pows[-1], 2 * sprec))
|
|
3446
|
+
GS = C * (1 + A2 * s1pows[4] + B2 * s1pows[6])
|
|
3447
|
+
dGS = C * (4 * A2 * s1pows[3] + 6 * B2 * s1pows[5])
|
|
3448
|
+
# s2' = (dGS / 2s1') s2 + (G(x, s1) - s1'2) / (2s1')
|
|
3449
|
+
denom = (2 * ds1).inverse_series_trunc(2 * sprec)
|
|
3450
|
+
a = dGS._mul_trunc_(denom, 2 * sprec)
|
|
3451
|
+
b = (GS - ds1**2)._mul_trunc_(denom, 2 * sprec)
|
|
3452
|
+
s2 = a.add_bigoh(2 * sprec).solve_linear_de(prec=2 * sprec, b=b, f0=0)
|
|
3453
|
+
S = s1 + Rx(s2)
|
|
3454
|
+
sprec = 2 * sprec
|
|
3455
|
+
# Reconstruct:
|
|
3456
|
+
# S = x * T(x^2)
|
|
3457
|
+
# Compute U = 1/T^2
|
|
3458
|
+
# Reconstruct N(1/x) / D(1/x) = U
|
|
3459
|
+
T = Rx([S[2 * i + 1] for i in range(2 * l)])
|
|
3460
|
+
U = T._mul_trunc_(T, 2 * l).inverse_series_trunc(2 * l)
|
|
3461
|
+
_, Q = Rx(U).rational_reconstruction(x ** (2 * l), l, l)
|
|
3462
|
+
Q = Q.add_bigoh((l + 1) // 2)
|
|
3463
|
+
if not Q.is_square():
|
|
3464
|
+
raise ValueError(f"the two curves are not linked by a cyclic normalized isogeny of degree {l}")
|
|
3465
|
+
Q = Q.sqrt()
|
|
3466
|
+
ker = Rx(Q).reverse(degree=l//2)
|
|
3467
|
+
return ker.monic()
|
|
3468
|
+
|
|
3469
|
+
|
|
3470
|
+
def compute_isogeny_stark(E1, E2, ell):
|
|
3471
|
+
r"""
|
|
3472
|
+
Return the kernel polynomial of an isogeny of degree ``ell``
|
|
3473
|
+
from ``E1`` to ``E2``.
|
|
3474
|
+
|
|
3475
|
+
INPUT:
|
|
3476
|
+
|
|
3477
|
+
- ``E1`` -- domain elliptic curve in short Weierstrass form
|
|
3478
|
+
|
|
3479
|
+
- ``E2`` -- codomain elliptic curve in short Weierstrass form
|
|
3480
|
+
|
|
3481
|
+
- ``ell`` -- the degree of an isogeny from ``E1`` to ``E2``
|
|
3482
|
+
|
|
3483
|
+
OUTPUT: the kernel polynomial of an isogeny from ``E1`` to ``E2``
|
|
3484
|
+
|
|
3485
|
+
.. NOTE::
|
|
3486
|
+
|
|
3487
|
+
If there is no degree-``ell``, cyclic, separable, normalized
|
|
3488
|
+
isogeny from ``E1`` to ``E2``, a :exc:`ValueError` will be
|
|
3489
|
+
raised.
|
|
3490
|
+
|
|
3491
|
+
ALGORITHM:
|
|
3492
|
+
|
|
3493
|
+
This function uses Stark's algorithm as presented in Section 6.2
|
|
3494
|
+
of [BMSS2006]_.
|
|
3495
|
+
|
|
3496
|
+
.. NOTE::
|
|
3497
|
+
|
|
3498
|
+
As published in [BMSS2006]_, the algorithm is incorrect, and a
|
|
3499
|
+
correct version (with slightly different notation) can be found
|
|
3500
|
+
in [Mo2009]_. The algorithm originates in [Sta1973]_.
|
|
3501
|
+
|
|
3502
|
+
EXAMPLES::
|
|
3503
|
+
|
|
3504
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark, compute_sequence_of_maps
|
|
3505
|
+
|
|
3506
|
+
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
|
|
3507
|
+
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
|
|
3508
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3509
|
+
sage: E2 = phi.codomain()
|
|
3510
|
+
sage: isom1, isom2, E1pr, E2pr, ker_poly = compute_sequence_of_maps(E, E2, 11)
|
|
3511
|
+
sage: compute_isogeny_stark(E1pr, E2pr, 11)
|
|
3512
|
+
x^10 + 37*x^9 + 53*x^8 + 66*x^7 + 66*x^6 + 17*x^5 + 57*x^4 + 6*x^3 + 89*x^2 + 53*x + 8
|
|
3513
|
+
|
|
3514
|
+
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
|
|
3515
|
+
sage: R.<x> = GF(37)[]
|
|
3516
|
+
sage: f = (x + 14) * (x + 30)
|
|
3517
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3518
|
+
sage: E2 = phi.codomain()
|
|
3519
|
+
sage: compute_isogeny_stark(E, E2, 5)
|
|
3520
|
+
x^4 + 14*x^3 + x^2 + 34*x + 21
|
|
3521
|
+
sage: f**2
|
|
3522
|
+
x^4 + 14*x^3 + x^2 + 34*x + 21
|
|
3523
|
+
|
|
3524
|
+
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
|
|
3525
|
+
sage: R.<x> = QQ[]
|
|
3526
|
+
sage: f = x
|
|
3527
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3528
|
+
sage: E2 = phi.codomain()
|
|
3529
|
+
sage: compute_isogeny_stark(E, E2, 2)
|
|
3530
|
+
x
|
|
3531
|
+
|
|
3532
|
+
TESTS:
|
|
3533
|
+
|
|
3534
|
+
Check for :issue:`21883`::
|
|
3535
|
+
|
|
3536
|
+
sage: E1 = EllipticCurve([0,1])
|
|
3537
|
+
sage: E2 = EllipticCurve([0,-27])
|
|
3538
|
+
sage: E1.isogeny(None, E2, degree=3)
|
|
3539
|
+
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Rational Field to Elliptic Curve defined by y^2 = x^3 - 27 over Rational Field
|
|
3540
|
+
"""
|
|
3541
|
+
K = E1.base_field()
|
|
3542
|
+
R, x = PolynomialRing(K, 'x').objgen()
|
|
3543
|
+
|
|
3544
|
+
wp1 = E1.weierstrass_p(prec=4*ell+4) #BMSS2006 claim 2*ell is enough, but it is not M09
|
|
3545
|
+
wp2 = E2.weierstrass_p(prec=4*ell+4)
|
|
3546
|
+
|
|
3547
|
+
# viewed them as power series in Z = z^2
|
|
3548
|
+
Z = LaurentSeriesRing(K, 'Z').gen()
|
|
3549
|
+
pe1 = pe2 = 1/Z
|
|
3550
|
+
for i in range(2*ell + 1):
|
|
3551
|
+
pe1 += wp1[2*i] * Z**i
|
|
3552
|
+
pe2 += wp2[2*i] * Z**i
|
|
3553
|
+
pe1 = pe1.add_bigoh(2*ell+3)
|
|
3554
|
+
pe2 = pe2.add_bigoh(2*ell+3)
|
|
3555
|
+
|
|
3556
|
+
n = 1
|
|
3557
|
+
q = [R.one(), R.zero()]
|
|
3558
|
+
T = pe2
|
|
3559
|
+
|
|
3560
|
+
while q[n].degree() < ell-1:
|
|
3561
|
+
n += 1
|
|
3562
|
+
a_n = 0
|
|
3563
|
+
r = -T.valuation()
|
|
3564
|
+
while 0 <= r:
|
|
3565
|
+
t_r = T[-r]
|
|
3566
|
+
a_n = a_n + t_r * x**r
|
|
3567
|
+
T = T - t_r*pe1**r
|
|
3568
|
+
r = -T.valuation()
|
|
3569
|
+
|
|
3570
|
+
q_n = a_n*q[n-1] + q[n-2]
|
|
3571
|
+
q.append(q_n)
|
|
3572
|
+
|
|
3573
|
+
if n == ell+1 or T == 0:
|
|
3574
|
+
if T == 0 or T.valuation() < 2:
|
|
3575
|
+
raise ValueError(f"the two curves are not linked by a cyclic normalized isogeny of degree {ell}")
|
|
3576
|
+
break
|
|
3577
|
+
|
|
3578
|
+
T = 1/T
|
|
3579
|
+
|
|
3580
|
+
qn = q[n]
|
|
3581
|
+
qn /= qn.leading_coefficient()
|
|
3582
|
+
return qn
|
|
3583
|
+
|
|
3584
|
+
|
|
3585
|
+
def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm=None):
|
|
3586
|
+
r"""
|
|
3587
|
+
Return the kernel polynomial of a cyclic, separable, normalized
|
|
3588
|
+
isogeny of degree ``ell`` from ``E1`` to ``E2``.
|
|
3589
|
+
|
|
3590
|
+
INPUT:
|
|
3591
|
+
|
|
3592
|
+
- ``E1`` -- domain elliptic curve in short Weierstrass form
|
|
3593
|
+
|
|
3594
|
+
- ``E2`` -- codomain elliptic curve in short Weierstrass form
|
|
3595
|
+
|
|
3596
|
+
- ``ell`` -- the degree of an isogeny from ``E1`` to ``E2``
|
|
3597
|
+
|
|
3598
|
+
- ``algorithm`` -- ``None`` (default, choose automatically) or
|
|
3599
|
+
``'bmss'`` (:func:`compute_isogeny_bmss`) or
|
|
3600
|
+
``'stark'`` (:func:`compute_isogeny_stark`)
|
|
3601
|
+
|
|
3602
|
+
OUTPUT: the kernel polynomial of an isogeny from ``E1`` to ``E2``
|
|
3603
|
+
|
|
3604
|
+
.. NOTE::
|
|
3605
|
+
|
|
3606
|
+
If there is no degree-``ell``, cyclic, separable, normalized
|
|
3607
|
+
isogeny from ``E1`` to ``E2``, a :exc:`ValueError` will be
|
|
3608
|
+
raised.
|
|
3609
|
+
|
|
3610
|
+
EXAMPLES::
|
|
3611
|
+
|
|
3612
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial
|
|
3613
|
+
|
|
3614
|
+
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
|
|
3615
|
+
sage: R.<x> = GF(37)[]
|
|
3616
|
+
sage: f = (x + 14) * (x + 30)
|
|
3617
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3618
|
+
sage: E2 = phi.codomain()
|
|
3619
|
+
sage: compute_isogeny_kernel_polynomial(E, E2, 5)
|
|
3620
|
+
x^2 + 7*x + 13
|
|
3621
|
+
sage: f
|
|
3622
|
+
x^2 + 7*x + 13
|
|
3623
|
+
|
|
3624
|
+
sage: # needs sage.rings.number_field
|
|
3625
|
+
sage: R.<x> = QQ[]
|
|
3626
|
+
sage: K.<i> = NumberField(x^2 + 1)
|
|
3627
|
+
sage: E = EllipticCurve(K, [0,0,0,1,0])
|
|
3628
|
+
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
|
|
3629
|
+
sage: compute_isogeny_kernel_polynomial(E, E2, 4)
|
|
3630
|
+
x^3 + x
|
|
3631
|
+
|
|
3632
|
+
TESTS:
|
|
3633
|
+
|
|
3634
|
+
Check that :meth:`Polynomial.radical` is doing the right thing for us::
|
|
3635
|
+
|
|
3636
|
+
sage: E = EllipticCurve(GF(37), [0,0,0,1,8])
|
|
3637
|
+
sage: R.<x> = GF(37)[]
|
|
3638
|
+
sage: f = (x + 10) * (x + 12) * (x + 16)
|
|
3639
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3640
|
+
sage: E2 = phi.codomain()
|
|
3641
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_stark
|
|
3642
|
+
sage: ker_poly = compute_isogeny_stark(E, E2, 7); ker_poly
|
|
3643
|
+
x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16
|
|
3644
|
+
sage: ker_poly.factor()
|
|
3645
|
+
(x + 10)^2 * (x + 12)^2 * (x + 16)^2
|
|
3646
|
+
sage: poly = ker_poly.radical(); poly
|
|
3647
|
+
x^3 + x^2 + 28*x + 33
|
|
3648
|
+
sage: poly.factor()
|
|
3649
|
+
(x + 10) * (x + 12) * (x + 16)
|
|
3650
|
+
"""
|
|
3651
|
+
if algorithm is None:
|
|
3652
|
+
char = E1.base_ring().characteristic()
|
|
3653
|
+
if char != 0 and char < 4*ell + 4:
|
|
3654
|
+
raise NotImplementedError(f'no algorithm for computing kernel polynomial from domain and codomain is implemented for degree {ell} and characteristic {char}')
|
|
3655
|
+
algorithm = 'stark' if ell < 10 else 'bmss'
|
|
3656
|
+
|
|
3657
|
+
if algorithm == 'bmss':
|
|
3658
|
+
return compute_isogeny_bmss(E1, E2, ell)
|
|
3659
|
+
if algorithm == 'stark':
|
|
3660
|
+
return compute_isogeny_stark(E1, E2, ell).radical()
|
|
3661
|
+
|
|
3662
|
+
raise NotImplementedError(f'unknown algorithm {algorithm}')
|
|
3663
|
+
|
|
3664
|
+
|
|
3665
|
+
def compute_intermediate_curves(E1, E2):
|
|
3666
|
+
r"""
|
|
3667
|
+
Return intermediate curves and isomorphisms.
|
|
3668
|
+
|
|
3669
|
+
.. NOTE::
|
|
3670
|
+
|
|
3671
|
+
This is used to compute `\wp` functions from the short
|
|
3672
|
+
Weierstrass model more easily.
|
|
3673
|
+
|
|
3674
|
+
.. WARNING::
|
|
3675
|
+
|
|
3676
|
+
The base field must be of characteristic not equal to `2` or `3`.
|
|
3677
|
+
|
|
3678
|
+
INPUT:
|
|
3679
|
+
|
|
3680
|
+
- ``E1``, ``E2`` -- elliptic curves
|
|
3681
|
+
|
|
3682
|
+
OUTPUT:
|
|
3683
|
+
|
|
3684
|
+
A tuple (``pre_isomorphism``, ``post_isomorphism``,
|
|
3685
|
+
``intermediate_domain``, ``intermediate_codomain``) where:
|
|
3686
|
+
|
|
3687
|
+
- ``intermediate_domain`` is a short Weierstrass curve isomorphic
|
|
3688
|
+
to ``E1``;
|
|
3689
|
+
|
|
3690
|
+
- ``intermediate_codomain`` is a short Weierstrass curve isomorphic
|
|
3691
|
+
to ``E2``;
|
|
3692
|
+
|
|
3693
|
+
- ``pre_isomorphism`` is a normalized isomorphism from ``E1`` to
|
|
3694
|
+
``intermediate_domain``;
|
|
3695
|
+
|
|
3696
|
+
- ``post_isomorphism`` is a normalized isomorphism from
|
|
3697
|
+
``intermediate_codomain`` to ``E2``.
|
|
3698
|
+
|
|
3699
|
+
EXAMPLES::
|
|
3700
|
+
|
|
3701
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_intermediate_curves
|
|
3702
|
+
sage: E = EllipticCurve(GF(83), [1,0,1,1,0])
|
|
3703
|
+
sage: R.<x> = GF(83)[]; f = x + 24
|
|
3704
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3705
|
+
sage: E2 = phi.codomain()
|
|
3706
|
+
sage: compute_intermediate_curves(E, E2)
|
|
3707
|
+
(Elliptic Curve defined by y^2 = x^3 + 62*x + 74 over Finite Field of size 83,
|
|
3708
|
+
Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83,
|
|
3709
|
+
Elliptic-curve morphism:
|
|
3710
|
+
From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x
|
|
3711
|
+
over Finite Field of size 83
|
|
3712
|
+
To: Elliptic Curve defined by y^2 = x^3 + 62*x + 74
|
|
3713
|
+
over Finite Field of size 83
|
|
3714
|
+
Via: (u,r,s,t) = (1, 76, 41, 3),
|
|
3715
|
+
Elliptic-curve morphism:
|
|
3716
|
+
From: Elliptic Curve defined by y^2 = x^3 + 65*x + 69
|
|
3717
|
+
over Finite Field of size 83
|
|
3718
|
+
To: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 16
|
|
3719
|
+
over Finite Field of size 83
|
|
3720
|
+
Via: (u,r,s,t) = (1, 7, 42, 42))
|
|
3721
|
+
|
|
3722
|
+
sage: # needs sage.rings.number_field
|
|
3723
|
+
sage: R.<x> = QQ[]
|
|
3724
|
+
sage: K.<i> = NumberField(x^2 + 1)
|
|
3725
|
+
sage: E = EllipticCurve(K, [0,0,0,1,0])
|
|
3726
|
+
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
|
|
3727
|
+
sage: compute_intermediate_curves(E, E2)
|
|
3728
|
+
(Elliptic Curve defined by y^2 = x^3 + x
|
|
3729
|
+
over Number Field in i with defining polynomial x^2 + 1,
|
|
3730
|
+
Elliptic Curve defined by y^2 = x^3 + 16*x
|
|
3731
|
+
over Number Field in i with defining polynomial x^2 + 1,
|
|
3732
|
+
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x
|
|
3733
|
+
over Number Field in i with defining polynomial x^2 + 1
|
|
3734
|
+
Via: (u,r,s,t) = (1, 0, 0, 0),
|
|
3735
|
+
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + 16*x
|
|
3736
|
+
over Number Field in i with defining polynomial x^2 + 1
|
|
3737
|
+
Via: (u,r,s,t) = (1, 0, 0, 0))
|
|
3738
|
+
"""
|
|
3739
|
+
if E1.base_ring().characteristic() in (2, 3):
|
|
3740
|
+
raise NotImplementedError("compute_intermediate_curves is only defined for characteristics not 2 or 3")
|
|
3741
|
+
|
|
3742
|
+
# We cannot just use
|
|
3743
|
+
# E1w = E1.short_weierstrass_model()
|
|
3744
|
+
# E2w = E2.short_weierstrass_model()
|
|
3745
|
+
# as the resulting isomorphisms would not be normalised (u=1)
|
|
3746
|
+
|
|
3747
|
+
c4, c6 = E1.c_invariants()
|
|
3748
|
+
E1w = EllipticCurve([0, 0, 0, -c4/48, -c6/864])
|
|
3749
|
+
c4, c6 = E2.c_invariants()
|
|
3750
|
+
E2w = EllipticCurve([0, 0, 0, -c4/48, -c6/864])
|
|
3751
|
+
|
|
3752
|
+
# We cannot even just use pre_iso = E1.isomorphism_to(E1w) since
|
|
3753
|
+
# it may have u=-1; similarly for E2
|
|
3754
|
+
|
|
3755
|
+
urst = [w for w in _isomorphisms(E1, E1w) if w[0] == 1][0]
|
|
3756
|
+
pre_iso = WeierstrassIsomorphism(E1, urst, E1w)
|
|
3757
|
+
urst = [w for w in _isomorphisms(E2w, E2) if w[0] == 1][0]
|
|
3758
|
+
post_iso = WeierstrassIsomorphism(E2w, urst, E2)
|
|
3759
|
+
return E1w, E2w, pre_iso, post_iso
|
|
3760
|
+
|
|
3761
|
+
|
|
3762
|
+
def compute_sequence_of_maps(E1, E2, ell):
|
|
3763
|
+
r"""
|
|
3764
|
+
Return intermediate curves, isomorphisms and kernel polynomial.
|
|
3765
|
+
|
|
3766
|
+
INPUT:
|
|
3767
|
+
|
|
3768
|
+
- ``E1``, ``E2`` -- elliptic curves
|
|
3769
|
+
|
|
3770
|
+
- ``ell`` -- a prime such that there is a degree-``ell`` separable
|
|
3771
|
+
normalized isogeny from ``E1`` to ``E2``
|
|
3772
|
+
|
|
3773
|
+
OUTPUT:
|
|
3774
|
+
|
|
3775
|
+
A tuple (``pre_isom``, ``post_isom``, ``E1pr``, ``E2pr``,
|
|
3776
|
+
``ker_poly``) where:
|
|
3777
|
+
|
|
3778
|
+
- ``E1pr`` is an elliptic curve in short Weierstrass form
|
|
3779
|
+
isomorphic to ``E1``;
|
|
3780
|
+
|
|
3781
|
+
- ``E2pr`` is an elliptic curve in short Weierstrass form
|
|
3782
|
+
isomorphic to ``E2``;
|
|
3783
|
+
|
|
3784
|
+
- ``pre_isom`` is a normalized isomorphism from ``E1`` to ``E1pr``;
|
|
3785
|
+
|
|
3786
|
+
- ``post_isom`` is a normalized isomorphism from ``E2pr`` to ``E2``;
|
|
3787
|
+
|
|
3788
|
+
- ``ker_poly`` is the kernel polynomial of an ``ell``-isogeny from
|
|
3789
|
+
``E1pr`` to ``E2pr``.
|
|
3790
|
+
|
|
3791
|
+
EXAMPLES::
|
|
3792
|
+
|
|
3793
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_sequence_of_maps
|
|
3794
|
+
sage: E = EllipticCurve('11a1')
|
|
3795
|
+
sage: R.<x> = QQ[]; f = x^2 - 21*x + 80
|
|
3796
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3797
|
+
sage: E2 = phi.codomain()
|
|
3798
|
+
sage: compute_sequence_of_maps(E, E2, 5)
|
|
3799
|
+
(Elliptic-curve morphism:
|
|
3800
|
+
From: Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20
|
|
3801
|
+
over Rational Field
|
|
3802
|
+
To: Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108
|
|
3803
|
+
over Rational Field
|
|
3804
|
+
Via: (u,r,s,t) = (1, 1/3, 0, -1/2),
|
|
3805
|
+
Elliptic-curve morphism:
|
|
3806
|
+
From: Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108
|
|
3807
|
+
over Rational Field
|
|
3808
|
+
To: Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580
|
|
3809
|
+
over Rational Field
|
|
3810
|
+
Via: (u,r,s,t) = (1, -1/3, 0, 1/2),
|
|
3811
|
+
Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field,
|
|
3812
|
+
Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field,
|
|
3813
|
+
x^2 - 61/3*x + 658/9)
|
|
3814
|
+
|
|
3815
|
+
sage: # needs sage.rings.number_field
|
|
3816
|
+
sage: K.<i> = NumberField(x^2 + 1)
|
|
3817
|
+
sage: E = EllipticCurve(K, [0,0,0,1,0])
|
|
3818
|
+
sage: E2 = EllipticCurve(K, [0,0,0,16,0])
|
|
3819
|
+
sage: compute_sequence_of_maps(E, E2, 4)
|
|
3820
|
+
(Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x
|
|
3821
|
+
over Number Field in i with defining polynomial x^2 + 1
|
|
3822
|
+
Via: (u,r,s,t) = (1, 0, 0, 0),
|
|
3823
|
+
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + 16*x
|
|
3824
|
+
over Number Field in i with defining polynomial x^2 + 1
|
|
3825
|
+
Via: (u,r,s,t) = (1, 0, 0, 0),
|
|
3826
|
+
Elliptic Curve defined by y^2 = x^3 + x
|
|
3827
|
+
over Number Field in i with defining polynomial x^2 + 1,
|
|
3828
|
+
Elliptic Curve defined by y^2 = x^3 + 16*x
|
|
3829
|
+
over Number Field in i with defining polynomial x^2 + 1,
|
|
3830
|
+
x^3 + x)
|
|
3831
|
+
|
|
3832
|
+
sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
|
|
3833
|
+
sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
|
|
3834
|
+
sage: phi = EllipticCurveIsogeny(E, f)
|
|
3835
|
+
sage: E2 = phi.codomain()
|
|
3836
|
+
sage: compute_sequence_of_maps(E, E2, 11)
|
|
3837
|
+
(Elliptic-curve morphism:
|
|
3838
|
+
From: Elliptic Curve defined by y^2 + x*y + y = x^3 + x
|
|
3839
|
+
over Finite Field of size 97
|
|
3840
|
+
To: Elliptic Curve defined by y^2 = x^3 + 52*x + 31
|
|
3841
|
+
over Finite Field of size 97
|
|
3842
|
+
Via: (u,r,s,t) = (1, 8, 48, 44),
|
|
3843
|
+
Elliptic-curve morphism:
|
|
3844
|
+
From: Elliptic Curve defined by y^2 = x^3 + 41*x + 66
|
|
3845
|
+
over Finite Field of size 97
|
|
3846
|
+
To: Elliptic Curve defined by y^2 + x*y + y = x^3 + 87*x + 26
|
|
3847
|
+
over Finite Field of size 97
|
|
3848
|
+
Via: (u,r,s,t) = (1, 89, 49, 49),
|
|
3849
|
+
Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
|
|
3850
|
+
Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97,
|
|
3851
|
+
x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
|
|
3852
|
+
"""
|
|
3853
|
+
E1pr, E2pr, pre_isom, post_isom = compute_intermediate_curves(E1, E2)
|
|
3854
|
+
|
|
3855
|
+
ker_poly = compute_isogeny_kernel_polynomial(E1pr, E2pr, ell)
|
|
3856
|
+
|
|
3857
|
+
return pre_isom, post_isom, E1pr, E2pr, ker_poly
|
|
3858
|
+
|
|
3859
|
+
# Utility functions for manipulating isogeny degree matrices
|
|
3860
|
+
|
|
3861
|
+
|
|
3862
|
+
def fill_isogeny_matrix(M):
|
|
3863
|
+
"""
|
|
3864
|
+
Return a filled isogeny matrix giving all degrees from one giving only prime degrees.
|
|
3865
|
+
|
|
3866
|
+
INPUT:
|
|
3867
|
+
|
|
3868
|
+
- ``M`` -- a square symmetric matrix whose off-diagonal `i`, `j`
|
|
3869
|
+
entry is either a prime `l` if the `i`-th and `j`-th curves
|
|
3870
|
+
have an `l`-isogeny between them, otherwise `0`
|
|
3871
|
+
|
|
3872
|
+
OUTPUT:
|
|
3873
|
+
|
|
3874
|
+
A square matrix with entries `1` on the diagonal, and in
|
|
3875
|
+
general the `i`, `j` entry is `d>0` if `d` is the minimal degree
|
|
3876
|
+
of an isogeny from the `i`-th to the `j`-th curve.
|
|
3877
|
+
|
|
3878
|
+
EXAMPLES::
|
|
3879
|
+
|
|
3880
|
+
sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0],
|
|
3881
|
+
....: [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
|
|
3882
|
+
[0 2 3 3 0 0]
|
|
3883
|
+
[2 0 0 0 3 3]
|
|
3884
|
+
[3 0 0 0 2 0]
|
|
3885
|
+
[3 0 0 0 0 2]
|
|
3886
|
+
[0 3 2 0 0 0]
|
|
3887
|
+
[0 3 0 2 0 0]
|
|
3888
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix
|
|
3889
|
+
sage: fill_isogeny_matrix(M)
|
|
3890
|
+
[ 1 2 3 3 6 6]
|
|
3891
|
+
[ 2 1 6 6 3 3]
|
|
3892
|
+
[ 3 6 1 9 2 18]
|
|
3893
|
+
[ 3 6 9 1 18 2]
|
|
3894
|
+
[ 6 3 2 18 1 9]
|
|
3895
|
+
[ 6 3 18 2 9 1]
|
|
3896
|
+
"""
|
|
3897
|
+
from sage.matrix.constructor import Matrix
|
|
3898
|
+
from sage.rings.infinity import Infinity
|
|
3899
|
+
|
|
3900
|
+
n = M.nrows()
|
|
3901
|
+
M0 = copy(M)
|
|
3902
|
+
for i in range(n):
|
|
3903
|
+
M0[i,i] = 1
|
|
3904
|
+
|
|
3905
|
+
def fix(d):
|
|
3906
|
+
return d if d != 0 else Infinity
|
|
3907
|
+
|
|
3908
|
+
def fix2(d):
|
|
3909
|
+
return d if d != Infinity else 0
|
|
3910
|
+
|
|
3911
|
+
def pr(M1, M2):
|
|
3912
|
+
return Matrix([[fix2(min([fix(M1[i,k]*M2[k,j]) for k in range(n)])) for i in range(n)] for j in range(n)])
|
|
3913
|
+
|
|
3914
|
+
M1 = M0
|
|
3915
|
+
M2 = pr(M0, M1)
|
|
3916
|
+
while M1 != M2:
|
|
3917
|
+
M1 = M2
|
|
3918
|
+
M2 = pr(M0, M1)
|
|
3919
|
+
return M1
|
|
3920
|
+
|
|
3921
|
+
|
|
3922
|
+
def unfill_isogeny_matrix(M):
|
|
3923
|
+
"""
|
|
3924
|
+
Reverses the action of ``fill_isogeny_matrix``.
|
|
3925
|
+
|
|
3926
|
+
INPUT:
|
|
3927
|
+
|
|
3928
|
+
- ``M`` -- a square symmetric matrix of integers
|
|
3929
|
+
|
|
3930
|
+
OUTPUT:
|
|
3931
|
+
|
|
3932
|
+
A square symmetric matrix obtained from ``M`` by
|
|
3933
|
+
replacing non-prime entries with `0`.
|
|
3934
|
+
|
|
3935
|
+
EXAMPLES::
|
|
3936
|
+
|
|
3937
|
+
sage: M = Matrix([[0, 2, 3, 3, 0, 0], [2, 0, 0, 0, 3, 3], [3, 0, 0, 0, 2, 0],
|
|
3938
|
+
....: [3, 0, 0, 0, 0, 2], [0, 3, 2, 0, 0, 0], [0, 3, 0, 2, 0, 0]]); M
|
|
3939
|
+
[0 2 3 3 0 0]
|
|
3940
|
+
[2 0 0 0 3 3]
|
|
3941
|
+
[3 0 0 0 2 0]
|
|
3942
|
+
[3 0 0 0 0 2]
|
|
3943
|
+
[0 3 2 0 0 0]
|
|
3944
|
+
[0 3 0 2 0 0]
|
|
3945
|
+
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix, unfill_isogeny_matrix
|
|
3946
|
+
sage: M1 = fill_isogeny_matrix(M); M1
|
|
3947
|
+
[ 1 2 3 3 6 6]
|
|
3948
|
+
[ 2 1 6 6 3 3]
|
|
3949
|
+
[ 3 6 1 9 2 18]
|
|
3950
|
+
[ 3 6 9 1 18 2]
|
|
3951
|
+
[ 6 3 2 18 1 9]
|
|
3952
|
+
[ 6 3 18 2 9 1]
|
|
3953
|
+
sage: unfill_isogeny_matrix(M1)
|
|
3954
|
+
[0 2 3 3 0 0]
|
|
3955
|
+
[2 0 0 0 3 3]
|
|
3956
|
+
[3 0 0 0 2 0]
|
|
3957
|
+
[3 0 0 0 0 2]
|
|
3958
|
+
[0 3 2 0 0 0]
|
|
3959
|
+
[0 3 0 2 0 0]
|
|
3960
|
+
sage: unfill_isogeny_matrix(M1) == M
|
|
3961
|
+
True
|
|
3962
|
+
"""
|
|
3963
|
+
n = M.nrows()
|
|
3964
|
+
M1 = copy(M)
|
|
3965
|
+
zero = Integer(0)
|
|
3966
|
+
for i in range(n):
|
|
3967
|
+
M1[i,i] = zero
|
|
3968
|
+
for j in range(i):
|
|
3969
|
+
if not M1[i,j].is_prime():
|
|
3970
|
+
M1[i,j] = zero
|
|
3971
|
+
M1[j,i] = zero
|
|
3972
|
+
return M1
|